Esempio n. 1
0
wxArrayString
wxGridCellAutoWrapStringRenderer::GetTextLines(wxGrid& grid,
                                               wxDC& dc,
                                               const wxGridCellAttr& attr,
                                               const wxRect& rect,
                                               int row, int col)
{
    dc.SetFont(attr.GetFont());
    const wxCoord maxWidth = rect.GetWidth();

    // Transform logical lines into physical ones, wrapping the longer ones.
    const wxArrayString
        logicalLines = wxSplit(grid.GetCellValue(row, col), '\n', '\0');

    wxArrayString physicalLines;
    for ( wxArrayString::const_iterator it = logicalLines.begin();
          it != logicalLines.end();
          ++it )
    {
        const wxString& line = *it;

        if ( dc.GetTextExtent(line).x > maxWidth )
        {
            // Line does not fit, break it up.
            BreakLine(dc, line, maxWidth, physicalLines);
        }
        else // The entire line fits as is
        {
            physicalLines.push_back(line);
        }
    }

    return physicalLines;
}
Esempio n. 2
0
nsresult
InternetCiter::Rewrap(const nsAString& aInString,
                      uint32_t aWrapCol,
                      uint32_t aFirstLineOffset,
                      bool aRespectNewlines,
                      nsAString& aOutString)
{
  // There shouldn't be returns in this string, only dom newlines.
  // Check to make sure:
#ifdef DEBUG
  int32_t cr = aInString.FindChar(char16_t('\r'));
  NS_ASSERTION((cr < 0), "Rewrap: CR in string gotten from DOM!\n");
#endif /* DEBUG */

  aOutString.Truncate();

  nsresult rv;

  RefPtr<mozilla::intl::LineBreaker> lineBreaker =
    mozilla::intl::LineBreaker::Create();
  MOZ_ASSERT(lineBreaker);

  // Loop over lines in the input string, rewrapping each one.
  uint32_t length;
  uint32_t posInString = 0;
  uint32_t outStringCol = 0;
  uint32_t citeLevel = 0;
  const nsPromiseFlatString &tString = PromiseFlatString(aInString);
  length = tString.Length();
  while (posInString < length) {
    // Get the new cite level here since we're at the beginning of a line
    uint32_t newCiteLevel = 0;
    while (posInString < length && tString[posInString] == gt) {
      ++newCiteLevel;
      ++posInString;
      while (posInString < length && tString[posInString] == space) {
        ++posInString;
      }
    }
    if (posInString >= length) {
      break;
    }

    // Special case: if this is a blank line, maintain a blank line
    // (retain the original paragraph breaks)
    if (tString[posInString] == nl && !aOutString.IsEmpty()) {
      if (aOutString.Last() != nl) {
        aOutString.Append(nl);
      }
      AddCite(aOutString, newCiteLevel);
      aOutString.Append(nl);

      ++posInString;
      outStringCol = 0;
      continue;
    }

    // If the cite level has changed, then start a new line with the
    // new cite level (but if we're at the beginning of the string,
    // don't bother).
    if (newCiteLevel != citeLevel && posInString > newCiteLevel+1 &&
        outStringCol) {
      BreakLine(aOutString, outStringCol, 0);
    }
    citeLevel = newCiteLevel;

    // Prepend the quote level to the out string if appropriate
    if (!outStringCol) {
      AddCite(aOutString, citeLevel);
      outStringCol = citeLevel + (citeLevel ? 1 : 0);
    }
    // If it's not a cite, and we're not at the beginning of a line in
    // the output string, add a space to separate new text from the
    // previous text.
    else if (outStringCol > citeLevel) {
      aOutString.Append(space);
      ++outStringCol;
    }

    // find the next newline -- don't want to go farther than that
    int32_t nextNewline = tString.FindChar(nl, posInString);
    if (nextNewline < 0) {
      nextNewline = length;
    }

    // For now, don't wrap unquoted lines at all.
    // This is because the plaintext edit window has already wrapped them
    // by the time we get them for rewrap, yet when we call the line
    // breaker, it will refuse to break backwards, and we'll end up
    // with a line that's too long and gets displayed as a lone word
    // on a line by itself.  Need special logic to detect this case
    // and break it ourselves without resorting to the line breaker.
    if (!citeLevel) {
      aOutString.Append(Substring(tString, posInString,
                                  nextNewline-posInString));
      outStringCol += nextNewline - posInString;
      if (nextNewline != (int32_t)length) {
        aOutString.Append(nl);
        outStringCol = 0;
      }
      posInString = nextNewline+1;
      continue;
    }

    // Otherwise we have to use the line breaker and loop
    // over this line of the input string to get all of it:
    while ((int32_t)posInString < nextNewline) {
      // Skip over initial spaces:
      while ((int32_t)posInString < nextNewline &&
             nsCRT::IsAsciiSpace(tString[posInString])) {
        ++posInString;
      }

      // If this is a short line, just append it and continue:
      if (outStringCol + nextNewline - posInString <= aWrapCol-citeLevel-1) {
        // If this short line is the final one in the in string,
        // then we need to include the final newline, if any:
        if (nextNewline+1 == (int32_t)length && tString[nextNewline-1] == nl) {
          ++nextNewline;
        }
        // Trim trailing spaces:
        int32_t lastRealChar = nextNewline;
        while ((uint32_t)lastRealChar > posInString &&
               nsCRT::IsAsciiSpace(tString[lastRealChar-1])) {
          --lastRealChar;
        }

        aOutString += Substring(tString,
                                posInString, lastRealChar - posInString);
        outStringCol += lastRealChar - posInString;
        posInString = nextNewline + 1;
        continue;
      }

      int32_t eol = posInString + aWrapCol - citeLevel - outStringCol;
      // eol is the prospective end of line.
      // We'll first look backwards from there for a place to break.
      // If it's already less than our current position,
      // then our line is already too long, so break now.
      if (eol <= (int32_t)posInString) {
        BreakLine(aOutString, outStringCol, citeLevel);
        continue;    // continue inner loop, with outStringCol now at bol
      }

      int32_t breakPt = 0;
      // XXX Why this uses NS_ERROR_"BASE"?
      rv = NS_ERROR_BASE;
      if (lineBreaker) {
        breakPt = lineBreaker->Prev(tString.get() + posInString,
                                 length - posInString, eol + 1 - posInString);
        if (breakPt == NS_LINEBREAKER_NEED_MORE_TEXT) {
          // if we couldn't find a breakpoint looking backwards,
          // and we're not starting a new line, then end this line
          // and loop around again:
          if (outStringCol > citeLevel + 1) {
            BreakLine(aOutString, outStringCol, citeLevel);
            continue;    // continue inner loop, with outStringCol now at bol
          }

          // Else try looking forwards:
          breakPt = lineBreaker->Next(tString.get() + posInString,
                                      length - posInString, eol - posInString);

          rv = breakPt == NS_LINEBREAKER_NEED_MORE_TEXT ? NS_ERROR_BASE :
                                                          NS_OK;
        } else {
          rv = NS_OK;
        }
      }
      // If rv is okay, then breakPt is the place to break.
      // If we get out here and rv is set, something went wrong with line
      // breaker.  Just break the line, hard.
      if (NS_FAILED(rv)) {
        breakPt = eol;
      }

      // Special case: maybe we should have wrapped last time.
      // If the first breakpoint here makes the current line too long,
      // then if we already have text on the current line,
      // break and loop around again.
      // If we're at the beginning of the current line, though,
      // don't force a break since the long word might be a url
      // and breaking it would make it unclickable on the other end.
      const int SLOP = 6;
      if (outStringCol + breakPt > aWrapCol + SLOP &&
          outStringCol > citeLevel+1) {
        BreakLine(aOutString, outStringCol, citeLevel);
        continue;
      }

      nsAutoString sub (Substring(tString, posInString, breakPt));
      // skip newlines or whitespace at the end of the string
      int32_t subend = sub.Length();
      while (subend > 0 && IsSpace(sub[subend-1])) {
        --subend;
      }
      sub.Left(sub, subend);
      aOutString += sub;
      outStringCol += sub.Length();
      // Advance past the whitespace which caused the wrap:
      posInString += breakPt;
      while (posInString < length && IsSpace(tString[posInString])) {
        ++posInString;
      }

      // Add a newline and the quote level to the out string
      if (posInString < length) {  // not for the last line, though
        BreakLine(aOutString, outStringCol, citeLevel);
      }
    } // end inner loop within one line of aInString
  } // end outer loop over lines of aInString

  return NS_OK;
}
Esempio n. 3
0
unsigned int FixedLine::SetLine(unsigned int startpos, unsigned int endpos, bool cache) {
	m_docLen = m_doc.GetLength();

#ifdef __WXDEBUG__
	wxASSERT(!m_inSetLine); // re-entrancy check
	m_inSetLine = true;

	wxASSERT(startpos >= 0 && startpos < m_docLen);
	wxASSERT(endpos > startpos && endpos <= m_docLen);

	if (startpos > 0) {
		wxChar c;
		cxLOCKDOC_READ(m_doc)
			c = doc.GetChar(startpos - 1);
		cxENDLOCK
		wxASSERT(c == wxT('\n'));
	}
	if (endpos < m_docLen) {
		wxChar c;
		cxLOCKDOC_READ(m_doc)
			c = doc.GetChar(endpos - 1);
		cxENDLOCK
		wxASSERT(c == wxT('\n'));
	}
#endif

	if (!cache || startpos != textstart || endpos != textend) {
		textstart = startpos;
		textend = endpos;
		m_lineLen = endpos - startpos;

		// Check if we need to resize line buffer
		if (m_lineBufferLen < m_lineLen) {
			m_lineBufferLen = m_lineLen;
			m_lineBuffer = wxCharBuffer(m_lineBufferLen); // wxCharBuffer allocs room for & adds nullbyte at len
		}

		// Cache the current line in lineBuffer (as UTF-8)
		cxLOCKDOC_READ(m_doc)
			doc.GetTextPart(textstart, textend, (unsigned char*)m_lineBuffer.data());
		cxENDLOCK

		// Style the lines
		m_sr.Init(textstart, textend);
		for (vector<Styler*>::iterator si = m_stylers.begin(); si != m_stylers.end(); ++si) {
			(*si)->Style(m_sr);
		}

		m_extents.clear();
		m_extents.reserve(m_lineLen);
		unsigned int xpos = 0;
		unsigned int lastpos = 0;
		unsigned int style_start = 0;
		int fontStyle = m_sr.GetFontStyle(0);
		char* dbi = m_lineBuffer.data();

		// Build list of text extends (totalling) compensating for tabs and styles
		// There is one extent per byte in the text. In utf8 chars composed out of multiple
		// bytes, they will all have same value.
		for (unsigned int style_id = 0; style_id < m_sr.GetStyleCount(); ++style_id) {
			const unsigned int style_end = m_sr.GetStyleEnd(style_id) - textstart;

			// Ignore zero length styles
			if (style_start == style_end) continue;

			// Check for style change
			if (m_sr.GetFontStyle(style_id) != fontStyle) {
				if (style_start > lastpos) {
					m_sr.ApplyFontStyle(fontStyle);

					// Get extends for current segment
					m_extsBuf.clear();
					const unsigned int seg_len = style_start-lastpos;
					const wxString text(dbi+lastpos, wxConvUTF8, seg_len);
					dc.GetPartialTextExtents(text, m_extsBuf);
					wxASSERT(!text.empty() && m_extsBuf.size() == text.size());

					// Add to main list adjusted for offset
					unsigned int extpos = 0;
					unsigned int offset = xpos;
					for (unsigned int i = lastpos; i < style_start; ++i) {
						if ((dbi[i] & 0xC0) == 0x80) m_extents.push_back(xpos); // Only count first byte of UTF-8 multibyte chars
						else if (dbi[i] == '\t') {
							// Add tab extend
							xpos = ((xpos / tabwidth)+1) * tabwidth; // GetTabPoint(xpos);
							offset += xpos - (m_extsBuf[extpos] + offset);
							m_extents.push_back(xpos);
							++extpos;
						}
						else {
							xpos = m_extsBuf[extpos] + offset;
							m_extents.push_back(xpos);
							++extpos;
						}
					}

					// Small hack to make lines that end with italics not cut off the edge of the last character
					if (fontStyle &= wxFONTFLAG_ITALIC) {
						m_extents.back() += 2;
						xpos = m_extents.back();
					}
				}

				lastpos = style_start;
				fontStyle = m_sr.GetFontStyle(style_id);
			}

			style_start = style_end;
		}

		// Add extends for last segment
		if (lastpos < m_lineLen) {
			m_sr.ApplyFontStyle(fontStyle);

			// Get extends for current segment
			m_extsBuf.clear();
			const unsigned int seg_len = m_lineLen-lastpos;
			const wxString text(dbi+lastpos, wxConvUTF8, seg_len);
			dc.GetPartialTextExtents(text, m_extsBuf);
			wxASSERT(!text.empty() && m_extsBuf.size() == text.size());

			// Add to main list adjusted for offset
			unsigned int extpos = 0;
			unsigned int offset = xpos;
			for (unsigned int i = lastpos; i < m_lineLen; ++i) {
				if ((dbi[i] & 0xC0) == 0x80) m_extents.push_back(xpos); // Only count first byte of UTF-8 multibyte chars
				else if (dbi[i] == '\t') {
					// Add tab extend
					xpos = ((xpos / tabwidth)+1) * tabwidth; // GetTabPoint(xpos);
					offset += xpos - (m_extsBuf[extpos] + offset);
					m_extents.push_back(xpos);
					++extpos;
				}
				else {
					xpos = m_extsBuf[extpos] + offset;
					m_extents.push_back(xpos);
					++extpos;
				}
			}
		}

		wxASSERT(m_extents.size() == m_lineLen);
		BreakLine();
	}
	else if (width != old_width) {
		wxASSERT(m_extents.size() == m_lineLen);
		BreakLine();
	}

#ifdef __WXDEBUG__
	m_inSetLine = false; // re-entrancy check
#endif

	return (m_wrapMode == cxWRAP_NONE) ? m_lineWidth : height;
}
Esempio n. 4
0
int InsertFromFrame( CFrameNode *pFrameNode, SelectNode &sn, int fUpCalc, SelectInfo *psi )
{
	if( pFrameNode == NULL ) return -1;

	if( pFrameNode->GetChildCount() == 1 )
	{
		Q_ASSERT( pFrameNode->GetFirstChild()->GetType() == NODE_LINE );
		return ::InsertFromLine( (CLineNode*) pFrameNode->GetChild( 0 ), sn, fUpCalc, psi );
	}
	else
	{
		Q_ASSERT( pFrameNode->GetChildCount() > 0 );

		BreakLine( sn, 0 );

		CNode *pPrev = sn.GetNode()->GetPrev();
		CNode *pNext = sn.GetNode();
		Q_ASSERT( pPrev );
		Q_ASSERT( pNext );

		CParentNode *pParentNode = sn.GetNode()->GetParent();

		int fRecalculateFull = (pFrameNode->GetLevel() != pParentNode->GetLevel());
		
		QVector<CNode*> children;
		pFrameNode->RecalculateSizeFull();
		pFrameNode->RemoveOrCopyChildren( children );

		if( psi )
		{
			psi->GetBegin().SetNode( children[ 0 ] );
			psi->GetBegin().SetPosition( 0 );
			psi->GetEnd() = sn;
		}

		pParentNode->InsertChildren( children, 
			pParentNode->GetChildIndex( sn.GetNode() ) );

		if( pNext->GetPrev() == pPrev )
		{
			Q_ASSERT( 0 );
		}
		else
		{
			if( GlueLine( sn, pNext->GetPrev(), 0, NULL ) == 0 )
				if( psi ) 
					psi->GetEnd() = sn;
			if( GlueLine( sn, pPrev, 0, NULL ) == 0 )
				if( psi ) 
					psi->GetBegin() = sn;
		}

		if( fRecalculateFull )
			pParentNode->RecalculateSizeFull();
		else
			pParentNode->RecalculateSize( 0 );

		if( pParentNode->GetParent() )
			pParentNode->GetParent()->RecalculateSize( fUpCalc );
		return 0;
	}
}
Esempio n. 5
0
CPlaneText* InsertPlaneText( 
	SelectNode &sn, QString &strText, 
	long style, RealFde kegl, CNodeInfo& ni, CNodeExInfo& ex_ni )
{
	if( ni.isUnicode() )
	{
		if( sn.GetNode()->GetType() == NODE_PLANETEXT ) 
			BreakPlaneText( sn, 0 );

		Q_ASSERT( sn.GetNode()->GetType() == NODE_LINE );
		CParentNode *pParentNode = sn.GetNode()->GetParent();

		CNode *pPrev, *pNext;
		GetPrevNextInLine( sn, pPrev, pNext );

		long nLen;
		QString str;
		CPlaneText *pPlaneText = NULL;
		while( (nLen = strText.length()) )
		{
			long nPos = strText.indexOf("\r\n");
			if( nPos == -1 )
			{
				str = strText.mid(0, nLen);
				strText.clear();
			}
			else
			{
				str = strText.mid(0, nPos + 2);
				strText.remove(0, nPos);
			}

			pPlaneText = InsertPlaneText_Internal( sn, str, style, kegl, ni, ex_ni );

			if( nPos != -1 ) 
				BreakLine( sn, 0 );
		}

		pPlaneText->GetParent()->RecalculateSize( 0 );

		if( pPrev ) GlueText( sn, pPrev, 0 );
		if( pNext ) GlueText( sn, pNext->GetPrev(), 0 );

		pParentNode->RecalculateSize( 1 );

		return pPlaneText;
	}
	else
	{
		if( sn.GetNode()->GetType() == NODE_PLANETEXT ) 
			BreakPlaneText( sn, 0 );

		Q_ASSERT( sn.GetNode()->GetType() == NODE_LINE );
		CParentNode *pParentNode = sn.GetNode()->GetParent();

		CNode *pPrev, *pNext;
		GetPrevNextInLine( sn, pPrev, pNext );

		//
		long i, cb;
		long nLen;
		QString str;
		QVector<CNode*> texts, lines;

		while( (nLen = strText.length()) )
		{
			long nPos = strText.indexOf( "\r\n" );
			if( nPos == -1 )
			{
				str = strText.mid(0, nLen);
				strText.clear();
			}
			else
			{
				str = strText.mid(0, nPos);
				strText.remove( 0, nPos + 2 );
			}
			lines.push_back( sn.GetNode()->GetLineNode() );

			char ch = 0;
			LFInsertStyle style2analyse;
			long nNewStyle = -1;
			for( cb = 0; ; )
			{
				ch = style2analyse.Analisys( str.midRef(cb) );
				if( ch == 0 ) break;
				if( style == -1 )
					nNewStyle = ::getCurrentFormulatorStyle().getStyleByCharOnly( ch );
				else
					nNewStyle = style;

				QString sV( style2analyse.getText() );
				texts.push_back( InsertPlaneText_Internal( sn, sV, nNewStyle, kegl, ni, ex_ni, 0 ) );

				cb += style2analyse.GetRead();
				style2analyse.reset();
			}

			if( nPos != -1 ) 
				BreakLine( sn, 0 );
		}

		for( i = 0; i < (long) texts.size(); i++ )
		{
			Q_ASSERT( texts[ i ] != NULL );
			texts[ i ]->RecalculateSize( 0 );
		}

		for( i = 0; i < (long) lines.size(); i++ )
		{
			Q_ASSERT( lines[ i ] != NULL );
			lines[ i ]->RecalculateSize( 0 );
		}

		if( pPrev ) GlueText( sn, pPrev, 0 );
		if( pNext ) GlueText( sn, pNext->GetPrev(), 0 );

		pParentNode->RecalculateSize( 1 );

		return (CPlaneText*) (texts.size() ? texts[ texts.size() - 1 ] : NULL);
	}
}
Esempio n. 6
0
NS_IMETHODIMP
nsInternetCiter::Rewrap(const nsAString& aInString,
                        PRUint32 aWrapCol, PRUint32 aFirstLineOffset,
                        PRBool aRespectNewlines,
                        nsAString& aOutString)
{
  // There shouldn't be returns in this string, only dom newlines.
  // Check to make sure:
#ifdef DEBUG
  PRInt32 cr = aInString.FindChar(PRUnichar('\r'));
  NS_ASSERTION((cr < 0), "Rewrap: CR in string gotten from DOM!\n");
#endif /* DEBUG */

  aOutString.Truncate();

  nsCOMPtr<nsILineBreaker> lineBreaker;
  nsILineBreakerFactory *lf;
  nsresult rv;
  rv = CallGetService(NS_LWBRK_CONTRACTID, &lf);
  if (NS_SUCCEEDED(rv))
  {
    nsAutoString lbarg;
    lf->GetBreaker(lbarg, getter_AddRefs(lineBreaker));
    NS_RELEASE(lf);
  }

  // Loop over lines in the input string, rewrapping each one.
  PRUint32 length;
  PRUint32 posInString = 0;
  PRUint32 outStringCol = 0;
  PRUint32 citeLevel = 0;
  const nsPromiseFlatString &tString = PromiseFlatString(aInString);
  length = tString.Length();
#ifdef DEBUG_wrapping
  int loopcount = 0;
#endif
  while (posInString < length)
  {
#ifdef DEBUG_wrapping
    printf("Outer loop: '%s'\n",
           NS_LossyConvertUCS2toASCII(Substring(tString, posInString,
                                                length-posInString)).get());
    printf("out string is now: '%s'\n",
           NS_LossyConvertUCS2toASCII(aOutString).get());

#endif

    // Get the new cite level here since we're at the beginning of a line
    PRUint32 newCiteLevel = 0;
    while (posInString < length && tString[posInString] == gt)
    {
      ++newCiteLevel;
      ++posInString;
      while (posInString < length && tString[posInString] == space)
        ++posInString;
    }
    if (posInString >= length)
      break;

    // Special case: if this is a blank line, maintain a blank line
    // (retain the original paragraph breaks)
    if (tString[posInString] == nl && !aOutString.IsEmpty())
    {
      if (aOutString.Last() != nl)
        aOutString.Append(nl);
      AddCite(aOutString, newCiteLevel);
      aOutString.Append(nl);

      ++posInString;
      outStringCol = 0;
      continue;
    }

    // If the cite level has changed, then start a new line with the
    // new cite level (but if we're at the beginning of the string,
    // don't bother).
    if (newCiteLevel != citeLevel && posInString > newCiteLevel+1
        && outStringCol != 0)
    {
      BreakLine(aOutString, outStringCol, 0);
    }
    citeLevel = newCiteLevel;

    // Prepend the quote level to the out string if appropriate
    if (outStringCol == 0)
    {
      AddCite(aOutString, citeLevel);
      outStringCol = citeLevel + (citeLevel ? 1 : 0);
    }
    // If it's not a cite, and we're not at the beginning of a line in
    // the output string, add a space to separate new text from the
    // previous text.
    else if (outStringCol > citeLevel)
    {
      aOutString.Append(space);
      ++outStringCol;
    }

    // find the next newline -- don't want to go farther than that
    PRInt32 nextNewline = tString.FindChar(nl, posInString);
    if (nextNewline < 0) nextNewline = length;

    // For now, don't wrap unquoted lines at all.
    // This is because the plaintext edit window has already wrapped them
    // by the time we get them for rewrap, yet when we call the line
    // breaker, it will refuse to break backwards, and we'll end up
    // with a line that's too long and gets displayed as a lone word
    // on a line by itself.  Need special logic to detect this case
    // and break it ourselves without resorting to the line breaker.
    if (citeLevel == 0)
    {
      aOutString.Append(Substring(tString, posInString,
                                  nextNewline-posInString));
      outStringCol += nextNewline - posInString;
      if (nextNewline != (PRInt32)length)
      {
        aOutString.Append(nl);
        outStringCol = 0;
      }
      posInString = nextNewline+1;
      continue;
    }

    // Otherwise we have to use the line breaker and loop
    // over this line of the input string to get all of it:
    while ((PRInt32)posInString < nextNewline)
    {
#ifdef DEBUG_wrapping
      if (++loopcount > 1000)
        NS_ASSERTION(PR_FALSE, "possible infinite loop in nsInternetCiter\n");

      printf("Inner loop: '%s'\n",
             NS_LossyConvertUCS2toASCII(Substring(tString, posInString,
                                              nextNewline-posInString)).get());
#endif

      // Skip over initial spaces:
      while ((PRInt32)posInString < nextNewline
             && nsCRT::IsAsciiSpace(tString[posInString]))
        ++posInString;

      // If this is a short line, just append it and continue:
      if (outStringCol + nextNewline - posInString <= aWrapCol-citeLevel-1)
      {
        // If this short line is the final one in the in string,
        // then we need to include the final newline, if any:
        if (nextNewline+1 == (PRInt32)length && tString[nextNewline-1] == nl)
          ++nextNewline;

        // Trim trailing spaces:
        PRInt32 lastRealChar = nextNewline;
        while ((PRUint32)lastRealChar > posInString
               && nsCRT::IsAsciiSpace(tString[lastRealChar-1]))
          --lastRealChar;

        aOutString += Substring(tString,
                                posInString, lastRealChar - posInString);
        outStringCol += lastRealChar - posInString;
        posInString = nextNewline + 1;
        continue;
      }

      PRInt32 eol = posInString + aWrapCol - citeLevel - outStringCol;
      // eol is the prospective end of line.
      // We'll first look backwards from there for a place to break.
      // If it's already less than our current position,
      // then our line is already too long, so break now.
      if (eol <= (PRInt32)posInString)
      {
        BreakLine(aOutString, outStringCol, citeLevel);
        continue;    // continue inner loop, with outStringCol now at bol
      }

      PRUint32 breakPt;
      rv = NS_ERROR_BASE;
      if (lineBreaker)
      {
        PRBool needMore;
        rv = lineBreaker->Prev(tString.get() + posInString,
                               length - posInString,
                               eol + 1 - posInString, &breakPt, &needMore);
        if (NS_FAILED(rv) || needMore)
        {
          // if we couldn't find a breakpoint looking backwards,
          // and we're not starting a new line, then end this line
          // and loop around again:
          if (outStringCol > citeLevel + 1)
          {
            BreakLine(aOutString, outStringCol, citeLevel);
            continue;    // continue inner loop, with outStringCol now at bol
          }

          // Else try looking forwards:
          rv = lineBreaker->Next(tString.get() + posInString,
                                 length - posInString,
                                 eol - posInString, &breakPt, &needMore);
          if (needMore) rv = NS_ERROR_BASE;
        }
      }
      // If rv is okay, then breakPt is the place to break.
      // If we get out here and rv is set, something went wrong with line
      // breaker.  Just break the line, hard.
      if (NS_FAILED(rv))
      {
#ifdef DEBUG_akkana
        printf("nsInternetCiter: LineBreaker not working -- breaking hard\n");
#endif
        breakPt = eol;
      }

      // Special case: maybe we should have wrapped last time.
      // If the first breakpoint here makes the current line too long,
      // then if we already have text on the current line,
      // break and loop around again.
      // If we're at the beginning of the current line, though,
      // don't force a break since the long word might be a url
      // and breaking it would make it unclickable on the other end.
      const int SLOP = 6;
      if (outStringCol + breakPt > aWrapCol + SLOP
          && outStringCol > citeLevel+1)
      {
        BreakLine(aOutString, outStringCol, citeLevel);
        continue;
      }

      nsAutoString sub (Substring(tString, posInString, breakPt));
      // skip newlines or whitespace at the end of the string
      PRInt32 subend = sub.Length();
      while (subend > 0 && IsSpace(sub[subend-1]))
        --subend;
      sub.Left(sub, subend);
      aOutString += sub;
      outStringCol += sub.Length();
      // Advance past the whitespace which caused the wrap:
      posInString += breakPt;
      while (posInString < length && IsSpace(tString[posInString]))
        ++posInString;

      // Add a newline and the quote level to the out string
      if (posInString < length)    // not for the last line, though
        BreakLine(aOutString, outStringCol, citeLevel);

    } // end inner loop within one line of aInString
#ifdef DEBUG_wrapping
    printf("---------\nEnd inner loop: out string is now '%s'\n-----------\n",
           NS_LossyConvertUCS2toASCII(aOutString).get());
#endif
  } // end outer loop over lines of aInString

#ifdef DEBUG_wrapping
  printf("Final out string is now: '%s'\n",
         NS_LossyConvertUCS2toASCII(aOutString).get());

#endif
  return NS_OK;
}