pentool.cpp

Go to the documentation of this file.
00001 // $Id: pentool.cpp 1282 2006-06-09 09:46:49Z 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 // The 'pen' line and curve drawing tool
00099 // Created by MIke on 19/9/94
00100 
00101 /*
00102 */
00103 
00104 #include "camtypes.h"
00105 //#include "binds.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00106 //#include "resource.h"
00107 //#include "barsdlgs.h" 
00108 //#include "stockcol.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00109 //#include "rndrgn.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00110 #include "pentool.h"
00111 #include "oilfiles.h"
00112 //#include "viewrc.h"
00113 //#include "mike.h"
00114 #include "csrstack.h"
00115 #include "blobs.h"
00116 //#include "app.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00117 //#include "docview.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00118 //#include "markn.h"
00119 #include "nodepath.h"
00120 //#include "mainfrm.h"
00121 #include "osrndrgn.h"
00122 #include "penedit.h"
00123 //#include "ops.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00124 #include "pathedit.h"
00125 #include "cutop.h"
00126 //#include "opdesc.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00127 #include "keypress.h"
00128 //#include "jim.h"
00129 #include "opbreak.h"
00130 #include "vkextra.h"
00131 #include "nodepath.h"
00132 
00133 //#include "will2.h"
00134 
00135 // These are still char* while we wait for resource technology to be developed for modules
00136 TCHAR* PenTool::FamilyName = _T("Drawing Tools");
00137 TCHAR* PenTool::ToolName = _T("Pen Tool");
00138 TCHAR* PenTool::Purpose = _T("To draw lines and curves");
00139 TCHAR* PenTool::Author = _T("Mike");
00140 
00141 
00142 CC_IMPLEMENT_MEMDUMP( PenTool, Tool_v1 )
00143 CC_IMPLEMENT_DYNCREATE( PenToolInfoBarOp, InformationBarOp )
00144 
00145 // Better memory tracking please
00146 #define new CAM_DEBUG_NEW
00147 
00148 
00149 
00150 /********************************************************************************************
00151 
00152 >   PenTool::PenTool()
00153 
00154     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
00155     Created:    19/9/94
00156     Purpose:    Dummy Constructor - It does nothing. All the real initialisation is done
00157                 in PenTool::Init which is called by the Tool Manager
00158     SeeAlso:    PenTool::Init
00159 
00160 ********************************************************************************************/
00161 
00162 PenTool::PenTool()
00163 {
00164     pcPenCursor = NULL;
00165     pcPenAdjustCursor = NULL;
00166     pcPenReshapeCursor = NULL;
00167     pcMoveBezCursor = NULL;
00168     MyCurrentCursor = NULL;
00169 
00170     CurrPenState = IS_Undefined;
00171 }
00172 
00173 
00174 
00175 
00176 /********************************************************************************************
00177 
00178 >   PenTool::~PenTool()
00179 
00180     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
00181     Created:    19/9/94
00182     Purpose:    Destructor (Virtual). Does nothing.
00183 
00184 ********************************************************************************************/
00185 
00186 PenTool::~PenTool()
00187 {
00188     // Dummy destructor
00189 }
00190 
00191 
00192 /********************************************************************************************
00193 
00194 >   BOOL PenTool::Init( INT32 Pass )
00195 
00196     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
00197     Created:    19/9/94
00198     Returns:    FALSE if it does not want to be created, TRUE otherwise
00199     Purpose:    Used to check if the Tool was properly constructed
00200     SeeAlso:    PenTool::PenTool
00201 
00202 ********************************************************************************************/
00203 
00204 BOOL PenTool::Init()
00205 {
00206 
00207     BOOL                    ok;
00208     CCResTextFile           file;               // Resource File
00209     PenToolInfoBarOpCreate  BarCreate;          // Object that creates PenInfoBarOp objects
00210 
00211     // This should be set to NULL by default. It will be set properly below, if
00212     // everthing is working as it should
00213     pPenInfoBarOp = NULL;
00214 
00215     // initially, no cursor
00216     pcPenCursor = 0;
00217 
00218     pPenInfoBarOp = new PenToolInfoBarOp();
00219     ok = (pPenInfoBarOp != NULL);
00220     if (ok) pPenInfoBarOp->pPenTool = this;             // Set a pointer from the op to this tool
00221 
00222 #if 0
00223         ok = file.open(_R(IDM_PENTOOL_BAR), _R(IDT_INFO_BAR_RES));          // Open resource
00224     if (ok) ok = DialogBarOp::ReadBarsFromFile(file,BarCreate);     // Read and create info bar
00225     if (ok) file.close();                                           // Close resource.
00226 
00227     ENSURE(ok,"Unable to load penbar.ini from resource\n"); 
00228 
00229     if (ok)
00230     {
00231         // Info bar now exists.  Now get a pointer to it
00232         String_32 str = String_32(_R(IDS_PENTOOL_INFOBARNAME));
00233         DialogBarOp* pDialogBarOp = DialogBarOp::FindDialogBarOp(str);
00234 
00235                 ok = (pDialogBarOp != NULL);
00236         if (ok) ok = pDialogBarOp->IsKindOf(CC_RUNTIME_CLASS(PenToolInfoBarOp));
00237         if (ok) pPenInfoBarOp = (PenToolInfoBarOp*)pDialogBarOp;
00238         if (ok) pPenInfoBarOp->pPenTool = this;             // Set a pointer from the op to this tool
00239 
00240         ENSURE(ok,"Failed to create PENTOOL info bar");
00241     }
00242 #endif
00243 
00244     if (ok) ok = EditPath.Initialise(12,24);                // create the edit path buffers please
00245 
00246     // Register our pen operation(s).
00247     if (ok)
00248     {
00249         ok = OpPenCreateInternal::Init();
00250         if (ok) ok = OpPenEditInternal::Init();
00251         if (ok) ok = OpPenCreatePath::Init();
00252         if (ok) ok = OpPenAddElement::Init();
00253         if (ok) ok = OpAddNewPath::Init();
00254         if (ok) ok = OpPenClosePath::Init();
00255     }
00256 
00257     return ok;
00258 
00259 }
00260 
00261 
00262 /********************************************************************************************
00263 
00264 >   void PenTool::Describe(void *InfoPtr)
00265 
00266     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
00267     Created:    20/9/94
00268     Inputs:     InfoPtr - A pointer to a tool info block. It is passed cast to void* as
00269                 the version of the tool is unknown at this point. Later versions of the
00270                 Tool class may have more items in this block, that this tool will not use
00271     Outputs:    InfoPtr - The structure pointed to by InfoPtr will have had all the info
00272                 that this version of the Tool knows about
00273     Purpose:    Allows the tool manager to extract information about the tool
00274 
00275 ********************************************************************************************/
00276 
00277 void PenTool::Describe(void *InfoPtr)
00278 {
00279     // Cast structure into the latest one we understand.
00280     ToolInfo_v1 *Info = (ToolInfo_v1 *) InfoPtr;
00281 
00282     Info -> InfoVersion = 1;
00283     
00284     Info -> InterfaceVersion = GetToolInterfaceVersion();  // You should always have this line.
00285         
00286     // These are all arbitrary at present.
00287     Info -> Version = 1;
00288     Info -> ID      = GetID();
00289     Info -> TextID  = _R(IDS_PEN_TOOL);
00290 
00291     Info -> Family  = FamilyName;
00292     Info -> Name    = ToolName;
00293     Info -> Purpose = Purpose;
00294     Info -> Author  = Author;
00295     
00296     Info -> InfoBarDialog = _R(IDD_FREEHANDTOOL);
00297 
00298     Info -> BubbleID = _R(IDBBL_PEN_TOOL);
00299 
00300 }
00301 
00302 
00303 /********************************************************************************************
00304 
00305 >   virtual void PenTool::SelectChange(BOOL isSelected)
00306 
00307     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
00308     Created:    20/9/94
00309     Inputs:     -
00310     Outputs:    -
00311     Returns:    -
00312     Purpose:    Called when the tool is selected or deselected.  Creates and pushes the
00313                 tool's cursor; pops and destroys it.
00314     Errors:     Sends warning to debugging terminal if creating the cursor fails.
00315     SeeAlso:    -
00316 
00317 ********************************************************************************************/
00318 
00319 void PenTool::SelectChange(BOOL isSelected)
00320 {
00321     if (isSelected)
00322     {
00323         // This tool has just been selected.  Create an appropriate cursor, and push it
00324         // onto the top of the cursor stack so it'll appear when the pointer moves into
00325         // our window.
00326 
00327         MyCurrentCursor = NULL;
00328 
00329         if (!CreatePenCursors())
00330             return;
00331 
00332         CurrentCursorID = CursorStack::GPush(pcPenCursor, FALSE);       // Push, but don't display now
00333         MyCurrentCursor = pcPenCursor;
00334         
00335         // Create and display my info bar please
00336         if (pPenInfoBarOp != NULL)
00337             pPenInfoBarOp->Create();
00338 
00339             // Which blobs do I want displayed
00340         BlobManager* BlobMgr = GetApplication()->GetBlobManager();
00341         if (BlobMgr != NULL)
00342         {
00343             // Decide which blobs we will display
00344             BlobStyle MyBlobs;
00345             MyBlobs.Pen = TRUE;
00346 
00347             // Tell the blob manager
00348             BlobMgr->ToolInterest(MyBlobs);
00349         }
00350 
00351         // Now set some local variables so we know what state we are
00352         // in to start with
00353         CurrPenState = IS_Undefined;
00354 
00355         // alias the delete function to delete points.
00356         // OpDescriptor* pOpDesc = OpDescriptor::FindOpDescriptor(OPTOKEN_DELETE);  
00357         // pOpDesc->AliasOperation(CC_RUNTIME_CLASS(OpDeletePoints),OpDeletePoints::GetState,0);  
00358 
00359         // remove the alias to the delete function
00360         // OpDescriptor* pOpDesc = OpDescriptor::FindOpDescriptor(OPTOKEN_DELETE);  
00361         // pOpDesc->RemoveAlias();
00362     }
00363     else
00364     {
00365         // Deselection - destroy the tool's current cursor, if there is one.
00366         if (MyCurrentCursor)
00367         {
00368             CursorStack::GPop(CurrentCursorID);
00369             MyCurrentCursor = NULL;
00370             CurrentCursorID = 0;
00371         }
00372         DeletePenCursors();
00373 
00374         // Hide and destroy my info bar please
00375         if (pPenInfoBarOp != NULL)
00376             pPenInfoBarOp->Delete();
00377 
00378         // ensure any tool object blobs are removed.
00379         BlobManager* BlobMgr = GetApplication()->GetBlobManager();
00380         if (BlobMgr != NULL)
00381         {
00382             BlobStyle bsRemoves;
00383             bsRemoves.ToolObject = TRUE;
00384             BlobMgr->RemoveInterest(bsRemoves);
00385         }
00386 
00387         ClearInternalState();
00388     }
00389 }
00390 
00391 
00392 
00393 /********************************************************************************************
00394 
00395     BOOL PenTool::CreatePenCursors()
00396 
00397     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
00398     Created:    27/9/94
00399     Inputs:     -
00400     Outputs:    -
00401     Returns:    -
00402     Purpose:    Pen tool cursor creation
00403 
00404 ********************************************************************************************/
00405 
00406 BOOL PenTool::CreatePenCursors()
00407 {
00408     pcPenAdjustCursor = new Cursor(this, _R(IDC_PENADJUSTCURSOR));
00409     if (!pcPenAdjustCursor || !pcPenAdjustCursor->IsValid())
00410     {
00411         DeletePenCursors();
00412         InformError( _R(IDS_OUT_OF_MEMORY), _R(IDS_OK) );
00413         return FALSE;                                        
00414     }
00415 
00416     pcPenCursor = new Cursor(this, _R(IDC_NEWPATHCURSOR));
00417     if (!pcPenCursor || !pcPenCursor->IsValid())
00418     {
00419         DeletePenCursors();
00420         InformError( _R(IDS_OUT_OF_MEMORY), _R(IDS_OK) );
00421         return FALSE;
00422     }
00423 
00424     pcPenReshapeCursor = new Cursor(this, _R(IDC_RESHAPECURSOR));
00425     if (!pcPenReshapeCursor || !pcPenReshapeCursor->IsValid())
00426     {
00427         DeletePenCursors();
00428         InformError( _R(IDS_OUT_OF_MEMORY), _R(IDS_OK) );
00429         return FALSE;                                        
00430     }
00431 
00432     pcMoveBezCursor = new Cursor(this, _R(IDC_MOVEBEZIERCURSOR));
00433     if (!pcMoveBezCursor || !pcMoveBezCursor->IsValid())
00434     {
00435         DeletePenCursors();
00436         InformError( _R(IDS_OUT_OF_MEMORY), _R(IDS_OK) );
00437         return FALSE;
00438     }
00439 
00440     return TRUE;
00441 }
00442 
00443 
00444 /********************************************************************************************
00445 
00446     BOOL PenTool::DeletePenCursors()
00447 
00448     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
00449     Created:    27/9/94
00450     Inputs:     -
00451     Outputs:    -
00452     Returns:    -
00453     Purpose:    Pen tool cursor deletion
00454 
00455 ********************************************************************************************/
00456 
00457 void PenTool::DeletePenCursors()
00458 {
00459     if (pcPenCursor!=NULL)
00460     {
00461         delete pcPenCursor;
00462         pcPenCursor=NULL;
00463     }
00464     
00465     if (pcPenAdjustCursor!=NULL)
00466     {
00467         delete pcPenAdjustCursor;
00468         pcPenAdjustCursor=NULL;
00469     }
00470 
00471     if (pcPenReshapeCursor!=NULL)
00472     {
00473         delete pcPenReshapeCursor;
00474         pcPenReshapeCursor=NULL;
00475     }
00476 
00477     if (pcMoveBezCursor!=NULL)
00478     {
00479         delete pcMoveBezCursor;
00480         pcMoveBezCursor=NULL;
00481     }
00482 }
00483         
00484 
00485 /********************************************************************************************
00486 
00487 >   MsgResult PenToolInfoBarOp::Message(Msg* Message) 
00488 
00489     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
00490     Created:    27/9/94
00491     Inputs:     Message: The message to handle
00492     Outputs:    -
00493     Returns:    -
00494     Purpose:    Pen tool info bar dialog message handler
00495     Errors:     -
00496     SeeAlso:    -
00497 
00498 ********************************************************************************************/
00499 
00500 MsgResult PenToolInfoBarOp::Message(Msg* Message) 
00501 {
00502     if (MESSAGE_IS_A(Message,OpMsg))    // Check for undo/redo
00503     {
00504         OpMsg* pOpMsg = (OpMsg*)Message;
00505 
00506         if (pOpMsg->MsgType == OpMsg::AFTER_UNDO)
00507         {
00508             pPenTool->ClearInternalState();
00509         }
00510 
00511         if (pOpMsg->MsgType == OpMsg::END)
00512         {
00513             // an operation has ended, so lets check our own opstate
00514             
00515             if (IS_A(pOpMsg->pOp, OpPenCreateInternal) ||
00516                 IS_A(pOpMsg->pOp, OpPenEditInternal))
00517                 if (pPenTool->GetPenOpState() == OS_EditInternal)
00518                 {
00519                     if (!(pOpMsg->pOp->GetOpFlgs()).Failed)
00520                         pPenTool->SetInternalState();
00521                     pPenTool->ClearOp();
00522                 }
00523 
00524             if (IS_A(pOpMsg->pOp, OpPenCreatePath))
00525                 if (pPenTool->GetPenOpState() == OS_CreatePath)
00526                     {
00527                         if (!(pOpMsg->pOp->GetOpFlgs()).Failed)
00528                             pPenTool->CreateNewPath();
00529                         pPenTool->ClearPath();
00530                         pPenTool->ClearOp();
00531                     }
00532 
00533             if (IS_A(pOpMsg->pOp, OpPenAddElement))
00534                 if (pPenTool->GetPenOpState() == OS_AddElement)
00535                 {
00536                     if (!(pOpMsg->pOp->GetOpFlgs()).Failed)
00537                         pPenTool->AddElementToPath();
00538                     pPenTool->ClearPath();
00539                     pPenTool->ClearOp();
00540                 }
00541 
00542             if (IS_A(pOpMsg->pOp, OpPenClosePath))
00543                 if (pPenTool->GetPenOpState() == OS_ClosePath)
00544                 {
00545                     if (!(pOpMsg->pOp->GetOpFlgs()).Failed)
00546                         pPenTool->CloseWithPath();
00547                     pPenTool->ClearPath();
00548                     pPenTool->ClearOp();
00549                 }
00550         }
00551     }
00552     else if (MESSAGE_IS_A(Message,DocChangingMsg))  // Check for changes in the doc system
00553     {
00554         DocChangingMsg* pDocChangingMsg = (DocChangingMsg*)Message;
00555 //      Document* pDoc = pDocChangingMsg->pChangingDoc;
00556 
00557         switch (pDocChangingMsg->State)
00558         {
00559             case DocChangingMsg::SELCHANGED:
00560             case DocChangingMsg::KILLED:
00561                 pPenTool->ClearInternalState();
00562                 break; 
00563             default:
00564                 break;
00565         }
00566     }
00567 
00568     // Pass the message on
00569     return (DialogBarOp::Message(Message));
00570 }
00571 
00572 
00573 /********************************************************************************************
00574 
00575 >   void PenTool::OnClick( DocCoord PointerPos, ClickType Click, 
00576                            ClickModifiers ClickMods, Spread *pSpread )
00577 
00578     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com> (via Rik)
00579     Created:    20/9/94
00580     Inputs:     PointerPos - The Coords (in spread coords) of the point where the mouse 
00581                 button was clicked
00582                 Click - Describes the type of click that was detected. 
00583                 ClickMods - Indicates which buttons caused the click and which modifers were
00584                 pressed at the same time
00585                 pSpread - the spread in which the click happened
00586     Returns:    TRUE if it handled the Click, FALSE otherwise
00587     Purpose:    To handle a Mouse Click event for the Pen Tool. 
00588     SeeAlso:    Tool::MouseClick; ClickType; ClickModifiers
00589 
00590 ********************************************************************************************/
00591 
00592 void PenTool::OnClick( DocCoord PointerPos, ClickType Click, ClickModifiers ClickMods,
00593                        Spread* pSpread )
00594 {
00595     if (ClickMods.Menu) return;                         // Don't do anything if the user clicked the Menu button
00596 
00597     // determine what to do with this click
00598     penclick WhatToDo = DetermineClickEffect(PointerPos, pSpread, &pNodePath);
00599 
00600     // if someones holding the adjust down and we're not on a point then clear the sel
00601     if (Click==CLICKTYPE_SINGLE && ClickMods.Adjust && WhatToDo!=PenOnPoint)
00602     {
00603         RemoveSelection(PointerPos,pSpread);
00604         ClearInternalState();   
00605         return;
00606     }
00607 
00608     switch (Click)
00609     {
00610         case CLICKTYPE_SINGLE:
00611             switch (WhatToDo)
00612             {
00613                 case PenNewPath:
00614                 {
00615                     BOOL MakeInternal = TRUE;
00616                     if ((CurrPenState == IS_MoveTo) || (CurrPenState == IS_DragTo))
00617                     {
00618                         // create a new path using the internal data points.
00619                         // If LastDragTo==LastMoveTo, then the internal is a point,
00620                         // else a curve control end exists.
00621                         if (EditHandles.pHndSpread==pSpread)
00622                         {
00623                             MakeInternal = FALSE;
00624                             OpPenCreatePath* pOpCreatePath = new OpPenCreatePath;
00625                             if (pOpCreatePath!=NULL)
00626                             {
00627                                 pOpCreatePath->DoPenCreatePath(&EditHandles, PointerPos, pSpread, &EditPath);
00628                                 CurrPenOpState = OS_CreatePath;
00629                             }
00630                         }
00631                     }
00632                     
00633                     if (MakeInternal)
00634                     {
00635                         // First remove any selected points in the spread and create an
00636                         // internal move or dragto
00637                         RemoveSelection(PointerPos,pSpread);
00638                         OpPenCreateInternal* pOpCreateInternal = new OpPenCreateInternal;
00639                         if (pOpCreateInternal!=NULL)
00640                         {
00641                             pOpCreateInternal->DoPenCreateInternal(PointerPos, pSpread, &EditHandles);
00642                             CurrPenOpState = OS_EditInternal; 
00643                         }
00644                     }
00645                 }
00646                 break;
00647 
00648                 case PenClosePath:
00649                     {
00650                         NodeIndex = pNodePath->InkPath.GetPathPosition();
00651                         OpPenClosePath* pOpPenClosePath = new OpPenClosePath;
00652                         if (pOpPenClosePath!=NULL)
00653                         {
00654                             pOpPenClosePath->DoPenClosePath(pNodePath, NodeIndex, pSpread, &EditPath);
00655                             CurrPenOpState = OS_ClosePath;
00656                         }
00657                     }
00658                     break;
00659 
00660                 case PenOnPoint:
00661                     {
00662                         // Ordinary click from OnPoint
00663                         // Currently this does not work correctly because of eor'ing. The wrong
00664                         // Eor blobs get put on by the node path click handler 
00665                         RemoveSelection(PointerPos,pSpread,pNodePath);
00666                         ClickOnEndPoint(Click, ClickMods, pSpread, pNodePath);
00667                     }
00668                     break;
00669 
00670                 case PenAddSegment:
00671                     {
00672                         NodeIndex = pNodePath->InkPath.GetPathPosition();
00673                         OpPenAddElement* pOpPenAddElement = new OpPenAddElement;
00674                         if (pOpPenAddElement!=NULL)
00675                         {
00676                             pOpPenAddElement->DoPenAddElement(pNodePath, NodeIndex, PointerPos, pSpread, &EditPath);
00677                             CurrPenOpState = OS_AddElement;
00678                         }
00679                     }
00680                     break;
00681 
00682                 case PenEditInternalMove:
00683                     {
00684                         OpPenEditInternal* pOpEditInternal = new OpPenEditInternal;
00685                         if (pOpEditInternal!=NULL)
00686                         {
00687                             pOpEditInternal->DoPenEditInternal(&EditHandles);
00688                             CurrPenOpState = OS_EditInternal;
00689                         }
00690                     }
00691                     break;
00692                
00693                case PenReshapeLine:
00694                     {
00695                         double pdist;
00696                         INT32 tempel;
00697                         pNodePath->InkPath.SqrDistanceToPoint(PointerPos, &tempel, &pdist);
00698                         INT32 PathPos = pNodePath->InkPath.GetPathPosition();
00699 
00700                         OpReshapeOrAddPoint* pOpReshape = new OpReshapeOrAddPoint;
00701                         if (pOpReshape!=NULL)
00702                             pOpReshape->DoStartDragEdit(pNodePath, PointerPos, pSpread, PathPos, pdist);
00703                         else
00704                             InformError( _R(IDS_OUT_OF_MEMORY), _R(IDS_OK) );
00705                     }
00706                     break;
00707 
00708             }
00709             break;
00710 
00711         case CLICKTYPE_DOUBLE:
00712             break;
00713 
00714         case CLICKTYPE_DRAG:
00715             switch (WhatToDo)
00716             {
00717                 case PenOnPoint:
00718                     if (pNodePath)
00719                         ClickOnEndPoint(Click, ClickMods, pSpread, pNodePath);
00720                     break;
00721 
00722                 default:
00723                     break;
00724             }
00725             break;
00726 
00727         default:
00728             break;
00729     }
00730 
00731 }
00732 
00733 
00734 
00735 
00736 /**************************************************************************************************************
00737 
00738 > penclick PenTool::DetermineClickEffect(DocCoord PointerPos, Spread* pSpread, NodePath** ReturnNode)
00739 
00740     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
00741     Created:    26/9/94
00742     Inputs:     PointerPos is the mouse position
00743                 pSpread is a pointer to the spread containing the mouse position
00744     Outputs:    ReturnNode returns a pointer to the node the mouse has clicked on
00745     Returns:    The effect of clicking - one of NewPath, AddSegment, OnPoint or EditInternalMove
00746     Purpose:    Used when single clicking. This routine determines what effect a click will have.
00747                 In this tool, clicking will add a segment to the end of a line, adjust the last element of
00748                 a path or start a new path entirely.
00749 
00750 ***************************************************************************************************************/
00751 
00752 penclick PenTool::DetermineClickEffect(DocCoord PointerPos, Spread* pSpread, NodePath** ReturnNode )
00753 {
00754     penclick WhatToDo = PenNewPath; // tells me what effect the click would have
00755     NodePath* WhichNode = NULL;     // Saves me using a pointer to a pointer
00756 
00757     // Now scan through the selected paths, and see if we've clicked on any endpoints
00758     DocView* pDocView = DocView::GetSelected();
00759     ENSURE( pDocView != NULL, "PenTool::DetermineClickEffect: Can't find selected DocView");
00760     if (pDocView==NULL)
00761         return(PenNewPath);
00762 
00763     // Find the Rect round the mouse pos that counts as a hit
00764     DocRect BlobRect;
00765     OSRenderRegion::GetBlobRect(pDocView->GetViewScale(), PointerPos, BT_SELECTEDLARGEST, &BlobRect);
00766 
00767     // if we're in an internal state and someone has clicked on the moveto
00768     // again then go and edit it.
00769     if (CurrPenState == IS_MoveTo || CurrPenState == IS_DragTo)
00770     {
00771         if (BlobRect.ContainsCoord(EditHandles.HndClick))
00772             return (PenEditInternalMove);
00773     }
00774 
00775 
00776     // Find the selected range of objects
00777     SelRange* Selected = GetApplication()->FindSelection();
00778     Node* pNode = Selected->FindFirst();
00779 
00780     INT32 TotSelectedEndpoints = 0;     // Count selected points in paths
00781     BOOL MultiSegments = FALSE;
00782 
00783     if (pNode != NULL)
00784     {
00785         Spread* NodeSpread = pNode->FindParentSpread();
00786         if (NodeSpread == pSpread)
00787         {
00788             // On the same spread, so see if the pointer is over an endpoint
00789             while ((pNode!=NULL) && ((WhatToDo==PenNewPath) || (WhatToDo==PenAddSegment)))
00790             {
00791                 if (pNode->GetRuntimeClass() == CC_RUNTIME_CLASS(NodePath))
00792                 {
00793                     // Now we know it's a NodePath, get a pointer to the Path object within it, so
00794                     // we can find any endpoints
00795                     Path* ThisPath = &(((NodePath*)pNode)->InkPath);
00796 
00797                     // count the number of selected points on the path (excluding control points)
00798                     INT32 NumCoords = ThisPath->GetNumCoords();
00799                     PathFlags* Flags = ThisPath->GetFlagArray();
00800 
00801                     // Count selected points in this path
00802                     INT32 NumSelectedEndpoints = 0;     
00803                     INT32 lastsel = 0;
00804                     for (INT32 i=0; i<NumCoords; i++)
00805                     {
00806                         if (Flags[i].IsSelected && Flags[i].IsEndPoint)
00807                         {
00808                             lastsel = i;
00809                             NumSelectedEndpoints++;
00810                         }
00811                     }
00812                     // Sum up
00813                     TotSelectedEndpoints+=NumSelectedEndpoints;
00814 
00815                     INT32 point;
00816                     // First, see if the mouse is over a point.
00817                     if (ThisPath->FindNearestPoint(PointerPos, POINTFLAG_ENDPOINTS | POINTFLAG_ENDSFIRST, &point))
00818                     {
00819                         WhichNode = (NodePath*)pNode;
00820                         WhatToDo = PenOnPoint;
00821                         ThisPath->SetPathPosition(point);
00822 
00823                         // if the mouse was over an open end and the other end of this sub
00824                         // path is selected then we need to close the path
00825                         // find the start and end of the subpath the point was found in
00826                         INT32 start = point;
00827                         INT32 end = point;
00828                         ThisPath->FindStartOfSubPath(&start);
00829                         ThisPath->FindEndElOfSubPath(&end);
00830                         if (point==start || point==end)
00831                         {
00832                             INT32 other;
00833                             (point==start) ? (other=end) : (other=start);
00834                             if ((Flags[other].IsSelected) && (!Flags[point].IsSelected))
00835                                 WhatToDo = PenClosePath;
00836                         }
00837                         continue;
00838                     }
00839 
00840                     // if the mouse is close to an element, then it might be close
00841                     // enough to reshape the line
00842                     if (ThisPath->PointCloseToLine(PointerPos, &point))
00843                     {
00844                         WhichNode=(NodePath*)pNode;
00845                         WhatToDo=PenReshapeLine;
00846                         ThisPath->SetPathPosition(point);
00847                         continue;
00848                     }
00849 
00850                     if (!MultiSegments && NumSelectedEndpoints==1)
00851                     {
00852                         INT32 start = lastsel;
00853                         INT32 end = lastsel;
00854                         ThisPath->FindStartOfSubPath(&start);
00855                         ThisPath->FindEndElOfSubPath(&end);
00856                         if (lastsel==start || lastsel==end)
00857                         {
00858                             if (WhatToDo==PenNewPath)
00859                             {   
00860                                 WhichNode=(NodePath*)pNode;
00861                                 WhatToDo=PenAddSegment;
00862                                 ThisPath->SetPathPosition(lastsel);
00863                             }
00864                             else
00865                             {
00866                                 // We have more than one path with a selected end point.
00867                                 // Adding elements is ambiguous so force a NewPath creation
00868                                 WhichNode=(NodePath*)pNode;
00869                                 WhatToDo=PenNewPath;
00870                                 MultiSegments=TRUE;
00871                             }
00872                         }
00873                     }
00874                 }
00875                 // Now find the next selected node
00876                 pNode = Selected->FindNext(pNode);
00877             }
00878         }
00879     }
00880 
00881     // WhatToDo tells us what the action will be
00882     // WhichNode points to the node we are dealing with
00883     // PathPosition is the index into that path of the element we are using
00884 
00885     *ReturnNode = WhichNode;
00886     return (WhatToDo);
00887 }
00888 
00889 
00890 
00891 
00892 /**************************************************************************************************************
00893 
00894 >   void PenTool::ClickOnEndPoint( ClickType Click,
00895                                    ClickModifiers ClickMods,
00896                                    Spread *pSpread,
00897                                    NodePath* pNodePath )
00898 
00899     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
00900     Created:    26/9/94
00901     Inputs:     Click       - Describes the type of click that was detected. 
00902                 ClickMods   - Indicates which buttons caused the click and which modifers were
00903                               pressed at the same time
00904                 pSpread     - the spread in which the click happened
00905                 pNodePath   - The path on which the click happened (current pos set to the point clicked on)
00906     Outputs:    -
00907     Returns:    -
00908     Purpose:    An end point click has occured. This function acts on such a click by selecting the point
00909                 or editing the point dependent on the click type
00910     
00911 ***************************************************************************************************************/
00912 
00913 void PenTool::ClickOnEndPoint( ClickType Click, ClickModifiers ClickMods, Spread *pSpread, NodePath* pNodePath )
00914 {
00915     Path* pPath = &(pNodePath->InkPath);
00916 
00917     DocCoord* Coords = pPath->GetCoordArray();
00918     PathFlags* Flags = pPath->GetFlagArray();
00919     PathVerb*  Verbs = pPath->GetVerbArray();
00920     INT32 NumCoords = pPath->GetNumCoords();
00921 
00922     // grab the index of the point actually clicked on!
00923     INT32 Pos = pPath->GetPathPosition();
00924 
00925     // Clicks on control points have no effect
00926     // but clicks on endpoints do have an effect
00927 
00928     switch (Click)
00929     {
00930         case CLICKTYPE_SINGLE:
00931         {
00932             if (Flags[Pos].IsEndPoint)
00933             {
00934                 pPath->RenderPathPenBlobs(pSpread);
00935 
00936                 BOOL CurSelState = Flags[Pos].IsSelected;
00937                 BOOL NewSelState = TRUE;
00938                 BOOL ClearOthers = FALSE;
00939                 
00940                 // if shift held down then toggle the point whatever
00941                 if (ClickMods.Adjust) 
00942                     NewSelState = !CurSelState;
00943 
00944                 // if the end point is not selected and we're not toggling
00945                 if (!CurSelState && !ClickMods.Adjust)
00946                     ClearOthers=TRUE;
00947                     
00948                 if (ClearOthers)
00949                 {
00950                     for (INT32 i=0; i<NumCoords; i++)
00951                     {
00952                         Flags[i].IsSelected = FALSE;
00953                     }
00954                 }
00955 
00956                 // Now change the selection of this point
00957                 Flags[Pos].IsSelected = NewSelState;
00958 
00959                 if ((Pos>0) && (!Flags[Pos-1].IsEndPoint))
00960                     Flags[Pos-1].IsSelected = NewSelState;
00961 
00962                 if ((Pos+1<NumCoords) && (!Flags[Pos+1].IsEndPoint))
00963                     Flags[Pos+1].IsSelected = NewSelState;
00964 
00965                 // Check for this being the first element in a closed subpath
00966                 // If this element is a moveto, and the end of the path has the
00967                 // CLOSEFIGURE flag set, we should select the endpoint as well
00968                 if (Verbs[Pos] == PT_MOVETO)
00969                 {
00970                     // This for loop will find either the end of the path, or the next moveto
00971                     INT32 j;
00972                     for (j=Pos+1;j<NumCoords && Verbs[j] != PT_MOVETO;j++); // ; is intentional!
00973                     j--;
00974                     if (Verbs[j] & PT_CLOSEFIGURE)
00975                     {
00976                         //HandleBlobClick(Coords,Flags,j,NumCoords,TRUE);
00977                         Flags[j].IsSelected = NewSelState;
00978                         // If the previous point is a control point then deal with it
00979                         if ((j>0) && (!Flags[j-1].IsEndPoint))
00980                         {
00981                             // Change the control point's selection state
00982                             Flags[j-1].IsSelected = NewSelState;
00983                         }
00984                     }
00985 
00986                 }
00987 
00988                 pPath->RenderPathPenBlobs(pSpread);
00989             }
00990             break;
00991         }
00992 
00993         case CLICKTYPE_DOUBLE:
00994             break;
00995 
00996         case CLICKTYPE_DRAG:
00997         {
00998             if ((Flags[Pos].IsEndPoint) && (!ClickMods.Menu))
00999             {
01000                 // Need to do a drag on the selected points, so we had better start an operation
01001                 OpNodePathEditBlob* pOpNodePath = new OpNodePathEditBlob;
01002                 if (pOpNodePath == NULL)
01003                     InformError();
01004                 else
01005                     pOpNodePath->DoStartDragEdit(pNodePath, Coords[Pos], pSpread);
01006             }
01007         }
01008         break;
01009 
01010         default:
01011         {
01012         }
01013         break;
01014     }
01015 }
01016 
01017 
01018 
01019 /********************************************************************************************
01020 
01021 >   virtual BOOL PenTool::OnKeyPress(KeyPress* pKeyPress)
01022 
01023     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
01024     Created:    15/08/94
01025     Inputs:     pKeyPress - pointer to a keypress object
01026     Returns:    TRUE if it handled the keypress, FALSE otherwise
01027     Purpose:    To handle keypress events for the Pen Tool. If it is a keypress that it
01028                 know about it starts up an appropiate operation.
01029                 THIS IS BADLY WRONG AT THE MOMENT - WHEN THERE IS THE TECHNOLOGY FOR USER
01030                 DEFINABLE KEY-SHORTCUTS THIS FUNCTION WILL NEED FIXING  
01031 
01032 ********************************************************************************************/
01033 
01034 BOOL PenTool::OnKeyPress(KeyPress* pKeyPress)
01035 {
01036     // We don't want to know about key release events
01037     if (pKeyPress->IsRelease())
01038         return FALSE;
01039 
01040     // If ESCAPE is pressed then clear the floating endpoint, but don't claim the keypress
01041     // so the selection is also cleared.
01042     if (*pKeyPress == KeyPress(CAMKEY(ESCAPE)))
01043     {
01044         if ((CurrPenState == IS_MoveTo) || (CurrPenState == IS_DragTo))
01045         {
01046             ClearInternalState();
01047             return TRUE;
01048         }
01049     }
01050 
01051     // break at points op key short cut.
01052     if (*pKeyPress == KeyPress(CAMKEY(B)))
01053     {
01054         OpDescriptor* pOpDesc = OpDescriptor::FindOpDescriptor(CC_RUNTIME_CLASS(OpBreakAtPoints));
01055         String_256  UIDesc;
01056         if (pOpDesc != NULL)
01057         {
01058             OpState State = OpBreakAtPoints::GetState(&UIDesc, pOpDesc);
01059             if (!State.Greyed)
01060             {
01061                 pOpDesc->Invoke();
01062                 return TRUE;
01063             }
01064         }
01065     } 
01066 
01067     // if delete pressed, try to delete the selected points in this path
01068     if ((pKeyPress->GetVirtKey() == CAMKEY(DELETE)) || (*pKeyPress == KeyPress(CAMKEY(BACK))) ) 
01069     {
01070         OpDescriptor* pOpDesc = OpDescriptor::FindOpDescriptor(CC_RUNTIME_CLASS(OpDeletePoints));
01071         String_256 UIDesc;
01072         if (pOpDesc != NULL)
01073         {
01074             OpState State = OpDeletePoints::GetState(&UIDesc, NULL);
01075             if (!State.Greyed)
01076             {
01077                 pOpDesc->Invoke();
01078                 return TRUE;
01079             }
01080         }
01081     } 
01082 
01083     // If we get this far then the keypress wasn't handled
01084     return FALSE;
01085 }
01086 
01087 
01088 /********************************************************************************************
01089 
01090 >   void PenTool::OnMouseMove(DocCoord coord, Spread* pSpread)
01091 
01092     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
01093     Created:    20/9/94
01094     Inputs:     coord       -   The DocCoord of the point where the mouse has moved to
01095                 pSpread     -   The spread in which the move occurred
01096                 mods        -   which buttons/keys are down
01097     Outputs:    -
01098     Returns:    -
01099     Purpose:    This routine is called whenever the mouse moves while we're in the pen 
01100                 tool. it sees what is under the pointer, and flips the cursor if clicking 
01101                 will have a different effect. 
01102     Errors:     -
01103     SeeAlso:    -
01104 
01105 ********************************************************************************************/
01106 
01107 void PenTool::OnMouseMove(DocCoord coord, Spread* pSpread, ClickModifiers mods)
01108 {
01109     NodePath* pNodePath = NULL;
01110     Cursor* whichCursor = NULL;
01111 
01112     // find what type of click we will generate
01113     penclick WhatToDo = DetermineClickEffect(coord, pSpread, &pNodePath);
01114 
01115     String_256 StatusMsg("");
01116     GenerateStatusLineText(pNodePath, WhatToDo, &StatusMsg);
01117     GetApplication()->UpdateStatusBarText(&StatusMsg);
01118 
01119     switch (WhatToDo)
01120     {
01121         case PenNewPath:
01122         case PenAddSegment:
01123         case PenEditInternalMove: 
01124             whichCursor = pcPenCursor;
01125             break;
01126         case PenOnPoint:
01127             whichCursor = pcMoveBezCursor;
01128             break;
01129         case PenClosePath:
01130             whichCursor = pcPenAdjustCursor;
01131             break;
01132         case PenReshapeLine:
01133             whichCursor = pcPenReshapeCursor;
01134             break;
01135         default:
01136             whichCursor = pcPenCursor;
01137     }
01138     ChangeCursor(whichCursor);
01139 }
01140 
01141 
01142 /********************************************************************************************
01143 
01144 >   virtual BOOL PenTool::GetStatusLineText(String_256* ptext,
01145                                             Spread* pSpread,
01146                                             DocCoord DocPos, 
01147                                             ClickModifiers ClickMods);
01148 
01149     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
01150     Created:    05/01/95
01151     Inputs:     pSpread   - pointer to spread under mouse (else NULL)
01152                 DocPos    - position of mouse in doc (in spread coords)
01153                 ClickMods - mouse click modifiers
01154     Outputs:    ptext - text for status line
01155     Returns:    TRUE if outputting valid text
01156     Purpose:    generate up-to-date text for the status line (called on idles)
01157     Errors:     ERROR3 if ptext is NULL
01158 
01159 ********************************************************************************************/
01160 
01161 BOOL PenTool::GetStatusLineText(String_256* ptext, Spread* pSpread, DocCoord coord, ClickModifiers mods)
01162 {
01163     ERROR1IF(ptext==NULL,FALSE,"PenTool::GetStatusLineText() passed a NULL text buffer");
01164 
01165     *ptext = "";
01166 
01167     NodePath* pNodePath = NULL;
01168 
01169     // find what type of click we will generate
01170     penclick WhatToDo = DetermineClickEffect(coord, pSpread, &pNodePath);
01171     GenerateStatusLineText(pNodePath, WhatToDo, ptext);
01172 
01173     return TRUE;
01174 }
01175 
01176 
01177 /********************************************************************************************
01178 
01179 >   void PenTool::GenerateStatusLineText(NodePath* pNodePath, 
01180                                          penclick WhatToDo,
01181                                          String_256* pStatusMsg)
01182 
01183     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
01184     Created:    05/01/95
01185     Inputs:     pNodePath   = pointer to a node path the mouse is over
01186                               (can be NULL)
01187                 WhatToDo    = what action to be taken 
01188                               (return param of DetermineClickEffect)
01189                 pStatusMsg  = pointer to a 256 character text buffer
01190                 
01191     Outputs:    pStatusMsg  - holds a text string
01192     Returns:    -
01193     Purpose:    Loads up an appropriate help string into the text buffer
01194                 pointed to by pStatusMsg, using the nodepath and 'whattodo'
01195                 information passed.
01196     Errors:     -
01197 
01198 ********************************************************************************************/
01199 
01200 void PenTool::GenerateStatusLineText(NodePath* pNodePath, penclick WhatToDo, String_256* pStatusMsg)
01201 {
01202     switch (WhatToDo)
01203     {
01204         case PenNewPath:
01205             switch(CurrPenState)
01206             {
01207                 case IS_MoveTo:
01208                     pStatusMsg->Load(_R(IDS_PENADDPOINT), Tool::GetModuleID(GetID()));  
01209                     break;
01210                 case IS_DragTo:
01211                     pStatusMsg->Load(_R(IDS_PENADDCURVE), Tool::GetModuleID(GetID()));  
01212                     break;
01213                 default:
01214                     pStatusMsg->Load(_R(IDS_PENNEWPATH), Tool::GetModuleID(GetID()));
01215                     break;
01216             }
01217             break;
01218 
01219         case PenAddSegment:
01220             if (pNodePath != NULL)
01221             {
01222                 PathVerb  Verb  = (pNodePath->InkPath).GetVerb();
01223                 PathFlags Flags = (pNodePath->InkPath).GetFlags();
01224                 switch (Verb)
01225                 {
01226                     case PT_BEZIERTO:
01227                         if (Flags.IsRotate)
01228                             pStatusMsg->Load(_R(IDS_PENADDCURVE), Tool::GetModuleID(GetID()));
01229                         else
01230                             pStatusMsg->Load(_R(IDS_PENADDPOINT), Tool::GetModuleID(GetID()));
01231                         break;
01232                     default:
01233                         pStatusMsg->Load(_R(IDS_PENADDPOINT), Tool::GetModuleID(GetID()));
01234                         break;
01235                 }
01236             }
01237             else
01238                 pStatusMsg->Load(_R(IDS_PENADDPOINT), Tool::GetModuleID(GetID()));
01239             break;
01240 
01241         case PenEditInternalMove: 
01242             pStatusMsg->Load(_R(IDS_PENEDITINT), Tool::GetModuleID(GetID()));
01243             break;
01244             
01245         case PenClosePath:
01246             pStatusMsg->Load(_R(IDS_PENCLOSEPATH), Tool::GetModuleID(GetID()));
01247             break;
01248 
01249         case PenReshapeLine:
01250             pStatusMsg->Load(_R(IDS_RESHAPE_LINE),Tool::GetModuleID(GetID()));
01251             break;
01252 
01253         case PenOnPoint:
01254             if (pNodePath != NULL)
01255             {
01256                 PathFlags Flags = (pNodePath->InkPath).GetFlags();
01257                 if (Flags.IsSelected)
01258                     pStatusMsg->Load(_R(IDS_PENONSELPOINT), Tool::GetModuleID(GetID()));
01259                 else
01260                     pStatusMsg->Load(_R(IDS_PENONPOINT), Tool::GetModuleID(GetID()));
01261             }
01262             break;
01263             
01264         default:
01265             pStatusMsg->Load(_R(IDS_PENADDPOINT), Tool::GetModuleID(GetID()));
01266             break;
01267     }
01268 }
01269 
01270 
01271 /********************************************************************************************
01272 
01273 >   void PenTool::OverPathEnd(DocCoord coord, Spread* pSpread, NodePath* pNodePath)
01274 
01275     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
01276     Created:    22/9/94
01277     Inputs:     coordinate of mouse move, pointer to spread containing coord
01278     Outputs:    pointer to path mouse point is over. The current position is set to
01279                 the end point on the path under the mouse.
01280     Returns:    BOOL - TRUE if the coord is over an open path end point
01281     Purpose:    This routine scans the specified spread for all path objects and checks
01282                 whether the mouse coord is over an open end point. It can be used to work
01283                 out whether to change the mouse shape or not.
01284                 This routine should really exist in some other file, available to all tools
01285                 but I dont know where to put it yet so it shall have to stay here for the
01286                 mo.
01287     Errors:     -
01288     SeeAlso:    GetCoord() - this will return you the coord of the element under the mouse
01289 
01290 ********************************************************************************************/
01291 /*
01292 BOOL PenTool::OverPathEnd(DocCoord coord, Spread* pSpread, NodePath** pOverNode)
01293 {
01294     // First, get a pointer to the DocView so we can do blob proximity checks
01295     DocView* pDocView = DocView::GetCurrent();
01296     ENSURE( pDocView != NULL, "OverPathEnd: Can't find current DocView");
01297     BOOL OverEndPoint = FALSE;
01298 
01299     if (pDocView==NULL)
01300         return OverEndPoint;
01301 
01302     // Find the Rect round the mouse pos that counts as a hit
01303     DocRect BlobRect;
01304     OSRenderRegion::GetBlobRect(pDocView->GetViewScale(), coord, BT_SELECTEDLARGEST, &BlobRect);
01305 
01306     // Find the selected range of objects
01307     SelRange* Selected = GetApplication()->FindSelection();
01308     Node* pNode = Selected->FindFirst();
01309 
01310     if (pNode != NULL)
01311     {
01312         Spread* NodeSpread = pNode->FindParentSpread();
01313         if (NodeSpread == pSpread)
01314         {
01315             // On the same spread, so see if the pointer is over an endpoint
01316             while ((pNode != NULL) && !OverEndPoint)
01317             {
01318                 if (pNode->GetRuntimeClass() == CC_RUNTIME_CLASS(NodePath))
01319                 {
01320                     // Now we know it's a NodePath, get a pointer to the Path object within it, 
01321                     // so we can find any endpoints
01322                     Path* ThisPath = &(((NodePath*)pNode)->InkPath);
01323                     
01324                     // See if it touches any point in this path
01325                     INT32 CloseSlot = 0;
01326                     if (ThisPath->IsNearOpenEnd(BlobRect, &CloseSlot))
01327                     {
01328                         // Set a few other details worth remembering
01329                         ThisPath->SetPathPosition(CloseSlot);
01330                         OverEndPoint = TRUE;
01331                         *pOverNode = (NodePath*)pNode;
01332                     }
01333                 }
01334                 // Now find the next selected node
01335                 pNode = Selected->FindNext(pNode);
01336             }
01337         }
01338     }
01339     return OverEndPoint;
01340 }
01341 */
01342 
01343 
01344 /********************************************************************************************
01345 
01346 >   void PenTool::RemoveSelection(DocCoord PointerPos, Spread *pSpread, NodePath* pNodePath=NULL)
01347 
01348     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
01349     Created:    3/10/94
01350     Inputs:     PointerPos  = The position of the mouse click
01351                 pSpread     = The spread that the occured on
01352                 pNodePath   = A pointer to a particular node path to leave out (defaults to
01353                               NULL)
01354     Purpose:
01355 
01356 ********************************************************************************************/
01357 
01358 void PenTool::RemoveSelection( DocCoord PointerPos, Spread *pSpread, NodePath* pNodePath )
01359 {                                                                   
01360     // deselect any points within the spread the click occured over
01361     SelRange* Selected = GetApplication()->FindSelection();
01362     Node* pNode = Selected->FindFirst();
01363     if (pNode && pNode->FindParentSpread() == pSpread )
01364     {
01365         while (pNode)
01366         {
01367             if (pNode!=pNodePath)
01368             {
01369                 if (pNode->GetRuntimeClass() == CC_RUNTIME_CLASS(NodePath))
01370                 {
01371                     Path* ThisPath = &(((NodePath*)pNode)->InkPath);
01372                     PathFlags* Flags = ThisPath->GetFlagArray();
01373                     INT32 UsedSlots = ThisPath->GetNumCoords();
01374 
01375                     BOOL selected = FALSE;
01376                     for (INT32 i=0; i<UsedSlots; i++)
01377                     {
01378                         if (Flags[i].IsSelected)
01379                         {
01380                             if (!selected)
01381                             {
01382                                 // right, red selection blobs off!
01383                                 ThisPath->RenderPathPenBlobs(pSpread);
01384                                 selected = TRUE;
01385                             }
01386                             Flags[i].IsSelected=FALSE;
01387                         }
01388                     }
01389                     // now, black selection blobs back on!
01390                     if (selected)
01391                         ThisPath->RenderPathPenBlobs(pSpread);
01392                 }
01393             }
01394             pNode = Selected->FindNext(pNode);
01395         }
01396     }
01397 }
01398 
01399 
01400 
01401 
01402 /********************************************************************************************
01403 
01404 >   void PenTool::RenderToolBlobs(Spread* pSpread, DocRect* pClipRect)
01405 
01406     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
01407     Created:    20/9/94
01408     Inputs:     pSpread - The spread that the blob is to appear on
01409                 pClipRect - Pointer to the rect that contains the blobs (Can be NULL)
01410     Purpose:    The blobs this tool renders are
01411                 (1) a moveto floating endpoint
01412                 (2) a dragto floating endpoint
01413                 These are usually rendered after the first click/drag on a document
01414 
01415 ********************************************************************************************/
01416 
01417 void PenTool::RenderToolBlobs(Spread* pSpread, DocRect* pClipRect)
01418 {
01419     // Can only draw the path if there is a path to draw
01420     ERROR3IF(pSpread==NULL,"PenTool::RenderToolBlobs() called with a null spread");
01421 
01422     RenderRegion* pRegion;
01423     DocCoord LastMove = EditHandles.HndClick;
01424     DocCoord LastDrag = EditHandles.HndDrag;
01425 
01426     switch (CurrPenState)
01427     {
01428         case IS_MoveTo:
01429             pRegion = DocView::RenderOnTop(pClipRect, pSpread, ClippedEOR);
01430             while (pRegion)
01431             {
01432                 // Draw a moveto blob
01433                 pRegion->SetLineColour(COLOUR_BEZIERBLOB);
01434                 pRegion->SetFillColour(COLOUR_TRANS);
01435                 pRegion->DrawBlob(LastMove,BT_SELECTED);
01436                 
01437                 // Get the next region in the list
01438                 pRegion = DocView::GetNextOnTop(pClipRect);
01439             }
01440             break;
01441 
01442         case IS_DragTo:
01443             {
01444                 pRegion = DocView::RenderOnTop(pClipRect, pSpread, ClippedEOR);
01445                 DocCoord OtherDrag;
01446                 OtherDrag.x = LastMove.x - (LastDrag.x - LastMove.x);
01447                 OtherDrag.y = LastMove.y - (LastDrag.y - LastMove.y);
01448     
01449                 while (pRegion)
01450                 {
01451                     // Draw three end blobs and an eor'd dotted line
01452                     pRegion->SetLineColour(COLOUR_BEZIERLINE);
01453                     pRegion->DrawLine(OtherDrag,LastDrag);
01454                     pRegion->SetLineColour(COLOUR_BEZIERBLOB);
01455                     pRegion->SetFillColour(COLOUR_TRANS);
01456                     pRegion->DrawBlob(LastMove,BT_SELECTED);
01457                     pRegion->SetFillColour(COLOUR_UNSELECTEDBLOB);
01458                     pRegion->SetLineColour(COLOUR_TRANS);
01459                     pRegion->DrawBlob(LastDrag,BT_UNSELECTED);
01460                     pRegion->DrawBlob(OtherDrag,BT_UNSELECTED);
01461     
01462                     // Get the next region in the list
01463                     pRegion = DocView::GetNextOnTop(pClipRect);
01464                 }
01465                 break;
01466             }
01467         default:
01468             break;
01469     }   
01470 }
01471 
01472 
01473 
01474 
01475 
01476 /********************************************************************************************
01477 
01478 >   void PenTool::ChangeCursor(Cursor* cursor)
01479 
01480     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
01481     Created:    20/9/94
01482     Inputs:     ID of the cursor you want to flip to
01483     Outputs:    -
01484     Returns:    -
01485     Purpose:    Changes to the specified cursor. Will only change the cursor if it isn't 
01486                 already this cursor, so it doesn't flicker.
01487     Errors:     can fail if the cursor cannot be created - the cursor code will fail.
01488     SeeAlso:    -
01489 
01490 ********************************************************************************************/
01491 
01492 void PenTool::ChangeCursor(Cursor* cursor)
01493 {
01494     // only change if this cursor is different from the current cursor
01495     if (cursor != MyCurrentCursor)
01496     {
01497         // set this cursor as the current cursor and immediately display it
01498         CursorStack::GSetTop(cursor, CurrentCursorID);
01499         // remember this is our current cursor
01500         MyCurrentCursor = cursor;
01501     }
01502 }
01503 
01504 
01505 
01506 
01507 /********************************************************************************************
01508 
01509 >   void PenTool::SetInternalState()
01510 
01511     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
01512     Created:    20/9/94
01513     Inputs:     -
01514     Outputs:    -
01515     Returns:    -
01516     Purpose:    This function is called from an operation which tells the tool that there
01517                 should be a virtual moveto coordinate and dragto coordinate set within the
01518                 tool. If the dragto is equal to the moveto, then an internal move should be
01519                 created, rather than an internal drag.
01520 
01521 ********************************************************************************************/
01522 
01523 void PenTool::SetInternalState()
01524 {
01525     // Check that the handles are actually defined.
01526     if (EditHandles.pHndSpread==NULL)
01527         return;
01528 
01529     if (EditHandles.HndClick == EditHandles.HndDrag)
01530         CurrPenState = IS_MoveTo;
01531     else
01532         CurrPenState = IS_DragTo;
01533 
01534     // Now display the blobs
01535     BlobManager* BlobMgr = GetApplication()->GetBlobManager();
01536     ENSURE(BlobMgr!=NULL, "Blob Manager was not there");
01537     BlobMgr->RenderToolBlobsOn(this, EditHandles.pHndSpread, NULL);
01538 
01539 }
01540 
01541 /********************************************************************************************
01542 
01543 >   void PenTool::ClearInternalState()
01544 
01545     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
01546     Created:    20/9/94
01547     Inputs:     -
01548     Outputs:    -
01549     Returns:    -
01550     Purpose:    Clears the internal condition.
01551     Errors:     -
01552     SeeAlso:    -
01553 
01554 ********************************************************************************************/
01555 
01556 void PenTool::ClearInternalState()
01557 {
01558     if (CurrPenState == IS_MoveTo || CurrPenState == IS_DragTo)
01559     {
01560         BlobManager* BlobMgr = GetApplication()->GetBlobManager();
01561         ENSURE(BlobMgr!=NULL, "Blob Manager was not there");
01562         BlobMgr->RenderToolBlobsOff(this, EditHandles.pHndSpread,NULL);
01563     }
01564     CurrPenState = IS_Undefined;
01565 }
01566 
01567 
01568 
01569 
01570 /********************************************************************************************
01571 
01572 >   void PenTool::ClearPath()
01573 
01574     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
01575     Created:    30/9/94
01576     Inputs:     -
01577     Outputs:    -
01578     Returns:    -
01579     Purpose:    Throw away the internal data of the pen path
01580     Errors:     -
01581     SeeAlso:    -
01582 
01583 ********************************************************************************************/
01584 
01585 void PenTool::ClearPath()
01586 {
01587     // call the path clearing function!
01588 
01589     ClearInternalState();
01590     EditPath.ClearPath();
01591 }
01592 
01593 /********************************************************************************************
01594 
01595 >   void PenTool::ClearOp()
01596 
01597     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
01598     Created:    30/9/94
01599     Inputs:     -
01600     Purpose:    Throw away the internal op state we've been holding
01601 
01602 ********************************************************************************************/
01603 
01604 void PenTool::ClearOp()
01605 {
01606     CurrPenOpState = OS_Undefined;
01607 }
01608 
01609 
01610 /********************************************************************************************
01611 
01612 >   penopstate PenTool::GetPenOpState() const
01613 
01614     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
01615     Created:    30/9/94
01616     Inputs:     -
01617     Purpose:    return the operation state the pen tool thinks its in.
01618 
01619 ********************************************************************************************/
01620 
01621 penopstate PenTool::GetPenOpState() const
01622 {
01623     return (CurrPenOpState);
01624 }
01625 
01626 
01627 /********************************************************************************************
01628 
01629 >   void PenTool::CreateNewPath()
01630 
01631     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
01632     Created:    30/9/94
01633     Inputs:     -
01634     Outputs:    -
01635     Returns:    -
01636     Purpose:    Once a drag has finished we need to build a new path with the data
01637     Errors:     -
01638     SeeAlso:    -
01639 
01640 ********************************************************************************************/
01641 
01642 void PenTool::CreateNewPath()
01643 {
01644     // Ok, the last drag has altered our internal drag path. We now
01645     // have a path ready to add to the tree, so we need to get on with it!
01646     OpAddNewPath* pOpAddNewPath = new OpAddNewPath;
01647     pOpAddNewPath->DoAddNewPath(&EditPath, EditHandles.pHndSpread);
01648 }
01649 
01650 
01651 
01652 /********************************************************************************************
01653 
01654 >   void PenTool::AddElementToPath()
01655 
01656     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
01657     Created:    30/9/94
01658     Inputs:     -
01659     Outputs:    -
01660     Returns:    -
01661     Purpose:    Once a drag has finished we need to add the newly edited element to the
01662                 specified path
01663     Errors:     -
01664     SeeAlso:    -
01665 
01666 ********************************************************************************************/
01667 
01668 void PenTool::AddElementToPath()
01669 {
01670     // Right, the last drag has created a new path section for me
01671     // I need to add it in to the specified nodepath object
01672 
01673     OpAddPathToPath* pOpAddPathToPath = new OpAddPathToPath;
01674     pOpAddPathToPath->DoAddPathToPath(pNodePath, &EditPath, NodeIndex);
01675 }
01676 
01677 
01678 /********************************************************************************************
01679 
01680 >   void PenTool::CloseWithPath()
01681 
01682     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
01683     Created:    30/9/94
01684     Inputs:     -
01685     Outputs:    -
01686     Returns:    -
01687     Purpose:    Once a drag has finished we need to add the newly edited element to the
01688                 specified path
01689     Errors:     -
01690     SeeAlso:    -
01691 
01692 ********************************************************************************************/
01693 
01694 void PenTool::CloseWithPath()
01695 {
01696     // Right, the last drag has closed a path.
01697     // I need to add it in to the specified nodepath object
01698 
01699     OpClosePathWithPath* pOpClosePathWithPath = new OpClosePathWithPath;
01700     pOpClosePathWithPath->DoClosePathWithPath(pNodePath, &EditPath, NodeIndex);
01701 }
01702 

Generated on Sat Nov 10 03:47:52 2007 for Camelot by  doxygen 1.4.4