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