colormgr.cpp

Go to the documentation of this file.
00001 // $Id: colormgr.cpp 1777 2007-07-04 10:37:58Z luke $
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 // colormgr.cpp - Implementation of the ColourManager class
00100 
00101 
00102 /*
00103 */
00104 
00105 
00106 #include "camtypes.h"
00107 
00108 //#include "app.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00109 //#include "attrmgr.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00110 #include "ccolbar.h"
00111 #include "colcomp.h"
00112 #include "colcontx.h"
00113 #include "coldlog.h"
00114 #include "colormgr.h"
00115 //#include "docmsgs.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00116 //#include "document.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00117 //#include "docview.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00118 //#include "jason.h"
00119 #include "lineattr.h"
00120 #include "newcol.h"     // For NewColourDlg
00121 #include "nodeblnd.h"   // For NodeBlend, for Blend bodge
00122 #include "objchge.h"
00123 #include "opimgset.h"
00124 //#include "ops.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00125 //#include "spread.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00126 //#include "tool.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00127 #include "fillramp.h"
00128 //#include "bitmapcache.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00129 
00130 #include "colmsg.h"
00131 
00132 #include "ophist.h"
00133 
00134 // "Implement" run-time typing for the classes we're defining here
00135 CC_IMPLEMENT_DYNAMIC(ColourChangingMsg, Msg)
00136 
00137 CC_IMPLEMENT_DYNAMIC(ColourManager, MessageHandler)
00138 
00139 #if !defined(EXCLUDE_FROM_RALPH)
00140 CC_IMPLEMENT_DYNCREATE(OpHideColours, UndoableOperation)
00141 CC_IMPLEMENT_DYNCREATE(ActionHideColours, Action)
00142 
00143 CC_IMPLEMENT_DYNCREATE(OpColourChange, UndoableOperation)
00144 CC_IMPLEMENT_DYNCREATE(ActionColourChange, Action)
00145 
00146 CC_IMPLEMENT_DYNCREATE(OpRedrawColours, Operation)
00147 #endif
00148 
00149 // Declare smart memory handling in Debug builds
00150 #define new CAM_DEBUG_NEW
00151 
00152 
00153 // --- Initialise static member variables
00154 ColourList  *ColourManager::CurrentColourList   = NULL;
00155 Document    *ColourManager::ScopeDocument       = NULL;
00156 
00157 /********************************************************************************************
00158 
00159 >   ColourManager::ColourManager()
00160 
00161     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
00162     Created:    11/5/94
00163     Purpose:    Constructs a ColourManager object
00164     SeeAlso:    ColourManager; MessageHandler
00165 
00166 ********************************************************************************************/
00167 
00168 ColourManager::ColourManager()
00169     : MessageHandler(CC_RUNTIME_CLASS(MessageHandler), TRUE)
00170 {
00171 }
00172 
00173 
00174 
00175 /********************************************************************************************
00176 
00177 >   static BOOL ColourManager::Init(void)
00178 
00179     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
00180     Created:    21/5/94
00181     Returns:    FALSE if initialisation failed
00182     Purpose:    Initialises the entire colour system. This includes the colour manager,
00183                 colour contexts, and colour dialogues, etc.
00184     Notes:      Should only be called once ever. Called by Application::Init()
00185     SeeAlso:    ColourManager; MessageHandler
00186 
00187 ********************************************************************************************/
00188 
00189 BOOL ColourManager::Init(void)
00190 {
00191 #if !defined(EXCLUDE_FROM_RALPH)
00192     // Init (register) all our operations
00193 #if !defined(EXCLUDE_FROM_XARALX)
00194     if (!::InitImagesettingOps())
00195         return(FALSE);
00196 #endif
00197 
00198     if (!OpHideColours::Init())
00199         return(FALSE);
00200 
00201     if (!OpColourChange::Init())
00202         return(FALSE);
00203 
00204     if (!OpRedrawColours::Init())
00205         return(FALSE);
00206 
00207     if (!OpMakeColourLocalToFrame::Init())
00208         return(FALSE);
00209 #endif
00210 
00211     // Init the global list of active colour contexts
00212     if (!ColourContextList::InitColourContexts())
00213         return(FALSE);
00214 
00215     return(TRUE);
00216 }
00217 
00218 
00219 
00220 /********************************************************************************************
00221 
00222 >   static void ColourManager::Deinit(void)
00223 
00224     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
00225     Created:    1/6/94
00226     Purpose:    De-Initlialises the entire colour system. This includes the colour manager,
00227                 colour contexts, and colour dialogues, etc.
00228     Notes:      Should only be called once ever. Called by Application::Deinit()
00229     SeeAlso:    ColourManager; MessageHandler
00230 
00231 ********************************************************************************************/
00232 
00233 void ColourManager::Deinit(void)
00234 {
00235     // shut down the colour contexts
00236     ColourContextList::DeinitColourContexts();
00237 }
00238 
00239 
00240 
00241 /********************************************************************************************
00242 
00243 >   void ColourManager::ForceRedrawOfChangedColours(Document *ScopeDoc,
00244                                                     IndexedColour **Colours)
00245     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
00246     Created:    7/9/94
00247     Inputs:     ScopeDoc - The document in which to cause redraw
00248                 Colours  - A NULL terminated list of pointers to IndexedColours
00249 
00250     Purpose:    Scans the document tree of the given scope document, and causes
00251                 a redraw of all objects which are affected by any attributes which
00252                 contain references to any of the colours in the given NULL-terminated
00253                 list of colours. If none of the colours are in use in the document
00254                 tree, nothing should result.
00255                 (i.e. it force redraws any areas of the document containing any of
00256                 the given colours)
00257     SeeAlso:    RecursivelyForceRedraw
00258 
00259 ********************************************************************************************/
00260 
00261 void ColourManager::ForceRedrawOfChangedColours(Document *ScopeDoc, IndexedColour **Colours)
00262 {
00263 #if !defined(EXCLUDE_FROM_RALPH)
00264     // Sanity check
00265     if (ScopeDoc == NULL)
00266     {
00267         ERROR3("Illegal NULL param");
00268         return;
00269     }
00270 
00271     OpDescriptor* OpDesc = OpDescriptor::FindOpDescriptor(OPTOKEN_REDRAWCOLOURS);
00272 
00273     if (OpDesc != NULL)
00274     {
00275         OpRedrawColoursInfo Info;
00276         Info.ScopeDoc = ScopeDoc;
00277         Info.Colours  = Colours;
00278 
00279         OpDesc->Invoke((OpParam *) &Info);
00280     }
00281 #endif
00282 }
00283 
00284 
00285 
00286 /********************************************************************************************
00287 
00288 >   virtual MsgResult ColourManager::Message(Msg* Message)
00289 
00290     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
00291     Created:    11/5/94
00292     Inputs:     Message: The message
00293     Outputs:    -
00294     Returns:    -
00295     Purpose:    Process messages sent to the ColourManager
00296                 Some messages (DocChanging) will cause new broadcasts indicating
00297                 that the colour system has changed (new colour list has been paged in, etc)
00298     Errors:     -
00299     SeeAlso:    ColourManager; MessageHandler; ColourChangingMsg 
00300 
00301 ********************************************************************************************/
00302 
00303 MsgResult ColourManager::Message(Msg* Message)
00304 {
00305     if (MESSAGE_IS_A(Message, DocChangingMsg))
00306     {
00307         DocChangingMsg *TheMsg = (DocChangingMsg *) Message;
00308 
00309         switch ( TheMsg->State )
00310         {
00311             case DocChangingMsg::SELCHANGED:
00312                 if (TheMsg->pNewDoc == NULL)            // No document selected any more
00313                 {
00314                     // Send PaletteChangingMsg - DESELECTED
00315                     CurrentColourList = NULL;           // No document present
00316                     ScopeDocument = NULL;
00317 
00318                     BROADCAST_TO_ALL(ColourChangingMsg(ScopeDocument, CurrentColourList, NULL,
00319                                             ColourChangingMsg::LISTDESELECTED));
00320                 }
00321                 else if (TheMsg->pOldDoc != TheMsg->pNewDoc)
00322                 {
00323                     // Send PaletteChangingMsg - PAGED
00324                     CurrentColourList = NULL;           // Cause re-caching of this info
00325                     ScopeDocument = NULL;
00326 
00327                     GetColourList(TRUE);                // Find the new colour list
00328 
00329                     BROADCAST_TO_ALL(ColourChangingMsg(ScopeDocument, CurrentColourList, NULL,
00330                                             ColourChangingMsg::LISTPAGED));
00331                 }
00332                 break;
00333 
00334 
00335             case DocChangingMsg::ABOUTTODIE:
00336                 // Pass this around as a ColourList killed message
00337                 if (TheMsg->pChangingDoc != NULL)
00338                     BROADCAST_TO_ALL(ColourChangingMsg(TheMsg->pChangingDoc,
00339                                         TheMsg->pChangingDoc->GetIndexedColours(), NULL,
00340                                         ColourChangingMsg::LISTDELETED));
00341 
00342                 if (TheMsg->pChangingDoc == ScopeDocument)
00343                 {
00344                     // We can't keep a cached value around, as soon after this point the
00345                     // colour list will cease to exist
00346                     CurrentColourList = NULL;
00347                     ScopeDocument = NULL;
00348                 }
00349                 break;
00350             default:
00351                 break;
00352         }
00353     }
00354     else if (MESSAGE_IS_A(Message, ColourChangingMsg))
00355     {
00356         ColourChangingMsg *TheMsg = (ColourChangingMsg *) Message;
00357 
00358         switch ( TheMsg->State )
00359         {
00360             case ColourChangingMsg::LISTUPDATED:
00361                 // The list has changed- may involve a colour having been deleted.
00362                 // We check if the attribute manager is using any colours which are
00363                 // now deleted, and if so, set it to use safer ones!                
00364                 if (TheMsg->ScopeDoc == NULL || TheMsg->NewColourList == NULL)
00365                 {
00366                     ERROR3("ColChangingMsg::LISTUPDATED broadcast with NULL colour list");
00367                 }
00368                 else
00369                     TheMsg->ScopeDoc->GetAttributeMgr().UpdateForDeletedColours(
00370                                                             TheMsg->NewColourList);
00371                 break;
00372 
00373 
00374             case ColourChangingMsg::COLOURUPDATED:
00375                 {
00376                     // An IndexedColour has been edited.
00377                     // We need to force an update on all colours Linked to this one, and
00378                     // then redraw all DocViews on this document.
00379 
00380                     if (TheMsg->ScopeDoc != NULL &&
00381                         TheMsg->NewColourList != NULL &&
00382                         TheMsg->ChangedColour != NULL)
00383                     {
00384                         IndexedColour **AffectedColours = TheMsg->NewColourList->
00385                                         CompileInheritanceArray(TheMsg->ChangedColour);
00386 
00387                         if (AffectedColours == NULL)
00388                         {
00389                             // Memory error means we can't get the array                            
00390                             // Tell *all* linked colours in the list they may have changed
00391                             List *TheList = TheMsg->NewColourList;
00392 
00393                             IndexedColour *Ptr = (IndexedColour *) (TheList->GetHead());
00394                             while (Ptr != NULL)
00395                             {
00396                                 if (Ptr->FindLinkedParent() != NULL)    // If is linked
00397                                     Ptr->LinkedAncestorHasChanged();    // Warn it of the change
00398                                 Ptr = (IndexedColour *) TheList->GetNext(Ptr);
00399                             }
00400 
00401                             // And all unnamed colours too
00402                             TheList = TheMsg->NewColourList->GetUnnamedColours();
00403 
00404                             Ptr = (IndexedColour *) (TheList->GetHead());
00405                             while (Ptr != NULL)
00406                             {
00407                                 if (Ptr->FindLinkedParent() != NULL)    // If is linked
00408                                     Ptr->LinkedAncestorHasChanged();    // Warn it of the change
00409                                 Ptr = (IndexedColour *) TheList->GetNext(Ptr);
00410                             }
00411 
00412                             // And force-redraw the entire document - all views
00413                             CBitmapCache *pBC = Camelot.GetBitmapCache();
00414                             if( NULL != pBC )
00415                                 pBC->DeInitialise();                // Brute force cache management!
00416                             TheMsg->ScopeDoc->ForceRedraw();
00417                         }
00418                         else
00419                         {
00420                             // Inform all the linked colours (except the first, which is the
00421                             // actual colour which has changed) that they may have changed
00422                             INT32 Index = 1;
00423                             while (AffectedColours[Index] != NULL)
00424                             {
00425                                 AffectedColours[Index]->LinkedAncestorHasChanged();
00426                                 Index++;
00427                             }
00428 
00429                             // And redraw all affected areas of the document
00430                             ForceRedrawOfChangedColours(TheMsg->ScopeDoc, AffectedColours);
00431                         }
00432                     }
00433                 }
00434                 break;
00435             default:
00436                 break;
00437         }
00438     }
00439 
00440     return OK;
00441 }      
00442 
00443 
00444 
00445 /********************************************************************************************
00446 
00447 >   ColourList *ColourManager::GetColourList(BOOL InvalidateCache = FALSE)
00448 
00449     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
00450     Created:    11/5/94
00451     Inputs:     InvalidateCache - TRUE if you want to force it to re-cache the 
00452                 list pointer (generally only used internally; the default setting
00453                 of FALSE is all external clients should ever need to use)
00454     Outputs:    -
00455     Returns:    NULL, or a pointer to the current colour list
00456 
00457     Purpose:    To obtain the ColourList of IndexedColours in the SelectedDocument (the
00458                 colours which the user can use within the document; this is the set of
00459                 colours displayed by the colour bar and edited by the ColourMgrDlg)
00460                 Note that an ordered list is used so that multiple display orders can
00461                 be used (e.g. for the ColourMgrDlg and ColourBar, etc)
00462 
00463     Notes:      This returns the list of colours from the SELECTED document, i.e.
00464                 the colours displayed in the colour bar etc. Use GetCurrentColourList
00465                 if you wish to get the colours for the CURRENT doc.
00466 
00467                 The list is cached so this call is generally pretty efficient
00468 
00469 ********************************************************************************************/
00470 
00471 ColourList *ColourManager::GetColourList(BOOL InvalidateCache)
00472 {
00473     Document *ParentDoc = Document::GetSelected();
00474 
00475     if (ParentDoc != NULL)
00476     {
00477         if (InvalidateCache || ParentDoc != ScopeDocument)
00478         {
00479             CurrentColourList = ParentDoc->GetIndexedColours();
00480             ScopeDocument = ParentDoc;
00481         }
00482         // else
00483         //    Cache is up to date, so quit messing about
00484     }
00485     else
00486     {
00487         ScopeDocument = NULL;       // ParentDocument is NULL - no colour list
00488         CurrentColourList = NULL;       
00489     }
00490 
00491     return(CurrentColourList);
00492 }
00493 
00494 
00495 
00496 
00497 /********************************************************************************************
00498 
00499 >   ColourList *ColourManager::GetCurrentColourList(void)
00500 
00501     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
00502     Created:    6/6/94
00503     Inputs:     -
00504     Outputs:    -
00505     Returns:    NULL, or a pointer to the current colour list
00506 
00507     Purpose:    To obtain the ColourList of IndexedColours in the CurrentDocument
00508 
00509     Notes:      This returns the colours for the CURRENT document, NOT the Selected
00510                 one - that is, these may not be the colours displayed in the colour
00511                 bar/input-focus document.
00512 
00513     SeeAlso:    ColourManager::GetColourList
00514 
00515 ********************************************************************************************/
00516 
00517 ColourList *ColourManager::GetCurrentColourList(void)
00518 {
00519     Document *ParentDoc = Document::GetCurrent();
00520 
00521     if (ParentDoc != NULL)
00522         return(ParentDoc->GetIndexedColours());
00523 
00524     return(NULL);
00525 }
00526 
00527 
00528 
00529 /********************************************************************************************
00530 
00531 >   static ColourContext *ColourManager::GetColourContext(ColourModel Model,
00532                                                             Document *ScopeDocument = NULL)
00533 
00534     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
00535     Created:    20/11/95
00536 
00537     Inputs:     Model - The colour model of the desired ColourContext
00538                 ScopeDocument - NULL (to retrieve a global context) or a pointer to 
00539                 the document for which you want the colour context.
00540 
00541     Returns:    NULL if it failed to find an appropriate colour context, else
00542                 a pointer to the context
00543 
00544     Purpose:    To easily find an appropriate colour context to use when converting
00545                 colours. There are global contexts, but in order to supply special
00546                 document-specific colour corrections etc, each document actually
00547                 has its own private set of contexts. Thus, you should call this function
00548                 to determine the most appropriate context to use.
00549 
00550 ********************************************************************************************/
00551 
00552 ColourContext *ColourManager::GetColourContext(ColourModel Model, Document *ScopeDocument)
00553 {
00554     if (ScopeDocument == NULL)
00555         return(ColourContext::GetGlobalDefault(Model));
00556 
00557     return(ScopeDocument->GetDefaultColourContexts()->Context[Model]);
00558 }
00559 
00560 
00561 
00562 /********************************************************************************************
00563 
00564 >   static ColourContext *ColourManager::GetColourContext(ColourModel Model,
00565                                                             View *ScopeView)
00566 
00567     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
00568     Created:    14/5/96
00569 
00570     Inputs:     Model - The colour model of the desired ColourContext
00571                 ScopeView - A pointer to the view for which you want the colour context.
00572                             This should not be NULL, but if it is, a global default
00573                             context will be returned
00574 
00575     Returns:    NULL if it failed to find an appropriate colour context, else
00576                 a pointer to the context
00577 
00578     Purpose:    To easily find an appropriate colour context to use when converting
00579                 colours. There are global contexts, but in order to supply special
00580                 view-specific colour corrections etc, each document view actually
00581                 has its own private set of contexts. Thus, you should call this function
00582                 to determine the most appropriate context to use.
00583 
00584 ********************************************************************************************/
00585 
00586 ColourContext *ColourManager::GetColourContext(ColourModel Model, View *ScopeView)
00587 {
00588     if (ScopeView != NULL)
00589         return(ScopeView->GetColourContext(Model));
00590 
00591     return(ColourContext::GetGlobalDefault(Model));
00592 }
00593 
00594 
00595 
00596 /********************************************************************************************
00597 
00598 >   static void ColourManager::SelViewContextHasChanged(void)
00599 
00600     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
00601     Created:    22/5/96
00602 
00603     Purpose:    To redraw all windows which depend upon the Selected View's colour context.
00604                 i.e. The selected view, and also the colour editor, gallery, & line.
00605                 (and maybe other windows in the future, e.g. object properties, grad
00606                 fill editors, etc)
00607 
00608                 This redraws the selected view, then broadcasts a ColourChangingMsg message
00609                 with a state of SELVIEWCONTEXTCHANGE to inform other interested parties.
00610 
00611     Notes:      This should only be called when the selected view's RGB (screen) colour
00612                 context is changed, which really shouldn't happen terribly often.
00613 
00614 ********************************************************************************************/
00615 
00616 void ColourManager::SelViewContextHasChanged(void)
00617 {
00618     View *SelView = DocView::GetSelected();
00619 
00620     if (SelView != NULL && SelView->IsKindOf(CC_RUNTIME_CLASS(DocView)))
00621     {
00622         CBitmapCache* pBC = Camelot.GetBitmapCache();
00623         if( NULL != pBC )
00624             pBC->DeInitialise();                    // Brute force cache management
00625 
00626         ((DocView *)SelView)->ForceRedraw();
00627     }
00628 
00629     BROADCAST_TO_ALL(ColourChangingMsg(NULL, NULL, NULL,
00630                                 ColourChangingMsg::SELVIEWCONTEXTCHANGE));
00631 }
00632 
00633 
00634 
00635 /********************************************************************************************
00636 
00637 >   inline Document *SetCurrentDoc(void)
00638 
00639     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
00640     Created:    28/7/94 (Copied from ccolbar.cpp, 25/11/94)
00641     Inputs:     -
00642     Outputs:    -
00643     Returns:    The old Current Document
00644 
00645     Purpose:    The ColourBar works exclusively on the SELECTED Doc.
00646                 Thus, on entry to any of its routines which call routines
00647                 outside this file (ccolbar.cpp), it must ensure that CurrentDoc
00648                 is equal to SelectedDoc.
00649                 This local macro inline does this, and returns the old setting of
00650                 CurrentDoc so that the caller can restore the previous CurrentDoc
00651                 on exit.
00652 
00653     Scope:      private (to kernel\colormgr.cpp)
00654     SeeAlso:    RestoreCurrentDoc
00655 
00656 ********************************************************************************************/
00657 
00658 inline Document *SetCurrentDoc(void)
00659 {
00660     Document *OldCurrentDoc = Document::GetCurrent();
00661     Document *NewCurrentDoc = Document::GetSelected();
00662 
00663     if (NewCurrentDoc != NULL && NewCurrentDoc != OldCurrentDoc)
00664         NewCurrentDoc->SetCurrent();
00665 
00666     return(OldCurrentDoc);
00667 }
00668 
00669 
00670 
00671 /********************************************************************************************
00672 
00673 >   inline void RestoreCurrentDoc(Document *OldCurrentDoc)
00674 
00675     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
00676     Created:    28/7/94 (Copied from ccolbar.cpp, 25/11/94)
00677     Inputs:     The old current document to restore
00678     Outputs:    -
00679     Returns:    -
00680 
00681     Purpose:    The ColourBar works exclusively on the SELECTED Doc.
00682                 After calling SetCurrentDoc on entry to a routine, you should call
00683                 RestoreCurrentDoc on exit to restore the old current document./
00684 
00685     Scope:      private (to kernel\colormgr.cpp)
00686     SeeAlso:    SetCurrentDoc
00687 
00688 ********************************************************************************************/
00689 
00690 inline void RestoreCurrentDoc(Document *OldCurrentDoc)
00691 {
00692     if (OldCurrentDoc != NULL)
00693         OldCurrentDoc->SetCurrent();
00694 }
00695 
00696 
00697 
00698 /********************************************************************************************
00699 
00700 >   static BOOL ColourManager::GetCurrentLineAndFillAttrs(AttrStrokeColour **LineAttr,
00701                                                           AttrFillGeometry **FillAttr)
00702 
00703     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
00704     Created:    6/7/94 (Moved from ccolbar.cpp, 25/11/94)
00705     Inputs:     -
00706     Outputs:    LineAttr - will be filled in with NULL, or pointer to the attribute
00707                 FillAttr - will be filled in with NULL, or pointer to the attribute
00708     Returns:    TRUE if these are default attributes; FALSE if the attributes are
00709                 those common to the current selection.
00710 
00711     Purpose:    Determines the current line and fill colour attributes.
00712                 These are the default ones, or if there is a selection, the attrs
00713                 which are common to all selected objects. If there are many different
00714                 attributes of the same type associated with the selection, NULL is
00715                 returned.
00716     Notes:      The ColourBar works exclusively on the SELECTED Doc. Change with care
00717                 This function ensures that Current == Selected during its operation
00718 
00719 ********************************************************************************************/
00720 
00721 BOOL ColourManager::GetCurrentLineAndFillAttrs(AttrStrokeColour **LineAttr,
00722                                             AttrFillGeometry **FillAttr)
00723 {
00724     ENSURE(LineAttr != NULL &&  FillAttr != NULL, 
00725             "ColourManager::GetCurrentLineAndFillAttrs - NULL pointers passed in!");
00726 
00727     *LineAttr = NULL;   // Ensure safe exit if we fail
00728     *FillAttr = NULL;
00729 
00730     // I don't think it is the colour bar's responsibility to check this, but
00731     // this is the only way that *I* can fix the problem of an ENSURE failure 
00732     // on exit (we ask for info when no tools are current... I believe this is
00733     // because some slurphead has thrown away the tools BEFORE the document,
00734     // because the Document::GetSelected still does not return NULL in this case!)
00735     if (Tool::GetCurrent() == NULL)
00736         return(TRUE);
00737 
00738     Document *SelectedDoc = Document::GetSelected();
00739     if (SelectedDoc == NULL)
00740         return(TRUE);
00741 
00742     // Save the current CurrentDoc, and set (CurrentDoc = SelectedDoc)
00743     Document *OldCurrentDoc = SetCurrentDoc();
00744 
00745     SelRange *Selection = Camelot.FindSelection();
00746     ENSURE(Selection != NULL, "No Selection SelRange!?!");
00747 
00748     SelRange::CommonAttribResult result =
00749         Selection->FindCommonAttribute(CC_RUNTIME_CLASS(AttrStrokeColour),
00750                                         (NodeAttribute **)LineAttr);
00751     if (result == SelRange::ATTR_MANY)
00752         *LineAttr = NULL;
00753 
00754     result = Selection->FindCommonAttribute(CC_RUNTIME_CLASS(AttrFillGeometry),
00755                                             (NodeAttribute **)FillAttr);
00756     if (result == SelRange::ATTR_MANY)
00757         *FillAttr = NULL;
00758 
00759     RestoreCurrentDoc(OldCurrentDoc);
00760 
00761     return(result == SelRange::ATTR_NONE);
00762 }
00763 
00764 
00765 
00766 /********************************************************************************************
00767 
00768 >   static BOOL ColourManager::GetCurrentLineAndFillColours(DocColour **LineCol,
00769                                                             DocColour **FillCol,
00770                                                             DocColour **EndGradFill = NULL,
00771                                                             DocColour **EndGradFill2 = NULL,
00772                                                             DocColour **EndGradFill3 = NULL)
00773 
00774     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com> (changed by Gerry 21/8/96)
00775     Created:    9/9/94 (Moved from ccolbar.cpp, 25/11/94)
00776     Inputs:     -
00777 
00778     Outputs:    LineCol - NULL, or will be filled in with a pointer to the current line colour
00779                 FillCol - NULL, or will be filled in with a pointer to the current fill colour
00780                 EndGradFill - NULL, or will be filled in with NULL or the grad-fill end colour
00781                 (see below)
00782                 EndGradFill2 - NULL, or will be filled in with NULL or the second end colour
00783                 EndGradFill3 - NULL, or will be filled in with NULL or the third end colour
00784 
00785     Returns:    TRUE if these are default attributes; FALSE if the attributes are
00786                 those common to the current selection.
00787 
00788     Purpose:    Finds the current line and fill attributes, and dereferences them to find
00789                 the current line and fill DocColours. These may be returned NULL if there
00790                 are no current attributes (no document/many colours selected)
00791 
00792     Notes:      The ColourBar works exclusively on the SELECTED Doc. Change with care
00793 
00794                 LineCol and/or FillCol may be passed in NULL if you are not interested in
00795                 having these results returned to you.
00796 
00797                 If EndGradFill is NULL and a grad fill is selected then FillColour will
00798                 be filled in with the selected fill blob's colour (or NULL if there are
00799                 zero or more than one selected blob).  EndGradFill2 and EndGradFill3
00800                 will be filled in with NULL (if they are non NULL)
00801   
00802                 If EndGradFill is non-NULL and a grad-fill is selected, instead of
00803                 returning only the selected endpoint colour, it returns the start 
00804                 and end colours in FillCol (start) and EndGradFill (end).  If the fill
00805                 has more than two colours (ie GetEndColour2() != NULL) then both 
00806                 FillCol and EndGradFill will bet filled in with NULL (to indicate many).
00807 
00808                 MULTIFILL: This system needs to be changed to support multistage fills.
00809 
00810     SeeAlso:    CColourBar::GetCurrentLineAndFillAttrs
00811 
00812 ********************************************************************************************/
00813 
00814 BOOL ColourManager::GetCurrentLineAndFillColours(DocColour **LineCol, DocColour **FillCol,
00815                                                     DocColour **EndGradFill)
00816 {
00817     if (LineCol != NULL)            // Ensure return NULL if we fail
00818         *LineCol = NULL;
00819 
00820     if (FillCol != NULL)
00821         *FillCol = NULL;
00822 
00823     if (EndGradFill != NULL)
00824         *EndGradFill = NULL;
00825 
00826     AttrStrokeColour *LineAttr = NULL;
00827     AttrFillGeometry *FillAttr = NULL;
00828 
00829     BOOL result = GetCurrentLineAndFillAttrs(&LineAttr, &FillAttr);
00830 
00831     if (LineAttr != NULL && LineCol != NULL)
00832         *LineCol = LineAttr->GetStartColour();
00833 
00834     if (FillAttr != NULL && FillCol != NULL)
00835     {
00836         // New bit added by will (3/11/94) to make colour bar show
00837         // the selected fill control point's colour.
00838         // Note this assumes there are only start and end colours,
00839         // and so will NOT work with multi-stage fills.
00840 
00841         if (FillAttr->GetRuntimeClass() == CC_RUNTIME_CLASS(AttrFlatColourFill))
00842         {
00843             *FillCol = FillAttr->GetStartColour();
00844         }
00845         else
00846         {           
00847             if (EndGradFill == NULL)
00848             {
00849                 // The caller wants the selected point's colour or NULL (already set)
00850                 
00851                 if (FillAttr->GetSelectionCount() == 1)                     // If there is only 1 selected blob
00852                 {
00853                     // Get the colour of the selected blob
00854                     
00855                     if (FillAttr->IsSelected(FILLCONTROL_STARTPOINT))       // Show Start Colour
00856                         *FillCol = FillAttr->GetStartColour();
00857                     else if (FillAttr->IsSelected(FILLCONTROL_ENDPOINT))    // Show End Colour
00858                         *FillCol = FillAttr->GetEndColour();
00859                     else if (FillAttr->IsSelected(FILLCONTROL_ENDPOINT2))   // Show End2 Colour
00860                         *FillCol = FillAttr->GetEndColour2();
00861                     else if (FillAttr->IsSelected(FILLCONTROL_ENDPOINT3))   // Show End3 Colour
00862                         *FillCol = FillAttr->GetEndColour3();
00863                 }
00864             }
00865             else
00866             {
00867                 // User wants two colours
00868 
00869                 if (FillAttr->GetEndColour2() == NULL)      // If the fill doesn't have a third colour
00870                 {                   
00871                     // We know FillCol is non-NULL
00872                     *FillCol = FillAttr->GetStartColour();
00873 
00874                     // We know EndGradFill is non-NULL
00875                     *EndGradFill = FillAttr->GetEndColour();
00876                 }
00877             }
00878         }
00879     }
00880 
00881     return(result);
00882 }
00883 
00884 
00885 
00886 /********************************************************************************************
00887 
00888 >   static void ColourManager::FindColourOfInterestToUser(IndexedColour **ResultCol,
00889                                                         ColourList **ResultList = NULL)
00890 
00891     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
00892     Created:    6/1/95
00893     Inputs:     -
00894     Outputs:    ResultCol  - NULL, or a pointer to the colour to use
00895                 ResultList - NULL, or a pointer to the ColourList that colour resides in
00896     Returns:    -
00897 
00898     Purpose:    Several things (Colour editor whenever the selection changes, generation
00899                 of a new colour) need to know a 'useful' colour to display/copy. 
00900                 Rather than just showing the current default colour or something, they
00901                 need to interactively update to show an appropriate colour from the
00902                 selection and suchlike.
00903 
00904                 NOTE that there is another overloaded form of this method, which returns
00905                 a DocColour istead. This method returns a pointer to a NAMED IndexedColour,
00906                 so may return an entirely different colour to the other function.
00907 
00908                 The exact colour returned will be NULL (in dire emergency, usually only
00909                 occurs if there are no documents open), or will be chosen from this
00910                 priority list of possible contenders:
00911                 MonoOn
00912                     (1) Colour of first selected fill-blob found,
00913                     (2) Fill-Colour of selection,
00914                     (3) current/default fill colour,
00915                     (4) line-Colour of selection,
00916                     (5) current/default line colour,
00917                     (6) the first active named colour in the document's colour list,
00918                     (7) NULL
00919                 MonoOff
00920 
00921     Notes:      'Colour of selection' is determined by using (if possible) the last
00922                 object selected, or the frontmost selected object if this fails.
00923 
00924                 If you are not interested in the ResultList return value, pass in NULL
00925                 (use the default) for this parameter.
00926 
00927                 In debug builds only, extra checks are made which can cause an ERROR3
00928                 if the colour found was not in the selected document's ColourList.
00929 
00930 ********************************************************************************************/
00931 
00932 void ColourManager::FindColourOfInterestToUser(IndexedColour **ResultCol, ColourList **ResultList)
00933 {
00934     ERROR3IF(ResultCol == NULL, "ColourManager::FindColourOfInterestToUser - ResultCol is NULL!");
00935 
00936     // Ensure safe return values
00937     *ResultCol = NULL;
00938     if (ResultList != NULL)
00939         *ResultList = NULL;
00940 
00941     // If there are no documents available, then we can't find a colour - Return NULLs
00942     if (Document::GetSelected() == NULL)
00943         return;
00944 
00945     // Ensure CurrentDoc == SelectedDoc
00946     Document *OldCurrentDoc = SetCurrentDoc();
00947 
00948     // Get the colour list. Just in case, we invalidate the cache (as this method is
00949     // called on document chnages and suchlike, so it is best to make sure we don't use
00950     // cached values for a no-longer-present document!)
00951     ColourList *ColList = ColourManager::GetColourList(TRUE);
00952     IndexedColour *UsefulColour = NULL;
00953 
00954     // First, check if there is a VISIBLE selected fill blob, and use the colour of
00955     // the first one that we find, if we are lucky enough to find one.
00956     if (AttrFillGeometry::CountSelectionControlPoints() > 0)
00957     {
00958         // Yep. There is at least 1 visible selected fill blob...
00959         AttrFillGeometry* Fill = AttrFillGeometry::FindFirstSelectedAttr();
00960         while (Fill != NULL && UsefulColour == NULL)
00961         {
00962             if (Fill->GetSelectionCount() > 0)
00963             {
00964                 if (Fill->IsSelected(FILLCONTROL_STARTPOINT) && Fill->GetStartColour() != NULL)
00965                 {
00966                     // Use Fill Start Colour
00967                     UsefulColour = Fill->GetStartColour()->FindParentIndexedColour();
00968                     break;
00969                 }
00970 
00971                 if (Fill->IsSelected(FILLCONTROL_ENDPOINT) && Fill->GetEndColour() != NULL)
00972                 {
00973                     // Use Fill End Colour
00974                     UsefulColour = Fill->GetEndColour()->FindParentIndexedColour();
00975                     break;
00976                 }
00977 
00978                 if (Fill->IsSelected(FILLCONTROL_ENDPOINT2) && Fill->GetEndColour2() != NULL)
00979                 {
00980                     // Use Fill End Colour
00981                     UsefulColour = Fill->GetEndColour2()->FindParentIndexedColour();
00982                     break;
00983                 }
00984 
00985                 if (Fill->IsSelected(FILLCONTROL_ENDPOINT3) && Fill->GetEndColour3() != NULL)
00986                 {
00987                     // Use Fill End Colour
00988                     UsefulColour = Fill->GetEndColour3()->FindParentIndexedColour();
00989                     break;
00990                 }
00991             }               
00992 
00993             Fill = AttrFillGeometry::FindNextSelectedAttr();
00994         }
00995     }
00996 
00997     if (UsefulColour == NULL)
00998     {
00999         // Find the last node which was made selected (if any)
01000         // Scan back from this to find the nearest previous NodeAttribute which contains
01001         // colour(s), and make UsefulColour the first IndexedColour which we find in this search.
01002         SelRange *Selection = GetApplication()->FindSelection();
01003 
01004         if (Selection != NULL)
01005         {
01006             Node *pNode = Selection->GetLastSelectedNode();
01007             // If the SelRange doesn't know what was last made selected, then see if anything
01008             // is selected, and if so, grab the last (topmost) selected object instead
01009             if (pNode == NULL)
01010                 pNode = Selection->FindLast();
01011 
01012             // Now search through the attributes applied to this node, to see if we can
01013             // find an IndexedColour FILL (i.e. we ignore stroke colours for now) - we
01014             // will use the first one that we find
01015             NodeAttribute *pAttr = NULL;
01016             if (pNode != NULL)
01017                 pAttr = NodeAttribute::FindFirstAppliedAttr(pNode);
01018 
01019             while (pAttr != NULL && UsefulColour == NULL)
01020             {
01021                 // Scan each NodeAttribute in turn to see if any of them contain any colours
01022                 DocColour *pColour;
01023                 UINT32 Context = 0;
01024 
01025                 // Only check non-line-colour attributes
01026                 if (!(IS_A(pAttr, AttrStrokeColour)))
01027                 {
01028                     do
01029                     {
01030                         // Get the next colour field (if any) from the attribute, and find the
01031                         // IndexedColour (if any) to which it refers
01032                         pColour = pAttr->EnumerateColourFields(Context++);
01033                         if (pColour != NULL)
01034                             UsefulColour = pColour->FindParentIndexedColour();
01035 
01036                     } while (pColour != NULL && UsefulColour == NULL);
01037                 }
01038             
01039                 pAttr = NodeAttribute::FindPrevAppliedAttr(pAttr);
01040             }
01041 
01042 
01043             // If we still haven't found a colour, try for a line/stroke colour instead
01044             if (pNode != NULL && UsefulColour == NULL)
01045                 pAttr = NodeAttribute::FindFirstAppliedAttr(pNode);
01046 
01047             while (pAttr != NULL && UsefulColour == NULL)
01048             {
01049                 // Scan each NodeAttribute in turn to see if any of them contain any colours
01050                 DocColour *pColour;
01051                 UINT32 Context = 0;
01052 
01053                 // Only check line-colour attributes
01054                 if (IS_A(pAttr, AttrStrokeColour))
01055                 {
01056                     do
01057                     {
01058                         // Get the next colour field (if any) from the attribute, and find the
01059                         // IndexedColour (if any) to which it refers
01060                         pColour = pAttr->EnumerateColourFields(Context++);
01061                         if (pColour != NULL)
01062                             UsefulColour = pColour->FindParentIndexedColour();
01063 
01064                     } while (pColour != NULL && UsefulColour == NULL);
01065                 }
01066 
01067                 pAttr = NodeAttribute::FindPrevAppliedAttr(pAttr);
01068             }
01069 
01070         }
01071     }
01072 
01073     if (UsefulColour == NULL)
01074     {
01075         // OK, then. let's see if there is a default fill colour, or failing that
01076         // (eg it could be 'no colour') a default line colour, which we can edit.
01077         AttributeManager &AttrMgr = Document::GetSelected()->GetAttributeMgr();
01078 
01079         DocColour LineCol;
01080         DocColour FillCol;
01081             
01082         AttrMgr.GetCurrentLineAndFillColour(CC_RUNTIME_CLASS(NodeRenderableInk),
01083                                                 &LineCol, &FillCol);
01084 
01085         UsefulColour = FillCol.FindParentIndexedColour();
01086         if (UsefulColour == NULL)
01087             UsefulColour = LineCol.FindParentIndexedColour();
01088     }
01089 
01090     if (UsefulColour == NULL)
01091     {
01092         // Ok, we're rapidly approaching a moment of panic. Try the first non-deleted named
01093         // colour in the selected document's colour list. If nothing else, there should
01094         // always be a default Black that we can use
01095 
01096         if (ColList != NULL)
01097         {
01098             IndexedColour *Ptr = (IndexedColour *) ColList->GetHead();
01099             while (Ptr != NULL && UsefulColour == NULL)
01100             {
01101                 if (Ptr->IsNamed() && !Ptr->IsDeleted())
01102                     UsefulColour = Ptr;
01103                 
01104                 Ptr = (IndexedColour *) ColList->GetNext(Ptr);
01105             }
01106         }
01107     }
01108 
01109     // Sanity check. Should never ever ever be the case, ever.
01110     if (UsefulColour != NULL)
01111         ERROR3IF(UsefulColour->IsDeleted(),
01112                 "ColourManager::FindColourOfInterestToUser - Colour found is DELETED!");
01113 
01114     // Fill in the return results
01115     *ResultCol = UsefulColour;
01116     if (UsefulColour != NULL && ResultList != NULL)
01117         *ResultList = ColList;
01118 
01119 #ifdef _DEBUG
01120     // In debug builds, verify that the returned colour is in the list we
01121     // expect it to be in!
01122 
01123     if (ColList != NULL && UsefulColour != NULL)
01124     {
01125         IndexedColour *Ptr = (IndexedColour *) ColList->GetHead();
01126         while (Ptr != NULL && Ptr != UsefulColour)
01127             Ptr = (IndexedColour *) ColList->GetNext(Ptr);
01128 
01129         if (Ptr == NULL)
01130         {
01131             Ptr = (IndexedColour *) ColList->GetUnnamedColours()->GetHead();
01132             while (Ptr != NULL && Ptr != UsefulColour)
01133                 Ptr = (IndexedColour *) ColList->GetUnnamedColours()->GetNext(Ptr);
01134         }
01135 
01136         ERROR3IF(Ptr != UsefulColour,
01137                 "ColourManager::FindColourOfInterestToUser"
01138                 " - Colour found is not in the current colour list!");
01139     }
01140 #endif
01141 
01142     // And restore the previous CurrentDocument state
01143     RestoreCurrentDoc(OldCurrentDoc);
01144 }
01145 
01146 
01147 
01148 /********************************************************************************************
01149 
01150 >   static void ColourManager::FindColourOfInterestToUser(DocColour *ResultCol,
01151                                                         ColourList **ResultList = NULL,
01152                                                         BOOL WantLineColour = FALSE)
01153 
01154     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
01155     Created:    6/1/95
01156     Inputs:     -
01157     Outputs:    ResultCol  - will be returned filled in with details of the colour to use
01158                 ResultList - NULL, or a pointer to the ColourList of the relevant document
01159                 WantLinecolour - TRUE if you'd rather get a line colour, FALSE if you want
01160                 a fill colour.
01161     Returns:    -
01162 
01163     Purpose:    Several things (Colour editor whenever the selection changes, generation
01164                 of a new colour) need to know a 'useful' colour to display/copy. 
01165                 Rather than just showing the current default colour or something, they
01166                 need to interactively update to show an appropriate colour from the
01167                 selection and suchlike.
01168 
01169                 This is very similar to the other overloaded method, but returns a DocColour.
01170                 NOTE however, that the returned colour may not match the IndexedColour
01171                 returned by the other method - a DocColour (which does not reference
01172                 a NAMED IndexedColour) may be returned before a suitable IndexedColour
01173                 would have been found.
01174 
01175                 This is used to determine the colour definition for a colour to use when
01176                 editing "local colours" in the colour editor.
01177 
01178                 The exact colour returned will be NULL (in dire emergency, usually only
01179                 occurs if there are no documents open), or will be chosen from this
01180                 priority list of possible contenders:
01181                 MonoOn
01182                     (1) Colour of first selected fill-blob found (if !WantLineColour),
01183                     (2) Fill-Colour of selection (if !WantLineColour),
01184                     (3) current/default fill colour (if !WantLineColour),
01185                     (4) line-Colour of selection (If WantLineColour),
01186                     (5) current/default line colour (if WantLineColour),
01187                     (6) COLOUR_BLACK
01188                 MonoOff
01189 
01190     Notes:      'Colour of selection' is determined by using (if possible) the last
01191                 object selected, or the frontmost selected object if this fails.
01192 
01193                 If you are not interested in the ResultList return value, pass in NULL
01194                 (use the default) for this parameter.
01195 
01196                 Camelot will not return a 'no fill' default colour (condit