1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #ifdef DBG_UTIL
21 
22 /*
23  * And here's the description:
24  *
25  * The PROTOCOL macros allow you to log events in frame methods. In places where
26  * logging is useful either one of the PROTOCOL(...) or PROTOCOL_ENTER(...) can
27  * be used. PROTOCOL_ENTER(...) additionally logs the leaving of a method.
28  *
29  * The PROTOCOL macros accept the following parameters:
30  * 1.   A pointer to an SwFrame (usually "this" or "rThis")
31  * 2.   The function group i.e. PROT::MakeAll. This is used to decide (inline)
32  *      whether this event shall be logged at the current time.
33  * 3.   The action, usually 0. For example DbgAction::Start indents output in the log
34  *      file and DbgAction::End stops the indentation. This allows for example
35  *      PROTOCOL_ENTER to indent at the beginning of a method and stop indenting
36  *      when leaving the method.
37  * 4.   The fourth parameter is a void pointer which allows to pass anything
38  *      which can be used in the log. A good example is PROT::Grow: this requires
39  *      a pointer to the value which defines how much to grow.
40  *
41  * The log file is called "dbg_lay.out", which is saved in the current (BIN-)
42  * directory. The file contains lines with FrameId, function group and additional
43  * information.
44  *
45  * What exactly is going to be logged, can be defined as follows:
46  * 1.   The static variable SwProtocol::nRecord contains the function groups
47  *      which shall be logged.
48  *      A value of i.e. PROT::Grow causes calls to SwFrame::Grow to be
49  *      logged; PROT::MakeAll logs the calls to xxx::MakeAll.
50  *      The PROT_XY values can be combined using binary OR, the default value
51  *      is null - no method calls are logged.
52  * 2.   The SwImplProtocol class contains a filter for frame types, only method
53  *      call of frame types which are defined there are logged.
54  *      The member nTypes can be set to values like SwFrameType::Page or SwFrameType::Section and
55  *      may be combined using binary OR. The default values is 0xFFFF - meaning
56  *      all frame types.
57  * 3.   The SwImplProtocol class contains an ArrayPointer to FrameIds which need to be
58  *      tracked. If the pointer is null, all frames will be logged; otherwise
59  *      only frames of linked from the array will be logged.
60  *
61  * Code changes are needed to start logging; either change the default of nRecord
62  * in SwProtocol::Init() or change the debugger. There are several possible
63  * places in the debugger:
64  * 1.   Set a breakpoint in SwProtocol::Init() and manipulate nRecord there, set
65  *      FrameIds accordingly then start logging during program start.
66  * 2.   Set a breakpoint before any PROTOCOL or PROTOCOL_ENTER macro during
67  *      program execution, then set the lowest bit (PROT::Init) of
68  *      SwProtocol::nRecord. This activates the function group of the following
69  *      macro and causes it to be logged in the future.
70  * 3.   There's a special case for 2: If one uses 2. in SwRootFrame::PaintSwFrame(..),
71  *      the log settings are taken from the file "dbg_lay.ini"!
72  *      In this INI-file you can have comment lines starting with a '#'.
73  *      The sections "[frmid]", "[frmtype]" and "[record]" are relevant.
74  *      In the [frmid] section, you can put FrameIds of the Frames to be logged.
75  *      If there are no entries in this section, all Frames will be logged.
76  *      In the [frmtype] section, the frame types which should be logged are
77  *      listed; default is USHRT_MAX which means that all types are logged.
78  *      It's possible to remove types from the list using '!' in front of a
79  *      value. The value !0xC000 would for example exclude SwContentFrames from
80  *      logging.
81  *      In the [record] section the functions group which should be logged are
82  *      listed; default is 0 which means that none are logged. It's also
83  *      possible to remove functions using '!'.
84  *      An example INI file:
85  *           #Functions: all(0x0007ffff), except PrintArea (0x200)
86  *           [record] 524287 !512
87  *           [frmid]
88  *           #the following FrameIds:
89  *           1 2 12 13 14 15
90  *           #no layout frames, except ColumnFrames
91  *           [frmtype] !0x3FFF 0x4
92  *
93  * As soon as the logging is in process, one can manipulate many things in
94  * SwImplProtocol::Record_(...) using a debugger, especially concerning
95  * frame types and FrameIds.
96  */
97 
98 #include <dbg_lay.hxx>
99 
100 #include <txtfrm.hxx>
101 #include <fntcache.hxx>
102 #include <tabfrm.hxx>
103 #include <rowfrm.hxx>
104 #include <cellfrm.hxx>
105 #include <layfrm.hxx>
106 #include <frame.hxx>
107 #include <swtable.hxx>
108 #include <ndtxt.hxx>
109 #include <rtl/strbuf.hxx>
110 #include <sal/log.hxx>
111 #include <tools/stream.hxx>
112 
113 PROT SwProtocol::s_nRecord = PROT::FileInit;
114 SwImplProtocol* SwProtocol::s_pImpl = nullptr;
115 
lcl_GetFrameId(const SwFrame * pFrame)116 static sal_uLong lcl_GetFrameId( const SwFrame* pFrame )
117 {
118 #if OSL_DEBUG_LEVEL > 1
119     static bool bFrameId = false;
120     if( bFrameId )
121         return pFrame->GetFrameId();
122 #endif
123     if( pFrame )
124         return pFrame->GetFrameId();
125     return 0;
126 }
127 
128 class SwImplProtocol
129 {
130     std::unique_ptr<SvFileStream> m_pStream; // output stream
131     std::unique_ptr<std::set<sal_uInt16>>
132         m_pFrameIds; // which FrameIds shall be logged ( NULL == all)
133     std::vector<tools::Long> m_aVars; // variables
134     OStringBuffer m_aLayer; // indentation of output ("  " per start/end)
135     SwFrameType m_nTypes; // which types shall be logged
136     sal_uInt16 m_nLineCount; // printed lines
137     sal_uInt16 m_nMaxLines; // max lines to be printed
138     sal_uInt8 m_nInitFile; // range (FrameId,FrameType,Record) during reading of the INI file
139     sal_uInt8
140         m_nTestMode; // special for test formatting, logging may only be done in test formatting.
141     void Record_( const SwFrame* pFrame, PROT nFunction, DbgAction nAct, void* pParam );
142     bool NewStream();
143     void CheckLine( OString& rLine );
144     static void SectFunc( OStringBuffer& rOut, DbgAction nAct, void const * pParam );
145 public:
146     SwImplProtocol();
147     ~SwImplProtocol();
148     // logging
Record(const SwFrame * pFrame,PROT nFunction,DbgAction nAct,void * pParam)149     void Record( const SwFrame* pFrame, PROT nFunction, DbgAction nAct, void* pParam )
150         {
151             if (m_pStream)
152                 Record_(pFrame, nFunction, nAct, pParam);
153         }
154     void InsertFrame( sal_uInt16 nFrameId );    // take FrameId for logging
155     void DeleteFrame( sal_uInt16 nFrameId );    // remove FrameId; don't log him anymore
156     void FileInit();                    // read the INI file
ChkStream()157     void ChkStream() {
158         if (!m_pStream)
159             NewStream();
160     }
161 };
162 
163 /* Through the PROTOCOL_ENTER macro a SwEnterLeave object gets created. If the
164  * current function should be logged as SwImplEnterLeace object gets created.
165  * The funny thing here is, that the Ctor of the Impl object is automatically
166  * called at the beginning of the function and the Dtor is automatically called
167  * when leaving the function. In the base implementation the Ctor calls only
168  * PROTOCOL(..) with DbgAction::Start and in the Dtor a PROTOCOL(..) with DbgAction::End.
169  * It's possible to derive from this class, for example to be able to document
170  * frame resize while leaving a function. To do this, one only needs to add the
171  * desired SwImplEnterLeave class in SwEnterLeave::Ctor().
172  */
173 
174 class SwImplEnterLeave
175 {
176 protected:
177     const SwFrame* m_pFrame; // the frame
178     PROT m_nFunction; // the function
179     DbgAction m_nAction; // the action if needed
180     void* m_pParam; // further parameter
181 public:
SwImplEnterLeave(const SwFrame * pF,PROT nFunct,DbgAction nAct,void * pPar)182     SwImplEnterLeave(const SwFrame* pF, PROT nFunct, DbgAction nAct, void* pPar)
183         : m_pFrame(pF)
184         , m_nFunction(nFunct)
185         , m_nAction(nAct)
186         , m_pParam(pPar)
187     {
188     }
~SwImplEnterLeave()189     virtual ~SwImplEnterLeave() {}
190     virtual void Enter();           // message when entering
191     virtual void Leave();           // message when leaving
192 };
193 
194 namespace {
195 
196 class SwSizeEnterLeave : public SwImplEnterLeave
197 {
198     tools::Long m_nFrameHeight;
199 
200 public:
SwSizeEnterLeave(const SwFrame * pF,PROT nFunct,DbgAction nAct,void * pPar)201     SwSizeEnterLeave(const SwFrame* pF, PROT nFunct, DbgAction nAct, void* pPar)
202         : SwImplEnterLeave(pF, nFunct, nAct, pPar)
203         , m_nFrameHeight(pF->getFrameArea().Height())
204     {
205     }
206 
207     virtual void Leave() override;           // resize message
208 };
209 
210 class SwUpperEnterLeave : public SwImplEnterLeave
211 {
212     sal_uInt16 m_nFrameId;
213 
214 public:
SwUpperEnterLeave(const SwFrame * pF,PROT nFunct,DbgAction nAct,void * pPar)215     SwUpperEnterLeave(const SwFrame* pF, PROT nFunct, DbgAction nAct, void* pPar)
216         : SwImplEnterLeave(pF, nFunct, nAct, pPar)
217         , m_nFrameId(0)
218     {
219     }
220 
221     virtual void Enter() override;           // message
222     virtual void Leave() override;           // message of FrameId from upper
223 };
224 
225 class SwFrameChangesLeave : public SwImplEnterLeave
226 {
227     SwRect m_aFrame;
228 
229 public:
SwFrameChangesLeave(const SwFrame * pF,PROT nFunct,DbgAction nAct,void * pPar)230     SwFrameChangesLeave(const SwFrame* pF, PROT nFunct, DbgAction nAct, void* pPar)
231         : SwImplEnterLeave(pF, nFunct, nAct, pPar)
232         , m_aFrame(pF->getFrameArea())
233     {
234     }
235 
236     virtual void Enter() override;           // no message
237     virtual void Leave() override;           // message when resizing the Frame area
238 };
239 
240 }
241 
Record(const SwFrame * pFrame,PROT nFunction,DbgAction nAct,void * pParam)242 void SwProtocol::Record( const SwFrame* pFrame, PROT nFunction, DbgAction nAct, void* pParam )
243 {
244     if( Start() )
245     {   // We reach this point if SwProtocol::nRecord is binary OR'd with PROT::Init(0x1) using the debugger
246         bool bFinit = false; // This gives the possibility to stop logging of this action in the debugger
247         if( bFinit )
248         {
249             s_nRecord &= ~nFunction;  // Don't log this function any longer
250             s_nRecord &= ~PROT::Init;  // Always reset PROT::Init
251             return;
252         }
253         s_nRecord |= nFunction;       // Activate logging of this function
254         s_nRecord &= ~PROT::Init;      // Always reset PROT::Init
255         if( s_pImpl )
256             s_pImpl->ChkStream();
257     }
258     if( !s_pImpl )                        // Create Impl object if needed
259         s_pImpl = new SwImplProtocol();
260     s_pImpl->Record( pFrame, nFunction, nAct, pParam ); // ...and start logging
261 }
262 
263 // The following function gets called when pulling in the writer DLL through
264 // TextInit(..) and gives the possibility to release functions
265 // and/or FrameIds to the debugger
266 
Init()267 void SwProtocol::Init()
268 {
269     s_nRecord = PROT::FileInit;
270     SvFileStream aStream( "dbg_lay.go", StreamMode::READ );
271     if( aStream.IsOpen() )
272     {
273         s_pImpl = new SwImplProtocol();
274         s_pImpl->FileInit();
275     }
276     aStream.Close();
277 }
278 
279 // End of logging
280 
Stop()281 void SwProtocol::Stop()
282 {
283      if( s_pImpl )
284      {
285         delete s_pImpl;
286         s_pImpl = nullptr;
287         if( pFntCache )
288             pFntCache->Flush();
289      }
290      s_nRecord = PROT::FileInit;
291 }
292 
SwImplProtocol()293 SwImplProtocol::SwImplProtocol()
294     : m_nTypes(FRM_ALL)
295     , m_nLineCount(0)
296     , m_nMaxLines(USHRT_MAX)
297     , m_nTestMode(0)
298 {
299     NewStream();
300 }
301 
NewStream()302 bool SwImplProtocol::NewStream()
303 {
304     m_nLineCount = 0;
305     m_pStream.reset(new SvFileStream("dbg_lay.out", StreamMode::WRITE | StreamMode::TRUNC));
306     if (m_pStream->GetError())
307     {
308         m_pStream.reset();
309     }
310     return nullptr != m_pStream;
311 }
312 
~SwImplProtocol()313 SwImplProtocol::~SwImplProtocol()
314 {
315     if (m_pStream)
316     {
317         m_pStream->Close();
318         m_pStream.reset();
319     }
320     m_pFrameIds.reset();
321     m_aVars.clear();
322 }
323 
324 /// analyze a line in the INI file
CheckLine(OString & rLine)325 void SwImplProtocol::CheckLine( OString& rLine )
326 {
327     rLine = rLine.toAsciiLowerCase(); // upper/lower case is the same
328     rLine = rLine.replace( '\t', ' ' );
329     if( '#' == rLine[0] )   // comments start with '#'
330         return;
331     if( '[' == rLine[0] )   // section: FrameIds, type or function
332     {
333         OString aTmp = rLine.getToken(0, ']');
334         if (aTmp == "[frmid")      // section FrameIds
335         {
336             m_nInitFile = 1;
337             m_pFrameIds.reset(); // default: log all frames
338         }
339         else if (aTmp == "[frmtype")// section types
340         {
341             m_nInitFile = 2;
342             m_nTypes = FRM_ALL; // default: log all frame types
343         }
344         else if (aTmp == "[record")// section functions
345         {
346             m_nInitFile = 3;
347             SwProtocol::SetRecord( PROT::FileInit );// default: don't log any function
348         }
349         else if (aTmp == "[test")// section functions
350         {
351             m_nInitFile = 4; // default:
352             m_nTestMode = 0; // log outside of test formatting
353         }
354         else if (aTmp == "[max")// Max number of lines
355         {
356             m_nInitFile = 5; // default:
357             m_nMaxLines = USHRT_MAX;
358         }
359         else if (aTmp == "[var")// variables
360         {
361             m_nInitFile = 6;
362         }
363         else
364             m_nInitFile = 0; // oops: unknown section?
365         rLine = rLine.copy(aTmp.getLength() + 1);
366     }
367 
368     // spaces (or tabs) are the delimiter
369     sal_Int32 nIndex = 0;
370     do
371     {
372         OString aTok = rLine.getToken( 0, ' ', nIndex );
373         bool bNo = false;
374         if( !aTok.isEmpty() && '!' == aTok[0] )
375         {
376             bNo = true;                 // remove this function/type
377             aTok = aTok.copy(1);
378         }
379         if( !aTok.isEmpty() )
380         {
381             sal_Int64 nVal = aTok.toInt64();
382             switch (m_nInitFile)
383             {
384                 case 1: InsertFrame( sal_uInt16( nVal ) );    // add FrameId
385                         break;
386                 case 2: {
387                             SwFrameType nNew = static_cast<SwFrameType>(nVal);
388                             if( bNo )
389                                 m_nTypes &= ~nNew; // remove type
390                             else
391                                 m_nTypes |= nNew; // add type
392                         }
393                         break;
394                 case 3: {
395                             PROT nOld = SwProtocol::Record();
396                             if( bNo )
397                                 nOld &= ~PROT(nVal & o3tl::typed_flags<PROT>::mask); // remove function
398                             else
399                                 nOld |= PROT(nVal & o3tl::typed_flags<PROT>::mask);  // remove function
400                             SwProtocol::SetRecord( nOld );
401                         }
402                         break;
403                 case 4: {
404                             sal_uInt8 nNew = static_cast<sal_uInt8>(nVal);
405                             if( bNo )
406                                 m_nTestMode &= ~nNew; // reset test mode
407                             else
408                                 m_nTestMode |= nNew; // set test mode
409                         }
410                         break;
411                 case 5:
412                     m_nMaxLines = o3tl::narrowing<sal_uInt16>(nVal);
413                     break;
414                 case 6:
415                     m_aVars.push_back(nVal);
416                     break;
417             }
418         }
419     }
420     while ( nIndex >= 0 );
421 }
422 
423 /// read the file "dbg_lay.ini" in the current directory and evaluate it.
FileInit()424 void SwImplProtocol::FileInit()
425 {
426     SvFileStream aStream( "dbg_lay.ini", StreamMode::READ );
427     if( aStream.IsOpen() )
428     {
429         OString aLine;
430         m_nInitFile = 0;
431         while( aStream.good() )
432         {
433             char c;
434             aStream.ReadChar( c );
435             if( '\n' == c || '\r' == c )    // line ending
436             {
437                 aLine = aLine.trim();
438                 if( !aLine.isEmpty() )
439                     CheckLine( aLine );     // evaluate line
440                 aLine.clear();
441             }
442             else
443                 aLine += OStringChar(c);
444         }
445         if( !aLine.isEmpty() )
446             CheckLine( aLine );     // evaluate last line
447     }
448     aStream.Close();
449 }
450 
451 /// enable indentation by two spaces during DbgAction::Start and disable it again at DbgAction::End.
lcl_Start(OStringBuffer & rOut,OStringBuffer & rLay,DbgAction nAction)452 static void lcl_Start(OStringBuffer& rOut, OStringBuffer& rLay, DbgAction nAction)
453 {
454     if( nAction == DbgAction::Start )
455     {
456         rLay.append("  ");
457         rOut.append(" On");
458     }
459     else if( nAction == DbgAction::End )
460     {
461         if( rLay.getLength() > 1 )
462         {
463             rLay.remove(rLay.getLength() - 2, rLay.getLength());
464             rOut.remove(0, 2);
465         }
466         rOut.append(" Off");
467     }
468 }
469 
470 /// output the ValidSize-, ValidPos- and ValidPrtArea-Flag ("Sz","Ps","PA")
471 /// of the frame; "+" stands for valid, "-" stands for invalid.
lcl_Flags(OStringBuffer & rOut,const SwFrame * pFrame)472 static void lcl_Flags(OStringBuffer& rOut, const SwFrame* pFrame)
473 {
474     rOut.append(" ValidSize");
475     rOut.append(pFrame->isFrameAreaSizeValid() ? '+' : '-');
476     rOut.append(" ValidPos");
477     rOut.append(pFrame->isFrameAreaPositionValid() ? '+' : '-');
478     rOut.append(" ValidPrtArea");
479     rOut.append(pFrame->isFramePrintAreaValid() ? '+' : '-');
480 }
481 
lcl_Padded(OStringBuffer & rOut,const OString & s,size_t length)482 static void lcl_Padded(OStringBuffer& rOut, const OString& s, size_t length)
483 {
484     if (sal_Int32(length) < s.getLength())
485         length = s.getLength();
486     rOut.append(s);
487     for (size_t i = 0; i < length - s.getLength(); i++)
488     {
489         rOut.append(" ");
490     }
491 }
492 
lcl_Padded(OStringBuffer & rOut,const tools::Long n,size_t length=5)493 static void lcl_Padded(OStringBuffer& rOut, const tools::Long n, size_t length = 5)
494 {
495     char sz[RTL_STR_MAX_VALUEOFINT64];
496     rtl_str_valueOfInt64(sz, n, 10);
497     OString s(sz);
498     lcl_Padded(rOut, s, length);
499 }
500 
501 /// output the frame as plain text.
lcl_FrameRect(OStringBuffer & rOut,const char * hint,const SwRect & rect)502 static void lcl_FrameRect(OStringBuffer& rOut, const char* hint, const SwRect& rect)
503 {
504     rOut.append("[");
505     rOut.append(hint);
506     rOut.append(":X:");
507     lcl_Padded(rOut, rect.Pos().X());
508     rOut.append(", Y:");
509     lcl_Padded(rOut, rect.Pos().Y());
510     rOut.append(", Width:");
511     lcl_Padded(rOut, rect.SSize().Width());
512     rOut.append(", Height:");
513     lcl_Padded(rOut, rect.SSize().Height());
514     rOut.append("] ");
515 }
516 
lcl_TableInfo(const SwTabFrame * pTabFrame)517 static OString lcl_TableInfo(const SwTabFrame* pTabFrame)
518 {
519     const SwTable* pTable = pTabFrame->GetTable();
520     const SwFormat* pFormat = static_cast<const SwFormat*>(pTable->GetRegisteredIn());
521     const OUString& text = pFormat->GetName();
522     return OUStringToOString(text, RTL_TEXTENCODING_ASCII_US);
523 }
524 
lcl_RowInfo(const SwRowFrame * pFrame)525 static OString lcl_RowInfo(const SwRowFrame* pFrame)
526 {
527     // dummy, needs actual functionality...
528     if (pFrame == nullptr)
529         return "";
530     const SwTableLine* pTabLine = pFrame->GetTabLine();
531     if (pTabLine == nullptr)
532         return "";
533 
534     return "RowInfo";
535 }
536 
lcl_CellText(const SwCellFrame * pFrame)537 static OUString lcl_CellText(const SwCellFrame* pFrame)
538 {
539     OUString result;
540     int n = 0;
541 
542     const SwStartNode* pStartNode = pFrame->GetTabBox()->GetSttNd();
543     const SwEndNode* pEndNode = pStartNode->EndOfSectionNode();
544     const SwNodes& nodes = pStartNode->GetNodes();
545 
546     for (sal_uLong i = pStartNode->GetIndex(); i < nodes.Count(); i++)
547     {
548         SwNode* pNode = nodes[i];
549 
550         if (pNode->IsEndNode())
551         {
552             if (pNode->EndOfSectionNode() == pEndNode)
553                 break;
554         }
555         else if (pNode->IsTextNode())
556         {
557             n++;
558             result += "Para:" + OUString::number(10) + " " +
559                 pNode->GetTextNode()->GetText();
560         }
561     }
562 
563     return OUString::number(n) + " para(s):" + result;
564 }
565 
lcl_CellInfo(const SwCellFrame * pFrame)566 static OString lcl_CellInfo(const SwCellFrame* pFrame)
567 {
568     const OUString text = "CellInfo: " + pFrame->GetTabBox()->GetName() + " Text: " + lcl_CellText(pFrame);
569     return OUStringToOString(text, RTL_TEXTENCODING_ASCII_US);
570 }
571 
572 /// output the type of the frame as plain text.
lcl_FrameType(OStringBuffer & rOut,const SwFrame * pFrame)573 static void lcl_FrameType( OStringBuffer& rOut, const SwFrame* pFrame )
574 {
575     if( pFrame->IsTextFrame() )
576         rOut.append("SwTextFrame ");
577     else if( pFrame->IsLayoutFrame() )
578     {
579         if( pFrame->IsPageFrame() )
580             rOut.append("SwPageFrame ");
581         else if( pFrame->IsColumnFrame() )
582             rOut.append("SwColumnFrame ");
583         else if( pFrame->IsBodyFrame() )
584         {
585             if( pFrame->GetUpper() && pFrame->IsColBodyFrame() )
586                 rOut.append("(Col)");
587             rOut.append("SwBodyFrame ");
588         }
589         else if( pFrame->IsRootFrame() )
590             rOut.append("SwRootFrame ");
591         else if( pFrame->IsCellFrame() )
592             rOut.append("SwCellFrame ");
593         else if( pFrame->IsTabFrame() )
594             rOut.append("SwTabFrame ");
595         else if( pFrame->IsRowFrame() )
596             rOut.append("SwRowFrame ");
597         else if( pFrame->IsSctFrame() )
598             rOut.append("SwSectionFrame ");
599         else if( pFrame->IsHeaderFrame() )
600             rOut.append("SwHeaderFrame ");
601         else if( pFrame->IsFooterFrame() )
602             rOut.append("SwFooterFrame ");
603         else if( pFrame->IsFootnoteFrame() )
604             rOut.append("SwFootnoteFrame ");
605         else if( pFrame->IsFootnoteContFrame() )
606             rOut.append("SwFootnoteContFrame ");
607         else if( pFrame->IsFlyFrame() )
608             rOut.append("SwFlyFrame ");
609         else
610             rOut.append("SwLayoutFrame ");
611     }
612     else if( pFrame->IsNoTextFrame() )
613         rOut.append("SwNoTextFrame");
614     else
615         rOut.append("Not impl. ");
616 }
617 
618 /**
619  * Is only called if the PROTOCOL macro finds out,
620  * that this function should be recorded ( @see{SwProtocol::nRecord} ).
621  *
622  * In this method we also check if FrameId and frame type should be logged.
623  */
Record_(const SwFrame * pFrame,PROT nFunction,DbgAction nAct,void * pParam)624 void SwImplProtocol::Record_( const SwFrame* pFrame, PROT nFunction, DbgAction nAct, void* pParam )
625 {
626     sal_uInt16 nSpecial = 0;
627     if( nSpecial )  // the possible debugger manipulations
628     {
629         sal_uInt16 nId = sal_uInt16(lcl_GetFrameId( pFrame ));
630         switch ( nSpecial )
631         {
632             case 1: InsertFrame( nId ); break;
633             case 2: DeleteFrame( nId ); break;
634             case 3:
635                 m_pFrameIds.reset();
636                 break;
637             case 4:
638                 m_pStream.reset();
639                 break;
640         }
641         return;
642     }
643     if (!m_pStream && !NewStream())
644         return; // still no stream
645 
646     if (m_pFrameIds && !m_pFrameIds->count(sal_uInt16(lcl_GetFrameId(pFrame))))
647         return; // doesn't belong to the wished FrameIds
648 
649     if (!(pFrame->GetType() & m_nTypes))
650         return; // the type is unwanted
651 
652     if (1 == m_nTestMode && nFunction != PROT::TestFormat)
653         return; // we may only log inside a test formatting
654     bool bTmp = false;
655     OStringBuffer aOut(m_aLayer);
656     aOut.append(static_cast<sal_Int64>(lcl_GetFrameId(pFrame)));
657     aOut.append(' ');
658     lcl_FrameType( aOut, pFrame );    // then the frame type
659     switch ( nFunction )            // and the function
660     {
661         case PROT::MakeAll:  aOut.append("SwFrame::MakeAll");
662             lcl_Start(aOut, m_aLayer, nAct);
663             if (nAct == DbgAction::Start)
664                 lcl_Flags(aOut, pFrame);
665             break;
666         case PROT::MoveFwd: bTmp = true;
667                             [[fallthrough]];
668         case PROT::MoveBack:
669                             if (nFunction == (bTmp ? PROT::Init : PROT::FileInit))
670                                 aOut.append("SwFlowFrame::MoveFwd");
671                             else
672                                 aOut.append("SwFlowFrame::MoveBwd");
673                             lcl_Start(aOut, m_aLayer, nAct);
674                             if( pParam )
675                             {
676                                 aOut.append(' ');
677                                 aOut.append(static_cast<sal_Int32>(*static_cast<sal_uInt16*>(pParam)));
678                             }
679                             break;
680         case PROT::GrowTest:
681                             aOut.append("SwFrame::Grow (test)");
682                             lcl_Start(aOut, m_aLayer, nAct);
683                             break;
684         case PROT::ShrinkTest:
685                             aOut.append("SwFrame::Shrink (test)");
686                             lcl_Start(aOut, m_aLayer, nAct);
687                             break;
688         case PROT::AdjustN :
689         case PROT::Shrink:   bTmp = true;
690                             [[fallthrough]];
691         case PROT::Grow:
692                             if (!bTmp)
693                                 aOut.append("SwFrame::Grow");
694                             else
695                             {
696                                 if (nFunction == PROT::Shrink)
697                                     aOut.append("SwFrame::Shrink");
698                                 else
699                                     aOut.append("SwFrame::AdjustNeighbourhood");
700                             }
701                             lcl_Start(aOut, m_aLayer, nAct);
702                             if( pParam )
703                             {
704                                 aOut.append(' ');
705                                 aOut.append(static_cast<sal_Int64>(*static_cast<tools::Long*>(pParam)));
706                             }
707                             break;
708         case PROT::PrintArea:  aOut.append("PROT::PrintArea");
709             lcl_Start(aOut, m_aLayer, nAct);
710             break;
711         case PROT::Size:     aOut.append("PROT::Size");
712             lcl_Start(aOut, m_aLayer, nAct);
713             aOut.append(' ');
714             aOut.append(static_cast<sal_Int64>(pFrame->getFrameArea().Height()));
715             break;
716         case PROT::Leaf:     aOut.append("SwFrame::GetPrev/NextSctLeaf");
717             lcl_Start(aOut, m_aLayer, nAct);
718             aOut.append(' ');
719             if (pParam)
720             {
721                 aOut.append(' ');
722                 aOut.append(static_cast<sal_Int64>(lcl_GetFrameId(static_cast<SwFrame*>(pParam))));
723                             }
724                             break;
725         case PROT::FileInit: FileInit();
726                             aOut.append("Initialize");
727                             break;
728         case PROT::Section:  SectFunc(aOut, nAct, pParam);
729                             break;
730         case PROT::Cut:      bTmp = true;
731                             [[fallthrough]];
732         case PROT::Paste:
733                             if (bTmp)
734                                 aOut.append("PROT::Cut from ");
735                             else
736                                 aOut.append("PROT::Paste to ");
737                             aOut.append(static_cast<sal_Int64>(lcl_GetFrameId(static_cast<SwFrame*>(pParam))));
738                             break;
739         case PROT::TestFormat:
740                             aOut.append("SwTextFrame::TestFormat");
741                             lcl_Start(aOut, m_aLayer, nAct);
742                             if( DbgAction::Start == nAct )
743                                 m_nTestMode |= 2;
744                             else
745                                 m_nTestMode &= ~2;
746                             break;
747         case PROT::FrmChanges:
748                             {
749                                 SwRect& rFrame = *static_cast<SwRect*>(pParam);
750                                 if( pFrame->getFrameArea().Pos() != rFrame.Pos() )
751                                 {
752                                     aOut.append("PosChg: (");
753                                     aOut.append(static_cast<sal_Int64>(rFrame.Left()));
754                                     aOut.append(", ");
755                                     aOut.append(static_cast<sal_Int64>(rFrame.Top()));
756                                     aOut.append(") -> (");
757                                     aOut.append(static_cast<sal_Int64>(pFrame->getFrameArea().Left()));
758                                     aOut.append(", ");
759                                     aOut.append(static_cast<sal_Int64>(pFrame->getFrameArea().Top()));
760                                     aOut.append(") ");
761                                 }
762                                 if( pFrame->getFrameArea().Height() != rFrame.Height() )
763                                 {
764                                     aOut.append("Height: ");
765                                     aOut.append(static_cast<sal_Int64>(rFrame.Height()));
766                                     aOut.append(" -> ");
767                                     aOut.append(static_cast<sal_Int64>(pFrame->getFrameArea().Height()));
768                                     aOut.append(" ");
769                                 }
770                                 if( pFrame->getFrameArea().Width() != rFrame.Width() )
771                                 {
772                                     aOut.append("Width: ");
773                                     aOut.append(static_cast<sal_Int64>(rFrame.Width()));
774                                     aOut.append(" -> ");
775                                     aOut.append(static_cast<sal_Int64>(pFrame->getFrameArea().Width()));
776                                     aOut.append(' ');
777                                 }
778                                 break;
779                             }
780         default: break;
781     }
782 
783     aOut.append("  ");
784     while (aOut.getLength() < 40) aOut.append(" ");
785     lcl_FrameRect(aOut, "SwFrame", pFrame->getFrameArea());
786 
787     aOut.append(" ");
788     while (aOut.getLength() < 90) aOut.append(" ");
789     lcl_FrameRect(aOut, "SwPrint", pFrame->getFramePrintArea());
790 
791     if (pFrame->IsTextFrame())
792     {
793         aOut.append(" ");
794         while (aOut.getLength() < 140) aOut.append(" ");
795         const OUString& text = static_cast<const SwTextFrame*>(pFrame)->GetText();
796         OString o = OUStringToOString(text, RTL_TEXTENCODING_ASCII_US);
797         aOut.append(o);
798     }
799     else if (pFrame->IsTabFrame())
800     {
801         const SwTabFrame* pTabFrame = static_cast<const SwTabFrame*>(pFrame);
802         aOut.append(lcl_TableInfo(pTabFrame));
803     }
804     else if (pFrame->IsRowFrame())
805     {
806         const SwRowFrame* pRowFrame = static_cast<const SwRowFrame*>(pFrame);
807         aOut.append(lcl_RowInfo(pRowFrame));
808 
809     }
810     else if (pFrame->IsCellFrame())
811     {
812         const SwCellFrame* pCellFrame = static_cast<const SwCellFrame*>(pFrame);
813         aOut.append(lcl_CellInfo(pCellFrame));
814     }
815 
816     SAL_INFO("sw.layout.debug", aOut.getStr());
817     m_pStream->WriteOString(aOut.makeStringAndClear());
818     (*m_pStream) << endl; // output
819     m_pStream->Flush(); // to the disk, so we can read it immediately
820     if (++m_nLineCount >= m_nMaxLines) // max number of lines reached?
821     {
822         SAL_WARN("sw.layout.debug", "max number of lines reached");
823         SwProtocol::SetRecord( PROT::FileInit );        // => end f logging
824     }
825 }
826 
827 /// Handle the output of the SectionFrames.
SectFunc(OStringBuffer & rOut,DbgAction nAct,void const * pParam)828 void SwImplProtocol::SectFunc(OStringBuffer &rOut, DbgAction nAct, void const * pParam)
829 {
830     bool bTmp = false;
831     switch( nAct )
832     {
833         case DbgAction::Merge:  rOut.append("Merge Section ");
834                                 rOut.append(static_cast<sal_Int64>(lcl_GetFrameId(static_cast<SwFrame const *>(pParam))));
835                                 break;
836         case DbgAction::CreateMaster: bTmp = true;
837                                 [[fallthrough]];
838         case DbgAction::CreateFollow: rOut.append("Create Section ");
839                                 if (bTmp)
840                                     rOut.append("Master to ");
841                                 else
842                                     rOut.append("Follow from ");
843                                 rOut.append(static_cast<sal_Int64>(lcl_GetFrameId(static_cast<SwFrame const *>(pParam))));
844                                 break;
845         case DbgAction::DelMaster:    bTmp = true;
846                                 [[fallthrough]];
847         case DbgAction::DelFollow:    rOut.append("Delete Section ");
848                                 if (bTmp)
849                                     rOut.append("Master to ");
850                                 else
851                                     rOut.append("Follow from ");
852                                 rOut.append(static_cast<sal_Int64>(lcl_GetFrameId(static_cast<SwFrame const *>(pParam))));
853                                 break;
854         default: break;
855     }
856 }
857 
858 /**
859  * if pFrameIds==NULL all Frames will be logged. But as soon as pFrameIds are
860  * set, only the added FrameIds are being logged.
861  *
862  * @param nId new FrameId for logging
863  * @return TRUE if newly added, FALSE if FrameId is already under control
864  */
InsertFrame(sal_uInt16 nId)865 void SwImplProtocol::InsertFrame( sal_uInt16 nId )
866 {
867     if (!m_pFrameIds)
868         m_pFrameIds.reset(new std::set<sal_uInt16>);
869     if (m_pFrameIds->count(nId))
870         return;
871     m_pFrameIds->insert(nId);
872 }
873 
874 /// Removes a FrameId from the pFrameIds array, so that it won't be logged anymore.
DeleteFrame(sal_uInt16 nId)875 void SwImplProtocol::DeleteFrame( sal_uInt16 nId )
876 {
877     if (!m_pFrameIds)
878         return;
879     m_pFrameIds->erase(nId);
880 }
881 
882 /*
883  * The task here is to find the right SwImplEnterLeave object based on the
884  * function; everything else is then done in his Ctor/Dtor.
885  */
SwEnterLeave(const SwFrame * pFrame,PROT nFunc,DbgAction nAct,void * pPar)886 SwEnterLeave::SwEnterLeave( const SwFrame* pFrame, PROT nFunc, DbgAction nAct, void* pPar )
887 {
888     if( !SwProtocol::Record( nFunc ) )
889         return;
890     switch( nFunc )
891     {
892         case PROT::AdjustN :
893         case PROT::Grow:
894         case PROT::Shrink : pImpl.reset( new SwSizeEnterLeave( pFrame, nFunc, nAct, pPar ) ); break;
895         case PROT::MoveFwd:
896         case PROT::MoveBack : pImpl.reset( new SwUpperEnterLeave( pFrame, nFunc, nAct, pPar ) ); break;
897         case PROT::FrmChanges : pImpl.reset( new SwFrameChangesLeave( pFrame, nFunc, nAct, pPar ) ); break;
898         default: pImpl.reset( new SwImplEnterLeave( pFrame, nFunc, nAct, pPar ) ); break;
899     }
900     pImpl->Enter();
901 }
902 
903 /* This is not inline because we don't want the SwImplEnterLeave definition inside
904  * dbg_lay.hxx.
905  */
~SwEnterLeave()906 SwEnterLeave::~SwEnterLeave()
907 {
908     if (pImpl)
909         pImpl->Leave();
910 }
911 
Enter()912 void SwImplEnterLeave::Enter()
913 {
914     SwProtocol::Record(m_pFrame, m_nFunction, DbgAction::Start, m_pParam);
915 }
916 
Leave()917 void SwImplEnterLeave::Leave() {
918     SwProtocol::Record(m_pFrame, m_nFunction, DbgAction::End, m_pParam);
919 }
920 
Leave()921 void SwSizeEnterLeave::Leave()
922 {
923     m_nFrameHeight = m_pFrame->getFrameArea().Height() - m_nFrameHeight;
924     SwProtocol::Record(m_pFrame, m_nFunction, DbgAction::End, &m_nFrameHeight);
925 }
926 
Enter()927 void SwUpperEnterLeave::Enter()
928 {
929     m_nFrameId = m_pFrame->GetUpper() ? sal_uInt16(lcl_GetFrameId(m_pFrame->GetUpper())) : 0;
930     SwProtocol::Record(m_pFrame, m_nFunction, DbgAction::Start, &m_nFrameId);
931 }
932 
Leave()933 void SwUpperEnterLeave::Leave()
934 {
935     m_nFrameId = m_pFrame->GetUpper() ? sal_uInt16(lcl_GetFrameId(m_pFrame->GetUpper())) : 0;
936     SwProtocol::Record(m_pFrame, m_nFunction, DbgAction::End, &m_nFrameId);
937 }
938 
Enter()939 void SwFrameChangesLeave::Enter()
940 {
941 }
942 
Leave()943 void SwFrameChangesLeave::Leave()
944 {
945     if (m_pFrame->getFrameArea() != m_aFrame)
946         SwProtocol::Record(m_pFrame, PROT::FrmChanges, DbgAction::NONE, &m_aFrame);
947 }
948 
949 #endif // DBG_UTIL
950 
951 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
952