groupops.cpp

Go to the documentation of this file.
00001 // $Id: groupops.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 /*
00099 */
00100 
00101 #include "camtypes.h" 
00102 #include "camelot.h"
00103 //#include "grptrans.h"
00104 #include "groupops.h"
00105 //#include "group.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00106 //#include "undoop.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00107 //#include "resource.h" 
00108 //#include "simon.h"
00109 //#include "errors.h"  - in camtypes.h [AUTOMATICALLY REMOVED]
00110 //#include "ensure.h"    - in camtypes.h [AUTOMATICALLY REMOVED]
00111 //#include "docview.h"               - in camtypes.h [AUTOMATICALLY REMOVED]
00112 //#include "opdesc.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00113 //#include "mario.h"
00114 //#include "nodeattr.h"      - in camtypes.h [AUTOMATICALLY REMOVED]
00115 //#include "ink.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00116 //#include "app.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00117 //#include "document.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00118 //#include "spread.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00119 #include "layer.h"
00120 //#include "tool.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00121 #include "bubbleid.h"
00122 #include "fthrattr.h"
00123 #include "opliveeffects.h"
00124 #include "effects_stack.h"
00125 //#include "phil.h"
00126 #include "ophist.h"
00127 
00128 DECLARE_SOURCE("$Revision: 1282 $");  
00129 
00130 CC_IMPLEMENT_DYNCREATE(OpGroup, SelOperation)  
00131 CC_IMPLEMENT_DYNCREATE(OpUngroup, SelOperation)  
00132 CC_IMPLEMENT_DYNCREATE(OpUngroupSpecial, OpUngroup)  
00133 
00134 #define new CAM_DEBUG_NEW
00135 
00136 /*********************************************************************************************
00137 
00138     Preference: AskUngroupEffect
00139     Section:    Groups
00140     Range:      FALSE - TRUE
00141     Purpose:    Flag controlling whether user is asked about factoring out effects during ungroup
00142     SeeAlso:    -
00143 
00144 **********************************************************************************************/ 
00145 BOOL OpGroup::bAskBeforeFactoringEffects = FALSE;
00146 
00147 
00148 /*********************************************************************************************
00149 
00150     Preference: UngroupLocaliseEffects
00151     Section:    Groups
00152     Range:      FALSE - TRUE
00153     Purpose:    Flag controlling whether effects are localised by default or removed by default
00154     SeeAlso:    -
00155 
00156 **********************************************************************************************/ 
00157 BOOL OpGroup::bLocaliseEffects = FALSE;
00158 
00159 
00160 // ------------------------------------------------------------------------------------------
00161 // OpGroup methods
00162             
00163 /********************************************************************************************
00164 
00165 >   OpGroup::OpGroup() 
00166 
00167     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
00168     Created:    29/9/93
00169     Purpose:    OpGroup constructor
00170 
00171 ********************************************************************************************/
00172 OpGroup::OpGroup(): SelOperation()                              
00173 {                              
00174 }
00175 
00176  /********************************************************************************************
00177 
00178 >   BOOL OpGroup::Init()
00179 
00180     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
00181     Created:    28/9/93
00182     Returns:    TRUE if the operation could be successfully initialised 
00183                 FALSE if no more memory could be allocated 
00184                 
00185     Purpose:    OpGroup initialiser method
00186     Errors:     ERROR will be called if there was insufficient memory to allocate the 
00187                 operation.
00188 
00189 ********************************************************************************************/
00190 BOOL OpGroup::Init()
00191 {
00192     return (RegisterOpDescriptor(0,
00193                             _R(IDS_GROUPOP),
00194                             CC_RUNTIME_CLASS(OpGroup),
00195                             OPTOKEN_GROUP,
00196                             OpGroup::GetState,
00197                             0,  /* help ID */
00198                             _R(IDBBL_GROUPOP),
00199                             0,  /* bitmap ID */
00200                             0,
00201                             SYSTEMBAR_ILLEGAL,          // For now !
00202                             TRUE,                       // Receive messages
00203                             FALSE,
00204                             FALSE,
00205                             0,
00206                             (GREY_WHEN_NO_CURRENT_DOC | GREY_WHEN_NO_SELECTION)
00207 
00208                             )); 
00209 
00210     if (Camelot.DeclareSection(_T("Groups"), 2))
00211     {
00212         Camelot.DeclarePref(_T("Groups"), _T("AskUngroupEffects"), &bAskBeforeFactoringEffects, FALSE, TRUE);
00213         Camelot.DeclarePref(_T("Groups"), _T("UngroupLocaliseEffects"), &bLocaliseEffects, FALSE, TRUE);
00214     }
00215 }               
00216     
00217 /********************************************************************************************
00218 
00219 >   OpState OpGroup::GetState(String_256*, OpDescriptor*)
00220 
00221     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
00222     Created:    28/9/93
00223     Returns:    The state of the OpGroup
00224     Purpose:    For finding the OpGroup's state. 
00225 
00226 ********************************************************************************************/
00227 OpState OpGroup::GetState(String_256* UIDescription, OpDescriptor*)
00228 {
00229     OpState OpSt;
00230     String_256 DisableReason; 
00231 
00232     // Obtain the current selections 
00233     Range Sel(*(GetApplication()->FindSelection()));
00234     RangeControl rg = Sel.GetRangeControlFlags();
00235     rg.PromoteToParent = TRUE;
00236     Sel.Range::SetRangeControl(rg);
00237       
00238     // This operation is disabled if there are no nodes selected, or if the only selected node
00239     // is a node group.                           
00240     Node* FirstSelected = Sel.FindFirst();  
00241     
00242     OpSt.Greyed = FALSE; 
00243     if (IS_A(FirstSelected,NodeGroup) 
00244          && (Sel.FindNext(FirstSelected) == NULL) )      // Only a NodeGroup selected
00245     {  
00246         OpSt.Greyed = TRUE; 
00247         // Load reason why operation is disabled
00248         // Grouping only a single group is pointless
00249         DisableReason = String_256(_R(IDS_ILLEGAL_TO_GROUP_SINGLE_GROUP));
00250         *UIDescription = DisableReason;          
00251     }
00252         
00253     return(OpSt);   
00254 }
00255 
00256 
00257 /********************************************************************************************
00258 
00259 >   BOOL OpGroup::NeedToInvalidateGroup()
00260 
00261     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
00262     Created:    12/7/94
00263     Inputs:     -
00264     Outputs:    -
00265     Returns:    TRUE if we need to invalidate
00266     Purpose:    This function determines if the selected objects will need to be invalidated
00267                 if they are to be made into a group. 
00268 
00269                 
00270 
00271     Errors:     -
00272     Scope:      private
00273     SeeAlso:    -
00274 
00275 ********************************************************************************************/
00276 
00277 BOOL OpGroup::NeedToInvalidateGroup()
00278 {
00279 //  if (TRUE /*GetApplication()->IsBkRendering()*/)
00280 //      return TRUE;        // Always invalidate if Background Redraw is occuring
00281 
00282     // If all selected objects are contiguous then there will be no need to invalidate
00283     // the group's bounds.
00284 
00285     // Obtain the current selection
00286     SelRange Sel(*(GetApplication()->FindSelection()));
00287     RangeControl rg = Sel.GetRangeControlFlags();
00288     rg.PromoteToParent = TRUE;
00289     Sel.Range::SetRangeControl(rg);
00290     
00291     Node* Current = Sel.FindFirst(); 
00292     
00293     ERROR2IF(Current == NULL, TRUE, "There are no selected objects"); 
00294 
00295     // Create a range of all objects starting with the first selected object
00296         
00297 //  RangeControl rc = { TRUE, TRUE, TRUE }; // selected + unselected nodes, cross layer
00298 
00299     Range ObjectRange(Current, NULL, RangeControl(TRUE,TRUE,TRUE,TRUE));
00300 
00301     BOOL Contiguous = TRUE; // Until we know better
00302     DocRect SelBounds;
00303 
00304     Node* SelObject = Current; 
00305     Node* Object = Current;
00306     
00307     // Traverse both ranges simultaneously
00308     while (SelObject != NULL)
00309     {
00310         if (SelObject != Object)
00311         {
00312             // All selected objects are not contiguous
00313             if (Contiguous)
00314             {
00315                 // We will need to calculate the bounds of the selection for what comes next
00316                 SelBounds = Sel.GetBoundingRect();
00317                 Contiguous = FALSE; // So we don't calculate the bounds again
00318             }
00319         
00320             // Check if the uncontiguous object intersects with the bounds of the selection
00321         
00322             if (Object->IsAnObject())
00323             { 
00324 
00325                 if ( ((NodeRenderableInk*)Object)->GetBoundingRect().IsIntersectedWith(SelBounds))
00326                 {
00327                     return TRUE; // An unselected object intersects with a selected object
00328                 }
00329             }
00330             // Objects don't intersect so it doesn't matter
00331             Object = ObjectRange.FindNext(Object);
00332             ERROR3IF(Object == NULL, "Reached end of object list"); 
00333         }
00334         else
00335         {
00336             // Get next items in both ranges
00337             SelObject = Sel.FindNext(SelObject); 
00338             Object = ObjectRange.FindNext(Object);
00339         }
00340     }
00341     return FALSE; // No need to invalidate
00342 
00343 }
00344 
00345 /********************************************************************************************
00346 
00347 >   void OpGroup::Do(OpDescriptor*)
00348 
00349     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
00350     Created:    16/8/93
00351     Inputs:     OpDescriptor (unused)
00352     Outputs:    -
00353     Returns:    -
00354     Purpose:    Performs the Group operation. 
00355                 
00356     Errors:     -
00357     SeeAlso:    -
00358 
00359 ********************************************************************************************/
00360     
00361 void OpGroup::Do(OpDescriptor*)
00362 {   
00363     // Obtain the current selections 
00364     Range Sel(*(GetApplication()->FindSelection()));
00365     RangeControl rg = Sel.GetRangeControlFlags();
00366     rg.PromoteToParent = TRUE;
00367     Sel.Range::SetRangeControl(rg);
00368 
00369     // Find the first node which is selected 
00370     Node* FirstSelectedNode = Sel.FindFirst(); 
00371     
00372     ENSURE(FirstSelectedNode != NULL, "Called group operation with no nodes selected"); 
00373     
00374     HideNodeAction* UndoHideNodeAction; 
00375 
00376     // In the retail build it is best to do nothing if we find there are no selected nodes 
00377     if (FirstSelectedNode != NULL) // No nodes selected so End
00378     {   
00379         // Get the current tool
00380         Tool* pTool = Tool::GetCurrent();
00381         Spread* pSelSpread = Document::GetSelectedSpread();
00382 
00383         // Get the tool to remove all its blobs before we deselect the nodes.
00384         // Only do this if the current tool dosent update itself on sel changed messages
00385         if (pSelSpread!=NULL && pTool!=NULL && !pTool->AreToolBlobsRenderedOnSelection())
00386             pTool->RenderToolBlobs(pSelSpread,NULL);
00387 
00388         // Determine if it will be neccessary to invalidate the groups bounds
00389         BOOL InvalidateBounds = TRUE; 
00390         BOOL IfBgRedraw = !NeedToInvalidateGroup();
00391 
00392         // Draw blobs if we are not invalidating the object bounds
00393         if (!DoStartSelOp(IfBgRedraw,IfBgRedraw)) 
00394         {
00395             End();
00396             return;
00397         }
00398         
00399         if (InvalidateBounds)
00400         {
00401             // We need to invalidate the region
00402             if (!DoInvalidateNodesRegions(Sel, TRUE, FALSE, IfBgRedraw))
00403             {
00404                 End();
00405                 return;
00406             } 
00407         }
00408 
00409         // Go and find the spread that the selected objects are on
00410 #ifdef _DEBUG
00411         Spread* pSpread = FirstSelectedNode->FindParentSpread();
00412         ENSURE(pSpread != NULL, "A selected nodes spread is NULL"); 
00413 #endif
00414 
00415         // Find the selected node with the highest z-order position, as we will need to insert 
00416         // the group node here
00417         Node* FinalNode = NULL; 
00418 
00419         for(Node* n = Sel.FindFirst(); n != NULL; n = Sel.FindNext(n))
00420         {   
00421             FinalNode = n; 
00422         }
00423 
00424         ENSURE(FinalNode != NULL, "The final selected node is NULL ?"); 
00425                                         
00426         // Create a group node and attach it as a next sibling of the final selected node    
00427         NodeGroup* Group; 
00428         ALLOC_WITH_FAIL(Group, (new NodeGroup(FinalNode, NEXT)), this);
00429         // If the allocation fails the FailAndExecute will have been called 
00430         if (Group != NULL)                 
00431         {    
00432             // Create an action to hide the group when we undo 
00433             if ( HideNodeAction::Init(this, 
00434                  &UndoActions, 
00435                  Group, 
00436                  FALSE, // Don't include subtree size
00437                  (Action**)(&UndoHideNodeAction),
00438                  FALSE) // Don't tell subtree when undone
00439                  == AC_FAIL)  
00440             {
00441                 Group->UnlinkNodeFromTree();
00442                 delete (Group); 
00443 
00444                 End();
00445                 return;
00446             };
00447                
00448             // Take each node in the range and attach it to the NodeGroup
00449             Node* n = Sel.FindFirst(); // Get first node to be added to group   
00450     
00451             // Loop until all nodes in range have been added to the group   
00452             Node* NxtInRange; 
00453             
00454             Node* LastObjectMoved = NULL;   
00455               
00456             DocRect GroupBounds; // bounding rectangle of the group  
00457             while (n != NULL) // Stop when there are no more selected nodes. 
00458             {                                                     
00459                 // Ensure that the selected node is a NodeRenderableInk
00460                 ENSURE(n->IsAnObject(), 
00461                        "Selected node is not a NodeRenderableInk");    
00462                 NxtInRange = Sel.FindNext(n); // Neccessary because n is 
00463                                               // just about to be moved
00464                 // Attach n to NodeGroup 
00465                 if (LastObjectMoved == NULL)
00466                 {
00467                     if (!DoMoveNode((NodeRenderableInk*)n, Group, LASTCHILD)) 
00468                     {
00469                         End();
00470                         return;
00471                     }
00472                 }
00473                 else
00474                 {
00475                     // We can do it quicker
00476                     if (!DoMoveNode((NodeRenderableInk*)n, LastObjectMoved, NEXT)) 
00477                     {
00478                         End();
00479                         return;
00480                     }
00481                 }
00482 
00483                 // Deselect the object
00484                 // Botch - until selstate restores child states
00485                 //if (!DoDeselectNode(((NodeRenderableInk*)n)))
00486                 //{
00487                 //  goto EndOperation;
00488                 //}
00489                 ((NodeRenderableInk*)n)->DeSelect(FALSE, TRUE); 
00490 
00491                 GroupBounds = 
00492                     GroupBounds.Union( ((NodeRenderableBounded*)n)->GetBoundingRect());
00493                 
00494                 
00495                 LastObjectMoved = n; 
00496 
00497                 n = NxtInRange;                 // Get the next node in the range   
00498             } 
00499             
00500                  
00501             // Set the group's bounding rectangle 
00502             Group->InvalidateBoundingRect();
00503 
00504             // Select the group and draw its blobs   
00505             ((NodeRenderable*)Group)->Select(FALSE);
00506             
00507             if (InvalidateBounds)
00508             {
00509                 if (!DoInvalidateNodesRegions(*(GetApplication()->FindSelection()), TRUE, FALSE, IfBgRedraw))
00510                 {
00511                     // We need to invalidate the region
00512                     End();
00513                     return;
00514                 }
00515             }
00516 
00517             // Now factor out all common attributes
00518             // bodge 
00519             if (!DoFactorOutCommonChildAttributes(Group))
00520             {
00521                 End();
00522                 return;
00523             }
00524         }
00525 
00526         // Get the tool to remove all its blobs before we deselect the nodes.
00527         // Only do this if the current tool dosent update itself on sel changed messages
00528         if (pSelSpread!=NULL && pTool!=NULL && !pTool->AreToolBlobsRenderedOnSelection())
00529             pTool->RenderToolBlobs(pSelSpread,NULL);
00530     }                   
00531     End(); 
00532 }           
00533 
00534 
00535     
00536 // ------------------------------------------------------------------------------------------
00537 // OpUngroup methods
00538     
00539 /********************************************************************************************
00540 
00541 >   OpUngroup::OpUngroup() 
00542 
00543     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
00544     Created:    29/9/93
00545     Inputs:     -
00546     Outputs:    -
00547     Returns:    -
00548     Purpose:    OpUngroup constructor
00549     Errors:     -
00550     SeeAlso:    -
00551 
00552 ********************************************************************************************/
00553             
00554             
00555 OpUngroup::OpUngroup(): SelOperation()                              
00556 {                              
00557 }
00558 
00559  /********************************************************************************************
00560 
00561 >   BOOL OpUngroup::Init()
00562 
00563     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
00564     Created:    28/9/93
00565     Inputs:     -
00566     Outputs:    -
00567     Returns:    TRUE if the operation could be successfully initialised 
00568                 FALSE if no more memory could be allocated 
00569                 
00570     Purpose:    OpUngroup initialiser method
00571     Errors:     ERROR will be called if there was insufficient memory to allocate the 
00572                 operation.
00573     SeeAlso:    -
00574 
00575 ********************************************************************************************/
00576 
00577 BOOL OpUngroup::Init()
00578 {
00579     return (RegisterOpDescriptor(0,
00580                                 _R(IDS_UNGROUPOP),
00581                                 CC_RUNTIME_CLASS(OpUngroup),
00582                                 OPTOKEN_UNGROUP,
00583                                 OpUngroup::GetState,
00584                                 0,  /* help ID */
00585                                 _R(IDBBL_UNGROUPOP),
00586                                 0,  /* bitmap ID */
00587                                 0,
00588                                 SYSTEMBAR_ILLEGAL,          // For now !
00589                                 TRUE,                       // Receive messages
00590                                 FALSE,
00591                                 FALSE,
00592                                 0,
00593                                 (GREY_WHEN_NO_CURRENT_DOC | GREY_WHEN_NO_SELECTION)
00594 
00595                                  )); 
00596 }               
00597 
00598 /********************************************************************************************
00599 
00600 >   OpState OpUngroup::GetState(String_256*, OpDescriptor*)
00601 
00602     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
00603     Created:    28/9/93
00604     Inputs:     -
00605     Outputs:    -
00606     Returns:    The state of the OpUngroup
00607     Purpose:    For finding the OpUngroup's state. 
00608     Errors:     -
00609     SeeAlso:    -
00610 
00611     Notes:      Karim 06/10/2000
00612                 Modified this method to check for shadowed, bevelled and contoured groups,
00613                 as these cannot be ungrouped until the shadow, bevel or contour is removed.
00614 
00615 ********************************************************************************************/
00616 
00617 OpState OpUngroup::GetState(String_256* UIDescription, OpDescriptor*)
00618 {
00619     OpState OpSt;
00620     
00621     String_256 DisableReason;
00622 
00623     // Obtain the current selections 
00624     Range Sel(*(GetApplication()->FindSelection()));
00625 
00626     // Determine if there are any group nodes selected,
00627     // and if so, whether they lie within controller nodes.
00628     Node* n = Sel.FindFirst();
00629 //  Node* pParentController = NULL;
00630     BOOL SelectedGroup = FALSE;
00631 //  BOOL InsideControllerNode = FALSE;
00632     while (n != NULL)
00633     {
00634         if (  n->IsSelected() && IS_A(n,NodeGroup) )
00635         {
00636 /*          pParentController = n->FindParent();
00637             if (pParentController != NULL)
00638             {
00639                 if (pParentController->IsABevelController() ||
00640 //                  pParentController->IsAShadowController() ||
00641                     pParentController->IsAContourController())
00642                 {
00643                     InsideControllerNode = TRUE;
00644                     break;
00645                 }
00646             }
00647 */
00648             SelectedGroup = TRUE;
00649             break; 
00650         }
00651         n = Sel.FindNext(n);
00652     }
00653 //  OpSt.Greyed = (InsideControllerNode || !SelectedGroup);
00654     OpSt.Greyed = !SelectedGroup;
00655     if (OpSt.Greyed)
00656     {
00657         // Load reason why operation is disabled.
00658 /*
00659         // we're inside a controller node.
00660         if (InsideControllerNode)
00661         {
00662             String_256 DisableReason(_R(IDS_GROUPINSIDECONTROLLER));
00663             *UIDescription = DisableReason;
00664         }
00665 */
00666         // No group nodes are selected.
00667 //      else if (!SelectedGroup)
00668         if(!SelectedGroup)
00669         {
00670             String_256 DisableReason(_R(IDS_NO_GROUPS_SEL));
00671             *UIDescription = DisableReason;
00672         }
00673     }
00674     return(OpSt);
00675 }
00676 
00677 /********************************************************************************************
00678 
00679 >   void OpUngroup::Do(OpDescriptor*)
00680 
00681     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
00682     Created:    16/8/93
00683     Inputs:     OpDescriptor (unused)
00684     Outputs:    -
00685     Returns:    -
00686     Purpose:    Performs the Ungroup operation
00687     Errors:     An ENSURE failure will occur if this function is called and there are no 
00688                 selected groups. 
00689     SeeAlso:    -
00690 
00691 ********************************************************************************************/
00692 
00693 void OpUngroup::Do(OpDescriptor*)
00694 {   
00695     if (DoStartSelOp(TRUE,TRUE))  // Try to record the selection state 
00696     {
00697         if (!UngroupSelectedGroups())
00698         {
00699             FailAndExecute(); 
00700         }    
00701     }
00702     End(); 
00703 }  
00704 
00705 
00706 
00707 /********************************************************************************************
00708 
00709 >   BOOL OpUngroup::ParentAllowsUngroup(NodeGroup* pChild)
00710 
00711     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
00712     Created:    09/10/2000
00713 
00714     Inputs:     pChild  the group node to check.
00715 
00716     Returns:    TRUE if our parent's type is good for ungrouping,
00717                 FALSE otherwise.
00718 
00719     Purpose:    Nasty BODGE code!
00720 
00721                 Checks for the user trying to ungroup a shadowed (maybe bevelled
00722                 or contoured too in future) group, disallows the operation and
00723                 explains to them what the problem is/how to solve it.
00724 
00725     Errors:     ERROR3 if the parent is an unexpected type - all such cases should
00726                 never get this far into the ungroup code, they should have been caught
00727                 by the GetState() checks.
00728 
00729 ********************************************************************************************/
00730 BOOL OpUngroup::ParentAllowsUngroup(NodeGroup* pChild)
00731 {
00732     Node* pParent = pChild->FindParent();
00733     UINT32 MessageID = 0;
00734 
00735     if (pParent != NULL)
00736     {
00737         // if the parent is a NodeShadowController,
00738         // then post a message informing the user.
00739         if (pParent->IsABevelController())
00740             MessageID = _R(IDS_GROUPINSIDEBEVEL);
00741 PORTNOTE("other", "Ungroup can't localise effects yet")
00742 #ifndef EXCLUDE_FROM_XARALX
00743 // We don't need this clause when shadows can be localised in ungroup
00744 //      else if (pParent->IsAShadowController())    // Shadows are now PostProcessors and allow ungrouping
00745 //          MessageID = _R(IDS_GROUPINSIDESHADOW);
00746 #else
00747         else if (pParent->IsAShadowController())    // Shadows are now PostProcessors and allow ungrouping
00748             MessageID = _R(IDS_GROUPINSIDESHADOW);
00749 #endif
00750         else if (pParent->IsAContourController())
00751             MessageID = _R(IDS_GROUPINSIDECONTOUR);
00752 
00753         if(MessageID != 0)
00754         {
00755 			::InformWarning(MessageID);
00756             return FALSE;
00757         }
00758 
00759         // we must catch any group-derived nodes which make it past any ungroup checks.
00760         // the GetState() code should catch all such instances, so if execution gets here,
00761         // then something somewhere else is broken!
00762         if (pParent->IsAGroup())
00763         {
00764             ERROR3("OpUngroup::UngroupSelectedGroups; Group-derived parent node got past OpUngroup checks!");
00765             return FALSE;
00766         }
00767     }
00768 
00769     return TRUE;
00770 }
00771 
00772 
00773      
00774 /********************************************************************************************
00775 
00776 >   BOOL OpUngroup::UngroupSelectedGroups()
00777 
00778     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
00779     Created:    22/10/93
00780     Inputs:     -
00781     Outputs:    -
00782     Returns:    TRUE if successful
00783                 FALSE if the operation was aborted, or if there are no selected group nodes. 
00784 
00785     Purpose:    The function scans the tree and ungroups selected group nodes. It implements 
00786                 the main body of the OpUngroup Do function. 
00787                 
00788                 The reason the Do function was split was so that the UngroupSpecial operation 
00789                 could re-use the code. 
00790                                 
00791     Errors:     -
00792     Scope:      protected
00793     SeeAlso:    OpUngroup::Do
00794 
00795 ********************************************************************************************/
00796      
00797 BOOL OpUngroup::UngroupSelectedGroups()
00798 { 
00799     // Obtain the current selections
00800      
00801     Range Sel(*(GetApplication()->FindSelection()));
00802 
00803     BOOL Ungrouped = FALSE; // Set to true when we ungroup a group      
00804     BOOL bSelectGroupsHaveEffectAttrs = FALSE;
00805     Node* FirstSelectedNode = Sel.FindFirst(); 
00806     if (FirstSelectedNode != NULL) // No nodes selected so End
00807     {
00808         BOOL bLocalisePostPros = FALSE;
00809         BOOL bDeletePostPros = FALSE;
00810         EffectsStack* pStack = GetApplication()->FindSelection()->GetEffectsStack();
00811         bSelectGroupsHaveEffectAttrs = FindSelectedEffectAttrs();
00812         if ((pStack && !pStack->IsEmpty()) || bSelectGroupsHaveEffectAttrs)
00813         {
00814             bLocalisePostPros = TRUE;
00815             // If the preference is set of the user forces it by holding down Shift
00816             // We will ask the user what he wanst to do with effects
00817             ClickModifiers clickmods = ClickModifiers::GetClickModifiers();
00818             if (OpGroup::bAskBeforeFactoringEffects || clickmods.Adjust)
00819             {
00820                 OpGroup::bAskBeforeFactoringEffects = TRUE;
00821 
00822                 // We need to ask the user about removing or localising PostProcessors
00823                 INT32 ret = AskQuestion(_R(IDS_LE_ASK_UNGROUPLOCALISE), _R(IDS_LE_COPY), _R(IDS_LE_ALWAYSCOPY), _R(IDS_LE_REMOVE), _R(IDS_CANCEL));
00824                 switch (ret)
00825                 {
00826                 case 1: bLocalisePostPros = TRUE;
00827                         break;
00828                 case 2: bLocalisePostPros = TRUE;
00829                         OpGroup::bAskBeforeFactoringEffects = FALSE;
00830                         OpGroup::bLocaliseEffects = TRUE;
00831                         break;
00832                 case 3: bDeletePostPros = TRUE;
00833                         OpGroup::bLocaliseEffects = FALSE;  // ?
00834                         break;
00835                 case 4: return FALSE;
00836                         break;
00837                 }
00838             }
00839             else
00840             {
00841                 bLocalisePostPros = OpGroup::bLocaliseEffects;
00842                 bDeletePostPros = !OpGroup::bLocaliseEffects;
00843             }
00844         }
00845 
00846         // Get the current tool
00847         Tool* pTool = Tool::GetCurrent();
00848         Spread* pSelSpread = Document::GetSelectedSpread();
00849 
00850         // Get the tool to remove all its blobs before we deselect the nodes.
00851         // Only do this if the current tool dosent update itself on sel changed messages
00852         if (pSelSpread!=NULL && pTool!=NULL && !pTool->AreToolBlobsRenderedOnSelection())
00853             pTool->RenderToolBlobs(pSelSpread,NULL);
00854 
00855         // Find out which spread the node to be ungrouped is in
00856 //      Spread* pSpread = FirstSelectedNode->FindParentSpread();
00857 
00858         Node* CurrentNode = FirstSelectedNode;
00859         Node* NextCurrent;
00860         Node* GrpNode;
00861         Node* NxtGrpNode;
00862         Node* Anchor;
00863 
00864         // Ungroup all selected groups   
00865         while (CurrentNode != NULL)
00866         {
00867             NextCurrent = Sel.FindNext(CurrentNode);
00868 
00869             if (CurrentNode->IsSelected() && IS_A(CurrentNode, NodeGroup))
00870             {
00871                 // Karim 09/10/2000
00872                 // Shadows BODGE - if our parent is a NodeShadowController, then we cannot ungroup.
00873                 // Phil 2005, Shadows are different now but other controllers still disallow 
00874                 // ungrouping!
00875                 if (ParentAllowsUngroup((NodeGroup*)CurrentNode))
00876                 {
00877                     Ungrouped = TRUE;
00878 
00879                     // Karim 09/10/2000
00880                     // Invalidate region ONLY if in the middle of a background redraw UNLESS we were feathered.
00881                     // Invalidate if we are changing effects so they the changes get redrawn
00882                     BOOL RedrawNow = (CurrentNode->FindFirstChild(CC_RUNTIME_CLASS(AttrFeather))!=NULL ||
00883                                         bDeletePostPros || bLocalisePostPros ||
00884                                         bSelectGroupsHaveEffectAttrs
00885                                         );
00886                     DoInvalidateNodeRegion( ((NodeRenderableBounded*)CurrentNode), TRUE, FALSE, !RedrawNow);
00887 
00888                     // Localise any containing PostProcessors
00889                     // (This will push down common attributes into the group)
00890 /*                  // Find topmost effect applied to the current node and localise attributes
00891                     // down from there...
00892                     Node* pTopNode = EffectsStack::EscapeOldControllers(CurrentNode);
00893                     Node* pHighestLE = CurrentNode;
00894                     do
00895                     {
00896                         if (pTopNode->IsPostProcessor())
00897                             pHighestLE = pTopNode;
00898 
00899                         pTopNode = pTopNode->FindParent();
00900                     }
00901                     while (pTopNode && pTopNode->IsPostProcessor());
00902 */
00903                     if (bLocalisePostPros)
00904                     {
00905 PORTNOTETRACE("other", "Ungroup can't localise effects yet");
00906 #if !defined(EXCLUDE_FROM_XARALX)
00907                         BOOL bOK = TRUE;
00908                         bOK = bOK && OpLiveEffect::DoLocaliseEffectAttrs(this, CurrentNode);
00909                         bOK = bOK && OpLiveEffect::DoLocaliseLiveEffects(this, CurrentNode);
00910                         if (!bOK) return FALSE;
00911 #endif
00912                     }
00913                     else if (bDeletePostPros)
00914                     {
00915 PORTNOTETRACE("other", "Ungroup can't localise effects yet");
00916 #if !defined(EXCLUDE_FROM_XARALX)
00917                         if (CurrentNode->IsAnObject())
00918                         {
00919                             BOOL bOK = (OpLiveEffect::DoDeleteAllPostProcessors(this, (NodeRenderableInk*)CurrentNode, FALSE, TRUE)!=NULL);
00920                             // Effect attrs will be deleted when the group and/or postpros are deleted
00921                             // because of this we need to force the Op to redraw (see If BgRedraw above)
00922                             if (!bOK) return FALSE;
00923                         }
00924 #endif
00925                     }
00926 
00927                     // Localise the attributes 
00928                     if (!DoLocaliseCommonAttributes(((NodeGroup*)CurrentNode)))
00929                         return FALSE;
00930 
00931                     // Deselect the group node, cos we have got to remove its blobs
00932                     ((NodeGroup*)CurrentNode)->DeSelect(FALSE);
00933 
00934                     ENSURE(!(CurrentNode->IsSelected()), "Deselect failed to deselect current node");
00935 
00936                     // Hide the group node
00937                     NodeHidden* Hidden; 
00938                     if(!DoHideNode(CurrentNode, FALSE, &Hidden, FALSE))
00939                         return FALSE;
00940 
00941                     Anchor = Hidden;
00942                     ENSURE(Anchor->IsNodeHidden(),
00943                            "The parent of a hidden group node is not a NodeHidden");
00944 
00945                     // Move and select each node in turn. 
00946                     GrpNode = CurrentNode->FindFirstChild();         // Group's first child node
00947 
00948                     // Make sure that the group has at least one child
00949                     #ifdef _DEBUG
00950                     if (GrpNode == NULL)
00951                     {
00952                         TRACE( _T("Whilst ungrouping a group node without any children was found - It must be a Corel file"));
00953                     }
00954                     #endif
00955 
00956                     //ENSURE(GrpNode != NULL, "Trying to ungroup a node which has no children");
00957                     while (GrpNode != NULL)
00958                     {
00959 
00960                         // Find the next node to ungroup before we move the group node
00961                         NxtGrpNode = GrpNode->FindNext(); 
00962 
00963                         // If the node is not a NodeHidden then     
00964                         // Move the node to its new location in the tree. There is no need
00965                         // to render the node.
00966                         if (!GrpNode->IsAnAttribute() && !GrpNode->IsNodeHidden())
00967                         {
00968                             if (!DoMoveNode(GrpNode, Anchor, NEXT))
00969                                 return FALSE;
00970 
00971                             if (GrpNode->IsAnObject())
00972                                 ((NodeRenderableInk*)GrpNode)->Select(FALSE); 
00973 
00974                             Anchor = GrpNode; // Attach the next node to ungroup to the last one
00975                                               // so maintaining z ordering.
00976                         }
00977                         GrpNode = NxtGrpNode;
00978                     }
00979                 }
00980             }
00981             CurrentNode = NextCurrent; 
00982         }
00983 
00984         // Get the tool to remove all its blobs before we deselect the nodes.
00985         // Only do this if the current tool dosent update itself on sel changed messages
00986         if (pSelSpread!=NULL && pTool!=NULL && !pTool->AreToolBlobsRenderedOnSelection())
00987             pTool->RenderToolBlobs(pSelSpread,NULL);
00988     }
00989     else
00990         return (FALSE); // No nodes selected
00991 
00992     return (Ungrouped); // Ungrouped will be TRUE if we have ungrouped any groups
00993 }
00994 
00995 
00996 BOOL OpUngroup::FindSelectedEffectAttrs() const
00997 {
00998     Range* pRange = GetApplication()->FindSelection();
00999     Node* pNode = pRange->FindFirst();
01000     while (pNode)
01001     {
01002         if (pNode->IsAGroup())
01003         {
01004             NodeAttribute* pAttr = NodeAttribute::FindFirstAppliedAttr(pNode);
01005             while (pAttr && pAttr->FindParent() == pNode)
01006             {
01007                 if (pAttr->IsEffectAttribute() && !pAttr->HasEquivalentDefaultValue(TRUE))
01008                     return TRUE;
01009 
01010                 pAttr = NodeAttribute::FindPrevAppliedAttr(pAttr);
01011             }
01012         }
01013 
01014         pNode = pRange->FindNext(pNode);
01015     }
01016 
01017     return FALSE;
01018 }
01019 
01020 
01021 
01022 // ------------------------------------------------------------------------------------------
01023 // OpUngroupSpecial methods
01024     
01025 /********************************************************************************************
01026 
01027 >   OpUngroupSpecial::OpUngroupSpecial() 
01028 
01029     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
01030     Created:    29/9/93
01031     Inputs:     -
01032     Outputs:    -
01033     Returns:    -
01034     Purpose:    OpUngroupSpecial constructor
01035     Errors:     -
01036     SeeAlso:    -
01037 
01038 ********************************************************************************************/
01039             
01040             
01041 OpUngroupSpecial::OpUngroupSpecial(): OpUngroup()                               
01042 {                              
01043 }
01044 
01045  /********************************************************************************************
01046 
01047 >   BOOL OpUngroupSpecial::Init()
01048 
01049     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
01050     Created:    28/9/93
01051     Inputs:     -
01052     Outputs:    -
01053     Returns:    TRUE if the operation could be successfully initialised 
01054                 FALSE if no more memory could be allocated 
01055                 
01056     Purpose:    OpUngroupSpecial initialiser method
01057     Errors:     ERROR will be called if there was insufficient memory to allocate the 
01058                 operation.
01059     SeeAlso:    -
01060 
01061 ********************************************************************************************/
01062 
01063 BOOL OpUngroupSpecial::Init()
01064 {
01065     return (RegisterOpDescriptor(0,
01066                                 _R(IDS_UNGROUPSPECIALOP),
01067                                 CC_RUNTIME_CLASS(OpUngroupSpecial),
01068                                 OPTOKEN_UNGROUPSPECIAL,
01069                                 OpUngroupSpecial::GetState,
01070                                 0,  /* help ID */
01071                                 _R(IDBBL_UNGROUPSPECIALOP),
01072                                 0,
01073                                 0,
01074                                 SYSTEMBAR_ILLEGAL,          // For now !
01075                                 TRUE,                       // Receive messages
01076                                 FALSE,
01077                                 FALSE,
01078                                 0,
01079                                 (GREY_WHEN_NO_CURRENT_DOC | GREY_WHEN_NO_SELECTION)
01080 
01081                                 )); 
01082 
01083 }               
01084 
01085 /********************************************************************************************
01086 
01087 >   OpState OpUngroupSpecial::GetState(String_256*, OpDescriptor*)
01088 
01089     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
01090     Created:    28/9/93
01091     Inputs:     -
01092     Outputs:    -
01093     Returns:    The state of the OpUngroupSpecial
01094     Purpose:    For finding the OpUngroupSpecial's state. 
01095     Errors:     -
01096     SeeAlso:    -
01097 
01098 ********************************************************************************************/
01099 
01100 OpState OpUngroupSpecial::GetState(String_256* s, OpDescriptor* o)
01101 {
01102     return(OpUngroup::GetState(s,o));  
01103 }
01104 
01105 /********************************************************************************************
01106 
01107 >   void OpUngroupSpecial::Do(OpDescriptor*)
01108 
01109     Author:     Simon_Maneggio (Xara Group Ltd) <camelotdev@xara.com>
01110     Created:    16/8/93
01111     Inputs:     OpDescriptor (unused)
01112     Outputs:    -
01113     Returns:    -
01114     Purpose:    Performs the Ungroup Special operation
01115     Errors:     An ENSURE failure will occur if this function is called and there are no 
01116                 selected groups. 
01117     SeeAlso:    -
01118 
01119 ********************************************************************************************/
01120         
01121 void OpUngroupSpecial::Do(OpDescriptor*)           
01122 {    
01123     // Call UngroupSelectedGroups until there are no more groups to ungroup or until 
01124     // the operation fails.
01125     if (DoStartSelOp(TRUE,TRUE))  // Try to record the selection state 
01126     {
01127         while (OpUngroup::UngroupSelectedGroups()); 
01128     }
01129     End(); 
01130 }                                                                               
01131 

Generated on Sat Nov 10 03:45:29 2007 for Camelot by  doxygen 1.4.4