slice.cpp

Go to the documentation of this file.
00001 // $Id: slice.cpp 1282 2006-06-09 09:46:49Z alex $
00002 /* @@tag:xara-cn@@ DO NOT MODIFY THIS LINE
00003 ================================XARAHEADERSTART===========================
00004  
00005                Xara LX, a vector drawing and manipulation program.
00006                     Copyright (C) 1993-2006 Xara Group Ltd.
00007        Copyright on certain contributions may be held in joint with their
00008               respective authors. See AUTHORS file for details.
00009 
00010 LICENSE TO USE AND MODIFY SOFTWARE
00011 ----------------------------------
00012 
00013 This file is part of Xara LX.
00014 
00015 Xara LX is free software; you can redistribute it and/or modify it
00016 under the terms of the GNU General Public License version 2 as published
00017 by the Free Software Foundation.
00018 
00019 Xara LX and its component source files are distributed in the hope
00020 that it will be useful, but WITHOUT ANY WARRANTY; without even the
00021 implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00022 See the GNU General Public License for more details.
00023 
00024 You should have received a copy of the GNU General Public License along
00025 with Xara LX (see the file GPL in the root directory of the
00026 distribution); if not, write to the Free Software Foundation, Inc., 51
00027 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
00028 
00029 
00030 ADDITIONAL RIGHTS
00031 -----------------
00032 
00033 Conditional upon your continuing compliance with the GNU General Public
00034 License described above, Xara Group Ltd grants to you certain additional
00035 rights. 
00036 
00037 The additional rights are to use, modify, and distribute the software
00038 together with the wxWidgets library, the wxXtra library, and the "CDraw"
00039 library and any other such library that any version of Xara LX relased
00040 by Xara Group Ltd requires in order to compile and execute, including
00041 the static linking of that library to XaraLX. In the case of the
00042 "CDraw" library, you may satisfy obligation under the GNU General Public
00043 License to provide source code by providing a binary copy of the library
00044 concerned and a copy of the license accompanying it.
00045 
00046 Nothing in this section restricts any of the rights you have under
00047 the GNU General Public License.
00048 
00049 
00050 SCOPE OF LICENSE
00051 ----------------
00052 
00053 This license applies to this program (XaraLX) and its constituent source
00054 files only, and does not necessarily apply to other Xara products which may
00055 in part share the same code base, and are subject to their own licensing
00056 terms.
00057 
00058 This license does not apply to files in the wxXtra directory, which
00059 are built into a separate library, and are subject to the wxWindows
00060 license contained within that directory in the file "WXXTRA-LICENSE".
00061 
00062 This license does not apply to the binary libraries (if any) within
00063 the "libs" directory, which are subject to a separate license contained
00064 within that directory in the file "LIBS-LICENSE".
00065 
00066 
00067 ARRANGEMENTS FOR CONTRIBUTION OF MODIFICATIONS
00068 ----------------------------------------------
00069 
00070 Subject to the terms of the GNU Public License (see above), you are
00071 free to do whatever you like with your modifications. However, you may
00072 (at your option) wish contribute them to Xara's source tree. You can
00073 find details of how to do this at:
00074   http://www.xaraxtreme.org/developers/
00075 
00076 Prior to contributing your modifications, you will need to complete our
00077 contributor agreement. This can be found at:
00078   http://www.xaraxtreme.org/developers/contribute/
00079 
00080 Please note that Xara will not accept modifications which modify any of
00081 the text between the start and end of this header (marked
00082 XARAHEADERSTART and XARAHEADEREND).
00083 
00084 
00085 MARKS
00086 -----
00087 
00088 Xara, Xara LX, Xara X, Xara X/Xtreme, Xara Xtreme, the Xtreme and Xara
00089 designs are registered or unregistered trademarks, design-marks, and/or
00090 service marks of Xara Group Ltd. All rights in these marks are reserved.
00091 
00092 
00093       Xara Group Ltd, Gaddesden Place, Hemel Hempstead, HP2 6EX, UK.
00094                         http://www.xara.com/
00095 
00096 =================================XARAHEADEREND============================
00097  */
00098 // code to perform image slicing when selected from the menu
00099 
00100 #include "camtypes.h"
00101 
00102 //#include "barsdlgs.h"     //For _R(IDD_BARCONTROLSTORE)
00103 //#include "resimmap.h"     //For _R(IDS_HTMLIMPORT_FILEDOWNLOAD)
00104 #include "slice.h"
00105 
00106 #include "ngcore.h"
00107 #include "ngitem.h"
00108 
00109 #include "xshelpid.h"       //For the help ID
00110 //#include "helppath.h"
00111 //#include "spread.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00112 //#include "simon.h"            // the string r%dc%d is in here
00113 #include "progress.h"
00114 #include "osrndrgn.h"
00115 
00116 #include "giffiltr.h"       // for TI_GIFFilter
00117 #include "bmpfiltr.h"
00118 #include "pngfiltr.h"
00119 #include "extfilts.h"
00120 
00121 #include "exjpeg.h"         // jpeg export options
00122 
00123 #include "page.h"
00124 //#include "fillattr.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00125 
00126 #include "mrhbits.h"        // for the bitmap render in to test if a slice is empty
00127 #include "selall.h"         // for the select all op in the test if a slice is empty
00128 
00129 #include "slicehelper.h"
00130 //#include "sliceres.h"
00131 
00132 #include "bmapprev.h"       // for setting the export type in the preview
00133 
00134 #include "ngprop.h"
00135 #include "ngsentry.h"
00136 
00137 #include "webattr.h"        // for the URL attrib
00138 
00139 // for the use of wix temple attribs
00140 //#include "cxfrech.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00141 #include "userattr.h"
00142 #include "tmpltatr.h"
00143 
00144 #include "desnotes.h"
00145 #include <io.h>             // for _taccess()
00146 #include "helpuser.h"       // for the SetNextMsgHelpContext function
00147 //#include "andy.h"         //For _R(IDM_OVERWRITE)
00148 //#include "resource.h"     //For _R(IDS_CANCEL)
00149 
00150 #include "nodetxts.h"
00151 #include "nodetxtl.h"
00152 #include "nodetext.h"
00153 
00154 //#include "mario.h"            // For _R(IDE_NOMORE_MEMORY) - Matt 12/11/2000
00155 
00156 
00157 #ifdef _DEBUG
00158 #undef THIS_FILE
00159 static char BASED_CODE THIS_FILE[] = __FILE__;
00160 #endif
00161 
00162 DECLARE_SOURCE("$Revision: 1282 $");
00163 
00164 CC_IMPLEMENT_DYNCREATE(OpSlice, OpMenuImport);
00165 
00166 #define new CAM_DEBUG_NEW
00167 
00168 
00169 /********************************************************************************************
00170 
00171 >   CSlice::CSlice()
00172 
00173     Author:     Simon_Knight (Xara Group Ltd) <camelotdev@xara.com>
00174     Created:    20/7/99
00175     Returns:    
00176     Purpose:    Creates a slice object either empty or pre-filled
00177     Errors:     -
00178 
00179 ********************************************************************************************/
00180 CSlice::CSlice()
00181 {
00182     top = bottom = left = right = 0;
00183     row = colm = -1;
00184     rowspan = colmspan = 1;
00185     rowheight = colmwidth = 0;
00186     name.Empty();
00187     FileTypeExtStr = "gif";
00188     deleteme = FALSE;
00189     IsNamedSlice = FALSE;
00190     ButtonNumber = 0;
00191     IsEmpty = FALSE;
00192     HasURL = FALSE;
00193     pFrameText = NULL;
00194     ExistsOnLayerState[0] = 0;
00195     ExistsOnLayerState[1] = 0;
00196     ExistsOnLayerState[2] = 0;
00197     ExistsOnLayerState[3] = 0;
00198 }
00199 
00200 CSlice::CSlice(INT32 Ax, INT32 Ay, INT32 Cx, INT32 Cy, String_256 ThisName, String_16 FileExt, BOOL Named)
00201 {
00202     top = min(Ay, Cy);
00203     left = min(Ax, Cx);
00204     right = max(Ax, Cx);
00205     bottom = max(Ay, Cy);
00206     name = ThisName;
00207     row = colm = -1;
00208     rowspan = colmspan = 1;
00209     rowheight = colmwidth = 0;
00210     FileTypeExtStr = FileExt;
00211     deleteme = FALSE;
00212     IsNamedSlice = Named;
00213     ButtonNumber = 0;
00214     IsEmpty = FALSE;
00215     HasURL = FALSE;
00216     pFrameText = NULL;
00217     ExistsOnLayerState[0] = 0;
00218     ExistsOnLayerState[1] = 0;
00219     ExistsOnLayerState[2] = 0;
00220     ExistsOnLayerState[3] = 0;
00221 }
00222 
00223 /********************************************************************************************
00224 
00225 >   void CSlice::SwapSpreadAndSliceCoords()
00226 
00227     Author:     Simon_Knight (Xara Group Ltd) <camelotdev@xara.com>
00228     Created:    27/7/99
00229     Returns:    
00230     Purpose:    Reverses the y-axis to convert between spread co-ords and slice co-ords
00231                 Call it again to swap back between co-ord systems.
00232     Errors:     -
00233 
00234 ********************************************************************************************/
00235 void CSlice::SwapSpreadAndSliceCoords()
00236 {
00237     INT32 temp = top;
00238     top = -bottom;
00239     bottom = -temp;
00240 }
00241 
00242 /********************************************************************************************
00243 
00244 >   BOOL OpSlice::Init()
00245 
00246     Author:     Simon_Knight (Xara Group Ltd) <camelotdev@xara.com>
00247     Created:    20/7/99
00248     Returns:    TRUE if worked, FALSE if failed (out of memory)
00249     Purpose:    Declares the OpDescriptor
00250     Errors:     -
00251 
00252 ********************************************************************************************/
00253 BOOL OpSlice::Init()
00254 {
00255     return RegisterOpDescriptor(
00256             0,                          // Tool ID
00257             _R(IDS_IMAGESLICE),                 // String resource ID
00258             CC_RUNTIME_CLASS(OpSlice),  // Runtime class
00259             OPTOKEN_IMAGESLICE,         // Token string
00260             GetState,                   // GetState function
00261             _R(IDH_Command_Import_from_Web),// help ID GTODO: Is this needed?
00262             _R(IDBBL_IMAGESLICE),           // bubble help
00263             _R(IDD_BARCONTROLSTORE),        // resource ID
00264             _R(IDC_IMAGESLICE),             // control ID
00265             SYSTEMBAR_FILE,             // Bar ID
00266             TRUE,                       // Receive system messages
00267             FALSE,                      // Smart duplicate operation
00268             TRUE,                       // Clean operation
00269             0,      // String for one copy only error
00270             (DONT_GREY_WHEN_SELECT_INSIDE | GREY_WHEN_NO_CURRENT_DOC) // Auto state flags
00271         );
00272     
00273 }
00274 
00275 /********************************************************************************************
00276 
00277 >   void OpSlice::Do(OpDescriptor*)
00278 
00279     Author:     Simon_Knight (Xara Group Ltd) <camelotdev@xara.com>
00280     Created:    20/7/99
00281     Purpose:    Exports the current document as slices defined by named selections
00282     Errors:     -
00283 
00284 ********************************************************************************************/
00285 void OpSlice::Do(OpDescriptor*)
00286 {
00287     // there needs to be a spread
00288     Spread* pSelSpread = Document::GetSelectedSpread();
00289     if (pSelSpread == NULL)
00290         return;
00291 
00292     //  Let's set a variable in BmapPrevDlg which will be used to determine
00293     //  which of the tab controls should be enabled or disabled.
00294     BmapPrevDlg::m_bSlicingImage = TRUE;
00295 
00296     m_ErrorStr = "none";
00297     m_HTMLext = "htm";
00298 
00299     m_NumberOfButtons = 0;
00300 
00301     // init the export path - which probably isn't needed
00302     // but better safe than sorry etc...
00303     // set to .non so that any slices that are defined from this can be
00304     // found and redefined later. see UpdateSliceFileExts() - sjk
00305     m_PathName.SetPathName("c:\\untitled.non");
00306 
00307     m_InjectHTML = FALSE; // don't inject without thinking about it - you know it makes sense!
00308 
00309     m_ExportedHTMLOK = TRUE; // asumes the best :-)
00310 
00311     // start a slow job
00312     Progress LongJob;
00313     LongJob.Start();
00314 
00315     // test if we use design notes
00316     m_UsesDesignNotes = UsesDesignNotes();
00317 
00318     // calculate the background colour
00319     DocColour dcol = Page::GetPageColour();
00320     m_lRed = m_lGreen = m_lBlue = 255;
00321 
00322     BOOL HasBitmapBackground = FALSE;
00323 
00324     Layer * pLayer = pSelSpread->FindFirstPageBackgroundLayer();
00325     if (pLayer)
00326     {
00327         Node * pNode = SliceHelper::FindNextOfClass(pLayer, pLayer, CC_RUNTIME_CLASS(AttrFlatColourFill));
00328         if (pNode)
00329             dcol = *(((AttrFlatFill *)pNode)->GetStartColour());
00330         else
00331         {
00332             if (SliceHelper::FindNextOfClass(pLayer, pLayer, CC_RUNTIME_CLASS(AttrBitmapColourFill)))
00333                 HasBitmapBackground = TRUE;
00334         }
00335     }
00336 
00337     dcol.GetRGBValue(&m_lRed, &m_lGreen, &m_lBlue);
00338 
00339     
00340     // calculate the bounds of the entire image
00341     // init the mosaic list which stores all the parts of the image
00342 
00343     CList <CSlice *, CSlice *> MosaicList;
00344     MosaicList.RemoveAll();
00345 
00346     // the bounds of the drawing
00347     DocRect SpreadBounds = BaseBitmapFilter::GetSizeOfDrawing(pSelSpread); // returns in spread co-ords ignoring silly layers
00348     
00349     // get the selection
00350     Range Sel(*(GetApplication()->FindSelection()));
00351 
00352     // set the range flags so it includes shadow and bevel manager nodes
00353     RangeControl rg = Sel.GetRangeControlFlags();
00354     rg.PromoteToParent = TRUE;
00355     Sel.Range::SetRangeControl(rg);
00356     BOOL UseWholeDrawing = TRUE;
00357     
00358     // get the size of the selection
00359     // if there is a selection use this as the area to export rather than the drawing area
00360     // which we will use if there is no selection made
00361     if (!Sel.IsEmpty())
00362     {
00363         SpreadBounds = Sel.GetBoundingRect();
00364         UseWholeDrawing = FALSE;
00365     }
00366 
00367     PixelAlignedInflate(&SpreadBounds); // pixel align the starting rect
00368 
00369     // find the defined slices
00370     NameGallery* pNameGallery = NameGallery::Instance();
00371     SGUsedNames* pNames = pNameGallery?NameGallery->GetUsedNames():NULL;
00372 
00373     if (!pNames)
00374     {
00375         InformWarning( _R(IDS_NOSLICES) );
00376         //  Reset:
00377         BmapPrevDlg::m_bSlicingImage = FALSE;
00378         return;
00379     }
00380 
00381     // make sure we are dealing with the most uptodate information
00382     pNameGallery->FastUpdateNamedSetSizes();
00383 
00384     // for each defined slice in the name gallery
00385     BOOL NoProblems = TRUE;
00386     BOOL AddedASlice = FALSE;
00387     BOOL OutsideSpread = FALSE;
00388 
00389     INT32 Attempts = 0; // first try with the large rects then if that fails try the smaller ones
00390     String_256 strName(TEXT("Empty"));
00391     String_256 Slice1ErrorStr = "";
00392     String_256 Slice2ErrorStr = "";
00393     INT32 SlicesDefinedInDrawing = 0;
00394 
00395     do
00396     {
00397         NoProblems = TRUE;
00398         AddedASlice = FALSE;
00399         OutsideSpread = FALSE;
00400         SlicesDefinedInDrawing = 0;
00401 
00402         // the original mosaic piece is the whole bounded image
00403         // which will be broken up
00404         TidyMosaicList(&MosaicList);
00405         MosaicList.AddHead(new CSlice(SpreadBounds.lox, SpreadBounds.loy, SpreadBounds.hix, SpreadBounds.hiy));
00406 
00407         SGNameItem* pItem = (SGNameItem*) pNames->GetChild();
00408 
00409         while (pItem != 0 && NoProblems)
00410         {
00411             // is the tick set in the name gallery to use this slice?
00412             BOOL UseThisSlice = TRUE;
00413             NamedTickboxProp * pSliceTick = (NamedTickboxProp *) pItem->GetProperty(1); // 1 is slices
00414             if (pSliceTick)
00415             {
00416                 UseThisSlice = pSliceTick->GetState();
00417                 if (UseThisSlice)
00418                     SlicesDefinedInDrawing++;
00419             }
00420 
00421             // dont use an unselected slice if it is not the whole drawing being exported
00422             if (!UseWholeDrawing && pItem->IsNoneSelected())
00423                 UseThisSlice = FALSE;
00424 
00425             if (UseThisSlice)
00426             {
00427                 DocRect r;
00428                 r = pItem->GetSetBounds();          // (returned in spread co-ords)
00429 
00430                 pItem->GetNameText(&strName);
00431 
00432                 // expand for actual text story visible size
00433                 ScanTextStorySliceBounds(strName, r);
00434 
00435                 if (Attempts == 0)
00436                 {
00437                     DocRect rLarge = ScanLargeSliceBounds(strName);
00438                     r = r.Union(rLarge); // use the large rects
00439                 }
00440 
00441                 // r must be in the spread bounds
00442                 // if it is outside the bounds it should be ignored
00443                 PixelAlignedInflate(&r);        // pixel align the slice
00444 
00445                 // trim anything that just strays over the bounds
00446                 r = r.Intersection(SpreadBounds);
00447 
00448                 // cut the existing mosaic pieces by the edges of this new piece
00449                 // which of course creates even more pieces
00450                 if (r.IsValid() && r.Height() > 0 && r.Width() > 0)
00451                 {
00452                     NoProblems = NoProblems && AddSlice(r.lox, r.loy, r.hix, r.hiy, &MosaicList, strName);
00453                     AddedASlice = TRUE;
00454                 }
00455                 else
00456                 {
00457                     TRACEUSER( "SimonK", _T("Slice outside bounds of the drawing!!!\n"));
00458                 }
00459             }
00460 
00461             if (NoProblems) pItem = (SGNameItem*) pItem->GetNext();
00462         }
00463 
00464         Attempts++;
00465         if (!NoProblems)
00466         {
00467             Slice1ErrorStr = m_ErrorStr;
00468             Slice2ErrorStr = strName;
00469         }
00470 
00471     } while (!NoProblems && Attempts < 2); // try twice , first with the max size slices, then with the smaller slices
00472 
00473 /* Dont ask the question Asumme that the user will choose to have the shadows cut if they do overlap (sjk 20/12/00)
00474     if (Attempts == 2 && NoProblems && AddedASlice)
00475     {   // worked with the smaller rects but not with the larger ones
00476 
00477         // I need to force this message to reappear again if they click 'Help' - Matt 10/11/2000
00478         INT32 result = 3;
00479         while (result == 3)
00480         {
00481             result = AskQuestion(_R(IDS_SLICES_MAY_OVERLAP), _R(IDS_YES), _R(IDS_NO), _R(IDS_HELP));
00482             if (result == 2)
00483             {
00484                 NoProblems = FALSE; // it is a problem if you say it is
00485                 m_ErrorStr = Slice1ErrorStr; // restore the shapes that were causing problems for the first case
00486                 strName = Slice2ErrorStr;
00487             }
00488             if (result == 3)
00489             {
00490                 HelpUserTopic(_R(IDH_Alert_Exclude_peripheral_elements));
00491             }
00492         }
00493     }
00494 */
00495     POSITION Test = NULL;
00496 
00497     if (NoProblems && AddedASlice) 
00498     {
00499         // sort the mosaic using SortSlices()
00500         // it also merges small bits back together if it can
00501         SortSlices(&MosaicList, SpreadBounds);
00502 
00503         // mark which states exist
00504         m_FoundRolloverStates = ScanForRolloverStates(&MosaicList);
00505 
00506         BOOL ReplaceFiles = TRUE;
00507         BOOL FilesOverwriten = FALSE;
00508         do // loop to ask where to save to which may be asked more than once if they dont want
00509             // to overwrite files
00510         {
00511 
00512             // ask where to save the files
00513             // and find out which slice export type to use
00514             if (!SaveFileDlg())
00515             {
00516                 // tidy up and leave if cancelled
00517                 TidyMosaicList(&MosaicList);
00518                 Error::ClearError();
00519                 //  Reset:
00520                 BmapPrevDlg::m_bSlicingImage = FALSE;
00521                 return;
00522             }
00523 
00524 
00525             // cope with things being .html, which the BmapPrevDlg doesn't set correctly as its not 8.3
00526             String_256 temp = m_PathName.GetFileName(FALSE);
00527             INT32 dot = temp.FindNextChar(TCHAR ('.'));
00528             if (dot > 0)
00529             {
00530                 String_256 end = "";
00531                 temp.Split(&temp, &end, dot, FALSE);
00532                 if (end.CompareTo(".html", FALSE) == 0)
00533                 {
00534                     m_HTMLext = "html";
00535                     m_PathName.SetFileName(temp);
00536                 }
00537             }
00538 
00539             CString ClickedFile(temp);
00540             ClickedFile += "." + m_HTMLext;
00541 
00542             ReplaceFiles = FileExists(ClickedFile);
00543             FilesOverwriten = FALSE;
00544 
00545             if (!ReplaceFiles)
00546             {
00547                 // test if these names we are creating are already in existance
00548                 Test = MosaicList.GetHeadPosition();
00549                 while (Test && !FilesOverwriten)
00550                 {
00551                     CSlice* pTestSlice = MosaicList.GetNext(Test);
00552                     CString TestName (pTestSlice->name);
00553 
00554                     // we dont know exactly what file type for each element will be
00555                     // but it is best to warn the user that they may be overwriting their files
00556                     FilesOverwriten = FileExists(TestName + ".gif");
00557                     if (!FilesOverwriten) FilesOverwriten = FileExists(TestName + ".jpg");
00558                     if (!FilesOverwriten) FilesOverwriten = FileExists(TestName + ".jpeg");
00559                     if (!FilesOverwriten) FilesOverwriten = FileExists(TestName + ".png");
00560                     if (!FilesOverwriten) FilesOverwriten = FileExists(TestName + ".bmp");
00561                 }
00562             }
00563 
00564             if (FilesOverwriten && !ReplaceFiles)
00565             {
00566                 // ask the user "This folder contains a graphic file with a conflicting name.
00567                 // Do you want to overwrite any conflicting files in this folder?"
00568                 if (InformWarning(_R(IDS_CONFLICTING_SLICE_FILENAMES), _R(IDS_OVERWRITE_GRAPHIC), _R(IDS_SAVE_ELSEWHERE)) == 1)
00569                     ReplaceFiles = TRUE;
00570             }
00571 
00572         } while (FilesOverwriten && !ReplaceFiles); // end of overwrite loop
00573 
00574 
00575         // The preview dlg wants to know the path of the image being
00576         // exported so give it the m_PathName
00577         BmapPrevDlg::m_pthExport = m_PathName;
00578 
00579         // set up the export options
00580         BitmapExportOptions * pExportOptions = NULL;
00581         BOOL ok = TRUE;
00582 
00583         ShowRolloverLayer(ALL_LAYERS);
00584 
00585         // WARNING SetUpExportOptions() actually 'new's the ExportOptions
00586         // so you will have to delete them or pass them to a function
00587         // that deletes them for you
00588         String_256 strExt = m_PathName.GetType();
00589         strExt.toLower();
00590         if (strExt.CompareTo("jpg") == 0)
00591         {
00592             JPEGExportFilter f;
00593             ok = f.SetUpExportOptions(&pExportOptions);
00594         }
00595         else
00596         if (strExt.CompareTo("bmp") == 0)
00597         {
00598             BMPFilter f;
00599             ok = f.SetUpExportOptions(&pExportOptions);
00600         }
00601         else
00602         if (strExt.CompareTo("png") == 0)
00603         {
00604             PNGFilter f;
00605             ok = f.SetUpExportOptions(&pExportOptions);
00606         }
00607         else
00608         //if (strExt == "gif")
00609         {
00610             TI_GIFFilter f;
00611             ok = f.SetUpExportOptions(&pExportOptions);
00612         }
00613 
00614         // the export options may have changed types
00615         // so ask the export dlg for the actual export options
00616         pExportOptions = BmapPrevDlg::m_pExportOptions;
00617         // take responsibility for the export options away from the bmp preview dlg
00618         BmapPrevDlg::m_pExportOptions = 0;
00619 
00620         // Cancelled from the export options dlg
00621         if (!ok)
00622         {
00623             if (pExportOptions)
00624             {
00625                 delete pExportOptions;
00626             }
00627             // tidy up and leave
00628             TidyMosaicList(&MosaicList);
00629             Error::ClearError();
00630             //  Reset:
00631             BmapPrevDlg::m_bSlicingImage = FALSE;
00632             return;
00633         }
00634 
00635         // read the m_PathName back since
00636         // setting up the export options could
00637         // have changed this value
00638         m_PathName = BmapPrevDlg::m_pthExport;
00639 
00640 
00641         // clear the screen by calling an idle
00642         (DocView::GetSelected())->ForceRedraw();
00643         GetApplication()->OnIdle(TRUE);             // Phil 2/7/2004 YUK! What's wrong with ServiceRendering?
00644 
00645         // change the non-user defined slices to use the graphic type
00646         // just picked by the user in the SaveFileDlg
00647         UpdateSliceFileExts(&MosaicList);
00648 
00649         // mark any "empty" slices in the list
00650         // but not if we have a background bitmap
00651         if (!HasBitmapBackground)
00652             MarkEmptySlices(&MosaicList, SpreadBounds, pExportOptions);
00653 
00654         // export each section of the mosaic as a seperate graphic
00655         Test = MosaicList.GetHeadPosition();
00656         while (Test)
00657         {
00658             CSlice* pTestSlice = MosaicList.GetNext(Test);
00659 
00660             if (m_FoundRolloverStates && pTestSlice->IsNamedSlice)
00661                 // export this slice as a rollover
00662                 ExportRollOverSlice(pTestSlice, pExportOptions);
00663             else
00664                 // do a normal slice
00665             {
00666                 // only export the graphic for a none-empty slice
00667                 if (!pTestSlice->IsEmpty)
00668                     ExportSliceGraphic(pTestSlice, pExportOptions, pTestSlice->name);
00669             }
00670 
00671             // This function calls DoExportWithParams() which used to
00672             // delete the export options (and not zero the pointer - tusk!)
00673             // Behaviour now changed by using DontLetFilterDeleteMyPointer()
00674             // so we can keep using the same export options, but of course we
00675             // must then delete them ourselves.
00676         }
00677 
00678         // tidy up our generated export options
00679         if (pExportOptions != 0)
00680         {
00681             delete pExportOptions;
00682             pExportOptions = 0;
00683         }
00684 
00685         // And clean up the image export options in the bitmap dialogue.
00686 //      BmapPrevDlg::CancelTransparency ();
00687 
00688         // export the HTML to display the mosaic put back together
00689         // the name is that typed in but with the htm extension
00690 
00691         // build up the list of link names with each button
00692         // rather than just going on adding a number to the file
00693         // users want to not have to edit HTML, do it all for them!
00694         m_UsedShimGraphic = FALSE;
00695         PathName HTMLPath = m_PathName;
00696         HTMLPath.SetType(m_HTMLext);
00697         m_pLinkName = NULL;
00698         INT32 i = 0;
00699         String_256 LaunchString = HTMLPath.GetFileName();
00700 
00701         INT32 LinkEntries = max (m_NumberOfButtons, 1);
00702         m_pLinkName = new String_256[LinkEntries];
00703 
00704         if (m_pLinkName)
00705         {
00706             m_pLinkName[0] = HTMLPath.GetPath();
00707 
00708             INT32 FileNumber = 0;
00709             // fill the link names with default names if there are no URLs found
00710             for (i = 0; i < m_NumberOfButtons; i++)
00711             {
00712                 m_pLinkName[i] = m_PathName.GetFileName(FALSE);
00713 
00714                 CSlice * pTestSlice = GetButtonNumber (&MosaicList, i+1);
00715                 if (pTestSlice && pTestSlice->ExistsOnLayerState[SELECTED])
00716                 {
00717                     FileNumber++;
00718 
00719                     if (m_FoundRolloverStates && m_RolloverState[SELECTED].Exists && FileNumber > 1)
00720                     {
00721                         String_256 NewName;
00722                         NewName.MakeMsg(_R(IDS_HTML_NAME_SPEC), (LPCSTR) m_PathName.GetFileName(FALSE), FileNumber);
00723                         m_pLinkName[i] = NewName;
00724                     }
00725                 }
00726 
00727                 m_pLinkName[i] += ".";
00728                 m_pLinkName[i] += m_HTMLext;
00729             }
00730 
00731             // scan the tree for any URLs used associated with the buttons in the Mosaic list
00732             // this will overwrite the defaults already set in m_pLinkName
00733             URLScan(m_pLinkName, &MosaicList);
00734 
00735             FileNumber = 0;
00736 
00737             // export each HTML file required
00738             if (m_RolloverState[SELECTED].Exists)
00739                 for (i = 0; i < LinkEntries; i++)
00740                 {
00741                     String_256 NewName = m_pLinkName[i];
00742                     String_256 Temp = "";
00743 
00744                     // dont try to create a file http://www.stuff...
00745                     NewName.Left(&Temp,5);
00746                     BOOL isURL = (Temp.CompareTo("http:", FALSE) == 0);
00747                     if (!isURL)
00748                     {
00749                         NewName.Right(&Temp,1);
00750                         isURL = (Temp.CompareTo("/") == 0);
00751                     }
00752 
00753                     CSlice * pTestSlice = GetButtonNumber (&MosaicList, i+1);
00754                     if (pTestSlice && pTestSlice->ExistsOnLayerState[SELECTED])
00755                     {
00756                         FileNumber++;
00757 
00758                         if (isURL)
00759                         {
00760                             NewName.MakeMsg(_R(IDS_HTML_NAME_SPEC), (LPCSTR) m_PathName.GetFileName(FALSE), FileNumber);
00761                             NewName += ".";
00762                             NewName += m_HTMLext;
00763                         }
00764 
00765                         HTMLPath.SetPathName(NewName);
00766 
00767                         if (!ExportImageSliceHTML(&MosaicList, HTMLPath.GetPath(), i+1))
00768                             FailledToExportHTML (HTMLPath.GetPath());
00769 
00770                         // set file to launch if changed by the URL
00771                         if (FileNumber == 1)
00772                         {
00773                             LaunchString = HTMLPath.GetFileName();
00774                         }
00775                     }
00776 
00777                 }
00778             else
00779                 if (!ExportImageSliceHTML(&MosaicList, HTMLPath.GetPath()))
00780                     FailledToExportHTML (HTMLPath.GetPath());
00781 
00782             delete [] m_pLinkName;
00783             m_pLinkName = NULL;
00784         }
00785 
00786         // Export out the required shim
00787         if (m_UsedShimGraphic)
00788         {
00789             // also needs shim.gif exported
00790             // set up the path of where to save it
00791             PathName ShimPath = m_PathName;
00792 
00793             ShimPath.SetFileName("shim");
00794             ShimPath.SetType("gif");
00795 
00796             CCDiskFile File;
00797             if(File.open(ShimPath.GetPath(), (ios::in | ios::out | ios::binary)))
00798             {
00799                 BYTE buf[] =   {0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x01, 0x00,
00800                                 0x01, 0x00, 0x80, 0x00, 0x00, 0xFF, 0xFF, 0xFF,
00801                                 0x00, 0x00, 0x00, 0x21, 0xF9, 0x04, 0x01, 0x14,
00802                                 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x00,
00803                                 0x01, 0x00, 0x01, 0x00, 0x00, 0x02, 0x02, 0x44,
00804                                 0x01, 0x00, 0x3B, 0x00  };
00805 
00806                 // make the shim of the correct colour
00807                 buf[13] = (BYTE) m_lRed;
00808                 buf[14] = (BYTE) m_lGreen;
00809                 buf[15] = (BYTE) m_lBlue;
00810 
00811                 // write the file
00812                 File.write((BYTE *)buf, 44);
00813                 File.close();
00814             }
00815         }
00816         
00817         // display the export HTML now?
00818         if (m_ExportedHTMLOK && AskQuestion(_R(IDS_SHOW_HTML_NOW), _R(IDS_YES), _R(IDS_NO)) == 1)
00819         {
00820             // shell execute the browser
00821             HINSTANCE hChild = ShellExecute(HWND_DESKTOP, "open", LaunchString, NULL, NULL ,SW_SHOW);
00822 
00823             // If the function (ShellExecute()) fails, then an error value that is less than or equal to 32 is returned. 
00824             INT32 Result = (INT32)hChild;
00825             // problems?
00826             if (Result <= 32)
00827             {
00828                 InformWarning(_R(IDS_NO_BROWSER));
00829             }
00830         }
00831     
00832     }
00833     else if (!AddedASlice) // user forgot to define any slices
00834     {
00835         if (SlicesDefinedInDrawing == 0)
00836             InformWarning( _R(IDS_NOSLICES) );
00837         else
00838             InformWarning( _R(IDS_NOSLICES_SELECTED) );
00839     }
00840     else // some error occured such as intersecting named slices
00841     {
00842         if (OutsideSpread)
00843         {
00844             // An invisible slice is outside the bounds of the rendered spread
00845             InformWarning(_R(IDS_SLICE_OUT_OF_BOUNDS));
00846         }
00847         else
00848         {
00849             // should mention that it is pTestSlice->name that is overlapping       
00850             String_256 temp(TEXT(""));
00851             temp.MakeMsg(_R(IDS_SLICES_OVERLAP), (LPCTSTR) m_ErrorStr, (LPCTSTR) strName);
00852 
00853             ErrorInfo Info;
00854             Info.ErrorMsg = 0;
00855             Info.Button[0] = _R(IDS_OK);
00856             Info.Button[1] = _R(IDS_HELP);
00857             Info.OK = 1;
00858             Info.Help = 2;
00859             Info.Title = _R(IDBBL_IMAGESLICE);
00860 
00861             BOOL Again;
00862             do
00863             {
00864                 // Set the error message
00865                 Error::SetError( 0, temp, 0 );
00866 
00867                 Again = FALSE;
00868                 switch (AskQuestion(&Info))
00869                 {
00870                     case _R(IDS_HELP):
00871                         HelpUserTopic(_R(IDS_HELPPATH_Alert_Named_Objects_Overlap));
00872                         Again = TRUE;
00873                         break;
00874                 }
00875             } while (Again);
00876         }
00877     }
00878 
00879     // tidey up before leaving
00880     TidyMosaicList(&MosaicList);
00881 
00882     // end a slow job
00883     // done through the progress class being destroyed
00884 
00885     //  Reset:
00886     BmapPrevDlg::m_bSlicingImage = FALSE;
00887 }
00888 
00889 /********************************************************************************************
00890 
00891 >   INT32 OpSlice::TidyMosaicList(CList <CSlice *, CSlice *> * pMosaicList)
00892 
00893     Author:     Simon_Knight (Xara Group Ltd) <camelotdev@xara.com>
00894     Created:    3/8/99
00895     Purpose:    Deletes all the slices in the list now that we are done with them.
00896                 So there will be no memory leeks.
00897 
00898 ********************************************************************************************/
00899 INT32 OpSlice::TidyMosaicList(CList <CSlice *, CSlice *> * pMosaicList)
00900 {
00901     // tidy the mosaic list by deleting all its entries
00902     INT32 Pieces = 0;
00903     POSITION Test = pMosaicList->GetHeadPosition();
00904     while (Test)
00905         {
00906             CSlice * pTestSlice = pMosaicList->GetNext(Test);
00907             pMosaicList->RemoveHead();
00908             delete pTestSlice;
00909             Pieces++;
00910         }
00911 
00912     return Pieces;
00913 }
00914 
00915 /********************************************************************************************
00916 
00917 >   void OpSlice::UpdateSliceFileExts(CList <CSlice *, CSlice *> * pMosaicList)
00918 
00919     Author:     Simon_Knight (Xara Group Ltd) <camelotdev@xara.com>
00920     Created:    3/8/99
00921     Purpose:    Changes all non set file types to be the type the user picks from the file dlg.
00922 
00923 ********************************************************************************************/
00924 void OpSlice::UpdateSliceFileExts(CList <CSlice *, CSlice *> * pMosaicList)
00925 {
00926     POSITION Test = pMosaicList->GetHeadPosition();
00927     while (Test)
00928         {
00929             CSlice * pTestSlice = pMosaicList->GetNext(Test);
00930             if (pTestSlice->FileTypeExtStr.CompareTo("non") == 0) // using non as a standard we haven't set this yet
00931                 pTestSlice->FileTypeExtStr = m_PathName.GetType();
00932         }
00933 
00934 }
00935 
00936 /********************************************************************************************
00937 
00938 >   OpState OpSlice::GetState(String_256*, OpDescriptor*)
00939 
00940     Author:     Simon_Knight (Xara Group Ltd) <camelotdev@xara.com>
00941     Created:    20/7/99
00942     Purpose:    This item is always available, so long as a document is visible.
00943                 Not really sure if I even need this!!!
00944 
00945 ********************************************************************************************/
00946 OpState OpSlice::GetState(String_256*, OpDescriptor*)
00947 {
00948     OpState OpSt;
00949 
00950     // if we don't allow it
00951     OpSt.Greyed = TRUE;
00952     Spread* pSpread = Document::GetSelectedSpread();
00953     if (pSpread && !pSpread->FindActiveLayer()->IsFrame())
00954         OpSt.Greyed = FALSE;
00955 
00956     return OpSt;
00957 }
00958 
00959 /********************************************************************************************
00960 
00961 >BOOL OpSlice::AddSlice (INT32 Ax, INT32 Ay, INT32 Cx, INT32 Cy, CList <CSlice *, CSlice *> * pMosaicList, String_256 SliceName)
00962 
00963     Author:     Simon_Knight (Xara Group Ltd) <camelotdev@xara.com>
00964     Created:    23/7/99
00965     Purpose:    
00966                  slice rect ABCD
00967                    A---B
00968                    |   |
00969                    D---C
00970                  pMosaicList should start with one entry which is the whole drawing extent
00971                  that is to be split up into sections.
00972                  The passed in slice (AC) is added to the list of pieces which is a named user
00973                  defined slice. Existing pieces that are occupy the same area are cut into
00974                  smaller parts or absorbed.
00975 
00976     Params:     Axy, Cxy defines the new slice
00977                 pMosaic is this list of the slices
00978                 SliceName is the name of the new slice
00979 
00980     Returns:    TRUE if it worked - FALSE if something failled
00981 
00982 ********************************************************************************************/   
00983 BOOL OpSlice::AddSlice (INT32 Ax, INT32 Ay, INT32 Cx, INT32 Cy, CList <CSlice *, CSlice *> * pMosaicList, String_256 SliceName)
00984 {
00985     // ignore empty slices as we could hardly export them
00986     // but since slices containing nothing could exist we should cater
00987     // for the possibility
00988     if (Ax == Cx || Ay == Cy) return TRUE;
00989 
00990     // if the inflate to pixel aligned rect has swapped over two named slices
00991     // set them to being on the same boundry
00992     NudgeSliceIfClose(Ax, Ay, Cx, Cy, pMosaicList);
00993 
00994     BOOL ok = TRUE;
00995     POSITION pos = pMosaicList->GetHeadPosition();
00996 
00997     while (pos)
00998     {
00999         POSITION thispos = pos;
01000         CSlice * pRect = pMosaicList->GetNext(pos);
01001         BOOL RemoveMe = TRUE;
01002         
01003         if (Ax == pRect->left && Cx == pRect->right && Ay == pRect->top && Cy == pRect->bottom)
01004         {
01005             // remove existing identical slices
01006             // keep the name if it has one and the replacement doesn't.
01007             if (!pRect->name.IsEmpty())
01008             {
01009                 TRACE( _T("Identically named slices\n"));
01010                 if (SliceName.IsEmpty())
01011                     SliceName = pRect->name;
01012             }
01013         }
01014         else
01015         if (InRect(pRect, Ax, Ay)) // A in rect
01016             ok = Slice (pRect, Ax, Ay, Cx, Ay, pMosaicList); // slice AB
01017         else
01018         if (InRect(pRect, Cx, Ay)) // B in rect
01019             ok = Slice (pRect, Ax, Ay, Cx, Ay, pMosaicList); // slice AB
01020         else
01021         if (CutsRect(pRect, Ax, Ay, Cx, Ay)) // cuts AB
01022             ok = Slice (pRect, Ax, Ay, Cx, Ay, pMosaicList); // slice AB
01023         else
01024         if (InRect(pRect, Cx, Cy)) // C in rect
01025             ok = Slice (pRect, Cx, Ay, Cx, Cy, pMosaicList); // slice BC
01026         else
01027         if (CutsRect(pRect, Cx, Ay, Cx, Cy)) // cuts BC
01028             ok = Slice (pRect, Cx, Ay, Cx, Cy, pMosaicList); // slice BC
01029         else
01030         if (InRect(pRect, Ax, Cy)) // D in rect
01031             ok = Slice (pRect, Ax, Ay, Ax, Cy, pMosaicList); // slice AD
01032         else
01033         if (CutsRect(pRect, Ax, Ay, Ax, Cy)) // cuts AD
01034             ok = Slice (pRect, Ax, Ay, Ax, Cy, pMosaicList); // slice AD
01035         else
01036         if (CutsRect(pRect, Ax, Cy, Cx, Cy)) // cuts DC
01037             ok = Slice (pRect, Ax, Cy, Cx, Cy, pMosaicList); // slice DC
01038         else
01039         if (pRect->left >= Ax && pRect->right <= Cx && pRect->top >= Ay && pRect->bottom <= Cy) // rect in ABCD
01040             //remove rect from list
01041         {
01042             TRACE( _T("remove sub rect\n"));
01043             // NB doing nothing will result in it being removed
01044         }
01045         else // this piece doesn't interact with the slice at all
01046             RemoveMe = FALSE; // so don't let it be taken from us
01047 
01048         if (!ok)
01049             return FALSE; // an error occurred
01050         
01051         pos = thispos;
01052         // reget the next item to check that we haven't extended the list
01053         pRect = pMosaicList->GetNext(pos);
01054 
01055         if (RemoveMe)
01056         {
01057             pMosaicList->RemoveAt(thispos);
01058             delete pRect;
01059         }
01060     }
01061 
01062     // add the slice passed in
01063     // remember to give it the users individually selected graphic type
01064     // which isn't yet implimented
01065     RemoveIlligalFileAndJavaChars(SliceName);
01066     pMosaicList->AddHead(new CSlice(Ax, Ay, Cx, Cy, SliceName, m_PathName.GetType(), TRUE));
01067 
01068     return ok;
01069 }
01070 
01071 /********************************************************************************************
01072 
01073 >   BOOL OpSlice::InRect(CSlice * pRect, INT32 x, INT32 y)
01074 
01075     Author:     Simon_Knight (Xara Group Ltd) <camelotdev@xara.com>
01076     Created:    23/7/99
01077     Purpose:    Tests if the point X,Y is in the rectangle defined by the slice
01078     Returns:    TRUE if x,y is in the rect
01079 
01080 ********************************************************************************************/
01081 BOOL OpSlice::InRect(CSlice * pRect, INT32 x, INT32 y)
01082 {
01083     if (x > pRect->left && x < pRect->right
01084         && y > pRect->top && y < pRect->bottom)
01085         return TRUE;
01086         
01087     return FALSE;
01088 }
01089 
01090 /********************************************************************************************
01091 
01092 >   BOOL OpSlice::Slice (CSlice * pRect, INT32 Ax, INT32 Ay, INT32 Bx, INT32 By, CList <CSlice *, CSlice *> * pMosaicList, POSITION pos)
01093 
01094     Author:     Simon_Knight (Xara Group Ltd) <camelotdev@xara.com>
01095     Created:    23/7/99
01096     Purpose:    Split the Slice pRect into smaller rectangles along the line AB
01097     Returns:    TRUE if it performed a slice
01098     Errors:     On overlapped named slices
01099 
01100 ********************************************************************************************/
01101 BOOL OpSlice::Slice (CSlice * pRect, INT32 Ax, INT32 Ay, INT32 Bx, INT32 By, CList <CSlice *, CSlice *> * pMosaicList)
01102 {
01103     if (!pRect->name.IsEmpty()) // Overlapping named slices
01104     {
01105         m_ErrorStr = pRect->name; // set the error string to be the name 
01106                                   // of the offending overlapping one
01107         return FALSE;
01108     }
01109 
01110     BOOL ok = FALSE;
01111     
01112     if (Ax == Bx) // Vert slice
01113     {
01114         pMosaicList->AddTail(new CSlice(pRect->left, pRect->top, Ax, pRect->bottom, "", m_PathName.GetType()));
01115         pMosaicList->AddTail(new CSlice(Ax, pRect->top, pRect->right, pRect->bottom, "", m_PathName.GetType()));
01116         ok = TRUE;
01117     }
01118     else
01119     if (Ay == By) // Horiz slice
01120     {
01121         pMosaicList->AddTail(new CSlice(pRect->left, pRect->top, pRect->right, Ay, "", m_PathName.GetType()));
01122         pMosaicList->AddTail(new CSlice(pRect->left, Ay, pRect->right, pRect->bottom, "", m_PathName.GetType()));
01123         ok = TRUE;
01124     }
01125     
01126     return ok;
01127 }
01128 
01129 /********************************************************************************************
01130 
01131 >   BOOL OpSlice::CutsRect(CSlice * pRect, INT32 Ax, INT32 Ay, INT32 Bx, INT32 By)
01132 
01133     Author:     Simon_Knight (Xara Group Ltd) <camelotdev@xara.com>
01134     Created:    23/7/99
01135     Purpose:    Tests to see if the line AB cuts the rectangle pRect
01136     Returns:    TRUE if it does cut
01137 
01138 ********************************************************************************************/
01139 BOOL OpSlice::CutsRect(CSlice * pRect, INT32 Ax, INT32 Ay, INT32 Bx, INT32 By)
01140 {
01141     if (Ax == Bx) // Vert cut
01142     {
01143         // top & bottom outside rect
01144         // while the vertical line cuts through the rect
01145         if (Ay <= pRect->top && By >= pRect->bottom
01146             && Ax < pRect->right && Ax > pRect->left)
01147             return TRUE;
01148     }
01149     else
01150     if (Ay == By) // Horiz cut
01151     {
01152         // left & right outside rect
01153         // while the horizontal line cuts through the rect
01154         if (Ax <= pRect->left && Bx >= pRect->right
01155             && Ay < pRect->bottom && Ay > pRect->top)
01156             return TRUE;
01157     }
01158     
01159     return FALSE;
01160 }
01161 
01162 // moves all edges slightly from one location to another keeping them all in line
01163 void ReAlignEdge ( INT32 OldEdge, INT32 NewEdge, BOOL Horiz, CList <CSlice *, CSlice *> * pMosaicList)
01164 {
01165     POSITION pos = pMosaicList->GetHeadPosition();
01166 
01167     while (pos)
01168     {
01169         CSlice * pRect = pMosaicList->GetNext(pos);
01170 
01171         if (Horiz)
01172         {
01173             if (pRect->left == OldEdge)
01174                 pRect->left = NewEdge;
01175             else
01176             if (pRect->right == OldEdge)
01177                 pRect->right = NewEdge;
01178         }
01179         else
01180         {
01181             if (pRect->bottom == OldEdge)
01182                 pRect->bottom = NewEdge;
01183             else
01184             if (pRect->top == OldEdge)
01185                 pRect->top = NewEdge;
01186         }
01187     }
01188 }
01189 
01190 /********************************************************************************************
01191 
01192 >   void OpSlice::NudgeSliceIfClose(INT32 &Ax, INT32 &Ay, INT32 &Cx, INT32 &Cy, CList <CSlice *, CSlice *> * pMosaicList)
01193 
01194     Author:     Simon_Knight (Xara Group Ltd) <camelotdev@xara.com>
01195     Created:    4/8/99
01196     Purpose:    Moves the new slice over a bit if it just overlaps with an
01197                 existing slice due to the pixel infation.
01198                 It does this by adjusting the AC rectangle passed in
01199     Returns:    nothing
01200 
01201 ********************************************************************************************/
01202 void OpSlice::NudgeSliceIfClose(INT32 &Ax, INT32 &Ay, INT32 &Cx, INT32 &Cy, CList <CSlice *, CSlice *> * pMosaicList)
01203 {
01204     INT32 OldAx = Ax;
01205     INT32 OldAy = Ay;
01206     INT32 OldCx = Cx;
01207     INT32 OldCy = Cy;
01208 
01209     BOOL done = FALSE;
01210 
01211     // allow how many pixels to be fudged?
01212     for (INT32 size = 750 ; size <= 1500 && !done ; size += 750)
01213     {
01214         POSITION pos = pMosaicList->GetHeadPosition();
01215 
01216         whil