texttool.cpp

Go to the documentation of this file.
00001 // $Id: texttool.cpp 1656 2006-08-03 12:59:39Z alex $
00002 /* @@tag:xara-cn@@ DO NOT MODIFY THIS LINE
00003 ================================XARAHEADERSTART===========================
00004  
00005                Xara LX, a vector drawing and manipulation program.
00006                     Copyright (C) 1993-2006 Xara Group Ltd.
00007        Copyright on certain contributions may be held in joint with their
00008               respective authors. See AUTHORS file for details.
00009 
00010 LICENSE TO USE AND MODIFY SOFTWARE
00011 ----------------------------------
00012 
00013 This file is part of Xara LX.
00014 
00015 Xara LX is free software; you can redistribute it and/or modify it
00016 under the terms of the GNU General Public License version 2 as published
00017 by the Free Software Foundation.
00018 
00019 Xara LX and its component source files are distributed in the hope
00020 that it will be useful, but WITHOUT ANY WARRANTY; without even the
00021 implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00022 See the GNU General Public License for more details.
00023 
00024 You should have received a copy of the GNU General Public License along
00025 with Xara LX (see the file GPL in the root directory of the
00026 distribution); if not, write to the Free Software Foundation, Inc., 51
00027 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
00028 
00029 
00030 ADDITIONAL RIGHTS
00031 -----------------
00032 
00033 Conditional upon your continuing compliance with the GNU General Public
00034 License described above, Xara Group Ltd grants to you certain additional
00035 rights. 
00036 
00037 The additional rights are to use, modify, and distribute the software
00038 together with the wxWidgets library, the wxXtra library, and the "CDraw"
00039 library and any other such library that any version of Xara LX relased
00040 by Xara Group Ltd requires in order to compile and execute, including
00041 the static linking of that library to XaraLX. In the case of the
00042 "CDraw" library, you may satisfy obligation under the GNU General Public
00043 License to provide source code by providing a binary copy of the library
00044 concerned and a copy of the license accompanying it.
00045 
00046 Nothing in this section restricts any of the rights you have under
00047 the GNU General Public License.
00048 
00049 
00050 SCOPE OF LICENSE
00051 ----------------
00052 
00053 This license applies to this program (XaraLX) and its constituent source
00054 files only, and does not necessarily apply to other Xara products which may
00055 in part share the same code base, and are subject to their own licensing
00056 terms.
00057 
00058 This license does not apply to files in the wxXtra directory, which
00059 are built into a separate library, and are subject to the wxWindows
00060 license contained within that directory in the file "WXXTRA-LICENSE".
00061 
00062 This license does not apply to the binary libraries (if any) within
00063 the "libs" directory, which are subject to a separate license contained
00064 within that directory in the file "LIBS-LICENSE".
00065 
00066 
00067 ARRANGEMENTS FOR CONTRIBUTION OF MODIFICATIONS
00068 ----------------------------------------------
00069 
00070 Subject to the terms of the GNU Public License (see above), you are
00071 free to do whatever you like with your modifications. However, you may
00072 (at your option) wish contribute them to Xara's source tree. You can
00073 find details of how to do this at:
00074   http://www.xaraxtreme.org/developers/
00075 
00076 Prior to contributing your modifications, you will need to complete our
00077 contributor agreement. This can be found at:
00078   http://www.xaraxtreme.org/developers/contribute/
00079 
00080 Please note that Xara will not accept modifications which modify any of
00081 the text between the start and end of this header (marked
00082 XARAHEADERSTART and XARAHEADEREND).
00083 
00084 
00085 MARKS
00086 -----
00087 
00088 Xara, Xara LX, Xara X, Xara X/Xtreme, Xara Xtreme, the Xtreme and Xara
00089 designs are registered or unregistered trademarks, design-marks, and/or
00090 service marks of Xara Group Ltd. All rights in these marks are reserved.
00091 
00092 
00093       Xara Group Ltd, Gaddesden Place, Hemel Hempstead, HP2 6EX, UK.
00094                         http://www.xara.com/
00095 
00096 =================================XARAHEADEREND============================
00097  */
00098 // Implementation of the text tool
00099 
00100 /*
00101 */
00102 
00103 #include "camtypes.h"
00104 #include "texttool.h"   
00105 
00106 // Resource files
00107 //#include "markn.h"
00108 //#include "markg.h"    
00109 //#include "peter.h"    
00110 //#include "resource.h"
00111 //#include "viewrc.h"
00112 //#include "will2.h"
00113 
00114 // Code headers
00115 //#include "app.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00116 //#include "attrmgr.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00117 #include "blobs.h"
00118 #include "csrstack.h"
00119 #include "unicdman.h"
00120 //#include "docview.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00121 #include "keypress.h"
00122 #include "nodetext.h"
00123 #include "nodetxts.h"
00124 #include "nodetxtl.h"
00125 #include "objchge.h"
00126 #include "oilfiles.h"
00127 #include "opscale.h"
00128 #include "opsquash.h"
00129 //#include "selmsg.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00130 //#include "txtattr.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00131 #include "textinfo.h"
00132 #include "textops.h"
00133 #include "textfuns.h"
00134 #include "vkextra.h"
00135 #include "nodetxts.h"
00136 #include "nodemold.h"
00137 #include "cutop.h"
00138 //#include "spread.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00139 #include "layer.h"
00140 #include "nodepostpro.h"
00141 #include "nodepath.h"
00142 #include "usercord.h"
00143 #include "camview.h"
00144 
00145 DECLARE_SOURCE( "$Revision: 1656 $" );
00146 
00147 CC_IMPLEMENT_MEMDUMP(TextTool,Tool_v1)
00148 CC_IMPLEMENT_DYNAMIC(TextToolBlobPosItem, ListItem);
00149 CC_IMPLEMENT_DYNAMIC(TextToolBlobPosList, List);
00150 
00151 
00152 // Must come after the last CC_IMPLEMENT.. macro
00153 #define new CAM_DEBUG_NEW     
00154 
00155 // These are still char* while we wait for resource technology to be developed for modules
00156 TCHAR* TextTool::FamilyName = _T("Text Tools");
00157 TCHAR* TextTool::ToolName   = _T("Text Tool");
00158 TCHAR* TextTool::Purpose    = _T("Text manipulation");
00159 TCHAR* TextTool::Author     = _T("TextToolTeam");
00160 
00161 // Init those other useful static vars
00162 BOOL            TextTool::CurrentTool           = FALSE;
00163 TextInfoBarOp*  TextTool::pTextInfoBarOp        = NULL;
00164 
00165 // Preference variables
00166 BOOL TextTool::UseDeadKeys = TRUE;
00167 
00168 extern void Beep();
00169 
00170 
00171 // These arrays consist of two UINT32 pairs, normal code followed by accented code.
00172 // Terminate with 0.
00173 static UINT32 GraveArray[] = {  0x0041, 0x00C0, // A
00174                                 0x0045, 0x00C8, // E
00175                                 0x0049, 0x00CC, // I
00176                                 0x004F, 0x00D2, // O
00177                                 0x0055, 0x00D9, // U
00178                                 0x0061, 0x00E0, // a
00179                                 0x0065, 0x00E8, // e
00180                                 0x0069, 0x00EC, // i
00181                                 0x006F, 0x00F2, // o
00182                                 0x0075, 0x00F9, // u
00183                                 0x0000 };
00184 static UINT32 AcuteArray[] = {  0x0041, 0x00C1, // A
00185                                 0x0045, 0x00C9, // E
00186                                 0x0049, 0x00CD, // I
00187                                 0x004F, 0x00D3, // O
00188                                 0x0055, 0x00DA, // U
00189                                 0x0059, 0x00DD, // Y
00190                                 0x0061, 0x00E1, // a
00191                                 0x0065, 0x00E9, // e
00192                                 0x0069, 0x00ED, // i
00193                                 0x006F, 0x00F3, // o
00194                                 0x0075, 0x00FA, // u
00195                                 0x0079, 0x00FD, // y
00196                                 0x0000 };
00197 static UINT32 HatArray[] =  {   0x0041, 0x00C2, // A
00198                                 0x0045, 0x00CA, // E
00199                                 0x0049, 0x00CE, // I
00200                                 0x004F, 0x00D4, // O
00201                                 0x0053, 0x008A, // S
00202                                 0x0055, 0x00DB, // U
00203                                 0x0061, 0x00E2, // a
00204                                 0x0065, 0x00EA, // e
00205                                 0x0069, 0x00EE, // i
00206                                 0x006F, 0x00F4, // o
00207                                 0x0073, 0x009A, // s
00208                                 0x0075, 0x00FB, // u
00209                                 0x0000 };
00210 static UINT32 TildeArray[] = {  0x0041, 0x00C3, // A
00211                                 0x004E, 0x00D1, // N
00212                                 0x004F, 0x00D5, // O
00213                                 0x0061, 0x00E3, // a
00214                                 0x006E, 0x00F1, // n
00215                                 0x006F, 0x00F5, // o
00216                                 0x0000 };
00217 static UINT32 ColonArray[] = {  0x0041, 0x00C4, // A
00218                                 0x0045, 0x00CB, // E
00219                                 0x0049, 0x00CF, // I
00220                                 0x004F, 0x00D6, // O
00221                                 0x0055, 0x00DC, // U
00222                                 0x0059, 0x009F, // Y
00223                                 0x0061, 0x00E4, // a
00224                                 0x0065, 0x00EB, // e
00225                                 0x0069, 0x00EF, // i
00226                                 0x006F, 0x00F6, // o
00227                                 0x0075, 0x00FC, // u
00228                                 0x0079, 0x00FF, // y
00229                                 0x0000 };
00230 static UINT32 AtArray[] =   {   0x0041, 0x00C5, // A
00231                                 0x0061, 0x00E5, // a
00232                                 0x0043, 0x00A9, // C
00233                                 0x0052, 0x00AE, // R
00234                                 0x0063, 0x00A9, // c
00235                                 0x0072, 0x00AE, // r
00236                                 0x0000 };
00237 static UINT32 CommaArray[] =    {   0x0043, 0x00C7, // C
00238                                 0x0063, 0x00E7, // c
00239                                 0x0000 };
00240 static UINT32 SlashArray[] =    {   0x004F, 0x00D8, // O
00241                                 0x0063, 0x00A2, // c
00242                                 0x006F, 0x00F8, // o
00243                                 0x0000 };
00244 
00245 
00246 
00247 /********************************************************************************************
00248 
00249 >   TextTool::TextTool()
00250 
00251     Author:     Mark_Goodall (Xara Group Ltd) <camelotdev@xara.com>
00252     Created:    3/10/94
00253     Purpose:    Default Constructor.
00254                 Other initialisation is done in TextTool::Init which is called by the Tool Manager
00255     SeeAlso:    TextTool::Init
00256 
00257 ********************************************************************************************/
00258 
00259 TextTool::TextTool()
00260 {
00261     pcCurrentCursor = NULL;
00262     IsBlankCursorUp = FALSE;
00263     UpdateAfterTyping = FALSE;
00264 
00265     PreviousDeadKey = NULL;
00266     GraveVirtKey = NULL;
00267     AcuteVirtKey = NULL;
00268     HatVirtKey = NULL;
00269     TildeVirtKey = NULL;
00270     ColonVirtKey = NULL;
00271     AtVirtKey = NULL;
00272     CommaVirtKey = NULL;
00273     SlashVirtKey = NULL;
00274     pLastFocusStory = NULL;
00275 }
00276 
00277 /********************************************************************************************
00278 
00279 >   TextTool::~TextTool()
00280 
00281     Author:     Mark_Goodall (Xara Group Ltd) <camelotdev@xara.com>
00282     Created:    3/10/94
00283     Purpose:    Destructor (Virtual). Does nothing.
00284 
00285 ********************************************************************************************/
00286 
00287 TextTool::~TextTool()
00288 {
00289     pTextInfoBarOp->pTextTool = NULL;
00290     TextInfoBarOp::DeInit(); 
00291 
00292     RemoveDeadKeys();
00293 }
00294 
00295 
00296 
00297 /********************************************************************************************
00298 
00299 >   BOOL TextTool::Init( INT32 Pass )
00300 
00301     Author:     Mark_Goodall (Xara Group Ltd) <camelotdev@xara.com>
00302     Created:    3/10/94
00303     Returns:    FALSE if it does not want to be created, TRUE otherwise
00304     Purpose:    Used to check if the Tool was properly constructed
00305     SeeAlso:    TextTool::TextTool
00306 
00307 ********************************************************************************************/
00308 
00309 BOOL TextTool::Init()
00310 {
00311     // Declare all your ops here and only succeed if all declarations succeed
00312 
00313     BOOL ok = TRUE;
00314 
00315     // Register CreateTextObjectOp 
00316     ok = ok && OpCreateTextObject::Init(); 
00317     ok = ok && OpTextSelection::Init(); 
00318     ok = ok && OpDeleteTextStory::Init(); 
00319     ok = ok && OpReverseStoryPath::Init(); 
00320     ok = ok && OpApplyJustificationToStory::Init(); 
00321     ok = ok && OpTextPaste::Init();
00322     ok = ok && OpTogglePrintTextAsShapes::Init();
00323     ok = ok && OpDragStoryNonPathLeftIndent::Init();
00324     ok = ok && OpDragStoryNonPathRightIndent::Init();
00325     ok = ok && OpDragStoryPathLeftIndent::Init();
00326     ok = ok && OpDragStoryPathRightIndent::Init();
00327     ok = ok && OpAffectFontChange::Init();
00328     ok = ok && TextInfoBarOp::Init(); // static initialisation
00329 
00330     // This section reads in the infobar definition and creates an instance of
00331     // TextInfoBarOp.  Also pTextInfoBarOp, the ptr to the tool's infobar, is set up
00332     // after the infobar is successfully read and created.
00333     if (ok)
00334     {
00335 #if 0
00336         CCResTextFile       file;               // Resource File
00337         TextInfoBarOpCreate BarCreate;          // Object that creates TextInfoBarOp objects
00338 
00339                 ok = file.open(_R(IDM_TEXT_BAR), _R(IDT_INFO_BAR_RES));     // Open resource
00340         if (ok) ok = DialogBarOp::ReadBarsFromFile(file,BarCreate); // Read and create info bar
00341         if (ok) file.close();                                       // Close resource
00342 
00343         ENSURE(ok,"Unable to load Textbar.ini from resource\n"); 
00344 
00345         if (ok)
00346         {
00347             // Info bar now exists.  Now get a pointer to it
00348             String_32 str = String_32(_R(IDS_TEXTTOOL_INFOBARNAME));
00349             DialogBarOp* pDialogBarOp = DialogBarOp::FindDialogBarOp(str);
00350 
00351                     ok = (pDialogBarOp != NULL);
00352             if (ok) ok = pDialogBarOp->IsKindOf(CC_RUNTIME_CLASS(TextInfoBarOp));
00353             if (ok) pTextInfoBarOp = (TextInfoBarOp*)pDialogBarOp;
00354 
00355             ENSURE(ok,"Error finding the Text tool info bar");
00356         }
00357 #endif
00358         pTextInfoBarOp = new TextInfoBarOp();
00359         ok = (pTextInfoBarOp != NULL);
00360         ENSURE(ok,"Error finding the Text tool info bar");
00361     }
00362 
00363     // Read preference settings
00364     GetApplication()->DeclareSection(_T("TextTool"),1);
00365     GetApplication()->DeclarePref(_T("TextTool"),_T("UseDeadKeys"), &UseDeadKeys, 0, 1);
00366 
00367     // Read deadkey virtual key codes for the current driver
00368     if (ok)
00369         ok = RegisterDeadKeys();
00370 
00371     return (ok);
00372 }
00373 
00374 
00375 
00376 /********************************************************************************************
00377 
00378 >   void TextTool::Describe(void *InfoPtr)
00379 
00380     Author:     Mark_Goodall (Xara Group Ltd) <camelotdev@xara.com>
00381     Created:    3/10/94
00382     Inputs:     InfoPtr -   A pointer to a tool info block. It is passed cast to void* as
00383                             the version of the tool is unknown at this point. Later versions 
00384                             of the Tool class may have more items in this block, that this 
00385                             tool will not use
00386     Outputs:    InfoPtr -   The structure pointed to by InfoPtr will have had all the info
00387                             that this version of the Tool knows about
00388     Purpose:    Allows the tool manager to extract information about the tool
00389 
00390 ********************************************************************************************/
00391 
00392 void TextTool::Describe(void *InfoPtr)
00393 {
00394     // Cast structure into the latest one we understand.
00395     ToolInfo_v1 *Info = (ToolInfo_v1 *) InfoPtr;
00396 
00397     Info->InfoVersion = 1;
00398     
00399     Info->InterfaceVersion = GetToolInterfaceVersion();  // You should always have this line.
00400         
00401     // These are all arbitrary at present.
00402     Info->Version = 1;
00403     Info->ID      = GetID();
00404     Info->TextID  = _R(IDS_TEXT_TOOL);
00405 
00406     Info->Family  = FamilyName;
00407     Info->Name    = ToolName;
00408     Info->Purpose = Purpose;
00409     Info->Author  = Author;
00410 
00411     Info->BubbleID = _R(IDBBL_TEXT_TOOLBOX);
00412     // Text tool is associated with the BaseTextClass attribute group
00413     Info->CurrentAttributeGroup = CC_RUNTIME_CLASS(BaseTextClass); 
00414 }
00415 
00416 /********************************************************************************************
00417 
00418 >   virtual void TextTool::SelectChange(BOOL isSelected)
00419 
00420     Author:     Mark_Goodall (Xara Group Ltd) <camelotdev@xara.com>
00421     Created:    3/10/94
00422     Inputs:     isSelected  - TRUE  = tool has been selected
00423                             - FALSE = tool has been deselected
00424     Outputs:    -
00425     Returns:    -
00426     Purpose:    Starts up and closes down the Text tool
00427     Errors:     Debug warning if creating the cursor fails.
00428     SeeAlso:    -
00429 
00430 ********************************************************************************************/
00431 void TextTool::SelectChange(BOOL isSelected)
00432 {
00433     BOOL ok = TRUE;
00434 
00435     if (isSelected)
00436         ok = OnToolSelect();
00437     else
00438         ok = OnToolDeselect();
00439 
00440     if (!ok)
00441         InformError();
00442 }
00443 
00444 
00445 
00446 /********************************************************************************************
00447 
00448 >   BOOL TextTool::CreateCursors()
00449 
00450     Author:     Mark_Goodall (Xara Group Ltd) <camelotdev@xara.com>
00451     Created:    3/10/94
00452     Inputs:     -
00453     Outputs:    -
00454     Returns:    TRUE if all the Text tool cursors have been successfully created
00455     Purpose:    Creates all the Text tool cursors
00456     SeeAlso:    -
00457 
00458 ********************************************************************************************/
00459 
00460 BOOL TextTool::CreateCursors()
00461 {
00462     // This tool has just been selected.  Create the cursors.
00463     pcNormalTextCursor = new Cursor(this, _R(IDC_TEXTTOOLBLOBCURSOR));
00464     pcBlobTextCursor = new Cursor(this, _R(IDC_TEXTTOOLCURSOR));
00465     pcIndentCursor = new Cursor(this, _R(IDC_TEXTTOOLINDENTCURSOR));
00466     pcBlankCursor = new Cursor(this, _R(IDC_TEXTTOOLBLANKCURSOR));
00467 
00468     if ( pcNormalTextCursor==NULL || !pcNormalTextCursor->IsValid() ||
00469          pcBlobTextCursor==NULL || !pcBlobTextCursor->IsValid() ||
00470          pcIndentCursor==NULL || !pcIndentCursor->IsValid() ||
00471          pcBlankCursor==NULL || !pcBlankCursor->IsValid() )
00472     {
00473         DestroyCursors();
00474         return FALSE;
00475     }
00476     else
00477         return TRUE;
00478 }
00479 
00480 
00481 
00482 /********************************************************************************************
00483 
00484 >   void TextTool::DestroyCursors()
00485 
00486     Author:     Mark_Goodall (Xara Group Ltd) <camelotdev@xara.com>
00487     Created:    3/10/94
00488     Inputs:     -
00489     Outputs:    -
00490     Returns:    -
00491     Purpose:    Destroys all the Text tool cursors
00492     SeeAlso:    -
00493 
00494 ********************************************************************************************/
00495 
00496 void TextTool::DestroyCursors()
00497 {
00498     if (pcNormalTextCursor != NULL) delete pcNormalTextCursor;
00499     if (pcBlobTextCursor != NULL) delete pcBlobTextCursor;
00500     if (pcIndentCursor != NULL) delete pcIndentCursor;
00501     if (pcBlankCursor != NULL) delete pcBlankCursor;
00502 }
00503 
00504 
00505 
00506 
00507 /********************************************************************************************
00508 
00509 >   void TextTool::OnClick( DocCoord PointerPos, ClickType Click, ClickModifiers ClickMods,
00510                         Spread* pSpread )
00511 
00512     Author:     Mark_Goodall (Xara Group Ltd) <camelotdev@xara.com>
00513     Created:    3/10/94
00514     Inputs:     PointerPos  -   The DocCoord of the point where the mouse button was clicked
00515                 Click       -   Describes the type of click that was detected. 
00516                 ClickMods   -   Indicates which buttons caused the click and which modifers were
00517                                 pressed at the same time
00518                 pSpread     -   The spread in which the click happened
00519     Returns:    -
00520     Purpose:    To handle a Mouse Click event for the Text Tool.
00521     SeeAlso:    Tool::MouseClick; ClickType; ClickModifiers; TextTool::HandleSingleClick;
00522                 TextTool::HandleDoubleClick; TextTool::HandleDragClick
00523 
00524 ********************************************************************************************/
00525 void TextTool::OnClick( DocCoord PointerPos, ClickType Click, ClickModifiers ClickMods, Spread* pSpread )
00526 {
00527     if (ClickMods.Menu) return;                         // Don't do anything if the user clicked the Menu button
00528 
00529     if (IsBlankCursorUp)
00530         return;
00531 
00532     DocView* pDocView = DocView::GetSelected();
00533     if (pDocView == NULL)
00534     {
00535         ERROR3("No selected doc view!");
00536     }
00537     else
00538     {
00539         BOOL Success = TRUE;
00540     
00541         switch (Click)
00542         {
00543             case CLICKTYPE_SINGLE:
00544                 if (pDocView->IsSingleClickReallyTriple())
00545                     Success = HandleTripleClick(pSpread, PointerPos, ClickMods);
00546                 else
00547                     Success = HandleSingleClick(pSpread, PointerPos, ClickMods);
00548                 break;
00549             case CLICKTYPE_DOUBLE:
00550                 if (pDocView->IsSingleClickReallyQuad())
00551                     Success = HandleQuadClick(pSpread, PointerPos, ClickMods);
00552                 else
00553                     Success = HandleDoubleClick(pSpread, PointerPos, ClickMods);
00554                 break;
00555             case CLICKTYPE_DRAG:
00556                 Success = HandleDragClick(pSpread, PointerPos, ClickMods);
00557                 break;
00558             default:
00559                 break;
00560         }
00561 
00562         if (!Success)
00563             InformError();
00564     }
00565 }
00566 
00567 
00568 
00569 /********************************************************************************************
00570 
00571 >   void TextTool::HandleSingleClick(Spread* pSpread, DocCoord ClickPos, ClickModifiers ClickMods)
00572 
00573     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
00574     Created:    15/02/95
00575     Inputs:     pSpread - The spread in which the click occurred
00576                 ClickPos - The DocCoord of the point where the mouse was clicked
00577                 ClickMods - The state of the various modifiers at the time of the click
00578     Returns:    FALSE if an error occurs during processing
00579     Purpose:    To handle single click events in the text tool.
00580     SeeAlso:    TextTool::OnClick
00581 
00582 ********************************************************************************************/
00583 
00584 BOOL TextTool::HandleSingleClick(Spread* pSpread, DocCoord ClickPos, ClickModifiers ClickMods)
00585 {
00586     Node* pHitObject = NULL;
00587     TextStory* pTextStoryToSelect = NULL;
00588 
00589     // Is the click over an indent blob?
00590     BOOL BlobIsLeft = TRUE;
00591     TextStory* pFoundStory = NULL;
00592     if (!m_BlobPosList.FindBlobStory(pSpread, ClickPos, &BlobIsLeft, &pFoundStory))
00593         return FALSE;
00594     
00595     if (pFoundStory != NULL)
00596     {
00597         if (!m_BlobPosList.RenderAndRemove(pFoundStory))
00598             return FALSE;
00599 
00600         // Invoke a operation to drag the indent position
00601         OpDragStoryIndent* pOp = NULL;
00602         if (BlobIsLeft)
00603         {
00604             if (pFoundStory->GetTextPath() != NULL)
00605                 pOp = new OpDragStoryPathLeftIndent;
00606             else
00607                 pOp = new OpDragStoryNonPathLeftIndent;
00608         }
00609         else
00610         {
00611             if (pFoundStory->GetTextPath() != NULL)
00612                 pOp = new OpDragStoryPathRightIndent;
00613             else
00614                 pOp = new OpDragStoryNonPathRightIndent;
00615         }
00616         if (pOp==NULL)
00617             return FALSE;
00618         else
00619             return pOp->DoDrag(ClickPos, pSpread, ClickMods, pFoundStory);
00620     }
00621     else if ( IsPointNearUnselectedTextStory(pSpread, ClickPos, &pTextStoryToSelect, &pHitObject, FALSE) )
00622     {
00623         // Position the caret with a story, and drag selection
00624         OpTextSelection* pOp = new OpTextSelection;
00625         if (pOp == NULL)
00626             return FALSE;
00627         else
00628             return pOp->DoDrag(ClickPos, pSpread, ClickMods, pHitObject);
00629     }
00630     else
00631     {
00632         // Create a new text story
00633         OpCreateTextObject* pOpCreateTextObject = new OpCreateTextObject;
00634         if (pOpCreateTextObject == NULL)
00635             return FALSE;
00636         else
00637         {
00638             NodePath* pPath = NULL;
00639             IsPointNearNonStoryPath(pSpread, ClickPos, &pPath, FALSE);
00640             if (pPath != NULL)
00641                 pOpCreateTextObject->DoImmediate(pSpread, ClickPos, pPath, ClickMods);
00642             else
00643                 pOpCreateTextObject->DoDrag(pSpread, ClickPos, ClickMods);
00644         }
00645     }
00646     return TRUE;
00647 }                                               
00648 
00649 
00650 
00651 /********************************************************************************************
00652 
00653 >   void TextTool::HandleDoubleClick(Spread* pSpread, DocCoord ClickPos, ClickModifiers ClickMods)
00654 
00655     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
00656     Created:    15/02/95
00657     Inputs:     pSpread - The spread in which the click occurred
00658                 ClickPos - The DocCoord of the point where the mouse was clicked
00659                 ClickMods - The state of the various modifiers at the time of the click
00660     Returns:    FALSE if an error occurs during processing
00661     Purpose:    To handle double click events in the text tool.
00662     SeeAlso:    TextTool::OnClick
00663 
00664 ********************************************************************************************/
00665 
00666 BOOL TextTool::HandleDoubleClick(Spread* pSpread, DocCoord ClickPos, ClickModifiers ClickMods)
00667 {
00668     BOOL Success = TRUE;
00669     // Select the word at the caret
00670     if ((TextStory::GetFocusStory() != NULL) && !ClickMods.Constrain && !ClickMods.Adjust)
00671     {
00672         OpTextCaret* pOp = new OpTextCaret();
00673         if (pOp != NULL)
00674             pOp->DoSelectWordAtCaret();
00675         else
00676             Success = FALSE;
00677     }
00678     return Success;
00679 }
00680 
00681 
00682 
00683 /********************************************************************************************
00684 >   void TextTool::HandleTripleClick(Spread* pSpread, DocCoord ClickPos, ClickModifiers ClickMods)
00685 
00686     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
00687     Created:    15/02/95
00688     Inputs:     pSpread - The spread in which the click occurred
00689                 ClickPos - The DocCoord of the point where the mouse was clicked
00690                 ClickMods - The state of the various modifiers at the time of the click
00691     Returns:    FALSE if an error occurs during processing
00692     Purpose:    To handle triple click events in the text tool.
00693     SeeAlso:    TextTool::OnClick
00694 ********************************************************************************************/
00695 BOOL TextTool::HandleTripleClick(Spread* pSpread, DocCoord ClickPos, ClickModifiers ClickMods)
00696 {
00697     ERROR2IF(TextStory::GetFocusStory()==NULL, FALSE, "No focus story");
00698     ERROR2IF(TextStory::GetFocusStory()->GetCaret()==NULL, FALSE, "No caret in focus story");
00699     
00700     TextLine* pLine = (TextLine*)TextStory::GetFocusStory()->GetCaret()->FindParent(CC_RUNTIME_CLASS(TextLine));
00701     ERROR2IF(pLine==NULL, FALSE, "Caret didn't have a parent TextLine");
00702 
00703     OpTextSelection* pOp = new OpTextSelection();
00704     if (pOp != NULL)
00705         return pOp->DoSelectLineText();
00706     else
00707         return FALSE;
00708 }
00709 
00710 
00711 
00712 /********************************************************************************************
00713 >   void TextTool::HandleQuadClick(Spread* pSpread, DocCoord ClickPos, ClickModifiers ClickMods)
00714 
00715     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
00716     Created:    22/12/95
00717     Inputs:     pSpread - The spread in which the click occurred
00718                 ClickPos - The DocCoord of the point where the mouse was clicked
00719                 ClickMods - The state of the various modifiers at the time of the click
00720     Returns:    FALSE if an error occurs during processing
00721     Purpose:    To handle quad click events in the text tool.
00722     SeeAlso:    TextTool::OnClick
00723 ********************************************************************************************/
00724 BOOL TextTool::HandleQuadClick(Spread* pSpread, DocCoord ClickPos, ClickModifiers ClickMods)
00725 {
00726     return TRUE;
00727 }
00728 
00729 
00730 
00731 /********************************************************************************************
00732 
00733 >   void TextTool::HandleDragClick(Spread* pSpread, DocCoord ClickPos, ClickModifiers ClickMods)
00734 
00735     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
00736     Created:    15/02/95
00737     Inputs:     pSpread - The spread in which the click occurred
00738                 ClickPos - The DocCoord of the point where the mouse was clicked
00739                 ClickMods - The state of the various modifiers at the time of the click
00740     Returns:    FALSE if an error occurs during processing
00741     Purpose:    To handle drag events in the text tool.
00742     SeeAlso:    TextTool::OnClick
00743 
00744 ********************************************************************************************/
00745 
00746 BOOL TextTool::HandleDragClick(Spread* pSpread, DocCoord ClickPos, ClickModifiers ClickMods)
00747 {
00748     return TRUE;
00749 }
00750 
00751 
00752 
00753 /********************************************************************************************
00754 
00755 >   void TextTool::IsPointNearUnselectedTextStory(  Spread* pSpread,
00756                                                     DocCoord ClickPos,
00757                                                     TextStory** pTS,
00758                                                     Node** pHitObject,
00759                                                     BOOL AllowInterrupts)
00760 
00761     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
00762     Created:    17/02/95
00763     Inputs:     PointerPos  -   The DocCoord of the point where the mouse has moved to
00764                 pSpread     -   The spread in which the move occurred
00765                 AllowInterrupts - TRUE to stop the hit detection if the mouse moves
00766     Outputs:    pTS will point to the TextStory to select, or NULL
00767                 pHitObject will point the the object under the pointer, or NULL
00768                     (Set to NULL if not interested about objects)
00769     Returns:    TRUE if the pointer is over an text story.
00770                 FALSE if not
00771     Purpose:    To see if a click at the current positon would select a TextStory
00772     SeeAlso:    TextTool::IsPointNearNonStoryPath
00773 
00774 ********************************************************************************************/
00775 
00776 BOOL TextTool::IsPointNearUnselectedTextStory(Spread* pSpread, DocCoord ClickPos, TextStory** pTS, Node** pHitObject, BOOL AllowInterrupts)
00777 {
00778     Node* pInterrupt;
00779     NodeRenderableInk* pObject = NULL;
00780 
00781     if (AllowInterrupts)
00782         pObject = NodeRenderableInk::FindSimpleAtPoint(pSpread, ClickPos, NULL, &pInterrupt);
00783     else
00784         pObject = NodeRenderableInk::FindSimpleAtPoint(pSpread, ClickPos);
00785 
00786     // The text tool has always ignored the compound node structure of the document
00787     // and allowed a text object to be selected inside any amount of nested nodes.
00788     // This is actually useful most of the time but we can't allow /anything/ to be
00789     // edited inside a locked effect because the changes will not be seen while
00790     // the locked effect's own bitmap is being rendered...
00791     NodeRenderableInk* pCompound = NodeRenderableInk::FindCompoundFromSimple(pObject);
00792     if (pCompound && pCompound->IsEffect() && ((NodeEffect*)pCompound)->IsLockedEffect())
00793         return FALSE;
00794 
00795     *pTS = NULL;
00796     if (pHitObject!=NULL)
00797         *pHitObject = NULL;
00798 
00799     // BODGE - lets assume the char is a child of the line of the story
00800     if (pObject != NULL)
00801     {
00802         if (pObject->IsAVisibleTextNode())
00803         {
00804             if (pHitObject!=NULL)
00805                 *pHitObject = pObject;
00806             *pTS = (TextStory*)(pObject->FindParent()->FindParent());
00807             return TRUE;
00808         }
00809     }
00810 
00811     // We didn't find anything via hittesting.  Now scan the stories on this spread to see
00812     // if we are over blank areas of the story.
00813     Node* pNode = pSpread->FindFirstDepthFirst();
00814     while (pNode != NULL)
00815     {
00816         if (pNode->IsABaseTextClass() && IS_A(pNode, TextStory))
00817         {
00818             // Check to see if the point is within the bounds of the story
00819             TextStory* pStory = (TextStory*)pNode;
00820             DocRect StoryBounds = pStory->GetBoundingRect(TRUE);
00821             if (StoryBounds.ContainsCoord(ClickPos))
00822             {
00823                 // Story must not be on a path and must be on an unlocked layer, and not in a mould
00824                 Layer* pLayer = (Layer*)pStory->FindParent(CC_RUNTIME_CLASS(Layer));
00825                 NodeMould* pMould = (NodeMould*)pStory->FindParent(CC_RUNTIME_CLASS(NodeMould));
00826                 if (pLayer!=NULL && pMould==NULL && pStory->GetTextPath()==NULL && !pLayer->IsLocked())
00827                 {
00828                     // If the client wants the object then we have to do more work
00829                     if (pHitObject!=NULL)
00830                     {
00831                         VisibleTextNode* pNearObject = NULL;
00832                         BOOL ToLeft = FALSE;
00833                         if (!pStory->GetCharacterAtPoint(TRUE, ClickPos, &pNearObject, &ToLeft))
00834                         {
00835                             InformError();
00836                             return FALSE;
00837                         }
00838                         else
00839                         {
00840                             if (pNearObject != NULL)
00841                             {
00842                                 *pHitObject = pNearObject;
00843                                 *pTS = pStory;
00844                                 return TRUE;
00845                             }
00846                         }
00847                     }
00848                     else
00849                     {
00850                         // Get untransformed story bounds and point
00851                         DocRect UTStory = pStory->GetUTStoryBounds();
00852                         DocCoord UTPoint = ClickPos;
00853                         Matrix StoryMat = pStory->GetStoryMatrix();
00854                         StoryMat=StoryMat.Inverse();
00855                         StoryMat.transform(&UTPoint);
00856 
00857                         if (UTStory.ContainsCoord(UTPoint))
00858                         {
00859                             *pTS=pStory;
00860                             return TRUE;
00861                         }
00862                     }
00863                 }
00864             }
00865         }
00866         pNode = pNode->FindNextDepthFirst(pSpread);
00867     }
00868 
00869     return FALSE;
00870 }
00871 
00872 
00873 
00874 /********************************************************************************************
00875 
00876 >   void TextTool::IsPointNearNonStoryPath( Spread* pSpread,
00877                                             DocCoord PointerPos,
00878                                             NodePath** pHitPath,
00879                                             BOOL AllowInterrupts)
00880 
00881     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
00882     Created:    17/02/95
00883     Inputs:     pSpread     -   The spread in which the point is in
00884                 PointerPos  -   The DocCoord of the point where the mouse has moved to
00885                 AllowInterrupts - TRUE to stop the hit detection if the mouse moves
00886     Outputs:    pHitPath will point to the NodePath the pointer is over, or NULL
00887     Returns:    TRUE if the pointer is over an NodePath, FALSE if not
00888     Purpose:    To see if a click at the current positon should create a story on a path
00889     SeeAlso:    TextTool::IsPointNearUnselectedTextStory
00890 
00891 ********************************************************************************************/
00892 
00893 BOOL TextTool::IsPointNearNonStoryPath( Spread* pSpread, DocCoord PointerPos, NodePath** pHitPath, BOOL AllowInterrupts)
00894 {
00895     Node* pInterrupt;
00896     NodeRenderableInk* pObject = NULL;
00897     *pHitPath = NULL;
00898 
00899     if (AllowInterrupts)
00900         pObject = NodeRenderableInk::FindSimpleAtPoint(pSpread, PointerPos, NULL, &pInterrupt);
00901     else
00902         pObject = NodeRenderableInk::FindSimpleAtPoint(pSpread, PointerPos);
00903 
00904     // If its a NodePath that isn't already a story path
00905     if (pObject != NULL)
00906     {
00907         Node* pParent = pObject->FindParent();
00908         ObjChangeParam ChangeData(OBJCHANGE_STARTING, ObjChangeFlags(TRUE), pObject, NULL);
00909         if (IS_A(pObject, NodePath) && pObject->IsSelected() &&     /* It must be a selected path */
00910             (pParent != NULL) && !IS_A(pParent, TextStory) &&       /* It's not already in a story */
00911             pObject->AllowOp(&ChangeData, FALSE) )                  /* And the path is happy with being deleted */
00912         {
00913             // Only allows clicks on the edge of the path (not the inside of filled shapes)
00914             NodePath* pPath = (NodePath*)pObject;
00915             INT32 PathSlot = 0;
00916             if (pPath->InkPath.PointCloseToLine(PointerPos, &PathSlot))
00917             {
00918                 *pHitPath = (NodePath*)pObject;
00919                 return TRUE;
00920             }
00921         }
00922     }
00923 
00924     return FALSE;
00925 }                                                                                 
00926 
00927 
00928 
00929 /********************************************************************************************
00930 >   void TextTool::OnMouseMove( DocCoord PointerPos,Spread* pSpread, ClickModifiers ClickMod )
00931 
00932     Author:     Mark_Goodall (Xara Group Ltd) <camelotdev@xara.com>
00933     Created:    3/10/94
00934     Inputs:     PointerPos  -   The DocCoord of the point where the mouse has moved to
00935                 pSpread     -   The spread in which the move occurred
00936                 ClickMods   -   The state of the various modifiers at the time of the mouse move
00937     Returns:    TRUE if it handled the Click, FALSE otherwise
00938     Purpose:    To handle a Mouse Move event for the Text Tool.
00939     SeeAlso:    Tool::MouseClick; ClickType; ClickModifiers
00940 ********************************************************************************************/
00941 void TextTool::OnMouseMove(DocCoord PointerPos, Spread* pSpread, ClickModifiers ClickMods)
00942 {
00943     // Display status bar text for the current position
00944     DisplayStatusBarHelp(PointerPos, pSpread, ClickMods);
00945     IsBlankCursorUp = FALSE;
00946 
00947     // Cursor changing over characters
00948     NodePath* pPath = NULL;
00949     TextStory* pTextStoryToSelect = NULL;
00950     Cursor* DisplayCur = pcNormalTextCursor;
00951 
00952     // Are we over text to put a caret in OR near a path to click to start a story on?
00953     if ( IsPointNearUnselectedTextStory(pSpread, PointerPos, &pTextStoryToSelect, NULL, TRUE) ||
00954         IsPointNearNonStoryPath(pSpread, PointerPos, &pPath, TRUE) )
00955     {
00956         DisplayCur = pcBlobTextCursor;  
00957     }
00958 
00959     // See if we are near any story path indent blobs
00960     TextStory* pFoundStory = NULL;
00961     if (!m_BlobPosList.FindBlobStory(pSpread, PointerPos, NULL, &pFoundStory))
00962         InformError();
00963     else if (pFoundStory != NULL)
00964         DisplayCur = pcIndentCursor;
00965 
00966     // Display that cursor!
00967     if ( (DisplayCur != pcCurrentCursor) && (DisplayCur != NULL) )
00968     {
00969         pcCurrentCursor = DisplayCur;
00970         CursorStack::GSetTop(pcCurrentCursor, CurrentCursorID);
00971     }
00972 }
00973 
00974 void TextTool::GetRulerOrigin(Spread* pSpread, UserCoord *pOrigin)
00975 {
00976     if (TextInfoBarOp::IsRulerOriginClaimed())
00977     {
00978         pOrigin->x = TextInfoBarOp::GetRulerOrigin();
00979     }
00980 }
00981 
00982 void TextTool::RenderRulerBlobs(RulerBase* pRuler, UserRect& UpdateRect, BOOL IsBackground)
00983 {
00984     // we only draw onto the horizontal ruler and only if we have claimed it
00985     if (!TextInfoBarOp::IsRulerOriginClaimed()) return;
00986 
00987     // draw the highlighted background or the blobs
00988     if (IsBackground)
00989     {
00990         TextInfoBarOp::HighlightRulerSection(pRuler, UpdateRect);
00991     }
00992     else
00993     {
00994         TextInfoBarOp::RenderRulerBlobs(pRuler, UpdateRect);
00995     }
00996 }
00997 
00998 /********************************************************************************************
00999 
01000 >   BOOL TextTool::OnRulerClick(UserCoord PointerPos, ClickType Click, ClickModifiers Mods,
01001                                 Spread* pSpread, RulerBase* pRuler)
01002 
01003     Author:     Martin Wuerthner <xara@mw-software.com>
01004     Created:    07/07/06
01005     Inputs:     PointerPos  - user coordinates of click on ruler (relative to origin set by tool)
01006                 Click       - Type of click enum
01007                 Mods        - Modifier flags struct
01008                 pSpread     - pointer to spread upon which click occurred
01009                 pRuler      - pointer to ruler which generated click
01010     Returns:    TRUE to claim the click
01011     Purpose:    Called when the user has clicked on the ruler and we are the current tool
01012 
01013 ********************************************************************************************/
01014 
01015 BOOL TextTool::OnRulerClick(UserCoord PointerPos, ClickType Click, ClickModifiers Mods,
01016                             Spread* pSpread, RulerBase* pRuler)
01017 {
01018     if (!TextInfoBarOp::IsRulerOriginClaimed()) return FALSE;
01019     return TextInfoBarOp::OnRulerClick(PointerPos, Click, Mods, pSpread, pRuler);
01020 }
01021 
01022 /********************************************************************************************
01023 
01024 >   BOOL TextTool::GetRulerStatusLineText(String_256* pText, UserCoord PointerPos,
01025                                           Spread* pSpread, RulerBase* pRuler)
01026 
01027     Author:     Martin Wuerthner <xara@mw-software.com>
01028     Created:    25/07/06
01029     Inputs:     PointerPos  - user coordinates of click on ruler (relative to origin set by tool)
01030                 pSpread     - pointer to spread upon which click occurred
01031                 pRuler      - pointer to ruler which generated click
01032     Outputs:    Status line text written to pText (if returning TRUE)
01033     Returns:    TRUE if the text has been set
01034     Purpose:    Allows the tool to set the status line text for the ruler
01035 
01036 ********************************************************************************************/
01037 
01038 BOOL TextTool::GetRulerStatusLineText(String_256* pText, UserCoord PointerPos,
01039                                       Spread* pSpread, RulerBase* pRuler)
01040 {
01041     if (!TextInfoBarOp::IsRulerOriginClaimed()) return FALSE;
01042     return TextInfoBarOp::GetRulerStatusLineText(pText, PointerPos, pSpread, pRuler);
01043 }
01044 
01045 /********************************************************************************************
01046 
01047     >   BOOL TextTool::OnKeyPress(KeyPress* pKeyPress)
01048 
01049     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
01050     Created:    13/02/95
01051     Inputs:     pKeyPress - pointer to a keypress object
01052     Returns:    TRUE if it handled the keypress, FALSE otherwise
01053     Purpose:    To handle a keypress event for the Text Tool.
01054     SeeAlso:    -
01055 
01056 ********************************************************************************************/
01057 
01058 BOOL TextTool::OnKeyPress(KeyPress* pKeyPress)
01059 {
01060     TRACEUSER( "wuerthne", _T("TextTool::OnKeyPress, IsChar=%d IsRelease=%d VirtKey=%d\n"),
01061                pKeyPress->IsChar(), pKeyPress->IsRelease(), pKeyPress->GetVirtKey() );
01062 
01063     // NB - each keypress casues three events:
01064     //      (1) a key down event (IsChar() = FALSE, IsRelease() = FALSE) - which may not have the
01065     //          correct Unicode char value because it has not been through the IM yet
01066     //      (2) a char event (IsChar() = TRUE, IsRelease() = FALSE) - with the correct Unicode
01067     //          char but with different values for GetVirtKey (e.g., Ctrl+char is reported as
01068     //          a ctrl code)
01069     //      (3) a release event (IsChar() = FALSE, IsRelease() = TRUE)
01070 
01071     // Our strategy is to use the key down event for shortcuts, to use the char events for
01072     // actual input and to ignore the release events. Care must be taken to swallow enough
01073     // events, e.g., though cursor movement is handled using the key down event, the
01074     // corresponding char event must be swallowed, too, otherwise the reported Unicode
01075     // character ends up being inserted in the text object. Finally, all non-char events
01076     // we do not swallow here will be processed by the hotkey system.
01077 
01078     // Filter out all key release events 
01079     if (pKeyPress->IsRelease())
01080     {
01081         TRACEUSER( "wuerthne", _T("Release\n") );
01082 
01083         if (GetApplication()->FindSelection()->IsSelRangeGagged())
01084         {
01085             GetApplication()->FindSelection()->SetGag(FALSE);
01086             GetApplication()->FindSelection()->Update(TRUE);
01087         }
01088         return FALSE;
01089     }
01090 
01091     // Deal with keypresses that don't dosen't depend on a focus story
01092     if (HandleSpecialStoryAndNonStoryKeys(pKeyPress))
01093     {
01094         TRACEUSER( "wuerthne", _T("SpecialStoryAndNonStoryKeys\n") );
01095 
01096         return TRUE;
01097     }
01098 
01099     // See if a caret has been selected without updating the focus story
01100     if (TextStory::GetFocusStory() == NULL)
01101     {
01102         SelRange* pSelection = GetApplication()->FindSelection();       
01103         Node* pNode = pSelection->FindFirst();
01104         while (pNode != NULL)
01105         {
01106             if (IS_A(pNode,CaretNode))
01107             {
01108                 TextStory::SetFocusStory((TextStory*)pNode->FindParent(CC_RUNTIME_CLASS(TextStory)));
01109                 break;
01110             }
01111                 
01112             pNode = pSelection->FindNext(pNode);
01113         }
01114     }
01115 
01116     // We need a focus story do handle a keypress
01117     if (TextStory::GetFocusStory() == NULL) 
01118     {
01119         TRACEUSER( "wuerthne", _T("GetFocusStory\n") );
01120 
01121         return HandleSpecialNonStoryKeys(pKeyPress);
01122     }
01123 
01124     // Just to be safe let's see if the TextStory has a selected caret or selected region
01125     if ( !(TextStory::GetFocusStory()->GetCaret()->IsSelected()) &&
01126          (TextStory::GetFocusStory()->GetSelectionEnd() == NULL) )
01127     {
01128         TextStory::SetFocusStory(NULL);
01129         return HandleSpecialNonStoryKeys(pKeyPress);
01130     }
01131 
01132     // Now clear out all selections from other stories/objects
01133     SelRange* Selection = GetApplication()->FindSelection();
01134     Node* pNode = Selection->FindFirst();
01135     while (pNode != NULL)
01136     {
01137         if (pNode->IsAnObject())
01138         {
01139             if (!pNode->IsABaseTextClass())
01140                 ((NodeRenderableInk*)pNode)->DeSelect(TRUE);
01141             else
01142             {
01143                 if (!IS_A(pNode,TextStory))
01144                 {
01145                     TextStory* pParentStory = (TextStory*)pNode->FindParent(CC_RUNTIME_CLASS(TextStory));
01146                     if (pParentStory != TextStory::GetFocusStory())
01147                         ((NodeRenderableInk*)pNode)->DeSelect(TRUE);
01148                 }
01149             }
01150         }
01151 
01152         pNode = Selection->FindNext(pNode);
01153     }
01154     
01155     // First see if this is a special meaning key
01156     if (HandleSpecialStoryKeys(pKeyPress, TextStory::GetFocusStory(), TextStory::GetFocusStory()->GetCaret()))
01157     {
01158         TRACEUSER( "wuerthne", _T("HandleSpecialStoryKeys\n") );
01159 
01160         return TRUE;
01161     }
01162 
01163     // finally, only proper text input remaining, so we only want Char events
01164     if( !pKeyPress->IsChar() )
01165     {
01166         TRACEUSER("wuerthne", _T("not char"));
01167 
01168         // We own all keys that don't have constrain or alt pressed and aren't extended chars
01169         if( !pKeyPress->IsConstrain() && !pKeyPress->IsAlternative() && !pKeyPress->IsExtended() )
01170             return true;
01171 
01172         return pKeyPress->GetUnicode() == _T(' ');          // always claim the Space key (tool switch)
01173     }
01174 
01175     // temporary fix: do not allow function keys to insert funny Unicode characters
01176     // (e.g., F8 to select text tool inserts an s circumflex character
01177     if (pKeyPress->GetVirtKey() >= CAMKEY(F1) && pKeyPress->GetVirtKey() <= CAMKEY(F12))
01178         return FALSE;
01179 
01180     if ( (!pKeyPress->IsAlternative()) ||                   // Alt not down
01181          (pKeyPress->IsAlternative() && pKeyPress->IsExtended()) || // Right alt down
01182          (pKeyPress->IsAlternative() && pKeyPress->IsConstrain()) ) // Ctrl & left alt down
01183     {
01184         WCHAR UnicodeValue = pKeyPress->GetUnicode();
01185         TRACEUSER("wuerthne", _T("UnicodeValue from keypress event = %04x %d"), UnicodeValue, pKeyPress->IsExtended() );
01186 
01187         // Extanded keys shouldn't produce a character
01188         if (HandleDeadKeys(pKeyPress, &UnicodeValue) || pKeyPress->IsExtended())
01189             return TRUE;
01190         else
01191         {
01192             if ( (UnicodeValue>=32) && ((UnicodeValue < CAMELOT_UNICODE_BASE) || (UnicodeValue > CAMELOT_UNICODE_LAST)))
01193             {
01194 #ifndef EXCLUDE_FROM_XARALX
01195                 if ((UnicodeValue < 256) /*&& !TextManager::IsUnicodeCompleteOS()*/)
01196                     UnicodeValue = UnicodeManager::MultiByteToUnicode(UnicodeValue);
01197 #endif
01198                 
01199                 // Display a blank cursor (thus hiding the pointer), but only if the pointer
01200                 // is over the current document window
01201                 if (!IsBlankCursorUp)
01202                 {   
01203                     DocView* pDocView = DocView::GetSelected();
01204                     if (pDocView != NULL)
01205                     {
01206                         OilCoord DummyPos;
01207                         CCamView* pCCamView = pDocView->GetConnectionToOilView();
01208                         if (pCCamView && pCCamView->GetCurrentMousePos(&DummyPos))
01209                         {
01210                             // mouse is over document window
01211                             pcCurrentCursor = pcBlankCursor;
01212                             CursorStack::GSetTop(pcCurrentCursor, CurrentCursorID);
01213                             IsBlankCursorUp = TRUE;
01214                         }       
01215                     }
01216                 }
01217                 
01218                 // Create EditTextOp
01219                 OpTextFormat* pOp = new OpTextFormat();
01220                 if (pOp != NULL)
01221                 {
01222                     TRACEUSER("jlh92", _T("inserting Unicode char %04x"), UnicodeValue);
01223                     pOp->DoInsertChar(UnicodeValue, OpTextFormat::INSERT);
01224                     UpdateAfterTyping = TRUE;
01225                     return TRUE;
01226                 } 
01227             }
01228             else
01229                 TRACEUSER( "jlh92", _T("Rejected\n" ) );
01230         }
01231     }
01232     TRACEUSER("wuerthne", _T("TextTool::OnKeyPress returns FALSE"));
01233     return FALSE;
01234 }
01235 
01236 
01237 
01238 /********************************************************************************************
01239 >   BOOL TextTool::HandleSpecialStoryAndNonStoryKeys(KeyPress* pKeyPress,)
01240 
01241     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
01242     Created:    1/06/95
01243     Inputs:     pKeyPress - pointer to a keypress object
01244     Returns:    TRUE if it handled the keypress, FALSE otherwise
01245     Purpose:    To handle keypress events that wotk with and without a text focus story
01246     SeeAlso:    TextTool::HandleSpecialStoryKeys, TextTool::HandleSpecialNonStoryKeys
01247 ********************************************************************************************/
01248 BOOL TextTool::HandleSpecialStoryAndNonStoryKeys(KeyPress* pKeyPress)
01249 {
01250     ERROR3IF(pKeyPress == NULL, "KeyPress pointer was NULL");
01251     if (pKeyPress == NULL)
01252         return FALSE;
01253 
01254     if (pKeyPress->IsChar()) return FALSE;      // only use non-char events for hotkeys
01255 
01256     BOOL UsedTheKeypress = FALSE;
01257     BOOL Errored = FALSE;
01258 
01259     switch (pKeyPress->GetVirtKey())
01260     {
01261         case CAMKEY(B):
01262             // Apply/Remove bold to/from the selection
01263             if (pKeyPress->IsConstrain() && !pKeyPress->IsAdjust() )
01264             {
01265                 UsedTheKeypress = TRUE;
01266                 Errored = !ApplyBold();
01267             }
01268             break;
01269         case CAMKEY(I):
01270             // Apply/Remove italic to/from the selection
01271             if (pKeyPress->IsConstrain() && !pKeyPress->IsAdjust() )
01272             {
01273                 UsedTheKeypress = TRUE;
01274                 Errored = !ApplyItalic();
01275             }
01276             break;
01277     }
01278 
01279     if (Errored)
01280         InformError();
01281     TRACEUSER("wuerthne", _T("HandleSpecialStoryAndNonStoryKeys returns %d"), UsedTheKeypress);
01282     return UsedTheKeypress;
01283 }
01284 
01285 
01286 
01287 /********************************************************************************************
01288 >   BOOL TextTool::HandleSpecialNonStoryKeys(KeyPress* pKeyPress,)
01289 
01290     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
01291     Created:    24/08/95
01292     Inputs:     pKeyPress - pointer to a keypress object
01293     Returns:    TRUE if it used the keypress, FALSE otherwise
01294     Purpose:    To deal with keys presses that work without a focus story.
01295     SeeAlso:    TextTool::HandleSpecialStoryKeys, TextTool::HandleSpecialStoryAndNonStoryKeys
01296 ********************************************************************************************/
01297 BOOL TextTool::HandleSpecialNonStoryKeys(KeyPress* pKeyPress)
01298 {
01299     ERROR3IF(pKeyPress == NULL, "KeyPress pointer was NULL");
01300     if (pKeyPress == NULL)
01301         return FALSE;
01302 
01303     BOOL UsedTheKeypress = FALSE;
01304 
01305     switch (pKeyPress->GetVirtKey())
01306     {
01307         case CAMKEY(UP):
01308         case CAMKEY(DOWN):
01309         case CAMKEY(LEFT):
01310         case CAMKEY(RIGHT):
01311             UsedTheKeypress = TRUE;     // Just eat the keypress
01312             break;
01313     }
01314     TRACEUSER("wuerthne", _T("HandleSpecialNonStoryKeys returns %d"), UsedTheKeypress);
01315     return UsedTheKeypress;
01316 }
01317 
01318 
01319 
01320 /********************************************************************************************
01321 >   BOOL TextTool::HandleSpecialStoryKeys(KeyPress* pKeyPress, TextStory* pStory, CaretNode* pCaret)
01322 
01323     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
01324     Created:    1/06/95
01325     Inputs:     pKeyPress - pointer to a keypress object
01326                 pStory - points to the story with the caret
01327                 pCaret - points to the caret
01328     Returns:    TRUE if it handled the keypress, FALSE otherwise
01329     Purpose:    To handle special keypress events that apply at a story and/or caret
01330     SeeAlso:    TextTool::HandleSpecialNonStoryKeys
01331 ********************************************************************************************/
01332 
01333 BOOL TextTool::HandleSpecialStoryKeys(KeyPress* pKeyPress, TextStory* pStory, CaretNode* pCaret)
01334 {
01335     TRACEUSER("wuerthne", _T("HandleSpecialStoryKeys %d"), pKeyPress->GetVirtKey());
01336     ERROR3IF(pKeyPress == NULL, "KeyPress pointer was NULL");
01337     ERROR3IF(pStory == NULL, "Story pointer was NULL");
01338     ERROR3IF(pCaret == NULL, "Caret pointer was NULL");
01339     if ((pKeyPress == NULL) || (pStory == NULL) || (pCaret == NULL))
01340         return FALSE;
01341 
01342     BOOL IsNonCharEvent = !pKeyPress->IsChar(); // only use non-char events for hotkeys, but eat char events matching our hotkeys
01343     BOOL UsedTheKeypress = FALSE;
01344     BOOL Errored = FALSE;
01345 
01346     switch (pKeyPress->GetVirtKey())
01347     {
01348         case CAMKEY(HOME):
01349             // Move caret to start of line
01350             if (IsNonCharEvent)
01351             {
01352                 OpTextCaret* pOp = new OpTextCaret();
01353                 if (pOp != NULL)
01354                     pOp->DoMoveCaretHome(pKeyPress->IsAdjust(), pKeyPress->IsConstrain()); 
01355                 else
01356                     Errored = TRUE;
01357             }
01358             UsedTheKeypress = TRUE;
01359             break;
01360         case CAMKEY(END):
01361             // Move caret to end of line
01362             if (IsNonCharEvent)
01363             {
01364                 OpTextCaret* pOp = new OpTextCaret();
01365                 if (pOp != NULL)
01366                     pOp->DoMoveCaretEnd(pKeyPress->IsAdjust(), pKeyPress->IsConstrain()); 
01367                 else
01368                     Errored = TRUE;
01369             }
01370             UsedTheKeypress = TRUE;
01371             break;
01372         case CAMKEY(BACK):
01373             // delete character before caret, or selected chars if either exists
01374             if (IsNonCharEvent && (pStory->GetSelectionEnd() || pCaret->FindPrevVTNInStory() != NULL))
01375             {
01376                 OpTextFormat* pOp = new OpTextFormat();
01377                 if (pOp != NULL)
01378                     pOp->DoDeleteChar(OpTextFormat::BACKSPACE); 
01379                 else
01380                     Errored = TRUE;
01381             }
01382             UsedTheKeypress = TRUE;
01383             break;
01384         case CAMKEY(DELETE):
01385             // delete character before caret, or selected chars if either exists
01386             if (IsNonCharEvent && (pStory->GetSelectionEnd() || pCaret->FindNextVTNInStory() != pStory->FindLastVTN()))
01387             {
01388                 OpTextFormat* pOp = new OpTextFormat();
01389                 if (pOp != NULL)
01390                     pOp->DoDeleteChar(OpTextFormat::DEL); 
01391                 else
01392                     Errored = TRUE;
01393             }
01394             UsedTheKeypress = TRUE;
01395             break;
01396         case CAMKEY(LEFT):
01397             // Move/Select caret left one word/character
01398             if (IsNonCharEvent)
01399             {
01400                 TRACEUSER("wuerthne", _T("caret left"));
01401                 OpTextCaret* pOp = new OpTextCaret();
01402                 if (pOp != NULL)
01403                     pOp->DoMoveCaretLeft(pKeyPress->IsAdjust(), pKeyPress->IsConstrain());
01404                 else
01405                     Errored = TRUE;
01406             }
01407             UsedTheKeypress = TRUE;
01408             break;
01409         case CAMKEY(RIGHT):
01410             // Move/Select caret right one word/character
01411             if (IsNonCharEvent)
01412             {
01413                 OpTextCaret* pOp = new OpTextCaret();
01414                 if (pOp != NULL)
01415                     pOp->DoMoveCaretRight(pKeyPress->IsAdjust(), pKeyPress->IsConstrain());
01416                 else
01417                     Errored = TRUE;
01418             }
01419             UsedTheKeypress = TRUE;
01420             break;
01421         case CAMKEY(UP):
01422             // Move/Select caret up a line
01423             if (IsNonCharEvent)
01424             {
01425                 OpTextCaret* pOp = new OpTextCaret();
01426                 if (pOp != NULL)
01427                     pOp->DoMoveCaretUp(pKeyPress->IsAdjust());
01428                 else
01429                     Errored = TRUE;
01430             }
01431             UsedTheKeypress = TRUE;
01432             break;
01433         case CAMKEY(DOWN):
01434             // Move/Select caret down a line
01435             if (IsNonCharEvent)         
01436             {
01437                 OpTextCaret* pOp = new OpTextCaret();
01438                 if (pOp != NULL)
01439                     pOp->DoMoveCaretDown(pKeyPress->IsAdjust());
01440                 else
01441                     Errored = TRUE;
01442             }
01443             UsedTheKeypress = TRUE;
01444             break;
01445         case CAMKEY(C):
01446             // Swap case (was Control-W, now Control-Shift-C)
01447             if (pKeyPress->IsConstrain() && pKeyPress->IsAdjust() )
01448             {
01449                 if (IsNonCharEvent && TextStory::GetFocusStory()->GetCaret()->FindNextTextCharInStory() != NULL)
01450                 {
01451                     OpTextFormat* pOp = new OpTextFormat();
01452                     if (pOp != NULL)
01453                     {
01454                         pOp->DoSwapCase();
01455                     }
01456                     else
01457                         Errored = TRUE;
01458                 }
01459                 UsedTheKeypress = TRUE;
01460             }
01461             break;
01462         case CAMKEY(A):
01463             // Select all in the focus story, if there is one
01464             if (pKeyPress->IsConstrain() && !pKeyPress->IsAdjust())
01465             {
01466                 if (IsNonCharEvent)
01467                 {
01468                     OpTextSelection* pOp = new OpTextSelection();
01469                     if (pOp != NULL)
01470                         Errored = !pOp->DoSelectAllText(TextStory::GetFocusStory());
01471                     else
01472                         Errored = TRUE;
01473                 }
01474                 UsedTheKeypress = TRUE;
01475             }
01476             break;
01477 
01478         case CAMKEY(V):
01479             // Paste text from the clipboard into a selected text story
01480             if (pKeyPress->IsConstrain() && !pKeyPress->IsAdjust())
01481             {
01482                 if (IsNonCharEvent)
01483                 {
01484                     // is there a focus story?
01485                     OpTextPaste* pOp = new OpTextPaste();
01486                     if (pOp != NULL)
01487                         pOp->Do(NULL);
01488                 }
01489                 UsedTheKeypress = TRUE;
01490             }
01491             break;
01492 
01493         case CAMKEY(L):
01494             // Select all text on the line with the caret
01495             if (pKeyPress->IsConstrain() && !pKeyPress->IsAdjust())
01496             {
01497                 TextLine* pLine = (TextLine*)TextStory::GetFocusStory()->GetCaret()->FindParent(CC_RUNTIME_CLASS(TextLine));
01498                 if (pLine != NULL)
01499                 {
01500                     if (IsNonCharEvent)
01501                     {
01502                         OpTextSelection* pOp = new OpTextSelection();
01503                         if (pOp != NULL)
01504                             Errored = !pOp->DoSelectLineText();
01505                         else
01506                             Errored = TRUE;
01507                     }
01508                     UsedTheKeypress = TRUE;
01509                 }
01510             }
01511             break;
01512         case CAMKEY(EQUALS):
01513             // Increase kerning/tracking
01514             if (pKeyPress->IsConstrain() && !pKeyPress->IsAdjust() )
01515             {
01516                 UsedTheKeypress = TRUE;
01517                 Errored = IsNonCharEvent ? !IncreaseTrackKern() : FALSE;
01518             }
01519             break;
01520         case CAMKEY(MINUS):
01521             // Decrease kerning/tracking
01522             if (pKeyPress->IsConstrain() && !pKeyPress->IsAdjust() )
01523             {
01524                 UsedTheKeypress = TRUE;
01525                 Errored = IsNonCharEvent ? !DecreaseTrackKern() : FALSE;
01526             }
01527             break;
01528         case CAMKEY(RETURN):
01529             // Create a new TextLine and move the caret to it.
01530             if (!pKeyPress->IsAlternative() && !pKeyPress->IsConstrain())
01531             {
01532                 if (IsNonCharEvent)
01533                 {
01534                     OpTextFormat* pOp = new OpTextFormat();
01535                     if (pOp != NULL)
01536                         Errored = !pOp->DoReturn(OpTextFormat::INSERT);
01537                     else
01538                         Errored = TRUE;
01539                 }
01540                 UsedTheKeypress = TRUE;
01541             }
01542             break;
01543         case CAMKEY(TAB):
01544             if (!pKeyPress->IsAlternative() && !pKeyPress->IsConstrain())
01545             {
01546                 if (IsNonCharEvent)
01547                 {
01548                     /* Create a tab node */
01549                     OpTextFormat* pOp = new OpTextFormat();
01550                     if (pOp != NULL)
01551                         Errored = !pOp->DoTab();
01552                     else
01553                         Errored = TRUE;
01554                 }
01555                 UsedTheKeypress = TRUE;
01556             }
01557             break;
01558         case CAMKEY(ESCAPE):
01559             // Deselect the caret and select the text story
01560             if (!pKeyPress->IsAlternative() && !pKeyPress->IsConstrain())
01561             {
01562                 if (IsNonCharEvent)
01563                 {
01564                     pCaret->DeSelect(TRUE);
01565                     pStory->Select(TRUE);
01566                     GetApplication()->FindSelection()->Update(TRUE);
01567                 }
01568                 UsedTheKeypress = TRUE;
01569 
01570                 // Remove the focus story if it was empty
01571                 Errored = IsNonCharEvent ? !OpDeleteTextStory::RemoveEmptyFocusStory() : FALSE;
01572             }
01573             break;
01574     }
01575 
01576     if (Errored)
01577         InformError();
01578 
01579     return UsedTheKeypress;
01580 }
01581 
01582 
01583 
01584 /********************************************************************************************
01585 
01586 >   void TextTool::DisplayStatusBarHelp(DocCoord DocPos, Spread* pSpread, ClickModifiers ClickMods)
01587 
01588     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
01589     Created:    12/12/94
01590     Inputs:     DocPos - the document coordinate of the point to display help on
01591                 pSpread - pointer to the spread containing DocPos
01592                 ClickMods - the current click modifiers
01593     Outputs:    -
01594     Returns:    -
01595     Purpose:    Displays status help string for the given position in the status bar.
01596     SeeAlso:    TextTool::GetCurrentStatusText
01597 
01598 ********************************************************************************************/
01599 
01600 void TextTool::DisplayStatusBarHelp(DocCoord DocPos, Spread* pSpread, ClickModifiers ClickMods)
01601 {
01602     String_256 StatusMsg("");
01603 
01604     // Get a string from the underlying help function and display it.
01605     GetCurrentStatusText(&StatusMsg, pSpread, DocPos, ClickMods);
01606     GetApplication()->UpdateStatusBarText(&StatusMsg);                           
01607 }
01608 
01609 
01610 
01611 /********************************************************************************************
01612 
01613 >   virtual BOOL TextTool::GetStatusLineText(String_256* ptext, Spread* pSpread, DocCoord DocPos, ClickModifiers ClickMods)
01614 
01615     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
01616     Created:    12/12/94
01617     Inputs:     ptest - pointer to a string
01618                 pSpread points to a spread
01619                 DocPos points to a point in a document
01620                 ClickMods are the current click modifiers
01621     Outputs:    Updates the string in ptext
01622     Returns:    TRUE if the string was updates (FALSE if not updated)
01623     Purpose:    Returns the current status line help string
01624     SeeAlso:    TextTool::GetCurrentStatusText, Tool_v1::GetStatusLineText
01625 
01626 ********************************************************************************************/
01627 
01628 BOOL TextTool::GetStatusLineText(String_256* ptext, Spread* pSpread, DocCoord DocPos, ClickModifiers ClickMods)
01629 {
01630     // We can call the underlying help function to get a string and return the result.
01631     GetCurrentStatusText(ptext, pSpread, DocPos, ClickMods);
01632     return TRUE;
01633 }
01634 
01635 
01636 
01637 /********************************************************************************************
01638 
01639 >   void TextTool::GetCurrentStatusText(String_256* ptext, Spread* pSpread, DocCoord DocPos, ClickModifiers ClickMods)
01640 
01641     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
01642     Created:    12/12/94
01643     Inputs:     pSpread points to a spread
01644                 DocPos points to a point in a document
01645                 ClickMods are the current click modifiers
01646     Outputs:    Updates the string in ptext
01647     Returns:    -
01648     Purpose:    Selects a suitable string for the status line based on the current location
01649                 (as input via the parameters).
01650     SeeAlso:    TextTool::GetStatusLineText, TextTool::DisplayStatusBarHelp
01651 
01652 ********************************************************************************************/
01653 
01654 void TextTool::GetCurrentStatusText(String_256* ptext, Spread* pSpread, DocCoord DocPos, ClickModifiers ClickMods)
01655 {
01656     // If nothing is detected, we are over the document
01657     INT32 IDToUse = _R(IDS_TEXTTOOL_OVERDOCUMENT);
01658 
01659     // Is the caret in the focus story selected (ie, can the user type?)
01660     if (TextStory::GetFocusStory()!=NULL && TextStory::GetFocusStory()->GetCaret()!=NULL
01661             && TextStory::GetFocusStory()->GetCaret()->IsSelected())
01662     {
01663         IDToUse = _R(IDS_TEXTTOOL_OVERDOCUMENTC);
01664     }
01665     
01666     // Are we over a story or a path for a new story?
01667     TextStory* pTextStoryToSelect = NULL;
01668     if (IsPointNearUnselectedTextStory(pSpread, DocPos, &pTextStoryToSelect, NULL, TRUE))
01669     {
01670         if (pTextStoryToSelect != NULL)
01671             IDToUse = _R(IDS_TEXTTOOL_OVERTEXTSTORY);
01672     }
01673     else
01674     {
01675         NodePath* pPath = NULL;
01676         if (IsPointNearNonStoryPath(pSpread, DocPos, &pPath, TRUE))
01677             IDToUse = _R(IDS_TEXTTOOL_OVERNODEPATH);
01678     }
01679 
01680     // See if we are near any story path indent blobs
01681     BOOL LeftBlob = TRUE;
01682     TextStory* pFoundStory = NULL;
01683     if (!m_BlobPosList.FindBlobStory(pSpread, DocPos, &LeftBlob, &pFoundStory))
01684         InformError();
01685     else if (pFoundStory != NULL)
01686     {
01687         if (pFoundStory->GetTextPath() != NULL)
01688             IDToUse = _R(IDS_TEXTTOOL_DRAGPATHINDENT);
01689         else
01690             IDToUse = _R(IDS_TEXTTOOL_DRAGSTORYWIDTH);
01691     }
01692 
01693     ptext->Load(IDToUse);
01694 }
01695 
01696 
01697 
01698 /********************************************************************************************
01699 >   BOOL TextTool::ApplyBold()
01700 
01701     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
01702     Created:    20/04/95
01703     Inputs:     -
01704     Outputs:    -
01705     Returns:    TRUE/FALSE for success/failure
01706     Purpose:    Applies or removes the bold attribute from the current selection or caret.
01707 
01708 ********************************************************************************************/
01709 BOOL TextTool::ApplyBold()
01710 {
01711     AttrTxtBold* ApplyBoldAttrib = new AttrTxtBold();
01712     BOOL Success = ApplyBoldAttrib != NULL;
01713 
01714     if (Success)
01715     {
01716         // See wether bold is currently on or off across the selection
01717         AttrTxtBold* CurrentBoldAttrib = NULL;
01718         SelRange::CommonAttribResult AttrRes = GetApplication()->FindSelection()->
01719                     FindCommonAttribute(CC_RUNTIME_CLASS(AttrTxtBold), (NodeAttribute **)&CurrentBoldAttrib);
01720         if (AttrRes == SelRange::ATTR_COMMON)
01721             ApplyBoldAttrib->Value.BoldOn = !(CurrentBoldAttrib->Value.BoldOn);
01722         else
01723             ApplyBoldAttrib->Value.BoldOn = TRUE;
01724 
01725         // Apply the new bold attribute to the selection
01726         AttributeManager::AttributeSelected(ApplyBoldAttrib, NULL);
01727     }
01728 
01729     return Success;
01730 }
01731 
01732 
01733 
01734 /********************************************************************************************
01735 >   BOOL TextTool::ApplyItalic()
01736 
01737     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
01738     Created:    20/04/95
01739     Inputs:     -
01740     Outputs:    -
01741     Returns:    TRUE/FALSE for success/failure
01742     Purpose:    Applies or removes the italic attribute from the current selection or caret.
01743 
01744 ********************************************************************************************/
01745 BOOL TextTool::ApplyItalic()
01746 {
01747     AttrTxtItalic* ApplyItalicAttrib = new AttrTxtItalic();
01748     BOOL Success = ApplyItalicAttrib != NULL;
01749 
01750     if (Success)
01751     {
01752         // See wether italic is currently on or off across the selection
01753         AttrTxtItalic* CurrentItalicAttrib = NULL;
01754         SelRange::CommonAttribResult AttrRes = GetApplication()->FindSelection()->
01755                     FindCommonAttribute(CC_RUNTIME_CLASS(AttrTxtItalic), (NodeAttribute **)&CurrentItalicAttrib);
01756         if (AttrRes == SelRange::ATTR_COMMON)
01757             ApplyItalicAttrib->Value.ItalicOn = !(CurrentItalicAttrib->Value.ItalicOn);
01758         else
01759             ApplyItalicAttrib->Value.ItalicOn = TRUE;
01760 
01761         // Apply the new italic attribute to the selection
01762         AttributeManager::AttributeSelected(ApplyItalicAttrib, NULL);
01763     }
01764 
01765     return Success;
01766 }
01767 
01768 
01769 
01770 /********************************************************************************************
01771 >   BOOL TextTool::IncreaseTrackKern()
01772 
01773     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
01774     Created:    20/04/95
01775     Inputs:     -
01776     Outputs:    -
01777     Returns:    TRUE/FALSE for success/failure
01778     Purpose:    If there is a text selection then the tracking applied to it is increased by
01779                 10/1000's of an em
01780                 If there is no selection then a kern up of 50/1000 is inserted  
01781 
01782 ********************************************************************************************/
01783 BOOL TextTool::IncreaseTrackKern()
01784 {
01785     // Get pointers to focus story and its caret
01786     TextStory* pStory = TextStory::GetFocusStory();
01787     ERROR3IF(pStory == NULL, "IncreaseTrackKern called without a focus story!");
01788     BOOL Success = TRUE;
01789 
01790     if (pStory != NULL)
01791     {
01792         if (pStory->GetSelectionEnd() == NULL)
01793             Success = CommonApplyKern(50);
01794         else
01795             Success = CommonApplyTracking(10);
01796     }
01797 
01798     return Success;
01799 }
01800 
01801 
01802 
01803 /********************************************************************************************
01804 >   BOOL TextTool::DecreaseTrackKern()
01805 
01806     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
01807     Created:    20/04/95
01808     Inputs:     -
01809     Outputs:    -
01810     Returns:    TRUE/FALSE for success/failure
01811     Purpose:    If there is a text selection then the tracking applied to it is decreased by
01812                 10/1000's of an em
01813                 If there is no selection then a kern down of 50/1000 is inserted    
01814 
01815 ********************************************************************************************/
01816 BOOL TextTool::DecreaseTrackKern()
01817 {
01818     // Get pointers to focus story and its caret
01819     TextStory* pStory = TextStory::GetFocusStory();
01820     ERROR3IF(pStory == NULL, "DecreaseTrackKern called without a focus story!");
01821     BOOL Success = TRUE;
01822 
01823     if (pStory != NULL)
01824     {
01825         if (pStory->GetSelectionEnd() == NULL)
01826             Success = CommonApplyKern(-50);
01827         else
01828             Success = CommonApplyTracking(-10);
01829     }
01830 
01831     return Success;
01832 }
01833 
01834 
01835 
01836 /********************************************************************************************
01837 >   BOOL TextTool::CommonApplyTracking(MILLIPOINT ChangeAmmount)
01838 
01839     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
01840     Created:    21/04/95
01841     Inputs:     ChangeAmmount - ammount to increase/decrease the tracking by
01842     Outputs:    -
01843     Returns:    TRUE/FALSE for success/failure
01844     Purpose:    Applys/increases/descreases the tracking across the selected characters 
01845 
01846 ********************************************************************************************/
01847 BOOL TextTool::CommonApplyTracking(MILLIPOINT ChangeAmmount)
01848 {
01849     // Create a new attribute to apply (will replace any existing)
01850     AttrTxtTracking* ApplyTrackingAttrib = new AttrTxtTracking();
01851     BOOL Success = (ApplyTrackingAttrib != NULL);
01852 
01853     if (Success)
01854     {
01855         // Get the current tracking ammount across the selection
01856         AttrTxtTracking* CurrentTrackingAttrib = NULL;
01857         SelRange::CommonAttribResult AttrRes = GetApplication()->FindSelection()->
01858                     FindCommonAttribute(CC_RUNTIME_CLASS(AttrTxtTracking), (NodeAttribute **)&CurrentTrackingAttrib);
01859         if (AttrRes == SelRange::ATTR_COMMON)
01860             ApplyTrackingAttrib->Value.Tracking = CurrentTrackingAttrib->Value.Tracking + ChangeAmmount;
01861         else
01862             ApplyTrackingAttrib->Value.Tracking = ChangeAmmount;
01863 
01864         // Apply the increased attribute to the selection
01865         AttributeManager::AttributeSelected(ApplyTrackingAttrib, NULL);
01866     }
01867 
01868     return Success;
01869 }
01870 
01871 
01872 
01873 /********************************************************************************************
01874 >   BOOL TextTool::CommonApplyKern(MILLIPOINT ChangeAmmount)
01875 
01876     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
01877     Created:    21/04/95
01878     Inputs:     ChangeAmmount - ammount to increase/decrease the kern at the caret by
01879     Outputs:    -
01880     Returns:    TRUE/FALSE for success/failure
01881     Purpose:    Applys/increases/descreases the horizontal kern at the caret
01882 ********************************************************************************************/
01883 
01884 BOOL TextTool::CommonApplyKern(MILLIPOINT ChangeAmmount)
01885 {
01886     MILLIPOINT NewKernVal = ChangeAmmount;
01887 
01888     // Get the current kern ammount - check the node to the left of the caret
01889     TextStory* pStory = TextStory::GetFocusStory();
01890     CaretNode* pCaret = NULL;
01891     if (pStory != NULL)
01892         pCaret = pStory->GetCaret();
01893     if (pCaret != NULL)
01894     {
01895         VisibleTextNode* LastNode = pCaret->FindPrevAbstractTextCharInStory();
01896         if ((LastNode != NULL) && IS_A(LastNode, KernCode))
01897             NewKernVal += (((KernCode*)LastNode)->GetValue()).x;
01898     }
01899 
01900     // Invoke an operation to apply the kern
01901     OpDescriptor* OpDesc = OpDescriptor::FindOpDescriptor(CC_RUNTIME_CLASS(OpTextKern));
01902     if (OpDesc != NULL)
01903     {
01904         OpParam param(NewKernVal,0);
01905         OpDesc->Invoke(&param);
01906     }
01907 
01908     return TRUE;
01909 }
01910 
01911 
01912 
01913 /********************************************************************************************
01914 >   BOOL TextTool::OnIdle()
01915 
01916     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
01917     Created:    25/08/95
01918     Inputs:     -
01919     Outputs:    -
01920     Returns:    TRUE/FALSE for more/no more idles needed
01921     Purpose:    Called on idle events.  Used to update the infobar kern field after typing
01922 ********************************************************************************************/
01923 
01924 BOOL TextTool::OnIdle()
01925 {
01926     if (UpdateAfterTyping)
01927     {
01928         if (!pTextInfoBarOp->UpdateFieldsAfterTyping())
01929             InformError();
01930         UpdateAfterTyping = FALSE;
01931     }
01932 
01933     return FALSE;
01934 }
01935 
01936 
01937 
01938 /********************************************************************************************
01939 >   static BOOL TextTool::DelayingUpdate()
01940 
01941     Author:     Justin_Flude (Xara Group Ltd) <camelotdev@xara.com>
01942     Created:    19/9/96
01943     Returns:    TRUE if the text tool is saving up any updates to do when it is idle, FALSE
01944                 if it isn't or if the text tool is not the current tool.
01945     Purpose:    Let's the caller know if the text-tool has finished mucking about with
01946                 the document's modified flag.
01947     SeeAlso:    CCamSrvrItem::QueryTypeAhead
01948 ********************************************************************************************/
01949 
01950 BOOL TextTool::DelayingUpdate()
01951 {
01952     // Is the text tool the current tool?
01953     Tool* pTool = Tool::GetCurrent();
01954     if (!pTool || pTool->GetID() != TOOLID_TEXT) return FALSE;
01955 
01956     // Is it saving up some updates?
01957     return ((TextTool*) pTool)->UpdateAfterTyping;
01958 }
01959 
01960 
01961 
01962 /********************************************************************************************
01963 >   BOOL TextTool::RegisterDeadKeys()
01964 
01965     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
01966     Created:    12/10/95
01967     Inputs:     -
01968     Outputs:    -
01969     Returns:    TRUE/FALSE for success/failure
01970     Purpose:    Registers all the dead keys virtual codes with the keypress system
01971     SeeAlso:    TextTool::RemoveDeadKeys
01972 ********************************************************************************************/
01973 BOOL TextTool::RegisterDeadKeys()
01974 {
01975     BOOL ok = TRUE;
01976     
01977     if (UseDeadKeys)
01978     {
01979         if (ok)
01980             ok = RegisterAdditionalVirtKey(&GraveVirtKey, (TCHAR)('`'));
01981         if (ok)
01982             ok = RegisterAdditionalVirtKey(&AcuteVirtKey, (TCHAR)0x27); // Single Quote
01983         if (ok)
01984             ok = RegisterAdditionalVirtKey(&TildeVirtKey, (TCHAR)('~'));
01985         if (ok)
01986             ok = RegisterAdditionalVirtKey(&HatVirtKey, (TCHAR)('^'));
01987         if (ok)
01988             ok = RegisterAdditionalVirtKey(&ColonVirtKey, (TCHAR)(':'));
01989         if (ok)
01990             ok = RegisterAdditionalVirtKey(&AtVirtKey, (TCHAR)('@'));
01991         if (ok)
01992             ok = RegisterAdditionalVirtKey(&CommaVirtKey, (TCHAR)(','));
01993         if (ok)
01994             ok = RegisterAdditionalVirtKey(&SlashVirtKey, (TCHAR)('/'));
01995     }
01996 
01997     return ok;
01998 }
01999 
02000 
02001 
02002 /********************************************************************************************
02003 >   BOOL TextTool::RemoveDeadKeys()
02004 
02005     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
02006     Created:    12/10/95
02007     Inputs:     -
02008     Outputs:    -
02009     Returns:    TRUE
02010     Purpose:    Deregisters all the dead keys
02011     SeeAlso:    TextTool::RegisterDeadKeys
02012 ********************************************************************************************/
02013 BOOL TextTool::RemoveDeadKeys()
02014 {
02015     if (GraveVirtKey != NULL)
02016     {
02017         KeyPress::RemoveVirtualKeyCode(GraveVirtKey->GetVirtKey(), GraveVirtKey->GetUnicode());
02018         delete GraveVirtKey;
02019     }
02020     if (AcuteVirtKey != NULL)
02021     {
02022         KeyPress::RemoveVirtualKeyCode(AcuteVirtKey->GetVirtKey(), AcuteVirtKey->GetUnicode());
02023         delete AcuteVirtKey;
02024     }
02025     if (TildeVirtKey != NULL)
02026     {
02027         KeyPress::RemoveVirtualKeyCode(TildeVirtKey->GetVirtKey(), TildeVirtKey->GetUnicode());
02028         delete TildeVirtKey;
02029     }
02030     if (HatVirtKey != NULL)
02031     {
02032         KeyPress::RemoveVirtualKeyCode(HatVirtKey->GetVirtKey(), HatVirtKey->GetUnicode());
02033         delete HatVirtKey;
02034     }
02035     if (ColonVirtKey != NULL)
02036     {
02037         KeyPress::RemoveVirtualKeyCode(ColonVirtKey->GetVirtKey(), ColonVirtKey->GetUnicode());
02038         delete ColonVirtKey;
02039     }
02040     if (AtVirtKey != NULL)
02041     {
02042         KeyPress::RemoveVirtualKeyCode(AtVirtKey->GetVirtKey(), AtVirtKey->GetUnicode());
02043         delete AtVirtKey;
02044     }
02045     if (CommaVirtKey != NULL)
02046     {
02047         KeyPress::RemoveVirtualKeyCode(CommaVirtKey->GetVirtKey(), CommaVirtKey->GetUnicode());
02048         delete CommaVirtKey;
02049     }
02050     if (SlashVirtKey != NULL)
02051     {
02052         KeyPress::RemoveVirtualKeyCode(SlashVirtKey->GetVirtKey(), SlashVirtKey->GetUnicode());
02053         delete SlashVirtKey;
02054     }
02055 
02056     return TRUE;
02057 }
02058 
02059 
02060 
02061 /********************************************************************************************
02062 
02063 >   BOOL TextTool::RegisterAdditionalVirtKey(KeyPress** pKey, TCHAR ch)
02064 
02065     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
02066     Created:    11/10/95
02067     Inputs:     pKey - pointer to a KeyPress object
02068                 ch - Unicode value of the required Virtual Key
02069     Outputs:    pKey is the KeyPress object representing the virtual key
02070     Purpose:    Registers a requirement for a virtual key message with the KeyPress system
02071 
02072 ********************************************************************************************/
02073 
02074 BOOL TextTool::RegisterAdditionalVirtKey(KeyPress** pKey, TCHAR ch)
02075 {
02076     BOOL ok = TRUE;
02077 
02078     *pKey = KeyPress::ConstructVK(ch);
02079     ok = (*pKey != NULL);
02080     if (ok)
02081         ok = KeyPress::AddVirtualKeyCode((*pKey)->GetVirtKey(), (*pKey)->GetUnicode());
02082 
02083     return ok;
02084 }
02085 
02086 
02087 /********************************************************************************************
02088 >   BOOL TextTool::HandleDeadKeys(KeyPress* pKeyPress, WCHAR* pNewUnicode)
02089 
02090     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
02091     Created:    10/10/95
02092     Inputs:     pKeyPress - points to the keypress object
02093     Outputs:    pNewUnicode contains the unicode value of the keypress, possibly changed to
02094                 an accented version of the pressed key.
02095     Returns:    TRUE if keypress used, FALSE if not
02096     Purpose:    Flags 'dead-keys'.  Attempts to apply an accent onto the next character.
02097 ********************************************************************************************/
02098 BOOL TextTool::HandleDeadKeys(KeyPress* pKeyPress, WCHAR* pNewUnicode)
02099 {
02100     BOOL UsedKey = FALSE;
02101     *pNewUnicode = pKeyPress->GetUnicode();
02102 //  WCHAR OldUnicode = *pNewUnicode;
02103 
02104     if (!UseDeadKeys)
02105         return FALSE;
02106 
02107     // Is this a dead key?
02108     if ( TestForDeadKey(pKeyPress, GraveVirtKey)    ||
02109          TestForDeadKey(pKeyPress, AcuteVirtKey)    ||
02110          TestForDeadKey(pKeyPress, HatVirtKey)      ||
02111          TestForDeadKey(pKeyPress, TildeVirtKey)    ||
02112          TestForDeadKey(pKeyPress, ColonVirtKey)    ||
02113          TestForDeadKey(pKeyPress, AtVirtKey)       ||
02114          TestForDeadKey(pKeyPress, SlashVirtKey)    ||
02115          TestForDeadKey(pKeyPress, CommaVirtKey) )
02116     {
02117         UsedKey = TRUE;
02118     }
02119 
02120     // This isn't a dead key, was the previous?
02121     if (!UsedKey && (PreviousDeadKey != NULL) && IsUsableUnicode(pKeyPress->GetUnicode()) )
02122     {
02123         // Get a pointer to the appropiate accent map
02124         UINT32* pMapArrays = NULL;
02125         if (PreviousDeadKey == GraveVirtKey)
02126             pMapArrays = GraveArray;
02127         else if (PreviousDeadKey == AcuteVirtKey)
02128             pMapArrays = AcuteArray;
02129         else if (PreviousDeadKey == HatVirtKey)
02130             pMapArrays = HatArray;
02131         else if (PreviousDeadKey == TildeVirtKey)
02132             pMapArrays = TildeArray;
02133         else if (PreviousDeadKey == ColonVirtKey)
02134             pMapArrays = ColonArray;
02135         else if (PreviousDeadKey == AtVirtKey)
02136             pMapArrays = AtArray;
02137         else if (PreviousDeadKey == SlashVirtKey)
02138             pMapArrays = SlashArray;
02139         else if (PreviousDeadKey == CommaVirtKey)
02140             pMapArrays = CommaArray;
02141         else
02142         {
02143             ERROR3("Unknown dead key");
02144             pMapArrays = NULL;
02145         }
02146 
02147         // See if the pressed key maps onto an accented version
02148         if (pMapArrays != NULL)
02149         {
02150             BOOL Found = FALSE;
02151             while (!Found && (*pMapArrays != 0))
02152             {
02153                 if (*pMapArrays == (UINT32)(*pNewUnicode))
02154                 {                                     
02155                     *pNewUnicode = *(pMapArrays+1);
02156                     Found = TRUE;
02157                 }
02158                 pMapArrays += 2;
02159             }
02160         }
02161 
02162         PreviousDeadKey = 0;
02163         UsedKey = FALSE;
02164     }
02165 
02166     return UsedKey;
02167 }
02168 
02169 
02170 
02171 /********************************************************************************************
02172 >   BOOL TextTool::TestForDeadKey(KeyPress* pKeyPress, KeyPress* pDeadKeyPress)
02173 
02174     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
02175     Created:    11/10/95
02176     Inputs:     pKeyPress - points to the current keypress object
02177                 pDeadKeyPress - points to a know dead key to test against
02178     Outputs:    
02179     Returns:    TRUE if keypress used, FALSE if not
02180     Purpose:    Flags 'dead-keys'.  Attempts to apply an accent onto the next character.
02181 ********************************************************************************************/
02182 BOOL TextTool::TestForDeadKey(KeyPress* pKeyPress, KeyPress* pDeadKeyPress)
02183 {
02184     if (    pDeadKeyPress != NULL && 
02185             pDeadKeyPress->GetVirtKey() != CAMKEY(CC_NONE) &&
02186             !pDeadKeyPress->IsConstrain() &&
02187             pKeyPress->IsConstrain() &&
02188             pDeadKeyPress->IsAdjust() == pKeyPress->IsAdjust() &&
02189             pDeadKeyPress->IsAlternative() == pKeyPress->IsAlternative() &&
02190             pDeadKeyPress->GetVirtKey() == pKeyPress->GetVirtKey()
02191         )
02192     {
02193         PreviousDeadKey = pDeadKeyPress;
02194         return TRUE;
02195     }
02196     else
02197         return FALSE;
02198 }
02199 
02200 
02201 
02202 /********************************************************************************************
02203 >   BOOL TextTool::IsUsableUnicode(WCHAR CodeValue)
02204 
02205     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
02206     Created:    10/10/95
02207     Inputs:     CodeValue - a number representing a unicode character
02208     Outputs:    -
02209     Returns:    TRUE if the text tool accepts the character, FALSE if it dosen't
02210     Purpose:    Central place to filter out unicode greater than 255.
02211 ********************************************************************************************/
02212 BOOL TextTool::IsUsableUnicode(WCHAR CodeValue)
02213 {
02214     return ( ((CodeValue>=32) && (CodeValue<=126)) || ((CodeValue>=128) && (CodeValue<=255)) );
02215 }
02216 
02217 
02218 
02219 /********************************************************************************************
02220 >   void TextTool::LocaleChanged()
02221 
02222     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
02223     Created:    12/10/95
02224     Inputs:     -
02225     Outputs:    -
02226     Returns:    -
02227     Purpose:    Called when the machines locale changes.  Re-registers the dead keys as their
02228                 virtual key code may have changed.
02229 ********************************************************************************************/
02230 void TextTool::LocaleChanged()
02231 {
02232     RemoveDeadKeys();
02233     if (!RegisterDeadKeys())
02234         InformError();
02235 }
02236 
02237 
02238 
02239 /********************************************************************************************
02240 >   BOOL TextTool::OnToolSelect()
02241 
02242     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
02243     Created:    3/1/96
02244     Inputs:     -
02245     Outputs:    -
02246     Returns:    FALSE if an error occured, TRUE if tool selected ok
02247     Purpose:    Called to do processing required when text tool is selected
02248 ********************************************************************************************/
02249 BOOL TextTool::OnToolSelect()
02250 {
02251     BOOL ok = TRUE;
02252     
02253     // Create the tools cursors
02254     ok = CreateCursors();
02255     if (ok)
02256     {
02257         CurrentCursorID = CursorStack::GPush(pcNormalTextCursor, FALSE);
02258         pcCurrentCursor = pcNormalTextCursor;
02259         IsBlankCursorUp = FALSE;
02260     }
02261     else
02262         pcCurrentCursor = NULL;
02263 
02264     // This tool is now the current one
02265     CurrentTool = TRUE;
02266     UpdateAfterTyping = FALSE;
02267     PreviousDeadKey = 0;
02268 
02269     // Say which blobs should be displayed
02270     BlobManager* BlobMgr = GetApplication()->GetBlobManager();
02271     if (BlobMgr != NULL)
02272     {
02273         // Decide which blobs we will display
02274         BlobStyle MyBlobs;
02275         MyBlobs.Object = TRUE;
02276         
02277         // Tell the blob manager
02278         BlobMgr->ToolInterest(MyBlobs);
02279     }
02280 
02281     // Render on the tool blobs
02282     if (ok)
02283         ok = m_BlobPosList.BuildFromSelection();
02284     if (ok && m_BlobPosList.GetBlobSpread()!=NULL)
02285         BlobMgr->RenderToolBlobsOn(this, m_BlobPosList.GetBlobSpread(), NULL);
02286     
02287     // Create and display the tool's info bar
02288     if (ok)
02289         ok = pTextInfoBarOp->Create();
02290     pTextInfoBarOp->pTextTool = this;
02291 
02292     // Alias the paste operation
02293     OpDescriptor* pOpDesc = OpDescriptor::FindOpDescriptor(OPTOKEN_PASTE);  
02294     pOpDesc->AliasOperation(CC_RUNTIME_CLASS(OpTextPaste),NULL,0);
02295 
02296     // Ensure the dead key mappings are correct
02297     LocaleChanged();
02298 
02299     // Give the input focus to the story with a subselection, as long as it is the only sub-selected story
02300     SelRange* pSelRange = GetApplication()->FindSelection();
02301     Node* pSel = pSelRange->FindFirst();
02302 //  BOOL SelectedVTN = FALSE;
02303     TextStory* pSelStory = NULL;
02304     while (pSel != NULL)
02305     {
02306         // if it's a text object get the story it is in
02307         TextStory* pParentStory = NULL;
02308         if (pSel->IsABaseTextClass())
02309         {
02310             if (IS_A(pSel,TextStory))
02311                 pParentStory = (TextStory*)pSel;
02312             else                
02313                 pParentStory = (TextStory*)pSel->FindParent(CC_RUNTIME_CLASS(TextStory));
02314         }
02315 
02316         if (pSelStory==NULL)
02317         {
02318             // Set the selected story pointer
02319             if (pSel->IsAVisibleTextNode())
02320                 pSelStory = pParentStory;
02321         }
02322         else
02323         {
02324             // See if we have found two selected stories
02325             if (pSelStory!=pParentStory)
02326             {
02327                 pSelStory = NULL;
02328                 break;
02329             }
02330         }
02331 
02332         pSel = pSelRange->FindNext(pSel);
02333     }
02334     if (pSelStory != NULL)
02335         TextStory::SetFocusStory(pSelStory);
02336 
02337     // See if there is just one selected TextStory and give it the focus
02338     // if it had the focus when we left the tool last time.  Also selected the caret within it 
02339     if ((pLastFocusStory!= NULL) && (pSelStory==NULL) && (GetApplication()->FindSelection()->Count()==1))
02340     {
02341         Node* pSelected = GetApplication()->FindSelection()->FindFirst();
02342         if (IS_A(pSelected,TextStory) && (pSelected == pLastFocusStory))
02343         {
02344             TextStory* pTS = (TextStory*)pSelected;
02345             CaretNode* pCaret = pTS->GetCaret();
02346             if (pCaret != NULL)
02347             {
02348                 pTS->DeSelect(TRUE);
02349                 pCaret->Select(TRUE);
02350                 TextStory::SetFocusStory(pTS);
02351                 GetApplication()->FindSelection()->Update(TRUE);
02352             }
02353         }
02354     }
02355 
02356     return ok;
02357 }
02358 
02359 
02360 
02361 /********************************************************************************************
02362 >   BOOL TextTool::OnToolDeselect()
02363 
02364     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
02365     Created:    3/1/96
02366     Inputs:     -
02367     Outputs:    -
02368     Returns:    FALSE if an error occured, TRUE if tool deselected ok
02369     Purpose:    Called to do processing required when text tool is deselected
02370 ********************************************************************************************/
02371 BOOL TextTool::OnToolDeselect()
02372 {
02373     BOOL ok = TRUE;
02374 
02375     // Remove the info bar from view by deleting the actual underlying window
02376     pTextInfoBarOp->Delete();   
02377 
02378     // Destroy the tool's cursors, if they exist, having stopped displaying them
02379     if (pcCurrentCursor != NULL)
02380     {
02381         CursorStack::GPop(CurrentCursorID);
02382         pcCurrentCursor = NULL;
02383         CurrentCursorID = 0;
02384     }
02385     DestroyCursors();
02386 
02387     // If a caret is selected alone then select its parent text story instead
02388     GetApplication()->FindSelection()->SetGag(FALSE);
02389     TextStory* pTS = TextStory::GetFocusStory();
02390     if (pTS != NULL)
02391     {
02392         // Move the selection up
02393         // If the caret is selected and there is no other selection
02394         CaretNode* pCaret = pTS->GetCaret();
02395         VisibleTextNode* pSel = pTS->GetSelectionEnd();
02396         if (pCaret->IsSelected() && pSel==NULL)
02397         {
02398             pCaret->DeSelect(TRUE);
02399             pTS->Select(TRUE);
02400             GetApplication()->FindSelection()->Update(TRUE);
02401         }
02402 
02403         ok = OpDeleteTextStory::RemoveEmptyFocusStory();
02404     }
02405 
02406     // render off the tool blobs
02407     if (m_BlobPosList.GetBlobSpread()!=NULL)
02408         m_BlobPosList.RenderAndRemoveAll();
02409 
02410     // ensure any tool object blobs are removed.
02411     BlobManager* BlobMgr = GetApplication()->GetBlobManager();
02412     if (BlobMgr != NULL)
02413     {
02414         BlobStyle bsRemoves;
02415         bsRemoves.ToolObject = TRUE;
02416         BlobMgr->RemoveInterest(bsRemoves);
02417     }
02418 
02419     // Clear the focus, remembering what it was for possible later reselection
02420     pLastFocusStory = TextStory::GetFocusStory();
02421     TextStory::SetFocusStory(NULL);
02422 
02423     // make sure the ruler is redrawn if we had claimed it
02424     TextInfoBarOp::ReleaseRuler();
02425     CurrentTool = FALSE;
02426 
02427     return ok;
02428 }
02429 
02430 
02431 
02432 /********************************************************************************************
02433 >   void TextTool::RenderToolBlobs(Spread* pSpread, DocRect* pClipRect)
02434 
02435     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
02436     Created:    5/2/96
02437     Inputs:     pSpread - the spread to render blobs on
02438                 pClipRect - blob rendering clipping rectangle, NULL for no clipping
02439     Outputs:    -
02440     Returns:    -
02441     Purpose:    Called to render the tool blobs for the text tool.
02442 ********************************************************************************************/
02443 void TextTool::RenderToolBlobs(Spread* pSpread, DocRect* pClipRect)
02444 {
02445     m_BlobPosList.RenderAllBlobs(pSpread, pClipRect);
02446 
02447 // ARGH! We may need to do this! Phil (15/12/2003)
02448 // See old implementation of RenderToolBlobsOff below.
02449 //  if (!bRenderingOn)
02450 //      m_BlobPosList.DeleteAll();
02451 }
02452 
02453 
02454 
02455 /********************************************************************************************
02456 >   BOOL TextTool::SelectionHasChanged()
02457 
02458     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
02459     Created:    5/2/96
02460     Inputs:     -
02461     Outputs:    -
02462     Returns:    TRUE if all ok, FALSE if error occured
02463     Purpose:    Called when the selection has changed.  Ensures tool blobs are up-to-date
02464 ********************************************************************************************/
02465 BOOL TextTool::SelectionHasChanged()
02466 {
02467     BOOL ok = TRUE;
02468     
02469     // Render the old blobs off
02470     if (ok)
02471         ok = m_BlobPosList.RenderAllBlobs();
02472 
02473     // Reconstruct the list of blobs
02474     if (ok)
02475         ok = m_BlobPosList.BuildFromSelection();
02476 
02477     // Render the new blobs on
02478     if (ok)
02479         ok = m_BlobPosList.RenderAllBlobs();
02480 
02481     return ok;
02482 }
02483 
02484 
02485 
02486 /********************************************************************************************
02487 >   void TextTool::RenderToolBlobsOff(Spread* pSpread, DocRect* Rect)
02488 
02489     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
02490     Created:    23/8/96
02491     Inputs:     pSpread - pointer to a spread to render onto
02492                 Rect - render clipping rectangle
02493     Outputs:    -
02494     Returns:    -
02495     Purpose:    Renders off tool blobs.
02496 ********************************************************************************************/
02497 //void TextTool::RenderToolBlobsOff(Spread* pSpread, DocRect* Rect)
02498 //{
02499 //  m_BlobPosList.RenderAndRemoveAll();
02500 //}
02501 
02502 
02503 
02504 
02505 
02506 
02507 
02508 /********************************************************************************************
02509 >   TextToolBlobPosItem::TextToolBlobPosItem(DocCoord Left, DocCoord Right, BOOL Connected, TextStory* pStory)
02510 
02511     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
02512     Created:    5/2/96
02513     Inputs:     Left - the left hand blobs position
02514                 Right - the right hand blobs position
02515                 Connected - TRUE if the two blobs are to be rendered with a line between them
02516                 pStory - the story that this blob is being rendered for
02517     Outputs:    -
02518     Returns:    -
02519     Purpose:    Constructor - sets member variables to the parameters.
02520 ********************************************************************************************/
02521 TextToolBlobPosItem::TextToolBlobPosItem(DocCoord Left, DocCoord Right, BOOL Connected, TextStory* pStory)
02522 {
02523     ERROR3IF(pStory==NULL, "NULL story pointer");
02524     
02525     m_LeftBlobPos = Left;
02526     m_RightBlobPos = Right;
02527     m_pTextStory = pStory;
02528     m_BlobsConnected = Connected;
02529 }
02530 
02531 
02532 
02533 /********************************************************************************************
02534 >   TextToolBlobPosItem::~TextToolBlobPosItem()
02535 
02536     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
02537     Created:    5/2/96
02538     Inputs:     -
02539     Outputs:    -
02540     Returns:    -
02541     Purpose:    Destructor
02542 ********************************************************************************************/
02543 TextToolBlobPosItem::~TextToolBlobPosItem()
02544 {
02545 
02546 }
02547 
02548 
02549 
02550 
02551 
02552 
02553 /********************************************************************************************
02554 >   TextToolBlobPosList::TextToolBlobPosList()
02555 
02556     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
02557     Created:    5/2/96
02558     Inputs:     -
02559     Outputs:    -
02560     Returns:    -
02561     Purpose:    Constructor - sets member variables to defaults
02562 ********************************************************************************************/
02563 TextToolBlobPosList::TextToolBlobPosList()
02564 {
02565     m_pSelSpread = NULL;
02566 }
02567 
02568 
02569 
02570 /********************************************************************************************
02571 >   TextToolBlobPosList::~TextToolBlobPosList()
02572 
02573     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
02574     Created:    5/2/96
02575     Inputs:     -
02576     Outputs:    -
02577     Returns:    -
02578     Purpose:    Destructor
02579 ********************************************************************************************/
02580 TextToolBlobPosList::~TextToolBlobPosList()
02581 {
02582     DeleteAll();
02583 }
02584 
02585 
02586 
02587 /********************************************************************************************
02588 >   BOOL TextToolBlobPosList::BuildFromSelection()
02589 
02590     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
02591     Created:    5/2/96
02592     Inputs:     -
02593     Outputs:    -
02594     Returns:    TRUE for success, FALSE if an error occured
02595     Purpose:    Builds a list of text tool blob positions based on the current selection.
02596 ********************************************************************************************/
02597 BOOL TextToolBlobPosList::BuildFromSelection()
02598 {
02599     // Delete what we currently have
02600     DeleteAll();
02601     m_pSelSpread = NULL;
02602 
02603     // Scan the selection for selected text stories & text stories with a sub-selection
02604     SelRange* pSel = GetApplication()->FindSelection();
02605     Node* pCurrent = pSel->FindFirst();
02606     TextStory* pLastSelStory = NULL;
02607     while (pCurrent!=NULL)
02608     {
02609         // Are we interested in this item?
02610         if (pCurrent->IsABaseTextClass())
02611         {
02612             TextStory* pCurrentStory = ((BaseTextClass*)pCurrent)->FindParentStory();
02613 
02614             if (pCurrentStory!=NULL && pCurrentStory!=pLastSelStory && pCurrentStory->IsWordWrapping())
02615             {
02616                 DocCoord LeftBlob;
02617                 DocCoord RightBlob;
02618 
02619                 if (pCurrentStory->GetTextPath() != NULL)
02620                 {
02621                     LeftBlob = pCurrentStory->GetLeftIndentPos();
02622                     RightBlob = pCurrentStory->GetRightIndentPos();
02623                 }
02624                 else
02625                 {
02626                     const Matrix* pStoryMat = pCurrentStory->GetpStoryMatrix();
02627                     LeftBlob = DocCoord(0,0);
02628                     RightBlob = DocCoord(pCurrentStory->GetStoryWidth(),0);
02629                     pStoryMat->transform(&LeftBlob);
02630                     pStoryMat->transform(&RightBlob);
02631                 }
02632 
02633                 Spread* pSpread = pCurrentStory->FindParentSpread();
02634 
02635                 // Add blob to list
02636                 ERROR3IF(pSpread==NULL, "Story didn't have a parent spread!");
02637                 if (pSpread!=NULL)
02638                 {
02639                     // Construct a list item
02640                     TextToolBlobPosItem* pNewItem = new TextToolBlobPosItem(LeftBlob, RightBlob, (pCurrentStory->GetTextPath()==NULL), pCurrentStory);
02641                     if (pNewItem==NULL)
02642                         return FALSE;
02643 
02644                     // Add it to the list
02645                     AddTail(pNewItem);
02646 
02647                     // Update spread pointer
02648                     ERROR3IF(m_pSelSpread!=NULL && m_pSelSpread!=pSpread, "Selection crosses spreads!  Text tool blobs wont work!");
02649                     m_pSelSpread = pSpread;
02650                 }
02651 
02652                 pLastSelStory = pCurrentStory;
02653             }
02654         }
02655 
02656         pCurrent = pSel->FindNext(pCurrent);
02657     }
02658 
02659     return TRUE;
02660 }
02661 
02662 
02663 
02664 /********************************************************************************************
02665 >   BOOL TextToolBlobPosList::RenderAllBlobs(Spread* pSpread = NULL, DocRect* pClipRect = NULL)
02666 
02667     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
02668     Created:    5/2/96
02669     Inputs:     pSpread - spread to render blobs for, NULL for all spreads
02670                 pClipRect - rendering clipping rectangle, NULL for no clipping
02671     Outputs:    -
02672     Returns:    TRUE for success, FALSE if an error occured
02673     Purpose:    Renders all the blobs currently in the list (optionally for a given spread)
02674 ********************************************************************************************/
02675 BOOL TextToolBlobPosList::RenderAllBlobs(Spread* pSpread, DocRect* pClipRect)
02676 {
02677     TextToolBlobPosItem* pItem = (TextToolBlobPosItem*) GetHead();
02678 
02679     // If no spread specified then use current, only render onto current spread
02680     if (pSpread==NULL)
02681         pSpread = m_pSelSpread;
02682     if (m_pSelSpread!=pSpread)
02683         return TRUE;
02684 
02685     while (pItem!=NULL)
02686     {
02687         RenderBlobPair(pSpread, pClipRect, pItem);
02688 
02689         pItem = (TextToolBlobPosItem*) GetNext(pItem);
02690     }
02691 
02692     return TRUE;
02693 }
02694 
02695 
02696 
02697 /********************************************************************************************
02698 >   static void TextToolBlobPosList::RenderBlobPair(Spread* pSpread, DocRect* pClipRect, TextToolBlobPosItem* pPair)
02699 
02700     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
02701     Created:    20/8/96
02702     Inputs:     pSpread - spread to render blobs for
02703                 pClipRect - rendering clipping rectangle
02704                 pPair - the TextToolBlobPosItem describing the blob pair
02705     Outputs:    -
02706     Returns:    TRUE for success, FALSE if an error occured
02707     Purpose:    Renders a pair of blobs (left, right and connecting line)
02708 ********************************************************************************************/
02709 void TextToolBlobPosList::RenderBlobPair(Spread* pSpread, DocRect* pClipRect, TextToolBlobPosItem* pPair)
02710 {
02711     ERROR3IF(pPair==NULL, "NULL pointer");
02712 
02713     if (pPair!=NULL)
02714     {
02715         // This code is very similar to RenderBlob but avoids having to create 3 times as
02716         // the number of RenderOnTop loops.
02717         RenderRegion* pRegion = DocView::RenderOnTop(pClipRect, pSpread, ClippedEOR);
02718         while (pRegion != NULL)
02719         {
02720             // Draw the two blobs
02721             pRegion->SetLineColour(COLOUR_NONE);
02722             pRegion->SetFillColour(COLOUR_BEZIERBLOB);
02723             pRegion->DrawBlob(pPair->GetLeftBlobPos(), BT_UNSELECTED);
02724             pRegion->DrawBlob(pPair->GetRightBlobPos(), BT_UNSELECTED);
02725 
02726             // Draw the connecting line
02727             if (pPair->IsConnected())
02728             {
02729                 pRegion->SetLineColour(COLOUR_BEZIERBLOB);
02730                 pRegion->DrawLine(pPair->GetLeftBlobPos(), pPair->GetRightBlobPos());
02731             }
02732 
02733             // Get the next region in the list
02734             pRegion = DocView::GetNextOnTop(pClipRect);
02735         }
02736     }
02737 }
02738 
02739 
02740 
02741 /********************************************************************************************
02742 >   static void TextToolBlobPosList::RenderBlob(Spread* pSpread, DocRect* pClipRect, DocCoord BlobPos)
02743 
02744     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
02745     Created:    22/2/96
02746     Inputs:     pSpread - spread to render blobs for
02747                 pClipRect - rendering clipping rectangle
02748                 BlobPos - the position to render the blob
02749     Outputs:    -
02750     Returns:    TRUE for success, FALSE if an error occured
02751     Purpose:    Renders one text origin blob
02752 ********************************************************************************************/
02753 void TextToolBlobPosList::RenderBlob(Spread* pSpread, DocRect* pClipRect, DocCoord BlobPos)
02754 {
02755     RenderRegion* pRegion = DocView::RenderOnTop(pClipRect, pSpread, ClippedEOR);
02756     while (pRegion != NULL)
02757     {
02758         // Draw a Cross Hair
02759         pRegion->SetLineColour(COLOUR_NONE);
02760         pRegion->SetFillColour(COLOUR_BEZIERBLOB);
02761         pRegion->DrawBlob(BlobPos, BT_UNSELECTED);
02762 
02763         // Get the next region in the list
02764         pRegion = DocView::GetNextOnTop(pClipRect);
02765     }
02766 }
02767 
02768 
02769 
02770 /********************************************************************************************
02771 >   BOOL TextToolBlobPosList::FindBlobStory(Spread* pSpread, DocCoord PointerPos, BOOL* pLeft, TextStory** pStory)
02772 
02773     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
02774     Created:    7/2/96
02775     Inputs:     pSpread - spread PointerPos is on
02776                 PointerPos - the coord to check
02777     Outputs:    pLeft - TRUE if point over left blob, FALSE if over right blob (Can be NULL)
02778                 pStory - pointer to found story, NULL if no story
02779     Returns:    TRUE/FALSE for success/failure
02780     Purpose:    Finds the textstory that the mouse is over.
02781 ********************************************************************************************/
02782 BOOL TextToolBlobPosList::FindBlobStory(Spread* pSpread, DocCoord PointerPos, BOOL* pLeft, TextStory** pStory)
02783 {
02784     // Is the spread the same as our current one?
02785     ERROR2IF(pSpread!=m_pSelSpread && m_pSelSpread!=NULL, FALSE, "Different spreads");
02786     ERROR2IF(pStory==NULL, FALSE, "NULL output parameter");
02787 
02788     // Work backwards through our blob list, thus in hit test order.
02789     TextToolBlobPosItem* pItem = (TextToolBlobPosItem*) GetTail();
02790     while (pItem!=NULL)
02791     {
02792         // Get a rectangle surrounding the left blob
02793         DocRect BlobBounds;
02794         DocCoord BlobPos = pItem->GetLeftBlobPos();
02795         GetApplication()->GetBlobManager()->GetBlobRect(BlobPos, &BlobBounds);
02796         if (BlobBounds.ContainsCoord(PointerPos))
02797         {
02798             if (pLeft!=NULL)
02799                 *pLeft = TRUE;
02800             *pStory = pItem->GetBlobStory();
02801 
02802             return TRUE;
02803         }
02804 
02805         // Test the right blob
02806         BlobPos = pItem->GetRightBlobPos();
02807         GetApplication()->GetBlobManager()->GetBlobRect(BlobPos, &BlobBounds);
02808         if (BlobBounds.ContainsCoord(PointerPos))
02809         {
02810             if (pLeft!=NULL)
02811                 *pLeft = FALSE;
02812             *pStory = pItem->GetBlobStory();
02813 
02814             return TRUE;
02815         }
02816 
02817         pItem = (TextToolBlobPosItem*) GetPrev(pItem);
02818     }
02819 
02820     return TRUE;
02821 }
02822 
02823 
02824 
02825 /********************************************************************************************
02826 >   BOOL TextToolBlobPosList::RenderAndRemove(TextStory* pStory)
02827 
02828     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
02829     Created:    23/2/96
02830     Inputs:     pStory - pointer to a text story
02831     Outputs:    -
02832     Returns:    TRUE/FALSE for success/failure
02833     Purpose:    If the story is in the list of blobs then it's blob is rendered (off) and the 
02834                 blob is removed from the blob list.
02835 ********************************************************************************************/
02836 BOOL TextToolBlobPosList::RenderAndRemove(TextStory* pStory)
02837 {
02838     ERROR2IF(pStory==NULL, FALSE, "NULL story pointer");
02839 
02840     // Work through our list to find the story
02841     TextToolBlobPosItem* pItem = (TextToolBlobPosItem*) GetHead();
02842     while (pItem!=NULL)
02843     {
02844         if (pItem->GetBlobStory() == pStory)
02845         {
02846             // Render the blob pair off
02847             RenderBlobPair(m_pSelSpread, NULL, pItem);
02848 
02849             // Remove it from the list
02850             RemoveItem(pItem);
02851             delete pItem;
02852             
02853             return TRUE;
02854         }
02855         else
02856             pItem = (TextToolBlobPosItem*) GetNext(pItem);
02857     }
02858 
02859     ERROR3("Story not found");
02860 
02861     return TRUE;
02862 }
02863 
02864 
02865 
02866 /********************************************************************************************
02867 >   void TextToolBlobPosList::RenderAndRemoveAll()
02868 
02869     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
02870     Created:    23/2/96
02871     Inputs:     -
02872     Outputs:    -
02873     Returns:    -
02874     Purpose:    renders off all the blobs and removes them from the list.
02875 ********************************************************************************************/
02876 void TextToolBlobPosList::RenderAndRemoveAll()
02877 {
02878     // Work through our list to find the story
02879     TextToolBlobPosItem* pItem = (TextToolBlobPosItem*) RemoveHead();
02880     while (pItem!=NULL)
02881     {
02882         // Render the blob pair off
02883         RenderBlobPair(m_pSelSpread, NULL, pItem);
02884 
02885         // Delte the item now we've finshed with it.
02886         delete pItem;
02887             
02888         pItem = (TextToolBlobPosItem*) RemoveHead();
02889     }
02890 }
02891 
02892 
02893 

Generated on Sat Nov 10 03:48:02 2007 for Camelot by  doxygen 1.4.4