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