View Issue Details

IDProjectCategoryView StatusLast Update
0010227ScribusStory Editor / Text Framespublic2017-10-26 07:08
Reportermecirt Assigned Tojghali  
PrioritynormalSeverityfeatureReproducibilityN/A
Status closedResolutionfixed 
Product Version1.5.0svn 
Fixed in Version1.5.0svn 
Summary0010227: Text flow implementation (widow/orphan control and more) - patch attached
DescriptionThe attached patch implements several paragraph text flow options. Specifically, these include the option not to leave isolated parts of paragraphs at the end/beginning of a line/column (known as widow/orphan control), option to never split a paragraph to multiple pages (it will move entirely if needed), and an option to keep a paragraph with the next one (useful for headings and such). These options are very important for typesetting of longer texts (books and such).

The patch implements the feature itself (hooked to the textitem layouting code), adds the necessary controls to the text property widget and the paragraph style dialog, and also supports loading/saving of the new options (1.5 format only). Additionally, it includes some cleanup of the text layouting code, which I did to help me understand how the whole thing works. It also fixes a problem with automatic tab breaks retaining the fill character, if any, which I have randomly noticed.

Known limitation: the option to ensure that isolated lines won't remain at the start of a new *column* currently doesn't work (it works fine for pages, or if the column break is also a page break). The layout code was making my head hurt enough by that point, so I left this alone for now.

The resulting patch is more than 100 KB, but most of it are context lines and some XML reformatting that Qt Designer did, so it's not as bad as it looks! :)
Tagstext frames, typography
Patch

Activities

mecirt

2011-08-28 18:22

reporter  

scribus-textflow.patch (108,064 bytes)   
Index: scribus/pdflib_core.cpp
===================================================================
--- scribus/pdflib_core.cpp	(revision 16811)
+++ scribus/pdflib_core.cpp	(working copy)
@@ -4811,7 +4811,7 @@
 				const QChar ch = hl->ch;
 				const CharStyle& chstyle(ite->itemText.charStyle(d));
 				const ParagraphStyle& pstyle(ite->itemText.paragraphStyle(d));
-				if ((ch == SpecialChars::PARSEP) || (ch == QChar(10)) || (ch == SpecialChars::LINEBREAK) || (ch == SpecialChars::FRAMEBREAK) || (ch == SpecialChars::COLBREAK))
+				if (SpecialChars::isBreak(ch, true) || (ch == QChar(10)))
 					continue;
 				if (chstyle.effects() & ScStyle_SuppressSpace)
 					continue;
@@ -4921,7 +4921,7 @@
 			const QChar ch = hl->ch;
 			const CharStyle& chstyle(ite->itemText.charStyle(d));
 			const ParagraphStyle& pstyle(ite->itemText.paragraphStyle(d));
-			if ((ch == SpecialChars::PARSEP) || (ch == QChar(10)) || (ch == SpecialChars::LINEBREAK) || (ch == SpecialChars::FRAMEBREAK) || (ch == SpecialChars::COLBREAK))
+			if (SpecialChars::isBreak(ch, true) || (ch == QChar(10)))
 				continue;
 			if (chstyle.effects() & ScStyle_SuppressSpace)
 				continue;
Index: scribus/pageitem_textframe.cpp
===================================================================
--- scribus/pageitem_textframe.cpp	(revision 16811)
+++ scribus/pageitem_textframe.cpp	(working copy)
@@ -557,16 +557,17 @@
 	double getLineAscent(const StoryText& itemText)
 	{
 		double result = 0;
-		QChar  firstChar = itemText.text(line.firstItem);
+		QChar firstChar = itemText.text (line.firstItem);
+		if ((firstChar == SpecialChars::PAGENUMBER) || (firstChar == SpecialChars::PAGECOUNT))
+			firstChar = '8';
+		PageItem *obj = itemText.object (line.firstItem);
 		const CharStyle& fcStyle(itemText.charStyle(line.firstItem));
-		if ((itemText.text(line.firstItem) == SpecialChars::PARSEP) || (itemText.text(line.firstItem) == SpecialChars::LINEBREAK))
+		if ((firstChar == SpecialChars::PARSEP) || (firstChar == SpecialChars::LINEBREAK))
 			result = fcStyle.font().ascent(fcStyle.fontSize() / 10.0);
-		else if (itemText.object(line.firstItem) != 0)
-			result = qMax(result, (itemText.object(line.firstItem)->gHeight + itemText.object(line.firstItem)->lineWidth()) * (fcStyle.scaleV() / 1000.0));
-		else if ((firstChar == SpecialChars::PAGENUMBER) || (firstChar == SpecialChars::PAGECOUNT))
-			result = fcStyle.font().realCharAscent('8', fcStyle.fontSize() / 10.0);
-		else //if (itemText.charStyle(current.line.firstItem).effects() & ScStyle_DropCap == 0)
-			result = fcStyle.font().realCharAscent(itemText.text(line.firstItem), fcStyle.fontSize() / 10.0);
+		else if (obj)
+			result = qMax(result, (obj->gHeight + obj->lineWidth()) * (fcStyle.scaleV() / 1000.0));
+		else
+			result = fcStyle.font().realCharAscent(firstChar, fcStyle.fontSize() / 10.0);
 		for (int zc = 0; zc < itemsInLine; ++zc)
 		{
 			QChar ch = itemText.text(line.firstItem + zc);
@@ -574,14 +575,13 @@
 				ch = '8'; // should have highest ascender even in oldstyle
 			const CharStyle& cStyle(itemText.charStyle(line.firstItem + zc));
 			if ((ch == SpecialChars::TAB) || (ch == QChar(10))
-				|| (ch == SpecialChars::PARSEP) || (ch == SpecialChars::NBHYPHEN)
-				|| (ch == SpecialChars::COLBREAK) || (ch == SpecialChars::LINEBREAK)
-				|| (ch == SpecialChars::FRAMEBREAK) || (ch.isSpace()))
+				|| SpecialChars::isBreak (ch, true) || (ch == SpecialChars::NBHYPHEN) || (ch.isSpace()))
 				continue;
 			double asce;
-			if (itemText.object(line.firstItem + zc) != 0)
-				asce = itemText.object(line.firstItem + zc)->gHeight + itemText.object(line.firstItem + zc)->lineWidth() * (cStyle.scaleV() / 1000.0);
-			else //if (itemText.charStyle(current.line.firstItem + zc).effects() & ScStyle_DropCap == 0)
+			PageItem *obj = itemText.object (line.firstItem + zc);
+			if (obj)
+				asce = obj->gHeight + obj->lineWidth() * (cStyle.scaleV() / 1000.0);
+			else
 				asce = cStyle.font().realCharAscent(ch, cStyle.fontSize() / 10.0);
 			//	qDebug() << QString("checking char 'x%2' with ascender %1 > %3").arg(asce).arg(ch.unicode()).arg(result);
 			result = qMax(result, asce);
@@ -593,14 +593,14 @@
 	{
 		double result = 0;
 		QChar  firstChar = itemText.text(line.firstItem);
+		if ((firstChar == SpecialChars::PAGENUMBER) || (firstChar == SpecialChars::PAGECOUNT))
+			firstChar = '8';
 		const CharStyle& fcStyle(itemText.charStyle(line.firstItem));
 		if ((firstChar == SpecialChars::PARSEP) || (firstChar == SpecialChars::LINEBREAK))
 			result = fcStyle.font().descent(fcStyle.fontSize() / 10.0);
-		else if (itemText.object(line.firstItem) != 0)
+		else if (itemText.object(line.firstItem))
 			result = 0.0;
-		else if ((firstChar == SpecialChars::PAGENUMBER) || (firstChar == SpecialChars::PAGECOUNT))
-			result = fcStyle.font().realCharDescent('8', fcStyle.fontSize() / 10.0);
-		else //if (itemText.charStyle(current.line.firstItem).effects() & ScStyle_DropCap == 0)
+		else
 			result = fcStyle.font().realCharDescent(firstChar, fcStyle.fontSize() / 10.0);
 		for (int zc = 0; zc < itemsInLine; ++zc)
 		{
@@ -609,14 +609,12 @@
 				ch = '8'; // should have highest ascender even in oldstyle
 			const CharStyle& cStyle(itemText.charStyle(line.firstItem + zc));
 			if ((ch == SpecialChars::TAB) || (ch == QChar(10))
-				|| (ch == SpecialChars::PARSEP) || (ch == SpecialChars::NBHYPHEN)
-				|| (ch == SpecialChars::COLBREAK) || (ch == SpecialChars::LINEBREAK)
-				|| (ch == SpecialChars::FRAMEBREAK) || (ch.isSpace()))
+				|| SpecialChars::isBreak (ch, true) || (ch == SpecialChars::NBHYPHEN) || (ch.isSpace()))
 				continue;
 			double desc;
-			if (itemText.object(line.firstItem + zc) != 0)
+			if (itemText.object(line.firstItem + zc))
 				desc = 0.0;
-			else //if (itemText.charStyle(current.line.firstItem + zc).effects() & ScStyle_DropCap == 0)
+			else
 				desc = cStyle.font().realCharDescent(ch, cStyle.fontSize() / 10.0);
 			//	qDebug() << QString("checking char 'x%2' with ascender %1 > %3").arg(asce).arg(ch.unicode()).arg(result);
 			result = qMax(result, desc);
@@ -627,23 +625,25 @@
 	double getLineHeight(const StoryText& itemText)
 	{
 		double result = 0;
-		if (itemText.object(line.firstItem) != 0)
-			result = qMax(result, (itemText.object(line.firstItem)->gHeight + itemText.object(line.firstItem)->lineWidth()) * (itemText.charStyle(line.firstItem).scaleV() / 1000.0));
-		else //if (itemText.charStyle(current.line.firstItem).effects() & ScStyle_DropCap == 0)
-			result = itemText.charStyle(line.firstItem).font().height(itemText.charStyle(line.firstItem).fontSize() / 10.0);
+		const CharStyle& firstStyle(itemText.charStyle(line.firstItem));
+		PageItem *obj = itemText.object (line.firstItem);
+		if (obj)
+			result = qMax(result, (obj->gHeight + obj->lineWidth()) * (firstStyle.scaleV() / 1000.0));
+		else
+			result = firstStyle.font().height(firstStyle.fontSize() / 10.0);
 		for (int zc = 0; zc < itemsInLine; ++zc)
 		{
 			QChar ch = itemText.text(line.firstItem+zc);
 			if ((ch == SpecialChars::TAB) || (ch == QChar(10))
-				|| (ch == SpecialChars::PARSEP) || (ch == SpecialChars::NBHYPHEN)
-				|| (ch == SpecialChars::COLBREAK) || (ch == SpecialChars::FRAMEBREAK)
-				|| (ch == SpecialChars::LINEBREAK) || (ch.isSpace()))
+				|| SpecialChars::isBreak (ch, true) || (ch == SpecialChars::NBHYPHEN) || (ch.isSpace()))
 				continue;
+			const CharStyle& cStyle(itemText.charStyle(line.firstItem + zc));
+			PageItem *obj = itemText.object (line.firstItem + zc);
 			double asce;
-			if (itemText.object(line.firstItem+zc) != 0)
-				asce = (itemText.object(line.firstItem+zc)->gHeight + itemText.object(line.firstItem+zc)->lineWidth()) * (itemText.charStyle(line.firstItem+zc).scaleV() / 1000.0);
-			else //if (itemText.charStyle(current.line.firstItem+zc).effects() & ScStyle_DropCap == 0)
-				asce = itemText.charStyle(line.firstItem+zc).font().height(itemText.charStyle(line.firstItem+zc).fontSize() / 10.0);
+			if (obj)
+				asce = (obj->gHeight + obj->lineWidth()) * (cStyle.scaleV() / 1000.0);
+			else
+				asce = cStyle.font().height (cStyle.fontSize() / 10.0);
 			//	qDebug() << QString("checking char 'x%2' with ascender %1 > %3").arg(asce).arg(ch.unicode()).arg(result);
 			result = qMax(result, asce);
 		}
@@ -659,9 +659,7 @@
 		{
 			QChar ch = itemText.text(line.firstItem+zc);
 			if ((ch == SpecialChars::TAB) || (ch == QChar(10))
-				|| (ch == SpecialChars::PARSEP) || (ch == SpecialChars::NBHYPHEN)
-				|| (ch == SpecialChars::COLBREAK) || (ch == SpecialChars::FRAMEBREAK)
-				|| (ch == SpecialChars::LINEBREAK) || (ch.isSpace()))
+				|| SpecialChars::isBreak (ch, true) || (ch == SpecialChars::NBHYPHEN) || (ch.isSpace()))
 				continue;
 			const CharStyle& cStyle(itemText.charStyle(line.firstItem + zc));
 			if (itemText.object(line.firstItem+zc) != 0)
@@ -903,10 +901,124 @@
 	return 0.0;
 }
 
+static double adjustToBaselineGrid (const LineControl &control, PageItem *item, int OwnPage)
+{
+	double by = item->yPos();
+	if (OwnPage != -1)
+		by = by - item->doc()->Pages->at(OwnPage)->yOffset();
+	int ol1 = qRound((by + control.yPos - item->doc()->guidesPrefs().offsetBaselineGrid) * 10000.0);
+	int ol2 = static_cast<int>(ol1 / item->doc()->guidesPrefs().valueBaselineGrid);
+//						qDebug() << QString("baseline adjust: y=%1->%2").arg(current.yPos).arg(ceil(  ol2 / 10000.0 ) * item->doc()->typographicSettings.valueBaselineGrid + item->doc()->typographicSettings.offsetBaselineGrid - by);
 
+	return ceil(  ol2 / 10000.0 ) * item->doc()->guidesPrefs().valueBaselineGrid + item->doc()->guidesPrefs().offsetBaselineGrid - by;
+}
 
+static double nextAutoTab (const LineControl &current, PageItem *item)
+{
+	double dtw = item->doc()->itemToolPrefs().textTabWidth;
+	if (current.xPos == current.colLeft)
+		return current.colLeft + dtw;
+	double res = current.colLeft + ceil ((current.xPos - current.colLeft) / dtw) * dtw;
+	if (res == current.xPos) res += dtw;
+	return res;
+}
 
 
+static double calculateLineSpacing (const ParagraphStyle &style, PageItem *item)
+{
+	if (style.lineSpacingMode() == ParagraphStyle::AutomaticLineSpacing)
+		return style.charStyle().font().height(style.charStyle().fontSize() / 10.0);
+	if (style.lineSpacingMode() == ParagraphStyle::BaselineGridLineSpacing)
+		return item->doc()->guidesPrefs().valueBaselineGrid;
+	return style.lineSpacing();
+}
+
+
+// This assumes that layout() ran on the previous page and set the incomplete* vars
+// It also clears the incomplete* vars, and changes the starting position for this frame
+// The incomplete* vars are used to ensure that we don't run into an endless loop
+// This setup won't cause any problems, as the only way for the starting position to change
+// back is if the previous frame's layout() runs again, but if it does, it also sets the
+// incomplete* vars for us
+bool PageItem_TextFrame::moveLinesFromPreviousFrame ()
+{
+	PageItem_TextFrame* prev = dynamic_cast<PageItem_TextFrame*>(BackBox);
+	if (!prev) return false;
+	if (!prev->incompleteLines) return false;   // no incomplete lines - nothing to do
+	int pos = itemText.lastInFrame();
+	QChar lastChar = itemText.text (pos);
+	// qDebug()<<"pos is"<<pos<<", length is"<<itemText.length()<<", incomplete is "<<prev->incompleteLines;
+	if ((pos != itemText.length()-1) && (!SpecialChars::isBreak (lastChar, true)))
+		return false;  // the paragraph isn't ending yet
+	uint lines = itemText.lines();  // lines added to the current frame
+
+	ParagraphStyle style = itemText.paragraphStyle (pos);
+	int need = style.keepLinesEnd () + 1;
+	int prevneed = style.keepLinesStart () + 1;
+	if (lines >= need) {
+		prev->incompleteLines = 0;   // so that further paragraphs don't pull anything
+		return false;   // we have enough lines
+	}
+
+	// too few lines - we need to pull some from the previous page
+	uint pull = need - lines;
+	// if pulling the lines would lead to the original frame having too few, pull the whole paragraph
+	if (prev->incompleteLines - pull < prevneed)
+		pull = prev->incompleteLines;
+	// qDebug()<<"pulling"<<pull<<"lines;
+	// Okay, move the starting/ending character
+	int startingPos = prev->incompletePositions[prev->incompleteLines - pull];
+	for (uint i = 0; i < pull; ++i)
+		prev->itemText.removeLastLine();
+	firstChar = prev->MaxChars = startingPos;
+	// keep the remaining incomplete lines flagged as such
+	// this ensures that if pulling one line won't be enough, the subsequent call to layout() will pull more
+	prev->incompleteLines -= pull;
+
+	return true;
+}
+
+// called at the end of a frame or column
+void PageItem_TextFrame::adjustParagraphEndings ()
+{
+	// More text to go - let's apply paragraph flowing options - orphans/widows, etc
+	int pos = itemText.lastInFrame();
+	if (pos >= itemText.length() - 1) return;
+
+	ParagraphStyle style = itemText.paragraphStyle (pos);
+	int paragraphStart = itemText.prevParagraph (pos) + 1;
+	QChar lastChar = itemText.text (pos);
+	bool keepWithNext = style.keepWithNext() && (lastChar == SpecialChars::PARSEP);
+	if (keepWithNext || (!SpecialChars::isBreak (lastChar, true))) {
+		// paragraph continues in the next frame, or needs to be kept with the next one
+		// check how many lines are in this frame
+		int lineStart = itemText.startOfLine (pos);
+		incompleteLines = 1;
+		incompletePositions.prepend (lineStart);
+		while (lineStart > paragraphStart) {
+			lineStart = itemText.startOfLine (lineStart - 1);
+			incompleteLines++;
+			incompletePositions.prepend (lineStart);
+		}
+		int need = style.keepLinesStart () + 1;
+		if (style.keepTogether()) need = incompleteLines;
+		int pull = 0;
+		if (style.keepTogether() || (incompleteLines < need)) pull = incompleteLines;
+		// if we need to keep it with the next one, pull one line. Next frame layouting
+		// will pull more from us if it proves necessary.
+		if (keepWithNext && (!pull)) pull = 1;
+
+		if (pull) {
+			// push this paragraph to the next frame
+			for (int i = 0; i < pull; ++i)
+				itemText.removeLastLine();
+			MaxChars = itemText.lastInFrame() + 1;
+			incompleteLines = 0;
+			incompletePositions.clear();
+		}
+	}
+}
+
 void PageItem_TextFrame::layout() 
 {
 // 	qDebug()<<"==Layout==" << itemName() ;
@@ -968,8 +1080,9 @@
 	int    DropLines = 0;
 	int    DropLinesCount = 0;
 	
-	
 	itemText.clearLines();
+	incompleteLines = 0;
+	incompletePositions.clear();
 
 	double lineCorr = 0;
 	if (lineColor() != CommonStrings::None)
@@ -1031,22 +1144,13 @@
 		{
 			hl = itemText.item(firstInFrame());
 			style = itemText.paragraphStyle(firstInFrame());
-			if (style.lineSpacingMode() == ParagraphStyle::AutomaticLineSpacing)
-			{
-				style.setLineSpacing(style.charStyle().font().height(style.charStyle().fontSize() / 10.0));
-//				qDebug() << QString("auto linespacing: %1").arg(style.lineSpacing());
-			}
-			else if (style.lineSpacingMode() == ParagraphStyle::BaselineGridLineSpacing)
-				style.setLineSpacing(m_Doc->guidesPrefs().valueBaselineGrid);
+			style.setLineSpacing (calculateLineSpacing (style, this));
 
 //			qDebug() << QString("style @0: %1 -- %2, %4/%5 char: %3").arg(style.leftMargin()).arg(style.rightMargin())
 //				   .arg(style.charStyle().asString()).arg(style.name()).arg(style.parent()?style.parent()->name():"");
 			if (style.hasDropCap())
 			{
-				if (style.lineSpacingMode() == ParagraphStyle::BaselineGridLineSpacing)
-					chs = qRound(m_Doc->guidesPrefs().valueBaselineGrid  * style.dropCapLines() * 10);
-				else
-					chs = qRound(style.lineSpacing() * style.dropCapLines() * 10);
+				chs = calculateLineSpacing (style, this) * style.dropCapLines() * 10;
 			}
 			else 
 				chs = hl->fontSize();
@@ -1087,12 +1191,7 @@
 			chstr = ExpandToken(a);
 			if (chstr.isEmpty())
 				chstr = SpecialChars::ZWNBSPACE;
-			if (style.lineSpacingMode() == ParagraphStyle::AutomaticLineSpacing)
-			{
-				style.setLineSpacing(style.charStyle().font().height(style.charStyle().fontSize() / 10.0));
-			}
-			else if (style.lineSpacingMode() == ParagraphStyle::BaselineGridLineSpacing)
-				style.setLineSpacing(m_Doc->guidesPrefs().valueBaselineGrid);
+			style.setLineSpacing (calculateLineSpacing (style, this));
 			// find out about par gap and dropcap
 			if (a == firstInFrame())
 			{
@@ -1161,15 +1260,7 @@
 					if (DropCmode)
 					{
 						DropLines = style.dropCapLines();
-						if (style.lineSpacingMode() == ParagraphStyle::BaselineGridLineSpacing)
-							DropCapDrop = m_Doc->guidesPrefs().valueBaselineGrid * (DropLines-1);
-						else
-						{
-							if (style.lineSpacingMode() == ParagraphStyle::FixedLineSpacing)
-								DropCapDrop = style.lineSpacing() * (DropLines-1);
-							else
-								DropCapDrop = charStyle.font().height(style.charStyle().fontSize() / 10.0) * (DropLines-1);
-						}
+						DropCapDrop = calculateLineSpacing (style, this) * (DropLines - 1);
 //						qDebug() << QString("dropcapdrop: y=%1+%2").arg(current.yPos).arg(DropCapDrop);
 						current.yPos += DropCapDrop;
 					}
@@ -1179,15 +1270,7 @@
 			if (DropCmode)
 			{
 				// dropcap active?
-				if (style.lineSpacingMode() == ParagraphStyle::BaselineGridLineSpacing)
-					DropCapDrop = m_Doc->guidesPrefs().valueBaselineGrid * (DropLines-1);
-				else
-				{
-					if (style.lineSpacingMode() == ParagraphStyle::FixedLineSpacing)
-						DropCapDrop = style.lineSpacing() * (DropLines-1);
-					else
-						DropCapDrop = charStyle.font().height(style.charStyle().fontSize() / 10.0) * (DropLines-1);
-				}
+				DropCapDrop = calculateLineSpacing (style, this) * (DropLines - 1);
 
 				// FIXME : we should ensure that fonts are loaded before calls to layout()
 				// ScFace::realCharHeight()/Ascent() ensure font is loaded thanks to an indirect call to char2CMap()
@@ -1199,25 +1282,8 @@
 					realCharHeight = charStyle.font().height(style.charStyle().fontSize() / 10.0);
 				if (realCharAscent == 0.0)
 					realCharAscent = fontAscent;
-				if (style.lineSpacingMode() == ParagraphStyle::BaselineGridLineSpacing)
-				{
-					chsd = (10 * ((m_Doc->guidesPrefs().valueBaselineGrid * (DropLines-1) + fontAscent) / realCharHeight));
-					chs  = (10 * ((m_Doc->guidesPrefs().valueBaselineGrid * (DropLines-1) + fontAscent) / realCharAscent));
-				}
-				else
-				{
-					if (style.lineSpacingMode() == ParagraphStyle::FixedLineSpacing)
-					{
-						chsd = (10 * ((style.lineSpacing() * (DropLines-1) + fontAscent) / realCharHeight));
-						chs  = (10 * ((style.lineSpacing() * (DropLines-1) + fontAscent) / realCharAscent));
-					}
-					else
-					{
-						double currasce = charStyle.font().height(style.charStyle().fontSize() / 10.0);
-						chsd = (10 * ((currasce * (DropLines-1) + fontAscent) / realCharHeight));
-						chs  = (10 * ((currasce * (DropLines-1) + fontAscent) / realCharAscent));
-					}
-				}
+				chsd = (10 * ((DropCapDrop + fontAscent) / realCharHeight));
+				chs  = (10 * ((DropCapDrop + fontAscent) / realCharAscent));
 				hl->setEffects(hl->effects() | ScStyle_DropCap);
 				hl->glyph.yoffset -= DropCapDrop;
 			}
@@ -1268,15 +1334,7 @@
 					if (itemHeight == 0)
 						itemHeight = charStyle.font().height(style.charStyle().fontSize() / 10.0);
 					wide = hl->embedded.getItem()->gWidth + hl->embedded.getItem()->lineWidth();
-					if (style.lineSpacingMode() == ParagraphStyle::BaselineGridLineSpacing)
-						asce = m_Doc->guidesPrefs().valueBaselineGrid * DropLines;
-					else
-					{
-						if (style.lineSpacingMode() == ParagraphStyle::FixedLineSpacing)
-							asce = style.lineSpacing() * DropLines;
-						else
-							asce = charStyle.font().height(style.charStyle().fontSize() / 10.0) * DropLines;
-					}
+					asce = calculateLineSpacing (style, this) * DropLines;
 					hl->glyph.scaleH /= hl->glyph.scaleV;
 					hl->glyph.scaleV = (asce / itemHeight);
 					hl->glyph.scaleH *= hl->glyph.scaleV;
@@ -1333,6 +1391,8 @@
 				{
 					// start next col
 					current.nextColumn(asce);
+					adjustParagraphEndings ();
+					a = itemText.lastInFrame() + 1;
 					if (((a > firstInFrame()) && (itemText.text(a-1) == SpecialChars::PARSEP)) 
 						|| ((a == firstInFrame()) && (BackBox == 0)))
 					{
@@ -1342,26 +1402,13 @@
 							DropCmode = false;
 						if (DropCmode)
 						{
-							if (style.lineSpacingMode() == ParagraphStyle::BaselineGridLineSpacing)
-								desc2 = -charStyle.font().descent() * m_Doc->guidesPrefs().valueBaselineGrid * style.dropCapLines();
-							else
-								desc2 = -charStyle.font().descent() * style.lineSpacing() * style.dropCapLines();
-						}
-						if (DropCmode)
+							desc2 = -charStyle.font().descent() * calculateLineSpacing (style, this) * style.dropCapLines();
 							DropLines = style.dropCapLines();
+						}
 					}
 //					qDebug() << QString("layout: nextcol grid=%1 x %2").arg(style.lineSpacingMode() == ParagraphStyle::BaselineGridLineSpacing).arg(m_Doc->typographicSettings.valueBaseGrid);
 					if (style.lineSpacingMode() == ParagraphStyle::BaselineGridLineSpacing)
-					{
-						double by = Ypos;
-						if (OwnPage != -1)
-							by = Ypos - m_Doc->Pages->at(OwnPage)->yOffset();
-						int ol1 = qRound((by + current.yPos - m_Doc->guidesPrefs().offsetBaselineGrid) * 10000.0);
-						int ol2 = static_cast<int>(ol1 / m_Doc->guidesPrefs().valueBaselineGrid);
-//						qDebug() << QString("baseline adjust: y=%1->%2").arg(current.yPos).arg(ceil(  ol2 / 10000.0 ) * m_Doc->typographicSettings.valueBaselineGrid + m_Doc->typographicSettings.offsetBaselineGrid - by);
-
-						current.yPos = ceil(  ol2 / 10000.0 ) * m_Doc->guidesPrefs().valueBaselineGrid + m_Doc->guidesPrefs().offsetBaselineGrid - by;
-					}
+						current.yPos = adjustToBaselineGrid (current, this, OwnPage);
 				}
 				else
 				{
@@ -1387,16 +1434,7 @@
 				}
 //				qDebug() << QString("layout: nextline grid=%1 x %2").arg(style.lineSpacingMode() == ParagraphStyle::BaselineGridLineSpacing).arg(m_Doc->typographicSettings.valueBaselineGrid);
 				if (style.lineSpacingMode() == ParagraphStyle::BaselineGridLineSpacing)
-				{
-					double by = Ypos;
-					if (OwnPage != -1)
-						by = Ypos - m_Doc->Pages->at(OwnPage)->yOffset();
-					int ol1 = qRound((by + current.yPos - m_Doc->guidesPrefs().offsetBaselineGrid) * 10000.0);
-					int ol2 = static_cast<int>(ol1 / m_Doc->guidesPrefs().valueBaselineGrid);
-//					qDebug() << QString("useBaselIneGrid: %1 * %2 + %3 - %4").arg(ol2 / 10000.0).arg(m_Doc->typographicSettings.valueBaselineGrid).arg(m_Doc->typographicSettings.offsetBaselineGrid).arg(by);
-//					qDebug() << QString("baseline adjust: y=%1->%2").arg(current.yPos).arg(ceil(  ol2 / 10000.0 ) * m_Doc->typographicSettings.valueBaselineGrid + m_Doc->typographicSettings.offsetBaselineGrid - by);
-					current.yPos = ceil(  ol2 / 10000.0 ) * m_Doc->guidesPrefs().valueBaselineGrid + m_Doc->guidesPrefs().offsetBaselineGrid - by;
-				}
+					current.yPos = adjustToBaselineGrid (current, this, OwnPage);
 				/* this causes different spacing for first line:
 				if (current.yPos-TopOffset < 0.0)
 				{
@@ -1432,15 +1470,7 @@
 							current.yPos += qMax(style.lineSpacing(), 1.0);
 //						qDebug() << QString("layout: next lower line grid=%1 x %2").arg(style.lineSpacingMode() == ParagraphStyle::BaselineGridLineSpacing).arg(m_Doc->typographicSettings.valueBaselineGrid);
 						if (style.lineSpacingMode() == ParagraphStyle::BaselineGridLineSpacing)
-						{
-							double by = Ypos;
-							if (OwnPage != -1)
-								by = Ypos - m_Doc->Pages->at(OwnPage)->yOffset();
-							int ol1 = qRound((by + current.yPos - m_Doc->guidesPrefs().offsetBaselineGrid) * 10000.0);
-							int ol2 = static_cast<int>(ol1 / m_Doc->guidesPrefs().valueBaselineGrid);
-//							qDebug() << QString("baseline adjust: y=%1->%2").arg(current.yPos).arg(ceil(  ol2 / 10000.0 ) * m_Doc->typographicSettings.valueBaselineGrid + m_Doc->typographicSettings.offsetBaselineGrid - by);
-							current.yPos = ceil(  ol2 / 10000.0 ) * m_Doc->guidesPrefs().valueBaselineGrid + m_Doc->guidesPrefs().offsetBaselineGrid - by;
-						}
+							current.yPos = adjustToBaselineGrid (current, this, OwnPage);
 						if (current.isEndOfCol())
 						{
 							fBorder = false;
@@ -1448,6 +1478,8 @@
 							if (current.column < Cols)
 							{
 								current.nextColumn(asce);
+								adjustParagraphEndings ();
+								a = itemText.lastInFrame() + 1;
 								if (((a > firstInFrame()) && (itemText.text(a-1) == SpecialChars::PARSEP)) || ((a == firstInFrame()) && (BackBox == 0)))
 								{
 									if (chstr[0] != SpecialChars::PARSEP)
@@ -1456,25 +1488,13 @@
 										DropCmode = false;
 									if (DropCmode)
 									{
-										if (style.lineSpacingMode() == ParagraphStyle::BaselineGridLineSpacing)
-											desc2 = -charStyle.font().descent() * m_Doc->guidesPrefs().valueBaselineGrid * style.dropCapLines();
-										else
-											desc2 = -charStyle.font().descent() * style.lineSpacing() * style.dropCapLines();
-									}
-									if (DropCmode)
+										desc2 = -charStyle.font().descent() * calculateLineSpacing (style, this) * style.dropCapLines();
 										DropLines = style.dropCapLines();
+									}
 								}
 //								qDebug() << QString("layout: nextcol2 grid=%1 x %2").arg(style.lineSpacingMode() == ParagraphStyle::BaselineGridLineSpacing).arg(m_Doc->guideSettings.valueBaselineGrid);
 								if (style.lineSpacingMode() == ParagraphStyle::BaselineGridLineSpacing)
-								{
-									double by = Ypos;
-									if (OwnPage != -1)
-										by = Ypos - m_Doc->Pages->at(OwnPage)->yOffset();
-									int ol1 = qRound((by + current.yPos - m_Doc->guidesPrefs().offsetBaselineGrid) * 10000.0);
-									int ol2 = static_cast<int>(ol1 / m_Doc->guidesPrefs().valueBaselineGrid);
-//									qDebug() << QString("baseline adjust: y=%1->%2").arg(current.yPos).arg(ceil(  ol2 / 10000.0 ) * m_Doc->guideSettings.valueBaseGrid + m_Doc->typographicSettings.offsetBaselineGrid - by);
-									current.yPos = ceil(  ol2 / 10000.0 ) * m_Doc->guidesPrefs().valueBaselineGrid + m_Doc->guidesPrefs().offsetBaselineGrid - by;
-								}
+									current.yPos = adjustToBaselineGrid (current, this, OwnPage);
 							}
 							else
 							{
@@ -1536,40 +1556,33 @@
 					tTabValues = style.tabValues();
 					if (tTabValues.isEmpty())
 					{
-						double dtw=m_Doc->itemToolPrefs().textTabWidth;
-						if ((current.xPos - current.colLeft) != 0)
-						{
-							if (current.xPos == current.colLeft + ceil((current.xPos-current.colLeft) / dtw) * dtw)
-								current.xPos += dtw;
-							else
-								current.xPos = current.colLeft + ceil((current.xPos-current.colLeft) / dtw) * dtw;
-						}
-						else
-							current.xPos = current.colLeft + dtw;
+						current.xPos = nextAutoTab (current, this);
 						tabs.status = TabNONE;
 						tabs.active = false;
 					}
 					else
 					{
-						double tCurX = current.xPos - current.colLeft;
-						double oCurX = tCurX + wide;
+						double tCurX = current.xPos;
+						double oCurX = current.xPos - current.colLeft + wide;
 						for (int yg = static_cast<int>(tTabValues.count()-1); yg > -1; yg--)
 						{
 							if (oCurX < tTabValues.at(yg).tabPosition)
 							{
 								tabs.status = static_cast<int>(tTabValues.at(yg).tabType);
-								tCurX = tTabValues.at(yg).tabPosition;
 								tabs.fillChar = tTabValues.at(yg).tabFillChar;
+								current.xPos = current.colLeft + tTabValues.at(yg).tabPosition;
 							}
 						}
 						tabs.active = (tabs.status != TabLEFT);
-						if (tCurX == oCurX-wide)
-							current.xPos = current.colLeft + ceil((current.xPos-current.colLeft) / m_Doc->itemToolPrefs().textTabWidth) * m_Doc->itemToolPrefs().textTabWidth;
-						else
-							current.xPos = current.colLeft + tCurX;
-						
+						if (current.xPos == tCurX)  // no more tabs found
+						{
+							current.xPos = nextAutoTab (current, this);
+							tabs.status = TabNONE;
+							tabs.active = false;
+							tabs.fillChar = QChar();
+						}
+
 						// remember fill char
-//						qDebug() << QString("tab: %1 '%2'").arg(tCurX).arg(tabs.fillChar);
 						if (!tabs.fillChar.isNull()) {
 							hl->glyph.growWithTabLayout();
 							TabLayout * tglyph = dynamic_cast<TabLayout*>(hl->glyph.more);
@@ -1613,31 +1626,20 @@
 			}		
 			
 			//FIXME: asce / desc set correctly?
+			double x = current.xPos + extra.Right - current.maxShrink;
 			if (legacy && 
 				(((hl->ch == '-' || (hl->effects() & ScStyle_HyphenationPossible)) && (current.hyphenCount < m_Doc->hyphConsecutiveLines() || m_Doc->hyphConsecutiveLines() == 0))
 				|| hl->ch == SpecialChars::SHYPHEN))
 			{
 				if (hl->effects() & ScStyle_HyphenationPossible || hl->ch == SpecialChars::SHYPHEN)
-				{
-					pt1 = QPoint(qRound(ceil(current.xPos+extra.Right - current.maxShrink + charStyle.font().charWidth('-', charStyle.fontSize() / 10.0) * (charStyle.scaleH() / 1000.0))), qRound(current.yPos+desc));
-					pt2 = QPoint(qRound(ceil(current.xPos+extra.Right - current.maxShrink + charStyle.font().charWidth('-', charStyle.fontSize() / 10.0) * (charStyle.scaleH() / 1000.0))), qRound(ceil(current.yPos-asce)));
-				}
-				else
-				{
-					pt1 = QPoint(qRound(ceil(current.xPos+extra.Right - current.maxShrink )), qRound(current.yPos+desc));
-					pt2 = QPoint(qRound(ceil(current.xPos+extra.Right - current.maxShrink )), qRound(ceil(current.yPos-asce)));
-				}
+					x += charStyle.font().charWidth('-', charStyle.fontSize() / 10.0) * (charStyle.scaleH() / 1000.0);
 			}
 			else if (!legacy && SpecialChars::isBreakingSpace(hl->ch))
-			{
-				pt1 = QPoint(qRound(ceil(breakPos + extra.Right - current.maxShrink )), qRound(current.yPos+desc));
-				pt2 = QPoint(qRound(ceil(breakPos + extra.Right - current.maxShrink )), qRound(ceil(current.yPos-asce)));
-			}
-			else
-			{
-				pt1 = QPoint(qRound(ceil(current.xPos+extra.Right - current.maxShrink)), qRound(current.yPos+desc));
-				pt2 = QPoint(qRound(ceil(current.xPos+extra.Right - current.maxShrink)), qRound(ceil(current.yPos-asce)));
-			}
+				x = breakPos + extra.Right - current.maxShrink;
+
+			x = qRound (ceil (x));
+			pt1 = QPoint(x, qRound (current.yPos + desc));
+			pt2 = QPoint(x, qRound (ceil(current.yPos - asce)));
 			
 			// test if end of line reached
 			if ((!cl.contains(pf2.map(pt1))) || (!cl.contains(pf2.map(pt2))) || (legacy && current.isEndOfLine(style.rightMargin())))
@@ -1649,7 +1651,6 @@
 			if ((hl->ch == SpecialChars::COLBREAK) && (Cols > 1))
 				goNextColumn = true;
 
-
 			// remember possible break
 			// #5783 : comment out "&& !outs" as this make it possible to perform a line break
 			// before a space which contradicts unicode line breaking rules
@@ -1717,40 +1718,15 @@
 				current.xPos = qMax(current.xPos, current.colLeft);
 				maxDX = current.xPos;
 				QPolygon tcli(4);
+				double spacing = calculateLineSpacing (style, this);
+				current.yPos -= spacing * (DropLines-1);
 				if (style.lineSpacingMode() == ParagraphStyle::BaselineGridLineSpacing)
-				{
-					current.yPos -= m_Doc->guidesPrefs().valueBaselineGrid * (DropLines-1);
-					double by = Ypos;
-					if (OwnPage != -1)
-						by = Ypos - m_Doc->Pages->at(OwnPage)->yOffset();
-					int ol1 = qRound((by + current.yPos - m_Doc->guidesPrefs().offsetBaselineGrid) * 10000.0);
-					int ol2 = static_cast<int>(ol1 / m_Doc->guidesPrefs().valueBaselineGrid);
-//					qDebug() << QString("baseline adjust after dropcaps: y=%1->%2").arg(current.yPos).arg(ceil(  ol2 / 10000.0 ) * m_Doc->guideSettings.valueBaselineGrid + m_Doc->typographicSettings.offsetBaselineGrid - by);
-					current.yPos = ceil(  ol2 / 10000.0 ) * m_Doc->guidesPrefs().valueBaselineGrid + m_Doc->guidesPrefs().offsetBaselineGrid - by;
-					//FIXME: use current.colLeft instead of xOffset?
-					tcli.setPoint(0, QPoint(qRound(hl->glyph.xoffset), qRound(maxDY-DropLines*m_Doc->guidesPrefs().valueBaselineGrid)));
-					tcli.setPoint(1, QPoint(qRound(maxDX), qRound(maxDY-DropLines*m_Doc->guidesPrefs().valueBaselineGrid)));
-				}
-				else
-				{
-					if (style.lineSpacingMode() == ParagraphStyle::FixedLineSpacing)
-					{
-						current.yPos -= style.lineSpacing() * (DropLines-1);
-//						qDebug() << QString("after dropcaps: y=%1").arg(current.yPos);
-						tcli.setPoint(0, QPoint(qRound(hl->glyph.xoffset), qRound(maxDY - DropLines * style.lineSpacing())));
-						tcli.setPoint(1, QPoint(qRound(maxDX), qRound(maxDY-DropLines*style.lineSpacing())));
-					}
-					else
-					{
-						double currasce = charStyle.font().height(style.charStyle().fontSize() / 10.0);
-						current.yPos -= currasce * (DropLines-1);
-//						qDebug() << QString("after dropcaps: y=%1").arg(current.yPos);
-						tcli.setPoint(0, QPoint(qRound(hl->glyph.xoffset), qRound(maxDY-DropLines*currasce)));
-						tcli.setPoint(1, QPoint(qRound(maxDX), qRound(maxDY-DropLines*currasce)));
-					}
-				}
-				tcli.setPoint(2, QPoint(qRound(maxDX), qRound(maxDY)));
-				tcli.setPoint(3, QPoint(qRound(hl->glyph.xoffset), qRound(maxDY)));
+					current.yPos = adjustToBaselineGrid (current, this, OwnPage);
+				//FIXME: use current.colLeft instead of xOffset?
+				tcli.setPoint(0, QPoint (qRound(hl->glyph.xoffset), qRound(maxDY - DropLines * spacing)));
+				tcli.setPoint(1, QPoint (qRound(maxDX), qRound(maxDY - DropLines * spacing)));
+				tcli.setPoint(2, QPoint (qRound(maxDX), qRound(maxDY)));
+				tcli.setPoint(3, QPoint (qRound(hl->glyph.xoffset), qRound(maxDY)));
 				// #6821 : the following two lines are causing bad text flow around drop caps 
 				// in some case, discarding them put more emphasis on user control of line spacing
 				/*cm = QRegion(pf2.map(tcli));
@@ -1815,13 +1791,8 @@
 						assert( a < itemText.length() );
 						hl = itemText.item(a);
 						style = itemText.paragraphStyle(a);
-						if (style.lineSpacingMode() == ParagraphStyle::AutomaticLineSpacing)
-						{
-							style.setLineSpacing(style.charStyle().font().height(style.charStyle().fontSize() / 10.0));
-//							qDebug() << QString("auto linespacing: %1").arg(style.lineSpacing());
-						}
-						else if (style.lineSpacingMode() == ParagraphStyle::BaselineGridLineSpacing)
-							style.setLineSpacing(m_Doc->guidesPrefs().valueBaselineGrid);
+			      style.setLineSpacing (calculateLineSpacing (style, this));
+
 						current.itemsInLine = a - current.line.firstItem + 1;
 //						qDebug() << QString("style outs pos %1: %2 (%3)").arg(a).arg(style.alignment()).arg(style.parent());
 //						qDebug() << QString("style <@%6: %1 -- %2, %4/%5 char: %3").arg(style.leftMargin()).arg(style.rightMargin())
@@ -1905,13 +1876,7 @@
 						{
 							hl = itemText.item(a);
 							style = itemText.paragraphStyle(a);
-							if (style.lineSpacingMode() == ParagraphStyle::AutomaticLineSpacing)
-							{
-								style.setLineSpacing(style.charStyle().font().height(style.charStyle().fontSize() / 10.0));
-//								qDebug() << QString("auto linespacing: %1").arg(style.lineSpacing());
-							}
-							else if (style.lineSpacingMode() == ParagraphStyle::BaselineGridLineSpacing)
-								style.setLineSpacing(m_Doc->guidesPrefs().valueBaselineGrid);
+			        style.setLineSpacing (calculateLineSpacing (style, this));
 						}
 						current.breakLine(itemText, style, firstLineOffset(), a);
 //						qDebug() << QString("style no break pos %1: %2 (%3)").arg(a).arg(style.alignment()).arg(style.parent());
@@ -1949,10 +1914,7 @@
 //								qDebug() << QString("layout: next lower2 grid=%1 x %2").arg(style.lineSpacingMode() == ParagraphStyle::BaselineGridLineSpacing).arg(m_Doc->typographicSettings.valueBaseGrid);
 //								qDebug() << QString("nextline: y=%1+%2").arg(current.yPos).arg(style.lineSpacingMode() == ParagraphStyle::BaselineGridLineSpacing? m_Doc->typographicSettings.valueBaseGrid : style.lineSpacing());
 
-								if (style.lineSpacingMode() == ParagraphStyle::BaselineGridLineSpacing)
-									current.yPos += m_Doc->guidesPrefs().valueBaselineGrid;
-								else
-									current.yPos += style.lineSpacing();
+								current.yPos += calculateLineSpacing (style, this);
 								if (current.isEndOfCol(desc) && (current.column+1 == Cols))
 								{
 									goNoRoom = true;
@@ -2020,26 +1982,10 @@
 						}
 //						qDebug() << QString("layout: next lower3 grid=%1 x %2").arg(style.lineSpacingMode() == ParagraphStyle::BaselineGridLineSpacing).arg(m_Doc->typographicSettings.valueBaseGrid);
 
-
-						if (style.lineSpacingMode() == ParagraphStyle::BaselineGridLineSpacing)
-						{
-//							qDebug() << QString("next line (grid): y=%1+%2").arg(current.yPos).arg(m_Doc->typographicSettings.valueBaseGrid);
-
-							current.yPos += m_Doc->guidesPrefs().valueBaselineGrid;
-						}
-						else
-						{
-//							qDebug() << QString("next line (auto): y=%1+%2").arg(current.yPos).arg(style.lineSpacing());
-							//#5845 : use next paragraph line spacing for switching to next paragraph instead of current one
-							//current.yPos += style.lineSpacing();
-							const  ParagraphStyle& pStyle = itemText.paragraphStyle(a+1);
-							double lineSpacing = pStyle.lineSpacing();
-							if (pStyle.lineSpacingMode() == ParagraphStyle::AutomaticLineSpacing)
-								lineSpacing = pStyle.charStyle().font().height(pStyle.charStyle().fontSize() / 10.0);
-							else if (pStyle.lineSpacingMode() == ParagraphStyle::BaselineGridLineSpacing)
-								lineSpacing = m_Doc->guidesPrefs().valueBaselineGrid;
-							current.yPos += lineSpacing;
-						}
+//						qDebug() << QString("next line (auto): y=%1+%2").arg(current.yPos).arg(style.lineSpacing());
+						//#5845 : use next paragraph line spacing for switching to next paragraph instead of current one
+						const  ParagraphStyle& pStyle = itemText.paragraphStyle(a+1);
+		        current.yPos += calculateLineSpacing (pStyle, this);
 						if (current.hasDropCap)
 						{
 							++DropLinesCount;
@@ -2122,7 +2068,14 @@
 				if (current.line.firstItem > current.line.lastItem)
 					; //qDebug() << QString("layout: empty line %1 - %2").arg(current.line.firstItem).arg(current.line.lastItem);
 				else if (current.itemsInLine > 0)
+				{
 					itemText.appendLine(current.line);
+					if (moveLinesFromPreviousFrame ()) 
+					{
+						layout ();    // line moving ensures that this won't be an endless loop
+						return;
+					}
+				}
 				current.startLine(a+1);
 				outs = false;
 				if (goNoRoom)
@@ -2139,6 +2092,7 @@
 					if (current.column < Cols)
 					{
 						current.nextColumn(asce);
+						// manual break - we do NOT call adjustParagraphEndings () here
 					}
 					else
 					{
@@ -2210,6 +2164,10 @@
 		
 		if (current.itemsInLine > 0) {
 			itemText.appendLine(current.line);
+			if (moveLinesFromPreviousFrame ()) {
+				layout ();  // line moving ensures that this won't be an endless loop
+				return;
+			}
 		}
 	}
 	MaxChars = itemText.length();
@@ -2230,6 +2188,9 @@
 NoRoom:     
 //	pf2.end();
 	invalid = false;
+
+	adjustParagraphEndings ();
+
 	PageItem_TextFrame * next = dynamic_cast<PageItem_TextFrame*>(NextBox);
 	if (next != NULL)
 	{
@@ -2493,18 +2454,8 @@
 				if (charStyle.effects() & ScStyle_DropCap)
 				{
 					const ParagraphStyle& style(itemText.paragraphStyle(a));
-					if (style.lineSpacingMode() == ParagraphStyle::BaselineGridLineSpacing)
-						chs = qRound(10 * ((m_Doc->guidesPrefs().valueBaselineGrid * (style.dropCapLines()-1) + (charStyle.font().ascent(style.charStyle().fontSize() / 10.0))) / charStyle.font().realCharHeight(chstr0, 10)));
-					else
-					{
-						if (style.lineSpacingMode() == ParagraphStyle::FixedLineSpacing)
-							chs = qRound(10 * ((style.lineSpacing() * (style.dropCapLines()-1)+(charStyle.font().ascent(style.charStyle().fontSize() / 10.0))) / charStyle.font().realCharHeight(chstr0, 10)));
-						else
-						{
-							double currasce = charStyle.font().height(style.charStyle().fontSize() / 10.0);
-							chs = qRound(10 * ((currasce * (style.dropCapLines()-1)+(charStyle.font().ascent(style.charStyle().fontSize() / 10.0))) / charStyle.font().realCharHeight(chstr0, 10)));
-						}
-					}
+					double spacing = calculateLineSpacing (style, this);
+					chs = qRound(10 * ((spacing * (style.dropCapLines()-1) + (charStyle.font().ascent(style.charStyle().fontSize() / 10.0))) / charStyle.font().realCharHeight(chstr0, 10)));
 				}
 				if (chstr0 == SpecialChars::TAB)
 					tabCc++;
Index: scribus/ui/propertywidget_flow.cpp
===================================================================
--- scribus/ui/propertywidget_flow.cpp	(revision 0)
+++ scribus/ui/propertywidget_flow.cpp	(revision 0)
@@ -0,0 +1,107 @@
+/*
+For general Scribus (>=1.3.2) copyright and licensing information please refer
+to the COPYING file provided with the program. Following this notice may exist
+a copyright and/or license notice that predates the release of Scribus 1.3.2
+for which a new license (GPL+exception) is in place.
+*/
+
+#include "propertywidget_flow.h"
+
+#include "scribus.h"
+
+PropertyWidget_Flow::PropertyWidget_Flow(QWidget* parent) : QFrame(parent)
+{
+	setupUi(this);
+
+	setFrameStyle(QFrame::Box | QFrame::Plain);
+	setLineWidth(1);
+	layout()->setAlignment( Qt::AlignTop );
+
+	languageChange();
+}
+
+void PropertyWidget_Flow::connectSignals()
+{
+	connect(keepLinesStart, SIGNAL(valueChanged(int)), this, SLOT(handleKeepLinesStart()));
+	connect(keepLinesEnd, SIGNAL(valueChanged(int)), this, SLOT(handleKeepLinesEnd()));
+	connect(keepTogether, SIGNAL(stateChanged(int)), this, SLOT(handleKeepTogether()));
+	connect(keepWithNext, SIGNAL(stateChanged(int)), this, SLOT(handleKeepWithNext()));
+}
+
+void PropertyWidget_Flow::disconnectSignals()
+{
+	disconnect(keepLinesStart, SIGNAL(valueChanged(int)), this, SLOT(handleKeepLinesStart()));
+	disconnect(keepLinesEnd, SIGNAL(valueChanged(int)), this, SLOT(handleKeepLinesEnd()));
+	disconnect(keepTogether, SIGNAL(stateChanged(int)), this, SLOT(handleKeepTogether()));
+	disconnect(keepWithNext, SIGNAL(stateChanged(int)), this, SLOT(handleKeepWithNext()));
+}
+
+void PropertyWidget_Flow::handleKeepLinesStart()
+{
+	if (!m_doc) return;
+	ParagraphStyle newStyle;
+	newStyle.setKeepLinesStart (keepLinesStart->value());
+	m_doc->itemSelection_ApplyParagraphStyle(newStyle);
+}
+
+void PropertyWidget_Flow::handleKeepLinesEnd()
+{
+	if (!m_doc) return;
+	ParagraphStyle newStyle;
+	newStyle.setKeepLinesEnd (keepLinesEnd->value());
+	m_doc->itemSelection_ApplyParagraphStyle(newStyle);
+}
+
+void PropertyWidget_Flow::handleKeepTogether()
+{
+	if (!m_doc) return;
+	ParagraphStyle newStyle;
+	newStyle.setKeepTogether (keepTogether->isChecked());
+	m_doc->itemSelection_ApplyParagraphStyle(newStyle);
+}
+
+void PropertyWidget_Flow::handleKeepWithNext()
+{
+	if (!m_doc) return;
+	ParagraphStyle newStyle;
+	newStyle.setKeepWithNext (keepWithNext->isChecked());
+	m_doc->itemSelection_ApplyParagraphStyle(newStyle);
+}
+
+void PropertyWidget_Flow::updateStyle(const ParagraphStyle& newCurrent)
+{
+	disconnectSignals ();
+  keepLinesStart->setValue (newCurrent.keepLinesStart());
+  keepLinesEnd->setValue (newCurrent.keepLinesEnd());
+	keepTogether->setChecked (newCurrent.keepTogether());
+	keepWithNext->setChecked (newCurrent.keepWithNext());
+	connectSignals ();
+}
+
+void PropertyWidget_Flow::changeEvent(QEvent *e)
+{
+	if (e->type() == QEvent::LanguageChange)
+	{
+		languageChange();
+		return;
+	}
+	QWidget::changeEvent(e);
+}
+
+void PropertyWidget_Flow::languageChange()
+{
+	keepLabelStart->setText (tr ("Don't separate first"));
+	keepLabelEnd->setText (tr ("Don't separate last"));
+	keepLinesStart->setSuffix (tr (" lines"));
+	keepLinesEnd->setSuffix (tr (" lines"));
+	keepTogether->setText (tr ("Do not split paragraph"));
+	keepWithNext->setText (tr ("Keep with next paragraph"));
+
+	keepLinesStart->setToolTip ("<qt>" + tr ("Ensure that first lines of a paragraph won't end up separated from the rest (known as widow/orphan control)") + "</qt>");
+	keepLinesEnd->setToolTip ("<qt>" + tr ("Ensure that last lines of a paragraph won't end up separated from the rest (known as widow/orphan control)") + "</qt>");
+	keepLabelStart->setToolTip (keepLinesStart->toolTip());
+	keepLabelEnd->setToolTip (keepLinesEnd->toolTip());
+	keepTogether->setToolTip ("<qt>" + tr ("If checked, ensures that the paragraph won't be split across multiple pages or columns") + "</qt>");
+	keepWithNext->setToolTip ("<qt>" + tr ("If checked, automatically moves the paragraph to the next column or page if the next paragraph isn't on the same page or column") + "</qt>");
+}
+
Index: scribus/ui/propertiespalette_text.cpp
===================================================================
--- scribus/ui/propertiespalette_text.cpp	(revision 16811)
+++ scribus/ui/propertiespalette_text.cpp	(working copy)
@@ -20,6 +20,7 @@
 #include "propertywidget_advanced.h"
 #include "propertywidget_distance.h"
 #include "propertywidget_flop.h"
+#include "propertywidget_flow.h"
 #include "propertywidget_pathtext.h"
 #include "propertywidget_optmargins.h"
 #include "propertywidget_textcolor.h"
@@ -71,6 +72,9 @@
 	flopBox = new PropertyWidget_Flop(textTree);
 	flopItem = textTree->addWidget( tr("First Line Offset"), flopBox);
 
+	flowBox = new PropertyWidget_Flow(textTree);
+	flowItem = textTree->addWidget( tr("Text Flow"), flowBox);
+
 	distanceWidgets = new PropertyWidget_Distance(textTree);
     distanceItem = textTree->addWidget( tr("Columns & Text Distances"), distanceWidgets);
 
@@ -168,6 +172,7 @@
 	colorWidgets->setDoc(m_doc);
 	distanceWidgets->setDoc(m_doc);
 	flopBox->setDoc(m_doc);
+	flowBox->setDoc(m_doc);
 	optMargins->setDoc(m_doc);
 	pathTextWidgets->setDoc(m_doc);
 
@@ -199,6 +204,7 @@
 	colorWidgets->setDoc(0);
 	distanceWidgets->setDoc(0);
 	flopBox->setDoc(0);
+	flowBox->setDoc(0);
 	optMargins->setDoc(0);
 
 	m_haveItem = false;
@@ -365,6 +371,7 @@
 	if (m_item->asPathText())
 	{
 		flopItem->setHidden(true);
+		flowItem->setHidden(true);
 		distanceItem->setHidden(true);
 		pathTextItem->setHidden(false);
 		pathTextWidgets->pathTextType->setCurrentIndex(m_item->textPathType);
@@ -376,12 +383,14 @@
 	else if (m_item->asTextFrame())
 	{
 		flopItem->setHidden(false);
+		flowItem->setHidden(false);
 		distanceItem->setHidden(false);
 		pathTextItem->setHidden(true);
 	}
 	else
 	{
 		flopItem->setHidden(false);
+		flowItem->setHidden(false);
 		distanceItem->setHidden(false);
 		pathTextItem->setHidden(true);
 	}
@@ -565,6 +574,7 @@
 
 	advancedWidgets->updateStyle(newCurrent);
 	colorWidgets->updateStyle(newCurrent);
+	flowBox->updateStyle (newCurrent);
 
 	displayFontFace(charStyle.font().scName());
 	displayFontSize(charStyle.fontSize());
@@ -915,6 +925,7 @@
 	colorWidgetsItem->setText(0, tr("Color & Effects"));
 	advancedWidgetsItem->setText(0, tr("Advanced Settings"));
 	flopItem->setText(0, tr("First Line Offset"));
+	flowItem->setText(0, tr("Text Flow"));
     distanceItem->setText(0, tr("Columns & Text Distances"));
 	optMarginsItem->setText(0, tr("Optical Margins"));
 	pathTextItem->setText(0, tr("Path Text Properties"));
@@ -935,6 +946,7 @@
 	colorWidgets->languageChange();
 	distanceWidgets->languageChange();
 	flopBox->languageChange();
+	flowBox->languageChange();
 	optMargins->languageChange();
 	pathTextWidgets->languageChange();
 
Index: scribus/ui/smpstylewidget.cpp
===================================================================
--- scribus/ui/smpstylewidget.cpp	(revision 16811)
+++ scribus/ui/smpstylewidget.cpp	(working copy)
@@ -106,8 +106,15 @@
 	minGlyphExtLabel->setToolTip(minGlyphExtSpin->toolTip());
 	maxGlyphExtSpin->setToolTip(tr("Maximum extension of glyphs"));
 	maxGlyphExtLabel->setToolTip(maxGlyphExtSpin->toolTip());
-	
 
+	keepLinesStart->setToolTip ("<qt>" + tr ("Ensure that first lines of a paragraph won't end up separated from the rest (known as widow/orphan control)") + "</qt>");
+	keepLinesEnd->setToolTip ("<qt>" + tr ("Ensure that last lines of a paragraph won't end up separated from the rest (known as widow/orphan control)") + "</qt>");
+	keepLabelStart->setToolTip (keepLinesStart->toolTip());
+	keepLabelEnd->setToolTip (keepLinesEnd->toolTip());
+	keepTogether->setToolTip ("<qt>" + tr ("If checked, ensures that the paragraph won't be split across multiple pages or columns") + "</qt>");
+	keepWithNext->setToolTip ("<qt>" + tr ("If checked, automatically moves the paragraph to the next column or page if the next paragraph isn't on the same page or column") + "</qt>");
+
+
 /***********************************/
 /*      End Tooltips               */
 /***********************************/
@@ -140,6 +147,13 @@
 	glyphExtensionLabel->setText(tr("Glyph Extension"));
 	minGlyphExtLabel->setText(tr("Min:", "Glyph Extension"));
 	maxGlyphExtLabel->setText(tr("Max:", "Glyph Extension"));
+
+	keepLabelStart->setText (tr ("Don't separate first"));
+	keepLabelEnd->setText (tr ("Don't separate last"));
+	keepLinesStart->setSuffix (tr (" lines"));
+	keepLinesEnd->setSuffix (tr (" lines"));
+	keepTogether->setText (tr ("Do not split paragraph"));
+	keepWithNext->setText (tr ("Keep with next paragraph"));
 }
 
 void SMPStyleWidget::unitChange(double oldRatio, double newRatio, int unitIndex)
@@ -236,6 +250,15 @@
 
 		tabList_->setRightIndentValue(pstyle->rightMargin() * unitRatio, pstyle->isInhRightMargin());
 		tabList_->setParentRightIndent(parent->rightMargin() * unitRatio);
+
+		keepLinesStart->setValue (pstyle->keepLinesStart(), pstyle->isInhKeepLinesStart());
+		keepLinesEnd->setValue (pstyle->keepLinesEnd(), pstyle->isInhKeepLinesEnd());
+		keepTogether->setChecked (pstyle->keepTogether(), pstyle->isInhKeepTogether());
+		keepWithNext->setChecked (pstyle->keepWithNext(), pstyle->isInhKeepWithNext());
+		keepLinesStart->setParentValue (parent->keepLinesStart());
+		keepLinesEnd->setParentValue (parent->keepLinesEnd());
+		keepTogether->setParentValue (parent->keepTogether());
+		keepWithNext->setParentValue (parent->keepWithNext());
 	}
 	else
 	{
@@ -260,7 +283,11 @@
 		tabList_->setLeftIndentValue(pstyle->leftMargin() * unitRatio);
 		tabList_->setFirstLineValue(pstyle->firstIndent() * unitRatio);
 		tabList_->setRightIndentValue(pstyle->rightMargin() * unitRatio);
-		
+
+		keepLinesStart->setValue (pstyle->keepLinesStart());
+		keepLinesEnd->setValue (pstyle->keepLinesEnd());
+		keepTogether->setChecked (pstyle->keepTogether());
+		keepWithNext->setChecked (pstyle->keepWithNext());
 	}
 
 	lineSpacing_->setEnabled(pstyle->lineSpacingMode() == ParagraphStyle::FixedLineSpacing);
Index: scribus/ui/smtextstyles.h
===================================================================
--- scribus/ui/smtextstyles.h	(revision 16811)
+++ scribus/ui/smtextstyles.h	(working copy)
@@ -84,6 +84,10 @@
 	void slotMinSpace();
 	void slotMinGlyphExt();
 	void slotMaxGlyphExt();
+	void handleKeepLinesStart();
+	void handleKeepLinesEnd();
+	void handleKeepTogether();
+	void handleKeepWithNext();
 	void slotTabRuler();
 	void slotLeftIndent();
 	void slotRightIndent();
Index: scribus/ui/propertywidget_flow.h
===================================================================
--- scribus/ui/propertywidget_flow.h	(revision 0)
+++ scribus/ui/propertywidget_flow.h	(revision 0)
@@ -0,0 +1,41 @@
+/*
+For general Scribus (>=1.3.2) copyright and licensing information please refer
+to the COPYING file provided with the program. Following this notice may exist
+a copyright and/or license notice that predates the release of Scribus 1.3.2
+for which a new license (GPL+exception) is in place.
+*/
+#ifndef PROPERTYWIDGET_FLOW_H
+#define PROPERTYWIDGET_FLOW_H
+
+#include "ui_propertywidget_flowbase.h"
+
+#include "propertywidgetbase.h"
+
+class ParagraphStyle;
+
+class PropertyWidget_Flow : public QFrame, public Ui::PropertyWidget_FlowBase, public PropertyWidgetBase
+{
+	Q_OBJECT
+
+public:
+
+	PropertyWidget_Flow(QWidget* parent);
+	~PropertyWidget_Flow() {};
+
+	virtual void changeEvent(QEvent *e);
+
+  void updateStyle(const ParagraphStyle& newCurrent);
+public slots:
+
+	void languageChange();
+	void handleKeepLinesStart();
+	void handleKeepLinesEnd();
+	void handleKeepTogether();
+	void handleKeepWithNext();
+
+private:
+	void connectSignals();
+	void disconnectSignals();
+};
+
+#endif
Index: scribus/ui/propertiespalette_text.h
===================================================================
--- scribus/ui/propertiespalette_text.h	(revision 16811)
+++ scribus/ui/propertiespalette_text.h	(working copy)
@@ -26,6 +26,7 @@
 class PropertyWidget_Advanced;
 class PropertyWidget_Distance;
 class PropertyWidget_Flop;
+class PropertyWidget_Flow;
 class PropertyWidget_OptMargins;
 class PropertyWidget_PathText;
 class PropertyWidget_TextColor;
@@ -150,6 +151,9 @@
 
 	PropertyWidget_Flop* flopBox;
 	QTreeWidgetItem* flopItem;
+
+	PropertyWidget_Flow* flowBox;
+	QTreeWidgetItem* flowItem;
 	
 	PropertyWidget_PathText* pathTextWidgets;
 	QTreeWidgetItem* pathTextItem;
Index: scribus/ui/smpstylewidget.ui
===================================================================
--- scribus/ui/smpstylewidget.ui	(revision 16811)
+++ scribus/ui/smpstylewidget.ui	(working copy)
@@ -1,47 +1,48 @@
-<ui version="4.0" >
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
  <class>SMPStyleWidget</class>
- <widget class="QWidget" name="SMPStyleWidget" >
-  <property name="geometry" >
+ <widget class="QWidget" name="SMPStyleWidget">
+  <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
     <width>714</width>
-    <height>533</height>
+    <height>580</height>
    </rect>
   </property>
-  <layout class="QVBoxLayout" >
+  <layout class="QVBoxLayout">
    <item>
-    <widget class="QTabWidget" name="tabWidget" >
-     <property name="currentIndex" >
+    <widget class="QTabWidget" name="tabWidget">
+     <property name="currentIndex">
       <number>0</number>
      </property>
-     <widget class="QWidget" name="tab" >
-      <attribute name="title" >
+     <widget class="QWidget" name="tab">
+      <attribute name="title">
        <string>Properties</string>
       </attribute>
-      <layout class="QVBoxLayout" name="verticalLayout_4" >
+      <layout class="QVBoxLayout" name="verticalLayout_4">
        <item>
-        <layout class="QHBoxLayout" >
-         <property name="spacing" >
+        <layout class="QHBoxLayout">
+         <property name="spacing">
           <number>5</number>
          </property>
-         <property name="margin" >
+         <property name="margin">
           <number>0</number>
          </property>
          <item>
-          <widget class="QLabel" name="parentLabel" >
-           <property name="text" >
+          <widget class="QLabel" name="parentLabel">
+           <property name="text">
             <string>Based On:</string>
            </property>
-           <property name="wordWrap" >
+           <property name="wordWrap">
             <bool>false</bool>
            </property>
           </widget>
          </item>
          <item>
-          <widget class="QComboBox" name="parentCombo" >
-           <property name="sizePolicy" >
-            <sizepolicy vsizetype="Fixed" hsizetype="Minimum" >
+          <widget class="QComboBox" name="parentCombo">
+           <property name="sizePolicy">
+            <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
              <horstretch>5</horstretch>
              <verstretch>0</verstretch>
             </sizepolicy>
@@ -51,49 +52,49 @@
         </layout>
        </item>
        <item>
-        <layout class="QHBoxLayout" name="horizontalLayout_5" >
+        <layout class="QHBoxLayout" name="horizontalLayout_5">
          <item>
-          <layout class="QVBoxLayout" name="verticalLayout_2" >
+          <layout class="QVBoxLayout" name="verticalLayout_2">
            <item>
-            <widget class="QGroupBox" name="distancesBox" >
-             <property name="sizePolicy" >
-              <sizepolicy vsizetype="Minimum" hsizetype="Preferred" >
+            <widget class="QGroupBox" name="distancesBox">
+             <property name="sizePolicy">
+              <sizepolicy hsizetype="Preferred" vsizetype="Minimum">
                <horstretch>0</horstretch>
                <verstretch>0</verstretch>
               </sizepolicy>
              </property>
-             <property name="font" >
+             <property name="font">
               <font>
                <weight>50</weight>
                <bold>false</bold>
               </font>
              </property>
-             <property name="title" >
+             <property name="title">
               <string>Distances and Alignment</string>
              </property>
-             <layout class="QVBoxLayout" >
+             <layout class="QVBoxLayout">
               <item>
-               <layout class="QHBoxLayout" >
+               <layout class="QHBoxLayout">
                 <item>
-                 <widget class="QLabel" name="lineSpacingLabel" >
-                  <property name="maximumSize" >
+                 <widget class="QLabel" name="lineSpacingLabel">
+                  <property name="maximumSize">
                    <size>
                     <width>22</width>
                     <height>22</height>
                    </size>
                   </property>
-                  <property name="text" >
+                  <property name="text">
                    <string>TextLabel</string>
                   </property>
-                  <property name="buddy" >
+                  <property name="buddy">
                    <cstring>lineSpacingMode_</cstring>
                   </property>
                  </widget>
                 </item>
                 <item>
-                 <widget class="SMScComboBox" name="lineSpacingMode_" >
-                  <property name="sizePolicy" >
-                   <sizepolicy vsizetype="Fixed" hsizetype="MinimumExpanding" >
+                 <widget class="SMScComboBox" name="lineSpacingMode_">
+                  <property name="sizePolicy">
+                   <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
                     <horstretch>0</horstretch>
                     <verstretch>0</verstretch>
                    </sizepolicy>
@@ -101,21 +102,21 @@
                  </widget>
                 </item>
                 <item>
-                 <widget class="SMScrSpinBox" name="lineSpacing_" >
-                  <property name="minimum" >
+                 <widget class="SMScrSpinBox" name="lineSpacing_">
+                  <property name="minimum">
                    <number>1</number>
                   </property>
-                  <property name="maximum" >
+                  <property name="maximum">
                    <number>300</number>
                   </property>
                  </widget>
                 </item>
                 <item>
                  <spacer>
-                  <property name="orientation" >
+                  <property name="orientation">
                    <enum>Qt::Horizontal</enum>
                   </property>
-                  <property name="sizeHint" stdset="0" >
+                  <property name="sizeHint" stdset="0">
                    <size>
                     <width>40</width>
                     <height>20</height>
@@ -126,36 +127,36 @@
                </layout>
               </item>
               <item>
-               <layout class="QHBoxLayout" >
+               <layout class="QHBoxLayout">
                 <item>
-                 <widget class="QLabel" name="spaceAboveLabel" >
-                  <property name="maximumSize" >
+                 <widget class="QLabel" name="spaceAboveLabel">
+                  <property name="maximumSize">
                    <size>
                     <width>22</width>
                     <height>22</height>
                    </size>
                   </property>
-                  <property name="text" >
+                  <property name="text">
                    <string>TextLabel</string>
                   </property>
-                  <property name="buddy" >
+                  <property name="buddy">
                    <cstring>spaceAbove_</cstring>
                   </property>
                  </widget>
                 </item>
                 <item>
-                 <widget class="SMScrSpinBox" name="spaceAbove_" >
-                  <property name="maximum" >
+                 <widget class="SMScrSpinBox" name="spaceAbove_">
+                  <property name="maximum">
                    <number>300</number>
                   </property>
                  </widget>
                 </item>
                 <item>
                  <spacer>
-                  <property name="orientation" >
+                  <property name="orientation">
                    <enum>Qt::Horizontal</enum>
                   </property>
-                  <property name="sizeHint" stdset="0" >
+                  <property name="sizeHint" stdset="0">
                    <size>
                     <width>40</width>
                     <height>20</height>
@@ -166,36 +167,36 @@
                </layout>
               </item>
               <item>
-               <layout class="QHBoxLayout" >
+               <layout class="QHBoxLayout">
                 <item>
-                 <widget class="QLabel" name="spaceBelowLabel" >
-                  <property name="maximumSize" >
+                 <widget class="QLabel" name="spaceBelowLabel">
+                  <property name="maximumSize">
                    <size>
                     <width>22</width>
                     <height>22</height>
                    </size>
                   </property>
-                  <property name="text" >
+                  <property name="text">
                    <string>TextLabel</string>
                   </property>
-                  <property name="buddy" >
+                  <property name="buddy">
                    <cstring>spaceBelow_</cstring>
                   </property>
                  </widget>
                 </item>
                 <item>
-                 <widget class="SMScrSpinBox" name="spaceBelow_" >
-                  <property name="maximum" >
+                 <widget class="SMScrSpinBox" name="spaceBelow_">
+                  <property name="maximum">
                    <number>300</number>
                   </property>
                  </widget>
                 </item>
                 <item>
                  <spacer>
-                  <property name="orientation" >
+                  <property name="orientation">
                    <enum>Qt::Horizontal</enum>
                   </property>
-                  <property name="sizeHint" stdset="0" >
+                  <property name="sizeHint" stdset="0">
                    <size>
                     <width>40</width>
                     <height>20</height>
@@ -206,11 +207,11 @@
                </layout>
               </item>
               <item>
-               <layout class="QHBoxLayout" >
+               <layout class="QHBoxLayout">
                 <item>
-                 <widget class="SMAlignSelect" native="1" name="alignement_" >
-                  <property name="sizePolicy" >
-                   <sizepolicy vsizetype="Minimum" hsizetype="Minimum" >
+                 <widget class="SMAlignSelect" name="alignement_" native="true">
+                  <property name="sizePolicy">
+                   <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
                     <horstretch>0</horstretch>
                     <verstretch>0</verstretch>
                    </sizepolicy>
@@ -219,10 +220,10 @@
                 </item>
                 <item>
                  <spacer>
-                  <property name="orientation" >
+                  <property name="orientation">
                    <enum>Qt::Horizontal</enum>
                   </property>
-                  <property name="sizeHint" stdset="0" >
+                  <property name="sizeHint" stdset="0">
                    <size>
                     <width>211</width>
                     <height>20</height>
@@ -236,45 +237,45 @@
             </widget>
            </item>
            <item>
-            <widget class="QGroupBox" name="dropCapsBox" >
-             <property name="sizePolicy" >
-              <sizepolicy vsizetype="Minimum" hsizetype="Preferred" >
+            <widget class="QGroupBox" name="dropCapsBox">
+             <property name="sizePolicy">
+              <sizepolicy hsizetype="Preferred" vsizetype="Minimum">
                <horstretch>0</horstretch>
                <verstretch>0</verstretch>
               </sizepolicy>
              </property>
-             <property name="title" >
+             <property name="title">
               <string>Drop Caps</string>
              </property>
-             <property name="checkable" >
+             <property name="checkable">
               <bool>true</bool>
              </property>
-             <property name="checked" >
+             <property name="checked">
               <bool>true</bool>
              </property>
-             <layout class="QVBoxLayout" >
+             <layout class="QVBoxLayout">
               <item>
-               <layout class="QHBoxLayout" >
+               <layout class="QHBoxLayout">
                 <item>
-                 <layout class="QVBoxLayout" >
+                 <layout class="QVBoxLayout">
                   <item>
-                   <layout class="QHBoxLayout" >
+                   <layout class="QHBoxLayout">
                     <item>
-                     <widget class="QLabel" name="label" >
-                      <property name="text" >
+                     <widget class="QLabel" name="label">
+                      <property name="text">
                        <string>&amp;Lines:</string>
                       </property>
-                      <property name="buddy" >
+                      <property name="buddy">
                        <cstring>dropCapLines_</cstring>
                       </property>
                      </widget>
                     </item>
                     <item>
-                     <widget class="SMSpinBox" name="dropCapLines_" >
-                      <property name="minimum" >
+                     <widget class="SMSpinBox" name="dropCapLines_">
+                      <property name="minimum">
                        <number>2</number>
                       </property>
-                      <property name="maximum" >
+                      <property name="maximum">
                        <number>20</number>
                       </property>
                      </widget>
@@ -282,23 +283,23 @@
                    </layout>
                   </item>
                   <item>
-                   <layout class="QHBoxLayout" >
+                   <layout class="QHBoxLayout">
                     <item>
-                     <widget class="QLabel" name="label_2" >
-                      <property name="text" >
+                     <widget class="QLabel" name="label_2">
+                      <property name="text">
                        <string>Distance from Text:</string>
                       </property>
-                      <property name="buddy" >
+                      <property name="buddy">
                        <cstring>dropCapOffset_</cstring>
                       </property>
                      </widget>
                     </item>
                     <item>
-                     <widget class="SMScrSpinBox" name="dropCapOffset_" >
-                      <property name="minimum" >
+                     <widget class="SMScrSpinBox" name="dropCapOffset_">
+                      <property name="minimum">
                        <number>-3000</number>
                       </property>
-                      <property name="maximum" >
+                      <property name="maximum">
                        <number>3000</number>
                       </property>
                      </widget>
@@ -309,10 +310,10 @@
                 </item>
                 <item>
                  <spacer>
-                  <property name="orientation" >
+                  <property name="orientation">
                    <enum>Qt::Horizontal</enum>
                   </property>
-                  <property name="sizeHint" stdset="0" >
+                  <property name="sizeHint" stdset="0">
                    <size>
                     <width>40</width>
                     <height>20</height>
@@ -322,28 +323,88 @@
                 </item>
                </layout>
               </item>
+              <item>
+               <widget class="QToolButton" name="parentDropCapButton">
+                <property name="enabled">
+                 <bool>true</bool>
+                </property>
+                <property name="text">
+                 <string>Use Parent Value</string>
+                </property>
+               </widget>
+              </item>
              </layout>
             </widget>
            </item>
            <item>
-            <widget class="QToolButton" name="parentDropCapButton" >
-             <property name="enabled" >
-              <bool>true</bool>
+            <widget class="QGroupBox" name="textFlowBox">
+             <property name="title">
+              <string>Text Flow</string>
              </property>
-             <property name="text" >
-              <string>Use Parent Value</string>
-             </property>
+             <layout class="QGridLayout" name="gridLayout_2">
+              <item row="0" column="0">
+               <widget class="QLabel" name="keepLabelStart">
+                <property name="toolTip">
+                 <string>Ensure that first lines of a paragraph won't end up separated from the rest (known as widow/orphan control)</string>
+                </property>
+                <property name="text">
+                 <string>Don't separate first</string>
+                </property>
+               </widget>
+              </item>
+              <item row="0" column="1">
+               <widget class="SMSpinBox" name="keepLinesStart">
+                <property name="suffix">
+                 <string> lines</string>
+                </property>
+                <property name="maximum">
+                 <number>10</number>
+                </property>
+               </widget>
+              </item>
+              <item row="1" column="0">
+               <widget class="QLabel" name="keepLabelEnd">
+                <property name="text">
+                 <string>Don't separate last</string>
+                </property>
+               </widget>
+              </item>
+              <item row="1" column="1">
+               <widget class="SMSpinBox" name="keepLinesEnd">
+                <property name="suffix">
+                 <string> lines</string>
+                </property>
+                <property name="maximum">
+                 <number>10</number>
+                </property>
+               </widget>
+              </item>
+              <item row="2" column="0" colspan="2">
+               <widget class="SMCheckBox" name="keepTogether">
+                <property name="text">
+                 <string>Do not split paragraph</string>
+                </property>
+               </widget>
+              </item>
+              <item row="3" column="0" colspan="2">
+               <widget class="SMCheckBox" name="keepWithNext">
+                <property name="text">
+                 <string>Keep with next paragraph</string>
+                </property>
+               </widget>
+              </item>
+             </layout>
             </widget>
            </item>
            <item>
             <spacer>
-             <property name="orientation" >
+             <property name="orientation">
               <enum>Qt::Vertical</enum>
              </property>
-             <property name="sizeType" >
+             <property name="sizeType">
               <enum>QSizePolicy::MinimumExpanding</enum>
              </property>
-             <property name="sizeHint" stdset="0" >
+             <property name="sizeHint" stdset="0">
               <size>
                <width>272</width>
                <height>0</height>
@@ -354,67 +415,67 @@
           </layout>
          </item>
          <item>
-          <layout class="QVBoxLayout" name="verticalLayout_3" >
+          <layout class="QVBoxLayout" name="verticalLayout_3">
            <item>
-            <widget class="QGroupBox" name="groupBox" >
-             <property name="font" >
+            <widget class="QGroupBox" name="groupBox">
+             <property name="font">
               <font>
                <weight>50</weight>
                <bold>false</bold>
               </font>
              </property>
-             <property name="title" >
+             <property name="title">
               <string>Optical Margins</string>
              </property>
-             <layout class="QFormLayout" name="formLayout" >
-              <property name="fieldGrowthPolicy" >
+             <layout class="QFormLayout" name="formLayout">
+              <property name="fieldGrowthPolicy">
                <enum>QFormLayout::AllNonFixedFieldsGrow</enum>
               </property>
-              <item row="4" column="1" >
-               <layout class="QHBoxLayout" name="horizontalLayout_4" >
+              <item row="4" column="1">
+               <layout class="QHBoxLayout" name="horizontalLayout_4">
                 <item>
-                 <widget class="QPushButton" name="optMarginDefaultButton" >
-                  <property name="text" >
+                 <widget class="QPushButton" name="optMarginDefaultButton">
+                  <property name="text">
                    <string>Reset to Default</string>
                   </property>
                  </widget>
                 </item>
                 <item>
-                 <widget class="QPushButton" name="optMarginParentButton" >
-                  <property name="text" >
+                 <widget class="QPushButton" name="optMarginParentButton">
+                  <property name="text">
                    <string>Use Parent Value</string>
                   </property>
                  </widget>
                 </item>
                </layout>
               </item>
-              <item row="0" column="1" >
-               <widget class="SMRadioButton" name="optMarginRadioNone" >
-                <property name="text" >
+              <item row="0" column="1">
+               <widget class="SMRadioButton" name="optMarginRadioNone">
+                <property name="text">
                  <string>None</string>
                 </property>
-                <property name="checked" >
+                <property name="checked">
                  <bool>true</bool>
                 </property>
                </widget>
               </item>
-              <item row="1" column="1" >
-               <widget class="SMRadioButton" name="optMarginRadioBoth" >
-                <property name="text" >
+              <item row="1" column="1">
+               <widget class="SMRadioButton" name="optMarginRadioBoth">
+                <property name="text">
                  <string>Both Sides</string>
                 </property>
                </widget>
               </item>
-              <item row="2" column="1" >
-               <widget class="SMRadioButton" name="optMarginRadioLeft" >
-                <property name="text" >
+              <item row="2" column="1">
+               <widget class="SMRadioButton" name="optMarginRadioLeft">
+                <property name="text">
                  <string>Left Only</string>
                 </property>
                </widget>
               </item>
-              <item row="3" column="1" >
-               <widget class="SMRadioButton" name="optMarginRadioRight" >
-                <property name="text" >
+              <item row="3" column="1">
+               <widget class="SMRadioButton" name="optMarginRadioRight">
+                <property name="text">
                  <string>Right Only</string>
                 </property>
                </widget>
@@ -423,56 +484,56 @@
             </widget>
            </item>
            <item>
-            <widget class="QGroupBox" name="groupBox_2" >
-             <property name="enabled" >
+            <widget class="QGroupBox" name="groupBox_2">
+             <property name="enabled">
               <bool>true</bool>
              </property>
-             <property name="sizePolicy" >
-              <sizepolicy vsizetype="Minimum" hsizetype="Preferred" >
+             <property name="sizePolicy">
+              <sizepolicy hsizetype="Preferred" vsizetype="Minimum">
                <horstretch>0</horstretch>
                <verstretch>0</verstretch>
               </sizepolicy>
              </property>
-             <property name="minimumSize" >
+             <property name="minimumSize">
               <size>
                <width>0</width>
                <height>163</height>
               </size>
              </property>
-             <property name="title" >
+             <property name="title">
               <string>Advanced Settings</string>
              </property>
-             <layout class="QGridLayout" name="gridLayout" >
-              <item row="0" column="0" >
-               <layout class="QHBoxLayout" name="horizontalLayout" >
-                <property name="sizeConstraint" >
+             <layout class="QGridLayout" name="gridLayout">
+              <item row="0" column="0">
+               <layout class="QHBoxLayout" name="horizontalLayout">
+                <property name="sizeConstraint">
                  <enum>QLayout::SetDefaultConstraint</enum>
                 </property>
-                <property name="topMargin" >
+                <property name="topMargin">
                  <number>0</number>
                 </property>
-                <property name="bottomMargin" >
+                <property name="bottomMargin">
                  <number>0</number>
                 </property>
                 <item>
-                 <widget class="QLabel" name="minSpaceLabel" >
-                  <property name="text" >
+                 <widget class="QLabel" name="minSpaceLabel">
+                  <property name="text">
                    <string>TextLabel</string>
                   </property>
-                  <property name="buddy" >
+                  <property name="buddy">
                    <cstring>minSpaceSpin</cstring>
                   </property>
                  </widget>
                 </item>
                 <item>
-                 <widget class="SMScrSpinBox" name="minSpaceSpin" >
-                  <property name="minimumSize" >
+                 <widget class="SMScrSpinBox" name="minSpaceSpin">
+                  <property name="minimumSize">
                    <size>
                     <width>0</width>
                     <height>32</height>
                    </size>
                   </property>
-                  <property name="baseSize" >
+                  <property name="baseSize">
                    <size>
                     <width>0</width>
                     <height>0</height>
@@ -481,11 +542,11 @@
                  </widget>
                 </item>
                 <item>
-                 <spacer name="horizontalSpacer" >
-                  <property name="orientation" >
+                 <spacer name="horizontalSpacer">
+                  <property name="orientation">
                    <enum>Qt::Horizontal</enum>
                   </property>
-                  <property name="sizeHint" stdset="0" >
+                  <property name="sizeHint" stdset="0">
                    <size>
                     <width>40</width>
                     <height>20</height>
@@ -495,30 +556,30 @@
                 </item>
                </layout>
               </item>
-              <item row="1" column="0" >
-               <widget class="QLabel" name="glyphExtensionLabel" >
-                <property name="text" >
+              <item row="1" column="0">
+               <widget class="QLabel" name="glyphExtensionLabel">
+                <property name="text">
                  <string>TextLabel</string>
                 </property>
                </widget>
               </item>
-              <item row="2" column="0" >
-               <layout class="QHBoxLayout" name="horizontalLayout_6" >
+              <item row="2" column="0">
+               <layout class="QHBoxLayout" name="horizontalLayout_6">
                 <item>
-                 <layout class="QHBoxLayout" name="horizontalLayout_2" >
+                 <layout class="QHBoxLayout" name="horizontalLayout_2">
                   <item>
-                   <widget class="QLabel" name="minGlyphExtLabel" >
-                    <property name="text" >
+                   <widget class="QLabel" name="minGlyphExtLabel">
+                    <property name="text">
                      <string>TextLabel</string>
                     </property>
-                    <property name="buddy" >
+                    <property name="buddy">
                      <cstring>minGlyphExtSpin</cstring>
                     </property>
                    </widget>
                   </item>
                   <item>
-                   <widget class="SMScrSpinBox" name="minGlyphExtSpin" >
-                    <property name="minimumSize" >
+                   <widget class="SMScrSpinBox" name="minGlyphExtSpin">
+                    <property name="minimumSize">
                      <size>
                       <width>0</width>
                       <height>32</height>
@@ -529,20 +590,20 @@
                  </layout>
                 </item>
                 <item>
-                 <layout class="QHBoxLayout" name="horizontalLayout_3" >
+                 <layout class="QHBoxLayout" name="horizontalLayout_3">
                   <item>
-                   <widget class="QLabel" name="maxGlyphExtLabel" >
-                    <property name="text" >
+                   <widget class="QLabel" name="maxGlyphExtLabel">
+                    <property name="text">
                      <string>TextLabel</string>
                     </property>
-                    <property name="buddy" >
+                    <property name="buddy">
                      <cstring>maxGlyphExtSpin</cstring>
                     </property>
                    </widget>
                   </item>
                   <item>
-                   <widget class="SMScrSpinBox" name="maxGlyphExtSpin" >
-                    <property name="minimumSize" >
+                   <widget class="SMScrSpinBox" name="maxGlyphExtSpin">
+                    <property name="minimumSize">
                      <size>
                       <width>0</width>
                       <height>32</height>
@@ -559,13 +620,13 @@
            </item>
            <item>
             <spacer>
-             <property name="orientation" >
+             <property name="orientation">
               <enum>Qt::Vertical</enum>
              </property>
-             <property name="sizeType" >
+             <property name="sizeType">
               <enum>QSizePolicy::Expanding</enum>
              </property>
-             <property name="sizeHint" stdset="0" >
+             <property name="sizeHint" stdset="0">
               <size>
                <width>270</width>
                <height>16</height>
@@ -578,23 +639,23 @@
         </layout>
        </item>
        <item>
-        <widget class="QGroupBox" name="tabsBox" >
-         <property name="sizePolicy" >
-          <sizepolicy vsizetype="Expanding" hsizetype="Preferred" >
+        <widget class="QGroupBox" name="tabsBox">
+         <property name="sizePolicy">
+          <sizepolicy hsizetype="Preferred" vsizetype="Expanding">
            <horstretch>0</horstretch>
            <verstretch>0</verstretch>
           </sizepolicy>
          </property>
-         <property name="title" >
+         <property name="title">
           <string>Tabulators and Indentation</string>
          </property>
-         <layout class="QVBoxLayout" >
+         <layout class="QVBoxLayout">
           <item>
-           <layout class="QHBoxLayout" >
+           <layout class="QHBoxLayout">
             <item>
-             <widget class="SMTabruler" native="1" name="tabList_" >
-              <property name="sizePolicy" >
-               <sizepolicy vsizetype="MinimumExpanding" hsizetype="MinimumExpanding" >
+             <widget class="SMTabruler" name="tabList_" native="true">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
                 <horstretch>0</horstretch>
                 <verstretch>0</verstretch>
                </sizepolicy>
@@ -603,10 +664,10 @@
             </item>
             <item>
              <spacer>
-              <property name="orientation" >
+              <property name="orientation">
                <enum>Qt::Horizontal</enum>
               </property>
-              <property name="sizeHint" stdset="0" >
+              <property name="sizeHint" stdset="0">
                <size>
                 <width>40</width>
                 <height>20</height>
@@ -620,11 +681,11 @@
         </widget>
        </item>
        <item>
-        <spacer name="verticalSpacer" >
-         <property name="orientation" >
+        <spacer name="verticalSpacer">
+         <property name="orientation">
           <enum>Qt::Vertical</enum>
          </property>
-         <property name="sizeHint" stdset="0" >
+         <property name="sizeHint" stdset="0">
           <size>
            <width>20</width>
            <height>0</height>
@@ -634,34 +695,34 @@
        </item>
       </layout>
      </widget>
-     <widget class="QWidget" name="TabPage" >
-      <attribute name="title" >
+     <widget class="QWidget" name="TabPage">
+      <attribute name="title">
        <string>Ch&amp;aracter Style</string>
       </attribute>
-      <layout class="QVBoxLayout" >
-       <property name="spacing" >
+      <layout class="QVBoxLayout">
+       <property name="spacing">
         <number>5</number>
        </property>
-       <property name="margin" >
+       <property name="margin">
         <number>0</number>
        </property>
        <item>
-        <widget class="QFrame" name="characterBox" >
-         <property name="enabled" >
+        <widget class="QFrame" name="characterBox">
+         <property name="enabled">
           <bool>true</bool>
          </property>
-         <property name="frameShape" >
+         <property name="frameShape">
           <enum>QFrame::NoFrame</enum>
          </property>
-         <property name="frameShadow" >
+         <property name="frameShadow">
           <enum>QFrame::Plain</enum>
          </property>
-         <property name="lineWidth" >
+         <property name="lineWidth">
           <number>0</number>
          </property>
-         <layout class="QGridLayout" >
-          <item row="0" column="0" >
-           <widget class="SMCStyleWidget" native="1" name="cpage" />
+         <layout class="QGridLayout">
+          <item row="0" column="0">
+           <widget class="SMCStyleWidget" name="cpage" native="true"/>
           </item>
          </layout>
         </widget>
@@ -672,7 +733,7 @@
    </item>
   </layout>
  </widget>
- <layoutdefault spacing="5" margin="5" />
+ <layoutdefault spacing="5" margin="5"/>
  <customwidgets>
   <customwidget>
    <class>SMScComboBox</class>
@@ -712,6 +773,11 @@
    <extends>QRadioButton</extends>
    <header>ui/smradiobutton.h</header>
   </customwidget>
+  <customwidget>
+   <class>SMCheckBox</class>
+   <extends>QCheckBox</extends>
+   <header>ui/smcheckbox.h</header>
+  </customwidget>
  </customwidgets>
  <resources/>
  <connections/>
Index: scribus/ui/smtextstyles.cpp
===================================================================
--- scribus/ui/smtextstyles.cpp	(revision 16811)
+++ scribus/ui/smtextstyles.cpp	(working copy)
@@ -516,6 +516,11 @@
 	connect(pwidget_->dropCapLines_, SIGNAL(valueChanged(int)), this, SLOT(slotDropCapLines(int)));
 	connect(pwidget_->dropCapOffset_, SIGNAL(valueChanged(double)), this, SLOT(slotDropCapOffset()));
 
+	connect(pwidget_->keepLinesStart, SIGNAL(valueChanged(int)), this, SLOT(handleKeepLinesStart()));
+	connect(pwidget_->keepLinesEnd, SIGNAL(valueChanged(int)), this, SLOT(handleKeepLinesEnd()));
+	connect(pwidget_->keepTogether, SIGNAL(stateChanged(int)), this, SLOT(handleKeepTogether()));
+	connect(pwidget_->keepWithNext, SIGNAL(stateChanged(int)), this, SLOT(handleKeepWithNext()));
+
 	connect(pwidget_->tabList_, SIGNAL(tabsChanged()), this, SLOT(slotTabRuler()));
 	connect(pwidget_->tabList_, SIGNAL(mouseReleased()), this, SLOT(slotTabRuler()));
 	connect(pwidget_->tabList_->left_, SIGNAL(valueChanged(double)), this, SLOT(slotLeftIndent()));
@@ -591,6 +596,11 @@
 	disconnect(pwidget_->parentCombo, SIGNAL(activated(const QString&)),
 			this, SLOT(slotParentChanged(const QString&)));
 
+	disconnect(pwidget_->keepLinesStart, SIGNAL(valueChanged(int)), this, SLOT(handleKeepLinesStart()));
+	disconnect(pwidget_->keepLinesEnd, SIGNAL(valueChanged(int)), this, SLOT(handleKeepLinesEnd()));
+	disconnect(pwidget_->keepTogether, SIGNAL(stateChanged(int)), this, SLOT(handleKeepTogether()));
+	disconnect(pwidget_->keepWithNext, SIGNAL(stateChanged(int)), this, SLOT(handleKeepWithNext()));
+
 	disconnect(pwidget_->tabList_, SIGNAL(tabsChanged()), this, SLOT(slotTabRuler()));
 	disconnect(pwidget_->tabList_->left_, SIGNAL(valueChanged(double)), this, SLOT(slotLeftIndent()));
 	disconnect(pwidget_->tabList_->right_, SIGNAL(valueChanged(double)), this, SLOT(slotRightIndent()));
@@ -901,6 +911,82 @@
 	}
 }
 
+void SMParagraphStyle::handleKeepLinesStart()
+{
+	if (pwidget_->keepLinesStart->useParentValue())
+		for (int i = 0; i < selection_.count(); ++i)
+			selection_[i]->resetKeepLinesStart();
+	else 
+	{
+		int value = pwidget_->keepLinesStart->value();
+		for (int i = 0; i < selection_.count(); ++i)
+			selection_[i]->setKeepLinesStart (value);
+	}
+	
+	if (!selectionIsDirty_)
+	{
+		selectionIsDirty_ = true;
+		emit selectionDirty();
+	}
+}
+
+void SMParagraphStyle::handleKeepLinesEnd()
+{
+	if (pwidget_->keepLinesEnd->useParentValue())
+		for (int i = 0; i < selection_.count(); ++i)
+			selection_[i]->resetKeepLinesEnd();
+	else 
+	{
+		int value = pwidget_->keepLinesEnd->value();
+		for (int i = 0; i < selection_.count(); ++i)
+			selection_[i]->setKeepLinesEnd (value);
+	}
+	
+	if (!selectionIsDirty_)
+	{
+		selectionIsDirty_ = true;
+		emit selectionDirty();
+	}
+}
+
+void SMParagraphStyle::handleKeepTogether()
+{
+	if (pwidget_->keepTogether->useParentValue())
+		for (int i = 0; i < selection_.count(); ++i)
+			selection_[i]->resetKeepTogether();
+	else 
+	{
+		bool value = pwidget_->keepTogether->isChecked();
+		for (int i = 0; i < selection_.count(); ++i)
+			selection_[i]->setKeepTogether (value);
+	}
+	
+	if (!selectionIsDirty_)
+	{
+		selectionIsDirty_ = true;
+		emit selectionDirty();
+	}
+}
+
+void SMParagraphStyle::handleKeepWithNext()
+{
+	if (pwidget_->keepWithNext->useParentValue())
+		for (int i = 0; i < selection_.count(); ++i)
+			selection_[i]->resetKeepWithNext();
+	else 
+	{
+		bool value = pwidget_->keepWithNext->isChecked();
+		for (int i = 0; i < selection_.count(); ++i)
+			selection_[i]->setKeepWithNext (value);
+	}
+	
+	if (!selectionIsDirty_)
+	{
+		selectionIsDirty_ = true;
+		emit selectionDirty();
+	}
+}
+
 void SMParagraphStyle::slotTabRuler()
 {
 	if (pwidget_->tabList_->useParentTabs())
Index: scribus/styles/paragraphstyle.attrdefs.cxx
===================================================================
--- scribus/styles/paragraphstyle.attrdefs.cxx	(revision 16811)
+++ scribus/styles/paragraphstyle.attrdefs.cxx	(working copy)
@@ -35,5 +35,8 @@
 ATTRDEF(bool, hasDropCap, HasDropCap, false)
 ATTRDEF(double, dropCapOffset, DropCapOffset, 0.0)
 ATTRDEF(bool, useBaselineGrid, UseBaselineGrid, false)
+ATTRDEF(int, keepLinesStart, KeepLinesStart, 0)
+ATTRDEF(int, keepLinesEnd, KeepLinesEnd, 0)
+ATTRDEF(bool, keepWithNext, KeepWithNext, false)
+ATTRDEF(bool, keepTogether, KeepTogether, false)
 
-
Index: scribus/text/storytext.h
===================================================================
--- scribus/text/storytext.h	(revision 16811)
+++ scribus/text/storytext.h	(working copy)
@@ -288,7 +288,21 @@
 			lastFrameItem = qMax(lastFrameItem, ls.lastItem);
 		}
 	}
-	
+
+	// Remove the last line from the list. Used when we need to backtrack on the layouting.
+	void removeLastLine ()
+	{
+		if (m_lines.isEmpty()) return;
+		LineSpec last = m_lines.takeLast ();
+		if (m_lines.isEmpty()) {
+			clearLines();
+			return;
+		}
+		// fix lastFrameItem
+		if (lastFrameItem != last.lastItem) return;
+		lastFrameItem = m_lines.last().lastItem;
+	}
+
 	void clearLines() 
 	{ 
 		m_lines.clear(); 
Index: scribus/scribus.cpp
===================================================================
--- scribus/scribus.cpp	(revision 16811)
+++ scribus/scribus.cpp	(working copy)
@@ -1107,7 +1107,7 @@
 		doc->currentStyle.charStyle().setStyle( currItem->currentCharStyle() );
 		emit TextStyle(doc->currentStyle);
 		// to go: (av)
-		propertiesPalette->textPal->updateCharStyle(doc->currentStyle.charStyle());
+		propertiesPalette->textPal->updateStyle(doc->currentStyle);
 	}
 }
 
@@ -2843,7 +2843,7 @@
 			propertiesPalette->textPal->displayCharStyle(doc->currentStyle.charStyle().parent());
 			emit TextStyle(doc->currentStyle);
 			// to go: (av)
-			propertiesPalette->textPal->updateCharStyle(doc->currentStyle.charStyle());
+			propertiesPalette->textPal->updateStyle(doc->currentStyle);
 			setStyleEffects(doc->currentStyle.charStyle().effects());
 		}
 
@@ -2909,7 +2909,7 @@
 			propertiesPalette->textPal->displayCharStyle(doc->currentStyle.charStyle().parent());
 			emit TextStyle(doc->currentStyle);
 			// to go: (av)
-			propertiesPalette->textPal->updateCharStyle(doc->currentStyle.charStyle());
+			propertiesPalette->textPal->updateStyle(doc->currentStyle);
 			setStyleEffects(doc->currentStyle.charStyle().effects());
 		}
 		break;
Index: scribus/pageitem_textframe.h
===================================================================
--- scribus/pageitem_textframe.h	(revision 16811)
+++ scribus/pageitem_textframe.h	(working copy)
@@ -98,9 +98,17 @@
 	virtual bool createInfoGroup(QFrame *, QGridLayout *);
 	virtual void applicableActions(QStringList& actionList);
 	virtual QString infoDescription();
-	
+	// Move incomplete lines from the previous frame if needed.
+	bool moveLinesFromPreviousFrame ();
+	void adjustParagraphEndings ();
+
 private:
 	bool cursorBiasBackward;
+	// If the last paragraph had to be split, this is how many lines of the paragraph are in this frame.
+	// Used for orphan/widow control
+	int incompleteLines;
+	// This holds the line splitting positions
+	QList<int> incompletePositions;
 
 	void setShadow();
 	QString currentShadow;
Index: scribus/plugins/fileloader/scribus150format/scribus150format_save.cpp
===================================================================
--- scribus/plugins/fileloader/scribus150format/scribus150format_save.cpp	(revision 16811)
+++ scribus/plugins/fileloader/scribus150format/scribus150format_save.cpp	(working copy)
@@ -619,6 +619,14 @@
 		docu.writeAttribute("MinGlyphShrink", style.minGlyphExtension());
 	if ( ! style.isInhMaxGlyphExtension())
 		docu.writeAttribute("MaxGlyphExtend", style.maxGlyphExtension());
+	if ( ! style.isInhKeepLinesStart())
+		docu.writeAttribute("KeepLinesStart", style.keepLinesStart());
+	if ( ! style.isInhKeepLinesEnd())
+		docu.writeAttribute("KeepLinesEnd", style.keepLinesEnd());
+	if ( ! style.isInhKeepWithNext())
+		docu.writeAttribute("KeepWithNext", style.keepWithNext());
+	if ( ! style.isInhKeepTogether())
+		docu.writeAttribute("KeepTogether", style.keepTogether());
 
 	if ( ! style.shortcut().isEmpty() )
 		docu.writeAttribute("PSHORTCUT", style.shortcut()); // shortcuts won't be inherited
@@ -1850,64 +1858,73 @@
 		docu.writeAttribute("PICART", item->imageShown() ? 1 : 0);
 		docu.writeAttribute("SCALETYPE", item->ScaleType ? 1 : 0);
 		docu.writeAttribute("RATIO", item->AspectRatio ? 1 : 0);
-		if ( ! item->itemText.defaultStyle().charStyle().isInhFillColor())
-			docu.writeAttribute("TXTFILL",item->itemText.defaultStyle().charStyle().fillColor());
-		if ( ! item->itemText.defaultStyle().charStyle().isInhStrokeColor())
-			docu.writeAttribute("TXTSTROKE",item->itemText.defaultStyle().charStyle().strokeColor());
-		if ( ! item->itemText.defaultStyle().charStyle().isInhStrokeShade())
-			docu.writeAttribute("TXTSTRSH",item->itemText.defaultStyle().charStyle().strokeShade());
-		if ( ! item->itemText.defaultStyle().charStyle().isInhFillShade())
-			docu.writeAttribute("TXTFILLSH",item->itemText.defaultStyle().charStyle().fillShade());
-		if ( ! item->itemText.defaultStyle().charStyle().isInhScaleH())
-			docu.writeAttribute("TXTSCALE",item->itemText.defaultStyle().charStyle().scaleH() / 10.0);
-		if ( ! item->itemText.defaultStyle().charStyle().isInhScaleV())
-			docu.writeAttribute("TXTSCALEV",item->itemText.defaultStyle().charStyle().scaleV() / 10.0);
-		if ( ! item->itemText.defaultStyle().charStyle().isInhBaselineOffset())
-			docu.writeAttribute("TXTBASE",item->itemText.defaultStyle().charStyle().baselineOffset() / 10.0);
-		if ( ! item->itemText.defaultStyle().charStyle().isInhShadowXOffset())
-			docu.writeAttribute("TXTSHX",item->itemText.defaultStyle().charStyle().shadowXOffset() / 10.0);
-		if ( ! item->itemText.defaultStyle().charStyle().isInhShadowYOffset())
-			docu.writeAttribute("TXTSHY",item->itemText.defaultStyle().charStyle().shadowYOffset() / 10.0);
-		if ( ! item->itemText.defaultStyle().charStyle().isInhOutlineWidth())
-			docu.writeAttribute("TXTOUT",item->itemText.defaultStyle().charStyle().outlineWidth() / 10.0);
-		if ( ! item->itemText.defaultStyle().charStyle().isInhUnderlineOffset())
-			docu.writeAttribute("TXTULP",item->itemText.defaultStyle().charStyle().underlineOffset() / 10.0);
-		if ( ! item->itemText.defaultStyle().charStyle().isInhUnderlineWidth())
-			docu.writeAttribute("TXTULW",item->itemText.defaultStyle().charStyle().underlineWidth() / 10.0);
-		if ( ! item->itemText.defaultStyle().charStyle().isInhStrikethruOffset())
-			docu.writeAttribute("TXTSTP",item->itemText.defaultStyle().charStyle().strikethruOffset() / 10.0);
-		if ( ! item->itemText.defaultStyle().charStyle().isInhStrikethruWidth())
-			docu.writeAttribute("TXTSTW",item->itemText.defaultStyle().charStyle().strikethruWidth() / 10.0);
-		if ( ! item->itemText.defaultStyle().charStyle().isInhTracking())
-			docu.writeAttribute("TXTKERN",item->itemText.defaultStyle().charStyle().tracking() / 10.0);
-		if ( ! item->itemText.defaultStyle().charStyle().isInhWordTracking())
-			docu.writeAttribute("wordTrack",item->itemText.defaultStyle().charStyle().wordTracking());
-		if ( ! item->itemText.defaultStyle().isInhMinWordTracking())
-			docu.writeAttribute("MinWordTrack", item->itemText.defaultStyle().minWordTracking());
-		if ( ! item->itemText.defaultStyle().isInhMinGlyphExtension())
-			docu.writeAttribute("MinGlyphShrink", item->itemText.defaultStyle().minGlyphExtension());
-		if ( ! item->itemText.defaultStyle().isInhMaxGlyphExtension())
-			docu.writeAttribute("MaxGlyphExtend", item->itemText.defaultStyle().maxGlyphExtension());
-		if ( ! item->itemText.defaultStyle().isInhOpticalMargins())
-			docu.writeAttribute("OpticalMargins", item->itemText.defaultStyle().opticalMargins());
-		if ( ! item->itemText.defaultStyle().isInhHyphenationMode())
-			docu.writeAttribute("HyphenationMode", item->itemText.defaultStyle().hyphenationMode());
-		if ( ! item->itemText.defaultStyle().isInhLeftMargin() )
-			docu.writeAttribute("leftMargin", item->itemText.defaultStyle().leftMargin());
-		if ( ! item->itemText.defaultStyle().isInhRightMargin())
-			docu.writeAttribute("rightMargin", item->itemText.defaultStyle().rightMargin());
-		if ( ! item->itemText.defaultStyle().isInhFirstIndent())
-			docu.writeAttribute("firstIndent", item->itemText.defaultStyle().firstIndent());
-		if ( ! item->itemText.defaultStyle().isInhLineSpacing())
-			docu.writeAttribute("LINESP",item->itemText.defaultStyle().lineSpacing());
-		if ( ! item->itemText.defaultStyle().isInhLineSpacingMode())
-			docu.writeAttribute("LINESPMode", item->itemText.defaultStyle().lineSpacingMode());
-		if ( ! item->itemText.defaultStyle().charStyle().isInhFont())
-			docu.writeAttribute("IFONT",item->itemText.defaultStyle().charStyle().font().scName());
-		if ( ! item->itemText.defaultStyle().charStyle().isInhFontSize())
-			docu.writeAttribute("ISIZE",item->itemText.defaultStyle().charStyle().fontSize() / 10.0 );
-		if ( ! item->itemText.defaultStyle().charStyle().isInhLanguage())
-			docu.writeAttribute("LANGUAGE", item->itemText.defaultStyle().charStyle().language());
+		ParagraphStyle dStyle = item->itemText.defaultStyle();
+		if ( ! dStyle.charStyle().isInhFillColor())
+			docu.writeAttribute("TXTFILL",dStyle.charStyle().fillColor());
+		if ( ! dStyle.charStyle().isInhStrokeColor())
+			docu.writeAttribute("TXTSTROKE",dStyle.charStyle().strokeColor());
+		if ( ! dStyle.charStyle().isInhStrokeShade())
+			docu.writeAttribute("TXTSTRSH",dStyle.charStyle().strokeShade());
+		if ( ! dStyle.charStyle().isInhFillShade())
+			docu.writeAttribute("TXTFILLSH",dStyle.charStyle().fillShade());
+		if ( ! dStyle.charStyle().isInhScaleH())
+			docu.writeAttribute("TXTSCALE",dStyle.charStyle().scaleH() / 10.0);
+		if ( ! dStyle.charStyle().isInhScaleV())
+			docu.writeAttribute("TXTSCALEV",dStyle.charStyle().scaleV() / 10.0);
+		if ( ! dStyle.charStyle().isInhBaselineOffset())
+			docu.writeAttribute("TXTBASE",dStyle.charStyle().baselineOffset() / 10.0);
+		if ( ! dStyle.charStyle().isInhShadowXOffset())
+			docu.writeAttribute("TXTSHX",dStyle.charStyle().shadowXOffset() / 10.0);
+		if ( ! dStyle.charStyle().isInhShadowYOffset())
+			docu.writeAttribute("TXTSHY",dStyle.charStyle().shadowYOffset() / 10.0);
+		if ( ! dStyle.charStyle().isInhOutlineWidth())
+			docu.writeAttribute("TXTOUT",dStyle.charStyle().outlineWidth() / 10.0);
+		if ( ! dStyle.charStyle().isInhUnderlineOffset())
+			docu.writeAttribute("TXTULP",dStyle.charStyle().underlineOffset() / 10.0);
+		if ( ! dStyle.charStyle().isInhUnderlineWidth())
+			docu.writeAttribute("TXTULW",dStyle.charStyle().underlineWidth() / 10.0);
+		if ( ! dStyle.charStyle().isInhStrikethruOffset())
+			docu.writeAttribute("TXTSTP",dStyle.charStyle().strikethruOffset() / 10.0);
+		if ( ! dStyle.charStyle().isInhStrikethruWidth())
+			docu.writeAttribute("TXTSTW",dStyle.charStyle().strikethruWidth() / 10.0);
+		if ( ! dStyle.charStyle().isInhTracking())
+			docu.writeAttribute("TXTKERN",dStyle.charStyle().tracking() / 10.0);
+		if ( ! dStyle.charStyle().isInhWordTracking())
+			docu.writeAttribute("wordTrack",dStyle.charStyle().wordTracking());
+		if ( ! dStyle.isInhMinWordTracking())
+			docu.writeAttribute("MinWordTrack", dStyle.minWordTracking());
+		if ( ! dStyle.isInhMinGlyphExtension())
+			docu.writeAttribute("MinGlyphShrink", dStyle.minGlyphExtension());
+		if ( ! dStyle.isInhMaxGlyphExtension())
+			docu.writeAttribute("MaxGlyphExtend", dStyle.maxGlyphExtension());
+		if ( ! dStyle.isInhOpticalMargins())
+			docu.writeAttribute("OpticalMargins", dStyle.opticalMargins());
+		if ( ! dStyle.isInhHyphenationMode())
+			docu.writeAttribute("HyphenationMode", dStyle.hyphenationMode());
+		if ( ! dStyle.isInhLeftMargin() )
+			docu.writeAttribute("leftMargin", dStyle.leftMargin());
+		if ( ! dStyle.isInhRightMargin())
+			docu.writeAttribute("rightMargin", dStyle.rightMargin());
+		if ( ! dStyle.isInhFirstIndent())
+			docu.writeAttribute("firstIndent", dStyle.firstIndent());
+		if ( ! dStyle.isInhLineSpacing())
+			docu.writeAttribute("LINESP",dStyle.lineSpacing());
+		if ( ! dStyle.isInhLineSpacingMode())
+			docu.writeAttribute("LINESPMode", dStyle.lineSpacingMode());
+		if ( ! dStyle.isInhKeepLinesStart())
+			docu.writeAttribute("KeepLinesStart", dStyle.keepLinesStart());
+		if ( ! dStyle.isInhKeepLinesEnd())
+			docu.writeAttribute("KeepLinesEnd", dStyle.keepLinesEnd());
+		if ( ! dStyle.isInhKeepWithNext())
+			docu.writeAttribute("KeepWithNext", dStyle.keepWithNext());
+		if ( ! dStyle.isInhKeepTogether())
+			docu.writeAttribute("KeepTogether", dStyle.keepTogether());
+		if ( ! dStyle.charStyle().isInhFont())
+			docu.writeAttribute("IFONT",dStyle.charStyle().font().scName());
+		if ( ! dStyle.charStyle().isInhFontSize())
+			docu.writeAttribute("ISIZE",dStyle.charStyle().fontSize() / 10.0 );
+		if ( ! dStyle.charStyle().isInhLanguage())
+			docu.writeAttribute("LANGUAGE", dStyle.charStyle().language());
 	}
 	if (item->asTextFrame() || item->asPathText())
 	{
Index: scribus/plugins/fileloader/scribus150format/scribus150format.cpp
===================================================================
--- scribus/plugins/fileloader/scribus150format/scribus150format.cpp	(revision 16811)
+++ scribus/plugins/fileloader/scribus150format/scribus150format.cpp	(working copy)
@@ -2117,6 +2117,22 @@
 	if (attrs.hasAttribute(MaxGlyphExtend))
 		newStyle.setMaxGlyphExtension(attrs.valueAsDouble(MaxGlyphExtend));
 	
+	static const QString KeepLinesStart("KeepLinesStart");
+	if (attrs.hasAttribute(KeepLinesStart))
+		newStyle.setKeepLinesStart(attrs.valueAsInt(KeepLinesStart));
+
+	static const QString KeepLinesEnd("KeepLinesEnd");
+	if (attrs.hasAttribute(KeepLinesEnd))
+		newStyle.setKeepLinesEnd(attrs.valueAsInt(KeepLinesEnd));
+
+	static const QString KeepWithNext("KeepWithNext");
+	if (attrs.hasAttribute(KeepWithNext))
+		newStyle.setKeepWithNext(attrs.valueAsInt(KeepWithNext));
+
+	static const QString KeepTogether("KeepTogether");
+	if (attrs.hasAttribute(KeepTogether))
+		newStyle.setKeepTogether(attrs.valueAsInt(KeepTogether));
+
 	readCharacterStyleAttrs( doc, attrs, newStyle.charStyle());
 
 	//	newStyle.tabValues().clear();
@@ -3534,6 +3550,14 @@
 		pstyle.setRightMargin(attrs.valueAsDouble("rightMargin"));
 	if (attrs.hasAttribute("firstIndent"))
 		pstyle.setFirstIndent(attrs.valueAsDouble("firstIndent"));
+	if (attrs.hasAttribute("keepLinesStart"))
+		pstyle.setKeepLinesStart(attrs.valueAsInt("keepLinesStart"));
+	if (attrs.hasAttribute("keepLinesEnd"))
+		pstyle.setKeepLinesEnd(attrs.valueAsInt("keepLinesEnd"));
+	if (attrs.hasAttribute("keepWithNext"))
+		pstyle.setKeepWithNext(attrs.valueAsBool("keepWithNext"));
+	if (attrs.hasAttribute("keepTogether"))
+		pstyle.setKeepTogether(attrs.valueAsBool("keepTogether"));
 	currItem->itemText.setDefaultStyle(pstyle);
 
 	if (attrs.hasAttribute("PSTYLE"))
Index: scribus/CMakeLists.txt
===================================================================
--- scribus/CMakeLists.txt	(revision 16811)
+++ scribus/CMakeLists.txt	(working copy)
@@ -131,6 +131,7 @@
   ui/propertywidget_advancedbase.ui
   ui/propertywidget_distancebase.ui
   ui/propertywidget_flopbase.ui
+  ui/propertywidget_flowbase.ui
   ui/propertywidget_optmarginsbase.ui
   ui/propertywidget_pathtextbase.ui
   ui/propertywidget_textcolorbase.ui
@@ -362,6 +363,7 @@
   ui/propertywidget_advanced.h
   ui/propertywidget_distance.h
   ui/propertywidget_flop.h
+  ui/propertywidget_flow.h
   ui/propertywidget_optmargins.h
   ui/propertywidget_pathtext.h
   ui/propertywidget_textcolor.h
@@ -758,6 +760,7 @@
   ui/propertywidget_advanced.cpp
   ui/propertywidget_distance.cpp
   ui/propertywidget_flop.cpp
+  ui/propertywidget_flow.cpp
   ui/propertywidget_optmargins.cpp
   ui/propertywidget_pathtext.cpp
   ui/propertywidget_textcolor.cpp
scribus-textflow.patch (108,064 bytes)   

Vladimir Savic

2011-08-28 21:19

reporter   ~0026809

I want this one in! Not that my vote counts, but I run into this problem a lot.

cezaryece

2011-08-28 21:20

updater   ~0026810

Really nice work!
But for me there is need for one more thing - add to document checker (Preflight Verifier) warnings where text was changed due to flow options. And in addition maybe it should be noticed be some unprintable signs at the edge of column/frame (like "no space for text" sign at the right bottom of frame if text don`t fit in it).
For me any changes in text flow should be done automatically without user notice!

mecirt

2011-08-29 08:06

reporter  

mecirt

2011-08-29 08:08

reporter   ~0026811

Forgot to include one file - it's attached, belongs to scribus/ui. Note that there are two more new files in the patch (ui/propertywidget_flow.cpp and ui/propertywidget_flow.h), these obviously need to be added to svn as well :)

Not sure if preflight needs to react on these - all the textflow stuff is in paragraph style and is disabled by default - if you don't enable the related options, you get no flow changes.

cezaryece

2011-08-30 05:16

updater   ~0026813

At least preflight should react if these flow controls inserts empty lines at columns`s bottom.

StefanM

2011-09-02 09:26

reporter   ~0026822

Thanks for the work. Your description reads great.

In July I produced a 24-page newspaper with scribus 1.4.0 RC5

orphan/widow auto and more flow control would have saved me lots of time. Looking forward for that in scribus.

jghali

2011-09-12 21:18

administrator   ~0026858

I have applied it here, starting to test it.

jghali

2011-09-14 21:14

administrator  

scribus-textflow-r16832.patch (112,399 bytes)   
Index: scribus/CMakeLists.txt
===================================================================
--- scribus/CMakeLists.txt	(revision 16830)
+++ scribus/CMakeLists.txt	(working copy)
@@ -131,6 +131,7 @@
   ui/propertywidget_advancedbase.ui
   ui/propertywidget_distancebase.ui
   ui/propertywidget_flopbase.ui
+  ui/propertywidget_flowbase.ui
   ui/propertywidget_optmarginsbase.ui
   ui/propertywidget_pathtextbase.ui
   ui/propertywidget_textcolorbase.ui
@@ -362,6 +363,7 @@
   ui/propertywidget_advanced.h
   ui/propertywidget_distance.h
   ui/propertywidget_flop.h
+  ui/propertywidget_flow.h
   ui/propertywidget_optmargins.h
   ui/propertywidget_pathtext.h
   ui/propertywidget_textcolor.h
@@ -758,6 +760,7 @@
   ui/propertywidget_advanced.cpp
   ui/propertywidget_distance.cpp
   ui/propertywidget_flop.cpp
+  ui/propertywidget_flow.cpp
   ui/propertywidget_optmargins.cpp
   ui/propertywidget_pathtext.cpp
   ui/propertywidget_textcolor.cpp
Index: scribus/pageitem_textframe.cpp
===================================================================
--- scribus/pageitem_textframe.cpp	(revision 16830)
+++ scribus/pageitem_textframe.cpp	(working copy)
@@ -557,16 +557,17 @@
 	double getLineAscent(const StoryText& itemText)
 	{
 		double result = 0;
-		QChar  firstChar = itemText.text(line.firstItem);
+		QChar firstChar = itemText.text (line.firstItem);
+		if ((firstChar == SpecialChars::PAGENUMBER) || (firstChar == SpecialChars::PAGECOUNT))
+			firstChar = '8';
+		PageItem *obj = itemText.object (line.firstItem);
 		const CharStyle& fcStyle(itemText.charStyle(line.firstItem));
-		if ((itemText.text(line.firstItem) == SpecialChars::PARSEP) || (itemText.text(line.firstItem) == SpecialChars::LINEBREAK))
+		if ((firstChar == SpecialChars::PARSEP) || (firstChar == SpecialChars::LINEBREAK))
 			result = fcStyle.font().ascent(fcStyle.fontSize() / 10.0);
-		else if (itemText.object(line.firstItem) != 0)
-			result = qMax(result, (itemText.object(line.firstItem)->gHeight + itemText.object(line.firstItem)->lineWidth()) * (fcStyle.scaleV() / 1000.0));
-		else if ((firstChar == SpecialChars::PAGENUMBER) || (firstChar == SpecialChars::PAGECOUNT))
-			result = fcStyle.font().realCharAscent('8', fcStyle.fontSize() / 10.0);
-		else //if (itemText.charStyle(current.line.firstItem).effects() & ScStyle_DropCap == 0)
-			result = fcStyle.font().realCharAscent(itemText.text(line.firstItem), fcStyle.fontSize() / 10.0);
+		else if (obj)
+			result = qMax(result, (obj->gHeight + obj->lineWidth()) * (fcStyle.scaleV() / 1000.0));
+		else
+			result = fcStyle.font().realCharAscent(firstChar, fcStyle.fontSize() / 10.0);
 		for (int zc = 0; zc < itemsInLine; ++zc)
 		{
 			QChar ch = itemText.text(line.firstItem + zc);
@@ -574,14 +575,13 @@
 				ch = '8'; // should have highest ascender even in oldstyle
 			const CharStyle& cStyle(itemText.charStyle(line.firstItem + zc));
 			if ((ch == SpecialChars::TAB) || (ch == QChar(10))
-				|| (ch == SpecialChars::PARSEP) || (ch == SpecialChars::NBHYPHEN)
-				|| (ch == SpecialChars::COLBREAK) || (ch == SpecialChars::LINEBREAK)
-				|| (ch == SpecialChars::FRAMEBREAK) || (ch.isSpace()))
+				|| SpecialChars::isBreak (ch, true) || (ch == SpecialChars::NBHYPHEN) || (ch.isSpace()))
 				continue;
 			double asce;
-			if (itemText.object(line.firstItem + zc) != 0)
-				asce = itemText.object(line.firstItem + zc)->gHeight + itemText.object(line.firstItem + zc)->lineWidth() * (cStyle.scaleV() / 1000.0);
-			else //if (itemText.charStyle(current.line.firstItem + zc).effects() & ScStyle_DropCap == 0)
+			PageItem *obj = itemText.object (line.firstItem + zc);
+			if (obj)
+				asce = obj->gHeight + obj->lineWidth() * (cStyle.scaleV() / 1000.0);
+			else
 				asce = cStyle.font().realCharAscent(ch, cStyle.fontSize() / 10.0);
 			//	qDebug() << QString("checking char 'x%2' with ascender %1 > %3").arg(asce).arg(ch.unicode()).arg(result);
 			result = qMax(result, asce);
@@ -593,14 +593,14 @@
 	{
 		double result = 0;
 		QChar  firstChar = itemText.text(line.firstItem);
+		if ((firstChar == SpecialChars::PAGENUMBER) || (firstChar == SpecialChars::PAGECOUNT))
+			firstChar = '8';
 		const CharStyle& fcStyle(itemText.charStyle(line.firstItem));
 		if ((firstChar == SpecialChars::PARSEP) || (firstChar == SpecialChars::LINEBREAK))
 			result = fcStyle.font().descent(fcStyle.fontSize() / 10.0);
-		else if (itemText.object(line.firstItem) != 0)
+		else if (itemText.object(line.firstItem))
 			result = 0.0;
-		else if ((firstChar == SpecialChars::PAGENUMBER) || (firstChar == SpecialChars::PAGECOUNT))
-			result = fcStyle.font().realCharDescent('8', fcStyle.fontSize() / 10.0);
-		else //if (itemText.charStyle(current.line.firstItem).effects() & ScStyle_DropCap == 0)
+		else
 			result = fcStyle.font().realCharDescent(firstChar, fcStyle.fontSize() / 10.0);
 		for (int zc = 0; zc < itemsInLine; ++zc)
 		{
@@ -609,14 +609,12 @@
 				ch = '8'; // should have highest ascender even in oldstyle
 			const CharStyle& cStyle(itemText.charStyle(line.firstItem + zc));
 			if ((ch == SpecialChars::TAB) || (ch == QChar(10))
-				|| (ch == SpecialChars::PARSEP) || (ch == SpecialChars::NBHYPHEN)
-				|| (ch == SpecialChars::COLBREAK) || (ch == SpecialChars::LINEBREAK)
-				|| (ch == SpecialChars::FRAMEBREAK) || (ch.isSpace()))
+				|| SpecialChars::isBreak (ch, true) || (ch == SpecialChars::NBHYPHEN) || (ch.isSpace()))
 				continue;
 			double desc;
-			if (itemText.object(line.firstItem + zc) != 0)
+			if (itemText.object(line.firstItem + zc))
 				desc = 0.0;
-			else //if (itemText.charStyle(current.line.firstItem + zc).effects() & ScStyle_DropCap == 0)
+			else
 				desc = cStyle.font().realCharDescent(ch, cStyle.fontSize() / 10.0);
 			//	qDebug() << QString("checking char 'x%2' with ascender %1 > %3").arg(asce).arg(ch.unicode()).arg(result);
 			result = qMax(result, desc);
@@ -627,23 +625,25 @@
 	double getLineHeight(const StoryText& itemText)
 	{
 		double result = 0;
-		if (itemText.object(line.firstItem) != 0)
-			result = qMax(result, (itemText.object(line.firstItem)->gHeight + itemText.object(line.firstItem)->lineWidth()) * (itemText.charStyle(line.firstItem).scaleV() / 1000.0));
-		else //if (itemText.charStyle(current.line.firstItem).effects() & ScStyle_DropCap == 0)
-			result = itemText.charStyle(line.firstItem).font().height(itemText.charStyle(line.firstItem).fontSize() / 10.0);
+		const CharStyle& firstStyle(itemText.charStyle(line.firstItem));
+		PageItem *obj = itemText.object (line.firstItem);
+		if (obj)
+			result = qMax(result, (obj->gHeight + obj->lineWidth()) * (firstStyle.scaleV() / 1000.0));
+		else
+			result = firstStyle.font().height(firstStyle.fontSize() / 10.0);
 		for (int zc = 0; zc < itemsInLine; ++zc)
 		{
 			QChar ch = itemText.text(line.firstItem+zc);
 			if ((ch == SpecialChars::TAB) || (ch == QChar(10))
-				|| (ch == SpecialChars::PARSEP) || (ch == SpecialChars::NBHYPHEN)
-				|| (ch == SpecialChars::COLBREAK) || (ch == SpecialChars::FRAMEBREAK)
-				|| (ch == SpecialChars::LINEBREAK) || (ch.isSpace()))
+				|| SpecialChars::isBreak (ch, true) || (ch == SpecialChars::NBHYPHEN) || (ch.isSpace()))
 				continue;
+			const CharStyle& cStyle(itemText.charStyle(line.firstItem + zc));
+			PageItem *obj = itemText.object (line.firstItem + zc);
 			double asce;
-			if (itemText.object(line.firstItem+zc) != 0)
-				asce = (itemText.object(line.firstItem+zc)->gHeight + itemText.object(line.firstItem+zc)->lineWidth()) * (itemText.charStyle(line.firstItem+zc).scaleV() / 1000.0);
-			else //if (itemText.charStyle(current.line.firstItem+zc).effects() & ScStyle_DropCap == 0)
-				asce = itemText.charStyle(line.firstItem+zc).font().height(itemText.charStyle(line.firstItem+zc).fontSize() / 10.0);
+			if (obj)
+				asce = (obj->gHeight + obj->lineWidth()) * (cStyle.scaleV() / 1000.0);
+			else
+				asce = cStyle.font().height (cStyle.fontSize() / 10.0);
 			//	qDebug() << QString("checking char 'x%2' with ascender %1 > %3").arg(asce).arg(ch.unicode()).arg(result);
 			result = qMax(result, asce);
 		}
@@ -659,9 +659,7 @@
 		{
 			QChar ch = itemText.text(line.firstItem+zc);
 			if ((ch == SpecialChars::TAB) || (ch == QChar(10))
-				|| (ch == SpecialChars::PARSEP) || (ch == SpecialChars::NBHYPHEN)
-				|| (ch == SpecialChars::COLBREAK) || (ch == SpecialChars::FRAMEBREAK)
-				|| (ch == SpecialChars::LINEBREAK) || (ch.isSpace()))
+				|| SpecialChars::isBreak (ch, true) || (ch == SpecialChars::NBHYPHEN) || (ch.isSpace()))
 				continue;
 			const CharStyle& cStyle(itemText.charStyle(line.firstItem + zc));
 			if (itemText.object(line.firstItem+zc) != 0)
@@ -903,10 +901,124 @@
 	return 0.0;
 }
 
+static double adjustToBaselineGrid (const LineControl &control, PageItem *item, int OwnPage)
+{
+	double by = item->yPos();
+	if (OwnPage != -1)
+		by = by - item->doc()->Pages->at(OwnPage)->yOffset();
+	int ol1 = qRound((by + control.yPos - item->doc()->guidesPrefs().offsetBaselineGrid) * 10000.0);
+	int ol2 = static_cast<int>(ol1 / item->doc()->guidesPrefs().valueBaselineGrid);
+//						qDebug() << QString("baseline adjust: y=%1->%2").arg(current.yPos).arg(ceil(  ol2 / 10000.0 ) * item->doc()->typographicSettings.valueBaselineGrid + item->doc()->typographicSettings.offsetBaselineGrid - by);
 
+	return ceil(  ol2 / 10000.0 ) * item->doc()->guidesPrefs().valueBaselineGrid + item->doc()->guidesPrefs().offsetBaselineGrid - by;
+}
 
+static double nextAutoTab (const LineControl &current, PageItem *item)
+{
+	double dtw = item->doc()->itemToolPrefs().textTabWidth;
+	if (current.xPos == current.colLeft)
+		return current.colLeft + dtw;
+	double res = current.colLeft + ceil ((current.xPos - current.colLeft) / dtw) * dtw;
+	if (res == current.xPos) res += dtw;
+	return res;
+}
 
 
+static double calculateLineSpacing (const ParagraphStyle &style, PageItem *item)
+{
+	if (style.lineSpacingMode() == ParagraphStyle::AutomaticLineSpacing)
+		return style.charStyle().font().height(style.charStyle().fontSize() / 10.0);
+	if (style.lineSpacingMode() == ParagraphStyle::BaselineGridLineSpacing)
+		return item->doc()->guidesPrefs().valueBaselineGrid;
+	return style.lineSpacing();
+}
+
+
+// This assumes that layout() ran on the previous page and set the incomplete* vars
+// It also clears the incomplete* vars, and changes the starting position for this frame
+// The incomplete* vars are used to ensure that we don't run into an endless loop
+// This setup won't cause any problems, as the only way for the starting position to change
+// back is if the previous frame's layout() runs again, but if it does, it also sets the
+// incomplete* vars for us
+bool PageItem_TextFrame::moveLinesFromPreviousFrame ()
+{
+	PageItem_TextFrame* prev = dynamic_cast<PageItem_TextFrame*>(BackBox);
+	if (!prev) return false;
+	if (!prev->incompleteLines) return false;   // no incomplete lines - nothing to do
+	int pos = itemText.lastInFrame();
+	QChar lastChar = itemText.text (pos);
+	// qDebug()<<"pos is"<<pos<<", length is"<<itemText.length()<<", incomplete is "<<prev->incompleteLines;
+	if ((pos != itemText.length()-1) && (!SpecialChars::isBreak (lastChar, true)))
+		return false;  // the paragraph isn't ending yet
+	uint lines = itemText.lines();  // lines added to the current frame
+
+	ParagraphStyle style = itemText.paragraphStyle (pos);
+	int need = style.keepLinesEnd () + 1;
+	int prevneed = style.keepLinesStart () + 1;
+	if (lines >= need) {
+		prev->incompleteLines = 0;   // so that further paragraphs don't pull anything
+		return false;   // we have enough lines
+	}
+
+	// too few lines - we need to pull some from the previous page
+	uint pull = need - lines;
+	// if pulling the lines would lead to the original frame having too few, pull the whole paragraph
+	if (prev->incompleteLines - pull < prevneed)
+		pull = prev->incompleteLines;
+	// qDebug()<<"pulling"<<pull<<"lines;
+	// Okay, move the starting/ending character
+	int startingPos = prev->incompletePositions[prev->incompleteLines - pull];
+	for (uint i = 0; i < pull; ++i)
+		prev->itemText.removeLastLine();
+	firstChar = prev->MaxChars = startingPos;
+	// keep the remaining incomplete lines flagged as such
+	// this ensures that if pulling one line won't be enough, the subsequent call to layout() will pull more
+	prev->incompleteLines -= pull;
+
+	return true;
+}
+
+// called at the end of a frame or column
+void PageItem_TextFrame::adjustParagraphEndings ()
+{
+	// More text to go - let's apply paragraph flowing options - orphans/widows, etc
+	int pos = itemText.lastInFrame();
+	if (pos >= itemText.length() - 1) return;
+
+	ParagraphStyle style = itemText.paragraphStyle (pos);
+	int paragraphStart = itemText.prevParagraph (pos) + 1;
+	QChar lastChar = itemText.text (pos);
+	bool keepWithNext = style.keepWithNext() && (lastChar == SpecialChars::PARSEP);
+	if (keepWithNext || (!SpecialChars::isBreak (lastChar, true))) {
+		// paragraph continues in the next frame, or needs to be kept with the next one
+		// check how many lines are in this frame
+		int lineStart = itemText.startOfLine (pos);
+		incompleteLines = 1;
+		incompletePositions.prepend (lineStart);
+		while (lineStart > paragraphStart) {
+			lineStart = itemText.startOfLine (lineStart - 1);
+			incompleteLines++;
+			incompletePositions.prepend (lineStart);
+		}
+		int need = style.keepLinesStart () + 1;
+		if (style.keepTogether()) need = incompleteLines;
+		int pull = 0;
+		if (style.keepTogether() || (incompleteLines < need)) pull = incompleteLines;
+		// if we need to keep it with the next one, pull one line. Next frame layouting
+		// will pull more from us if it proves necessary.
+		if (keepWithNext && (!pull)) pull = 1;
+
+		if (pull) {
+			// push this paragraph to the next frame
+			for (int i = 0; i < pull; ++i)
+				itemText.removeLastLine();
+			MaxChars = itemText.lastInFrame() + 1;
+			incompleteLines = 0;
+			incompletePositions.clear();
+		}
+	}
+}
+
 void PageItem_TextFrame::layout() 
 {
 // 	qDebug()<<"==Layout==" << itemName() ;
@@ -968,8 +1080,9 @@
 	int    DropLines = 0;
 	int    DropLinesCount = 0;
 	
-	
 	itemText.clearLines();
+	incompleteLines = 0;
+	incompletePositions.clear();
 
 	double lineCorr = 0;
 	if (lineColor() != CommonStrings::None)
@@ -1031,22 +1144,13 @@
 		{
 			hl = itemText.item(firstInFrame());
 			style = itemText.paragraphStyle(firstInFrame());
-			if (style.lineSpacingMode() == ParagraphStyle::AutomaticLineSpacing)
-			{
-				style.setLineSpacing(style.charStyle().font().height(style.charStyle().fontSize() / 10.0));
-//				qDebug() << QString("auto linespacing: %1").arg(style.lineSpacing());
-			}
-			else if (style.lineSpacingMode() == ParagraphStyle::BaselineGridLineSpacing)
-				style.setLineSpacing(m_Doc->guidesPrefs().valueBaselineGrid);
+			style.setLineSpacing (calculateLineSpacing (style, this));
 
 //			qDebug() << QString("style @0: %1 -- %2, %4/%5 char: %3").arg(style.leftMargin()).arg(style.rightMargin())
 //				   .arg(style.charStyle().asString()).arg(style.name()).arg(style.parent()?style.parent()->name():"");
 			if (style.hasDropCap())
 			{
-				if (style.lineSpacingMode() == ParagraphStyle::BaselineGridLineSpacing)
-					chs = qRound(m_Doc->guidesPrefs().valueBaselineGrid  * style.dropCapLines() * 10);
-				else
-					chs = qRound(style.lineSpacing() * style.dropCapLines() * 10);
+				chs = calculateLineSpacing (style, this) * style.dropCapLines() * 10;
 			}
 			else 
 				chs = hl->fontSize();
@@ -1087,12 +1191,7 @@
 			chstr = ExpandToken(a);
 			if (chstr.isEmpty())
 				chstr = SpecialChars::ZWNBSPACE;
-			if (style.lineSpacingMode() == ParagraphStyle::AutomaticLineSpacing)
-			{
-				style.setLineSpacing(style.charStyle().font().height(style.charStyle().fontSize() / 10.0));
-			}
-			else if (style.lineSpacingMode() == ParagraphStyle::BaselineGridLineSpacing)
-				style.setLineSpacing(m_Doc->guidesPrefs().valueBaselineGrid);
+			style.setLineSpacing (calculateLineSpacing (style, this));
 			// find out about par gap and dropcap
 			if (a == firstInFrame())
 			{
@@ -1161,15 +1260,7 @@
 					if (DropCmode)
 					{
 						DropLines = style.dropCapLines();
-						if (style.lineSpacingMode() == ParagraphStyle::BaselineGridLineSpacing)
-							DropCapDrop = m_Doc->guidesPrefs().valueBaselineGrid * (DropLines-1);
-						else
-						{
-							if (style.lineSpacingMode() == ParagraphStyle::FixedLineSpacing)
-								DropCapDrop = style.lineSpacing() * (DropLines-1);
-							else
-								DropCapDrop = charStyle.font().height(style.charStyle().fontSize() / 10.0) * (DropLines-1);
-						}
+						DropCapDrop = calculateLineSpacing (style, this) * (DropLines - 1);
 //						qDebug() << QString("dropcapdrop: y=%1+%2").arg(current.yPos).arg(DropCapDrop);
 						current.yPos += DropCapDrop;
 					}
@@ -1179,15 +1270,7 @@
 			if (DropCmode)
 			{
 				// dropcap active?
-				if (style.lineSpacingMode() == ParagraphStyle::BaselineGridLineSpacing)
-					DropCapDrop = m_Doc->guidesPrefs().valueBaselineGrid * (DropLines-1);
-				else
-				{
-					if (style.lineSpacingMode() == ParagraphStyle::FixedLineSpacing)
-						DropCapDrop = style.lineSpacing() * (DropLines-1);
-					else
-						DropCapDrop = charStyle.font().height(style.charStyle().fontSize() / 10.0) * (DropLines-1);
-				}
+				DropCapDrop = calculateLineSpacing (style, this) * (DropLines - 1);
 
 				// FIXME : we should ensure that fonts are loaded before calls to layout()
 				// ScFace::realCharHeight()/Ascent() ensure font is loaded thanks to an indirect call to char2CMap()
@@ -1199,25 +1282,8 @@
 					realCharHeight = charStyle.font().height(style.charStyle().fontSize() / 10.0);
 				if (realCharAscent == 0.0)
 					realCharAscent = fontAscent;
-				if (style.lineSpacingMode() == ParagraphStyle::BaselineGridLineSpacing)
-				{
-					chsd = (10 * ((m_Doc->guidesPrefs().valueBaselineGrid * (DropLines-1) + fontAscent) / realCharHeight));
-					chs  = (10 * ((m_Doc->guidesPrefs().valueBaselineGrid * (DropLines-1) + fontAscent) / realCharAscent));
-				}
-				else
-				{
-					if (style.lineSpacingMode() == ParagraphStyle::FixedLineSpacing)
-					{
-						chsd = (10 * ((style.lineSpacing() * (DropLines-1) + fontAscent) / realCharHeight));
-						chs  = (10 * ((style.lineSpacing() * (DropLines-1) + fontAscent) / realCharAscent));
-					}
-					else
-					{
-						double currasce = charStyle.font().height(style.charStyle().fontSize() / 10.0);
-						chsd = (10 * ((currasce * (DropLines-1) + fontAscent) / realCharHeight));
-						chs  = (10 * ((currasce * (DropLines-1) + fontAscent) / realCharAscent));
-					}
-				}
+				chsd = (10 * ((DropCapDrop + fontAscent) / realCharHeight));
+				chs  = (10 * ((DropCapDrop + fontAscent) / realCharAscent));
 				hl->setEffects(hl->effects() | ScStyle_DropCap);
 				hl->glyph.yoffset -= DropCapDrop;
 			}
@@ -1268,15 +1334,7 @@
 					if (itemHeight == 0)
 						itemHeight = charStyle.font().height(style.charStyle().fontSize() / 10.0);
 					wide = hl->embedded.getItem()->gWidth + hl->embedded.getItem()->lineWidth();
-					if (style.lineSpacingMode() == ParagraphStyle::BaselineGridLineSpacing)
-						asce = m_Doc->guidesPrefs().valueBaselineGrid * DropLines;
-					else
-					{
-						if (style.lineSpacingMode() == ParagraphStyle::FixedLineSpacing)
-							asce = style.lineSpacing() * DropLines;
-						else
-							asce = charStyle.font().height(style.charStyle().fontSize() / 10.0) * DropLines;
-					}
+					asce = calculateLineSpacing (style, this) * DropLines;
 					hl->glyph.scaleH /= hl->glyph.scaleV;
 					hl->glyph.scaleV = (asce / itemHeight);
 					hl->glyph.scaleH *= hl->glyph.scaleV;
@@ -1333,6 +1391,8 @@
 				{
 					// start next col
 					current.nextColumn(asce);
+					adjustParagraphEndings ();
+					a = itemText.lastInFrame() + 1;
 					if (((a > firstInFrame()) && (itemText.text(a-1) == SpecialChars::PARSEP)) 
 						|| ((a == firstInFrame()) && (BackBox == 0)))
 					{
@@ -1342,26 +1402,13 @@
 							DropCmode = false;
 						if (DropCmode)
 						{
-							if (style.lineSpacingMode() == ParagraphStyle::BaselineGridLineSpacing)
-								desc2 = -charStyle.font().descent() * m_Doc->guidesPrefs().valueBaselineGrid * style.dropCapLines();
-							else
-								desc2 = -charStyle.font().descent() * style.lineSpacing() * style.dropCapLines();
-						}
-						if (DropCmode)
+							desc2 = -charStyle.font().descent() * calculateLineSpacing (style, this) * style.dropCapLines();
 							DropLines = style.dropCapLines();
+						}
 					}
 //					qDebug() << QString("layout: nextcol grid=%1 x %2").arg(style.lineSpacingMode() == ParagraphStyle::BaselineGridLineSpacing).arg(m_Doc->typographicSettings.valueBaseGrid);
 					if (style.lineSpacingMode() == ParagraphStyle::BaselineGridLineSpacing)
-					{
-						double by = Ypos;
-						if (OwnPage != -1)
-							by = Ypos - m_Doc->Pages->at(OwnPage)->yOffset();
-						int ol1 = qRound((by + current.yPos - m_Doc->guidesPrefs().offsetBaselineGrid) * 10000.0);
-						int ol2 = static_cast<int>(ol1 / m_Doc->guidesPrefs().valueBaselineGrid);
-//						qDebug() << QString("baseline adjust: y=%1->%2").arg(current.yPos).arg(ceil(  ol2 / 10000.0 ) * m_Doc->typographicSettings.valueBaselineGrid + m_Doc->typographicSettings.offsetBaselineGrid - by);
-
-						current.yPos = ceil(  ol2 / 10000.0 ) * m_Doc->guidesPrefs().valueBaselineGrid + m_Doc->guidesPrefs().offsetBaselineGrid - by;
-					}
+						current.yPos = adjustToBaselineGrid (current, this, OwnPage);
 				}
 				else
 				{
@@ -1387,16 +1434,7 @@
 				}
 //				qDebug() << QString("layout: nextline grid=%1 x %2").arg(style.lineSpacingMode() == ParagraphStyle::BaselineGridLineSpacing).arg(m_Doc->typographicSettings.valueBaselineGrid);
 				if (style.lineSpacingMode() == ParagraphStyle::BaselineGridLineSpacing)
-				{
-					double by = Ypos;
-					if (OwnPage != -1)
-						by = Ypos - m_Doc->Pages->at(OwnPage)->yOffset();
-					int ol1 = qRound((by + current.yPos - m_Doc->guidesPrefs().offsetBaselineGrid) * 10000.0);
-					int ol2 = static_cast<int>(ol1 / m_Doc->guidesPrefs().valueBaselineGrid);
-//					qDebug() << QString("useBaselIneGrid: %1 * %2 + %3 - %4").arg(ol2 / 10000.0).arg(m_Doc->typographicSettings.valueBaselineGrid).arg(m_Doc->typographicSettings.offsetBaselineGrid).arg(by);
-//					qDebug() << QString("baseline adjust: y=%1->%2").arg(current.yPos).arg(ceil(  ol2 / 10000.0 ) * m_Doc->typographicSettings.valueBaselineGrid + m_Doc->typographicSettings.offsetBaselineGrid - by);
-					current.yPos = ceil(  ol2 / 10000.0 ) * m_Doc->guidesPrefs().valueBaselineGrid + m_Doc->guidesPrefs().offsetBaselineGrid - by;
-				}
+					current.yPos = adjustToBaselineGrid (current, this, OwnPage);
 				/* this causes different spacing for first line:
 				if (current.yPos-TopOffset < 0.0)
 				{
@@ -1432,15 +1470,7 @@
 							current.yPos += qMax(style.lineSpacing(), 1.0);
 //						qDebug() << QString("layout: next lower line grid=%1 x %2").arg(style.lineSpacingMode() == ParagraphStyle::BaselineGridLineSpacing).arg(m_Doc->typographicSettings.valueBaselineGrid);
 						if (style.lineSpacingMode() == ParagraphStyle::BaselineGridLineSpacing)
-						{
-							double by = Ypos;
-							if (OwnPage != -1)
-								by = Ypos - m_Doc->Pages->at(OwnPage)->yOffset();
-							int ol1 = qRound((by + current.yPos - m_Doc->guidesPrefs().offsetBaselineGrid) * 10000.0);
-							int ol2 = static_cast<int>(ol1 / m_Doc->guidesPrefs().valueBaselineGrid);
-//							qDebug() << QString("baseline adjust: y=%1->%2").arg(current.yPos).arg(ceil(  ol2 / 10000.0 ) * m_Doc->typographicSettings.valueBaselineGrid + m_Doc->typographicSettings.offsetBaselineGrid - by);
-							current.yPos = ceil(  ol2 / 10000.0 ) * m_Doc->guidesPrefs().valueBaselineGrid + m_Doc->guidesPrefs().offsetBaselineGrid - by;
-						}
+							current.yPos = adjustToBaselineGrid (current, this, OwnPage);
 						if (current.isEndOfCol())
 						{
 							fBorder = false;
@@ -1448,6 +1478,8 @@
 							if (current.column < Cols)
 							{
 								current.nextColumn(asce);
+								adjustParagraphEndings ();
+								a = itemText.lastInFrame() + 1;
 								if (((a > firstInFrame()) && (itemText.text(a-1) == SpecialChars::PARSEP)) || ((a == firstInFrame()) && (BackBox == 0)))
 								{
 									if (chstr[0] != SpecialChars::PARSEP)
@@ -1456,25 +1488,13 @@
 										DropCmode = false;
 									if (DropCmode)
 									{
-										if (style.lineSpacingMode() == ParagraphStyle::BaselineGridLineSpacing)
-											desc2 = -charStyle.font().descent() * m_Doc->guidesPrefs().valueBaselineGrid * style.dropCapLines();
-										else
-											desc2 = -charStyle.font().descent() * style.lineSpacing() * style.dropCapLines();
-									}
-									if (DropCmode)
+										desc2 = -charStyle.font().descent() * calculateLineSpacing (style, this) * style.dropCapLines();
 										DropLines = style.dropCapLines();
+									}
 								}
 //								qDebug() << QString("layout: nextcol2 grid=%1 x %2").arg(style.lineSpacingMode() == ParagraphStyle::BaselineGridLineSpacing).arg(m_Doc->guideSettings.valueBaselineGrid);
 								if (style.lineSpacingMode() == ParagraphStyle::BaselineGridLineSpacing)
-								{
-									double by = Ypos;
-									if (OwnPage != -1)
-										by = Ypos - m_Doc->Pages->at(OwnPage)->yOffset();
-									int ol1 = qRound((by + current.yPos - m_Doc->guidesPrefs().offsetBaselineGrid) * 10000.0);
-									int ol2 = static_cast<int>(ol1 / m_Doc->guidesPrefs().valueBaselineGrid);
-//									qDebug() << QString("baseline adjust: y=%1->%2").arg(current.yPos).arg(ceil(  ol2 / 10000.0 ) * m_Doc->guideSettings.valueBaseGrid + m_Doc->typographicSettings.offsetBaselineGrid - by);
-									current.yPos = ceil(  ol2 / 10000.0 ) * m_Doc->guidesPrefs().valueBaselineGrid + m_Doc->guidesPrefs().offsetBaselineGrid - by;
-								}
+									current.yPos = adjustToBaselineGrid (current, this, OwnPage);
 							}
 							else
 							{
@@ -1536,40 +1556,33 @@
 					tTabValues = style.tabValues();
 					if (tTabValues.isEmpty())
 					{
-						double dtw=m_Doc->itemToolPrefs().textTabWidth;
-						if ((current.xPos - current.colLeft) != 0)
-						{
-							if (current.xPos == current.colLeft + ceil((current.xPos-current.colLeft) / dtw) * dtw)
-								current.xPos += dtw;
-							else
-								current.xPos = current.colLeft + ceil((current.xPos-current.colLeft) / dtw) * dtw;
-						}
-						else
-							current.xPos = current.colLeft + dtw;
+						current.xPos = nextAutoTab (current, this);
 						tabs.status = TabNONE;
 						tabs.active = false;
 					}
 					else
 					{
-						double tCurX = current.xPos - current.colLeft;
-						double oCurX = tCurX + wide;
+						double tCurX = current.xPos;
+						double oCurX = current.xPos - current.colLeft + wide;
 						for (int yg = static_cast<int>(tTabValues.count()-1); yg > -1; yg--)
 						{
 							if (oCurX < tTabValues.at(yg).tabPosition)
 							{
 								tabs.status = static_cast<int>(tTabValues.at(yg).tabType);
-								tCurX = tTabValues.at(yg).tabPosition;
 								tabs.fillChar = tTabValues.at(yg).tabFillChar;
+								current.xPos = current.colLeft + tTabValues.at(yg).tabPosition;
 							}
 						}
 						tabs.active = (tabs.status != TabLEFT);
-						if (tCurX == oCurX-wide)
-							current.xPos = current.colLeft + ceil((current.xPos-current.colLeft) / m_Doc->itemToolPrefs().textTabWidth) * m_Doc->itemToolPrefs().textTabWidth;
-						else
-							current.xPos = current.colLeft + tCurX;
-						
+						if (current.xPos == tCurX)  // no more tabs found
+						{
+							current.xPos = nextAutoTab (current, this);
+							tabs.status = TabNONE;
+							tabs.active = false;
+							tabs.fillChar = QChar();
+						}
+
 						// remember fill char
-//						qDebug() << QString("tab: %1 '%2'").arg(tCurX).arg(tabs.fillChar);
 						if (!tabs.fillChar.isNull()) {
 							hl->glyph.growWithTabLayout();
 							TabLayout * tglyph = dynamic_cast<TabLayout*>(hl->glyph.more);
@@ -1613,31 +1626,20 @@
 			}		
 			
 			//FIXME: asce / desc set correctly?
+			double x = current.xPos + extra.Right - current.maxShrink;
 			if (legacy && 
 				(((hl->ch == '-' || (hl->effects() & ScStyle_HyphenationPossible)) && (current.hyphenCount < m_Doc->hyphConsecutiveLines() || m_Doc->hyphConsecutiveLines() == 0))
 				|| hl->ch == SpecialChars::SHYPHEN))
 			{
 				if (hl->effects() & ScStyle_HyphenationPossible || hl->ch == SpecialChars::SHYPHEN)
-				{
-					pt1 = QPoint(qRound(ceil(current.xPos+extra.Right - current.maxShrink + charStyle.font().charWidth('-', charStyle.fontSize() / 10.0) * (charStyle.scaleH() / 1000.0))), qRound(current.yPos+desc));
-					pt2 = QPoint(qRound(ceil(current.xPos+extra.Right - current.maxShrink + charStyle.font().charWidth('-', charStyle.fontSize() / 10.0) * (charStyle.scaleH() / 1000.0))), qRound(ceil(current.yPos-asce)));
-				}
-				else
-				{
-					pt1 = QPoint(qRound(ceil(current.xPos+extra.Right - current.maxShrink )), qRound(current.yPos+desc));
-					pt2 = QPoint(qRound(ceil(current.xPos+extra.Right - current.maxShrink )), qRound(ceil(current.yPos-asce)));
-				}
+					x += charStyle.font().charWidth('-', charStyle.fontSize() / 10.0) * (charStyle.scaleH() / 1000.0);
 			}
 			else if (!legacy && SpecialChars::isBreakingSpace(hl->ch))
-			{
-				pt1 = QPoint(qRound(ceil(breakPos + extra.Right - current.maxShrink )), qRound(current.yPos+desc));
-				pt2 = QPoint(qRound(ceil(breakPos + extra.Right - current.maxShrink )), qRound(ceil(current.yPos-asce)));
-			}
-			else
-			{
-				pt1 = QPoint(qRound(ceil(current.xPos+extra.Right - current.maxShrink)), qRound(current.yPos+desc));
-				pt2 = QPoint(qRound(ceil(current.xPos+extra.Right - current.maxShrink)), qRound(ceil(current.yPos-asce)));
-			}
+				x = breakPos + extra.Right - current.maxShrink;
+
+			x = qRound (ceil (x));
+			pt1 = QPoint(x, qRound (current.yPos + desc));
+			pt2 = QPoint(x, qRound (ceil(current.yPos - asce)));
 			
 			// test if end of line reached
 			if ((!cl.contains(pf2.map(pt1))) || (!cl.contains(pf2.map(pt2))) || (legacy && current.isEndOfLine(style.rightMargin())))
@@ -1649,7 +1651,6 @@
 			if ((hl->ch == SpecialChars::COLBREAK) && (Cols > 1))
 				goNextColumn = true;
 
-
 			// remember possible break
 			// #5783 : comment out "&& !outs" as this make it possible to perform a line break
 			// before a space which contradicts unicode line breaking rules
@@ -1717,40 +1718,15 @@
 				current.xPos = qMax(current.xPos, current.colLeft);
 				maxDX = current.xPos;
 				QPolygon tcli(4);
+				double spacing = calculateLineSpacing (style, this);
+				current.yPos -= spacing * (DropLines-1);
 				if (style.lineSpacingMode() == ParagraphStyle::BaselineGridLineSpacing)
-				{
-					current.yPos -= m_Doc->guidesPrefs().valueBaselineGrid * (DropLines-1);
-					double by = Ypos;
-					if (OwnPage != -1)
-						by = Ypos - m_Doc->Pages->at(OwnPage)->yOffset();
-					int ol1 = qRound((by + current.yPos - m_Doc->guidesPrefs().offsetBaselineGrid) * 10000.0);
-					int ol2 = static_cast<int>(ol1 / m_Doc->guidesPrefs().valueBaselineGrid);
-//					qDebug() << QString("baseline adjust after dropcaps: y=%1->%2").arg(current.yPos).arg(ceil(  ol2 / 10000.0 ) * m_Doc->guideSettings.valueBaselineGrid + m_Doc->typographicSettings.offsetBaselineGrid - by);
-					current.yPos = ceil(  ol2 / 10000.0 ) * m_Doc->guidesPrefs().valueBaselineGrid + m_Doc->guidesPrefs().offsetBaselineGrid - by;
-					//FIXME: use current.colLeft instead of xOffset?
-					tcli.setPoint(0, QPoint(qRound(hl->glyph.xoffset), qRound(maxDY-DropLines*m_Doc->guidesPrefs().valueBaselineGrid)));
-					tcli.setPoint(1, QPoint(qRound(maxDX), qRound(maxDY-DropLines*m_Doc->guidesPrefs().valueBaselineGrid)));
-				}
-				else
-				{
-					if (style.lineSpacingMode() == ParagraphStyle::FixedLineSpacing)
-					{
-						current.yPos -= style.lineSpacing() * (DropLines-1);
-//						qDebug() << QString("after dropcaps: y=%1").arg(current.yPos);
-						tcli.setPoint(0, QPoint(qRound(hl->glyph.xoffset), qRound(maxDY - DropLines * style.lineSpacing())));
-						tcli.setPoint(1, QPoint(qRound(maxDX), qRound(maxDY-DropLines*style.lineSpacing())));
-					}
-					else
-					{
-						double currasce = charStyle.font().height(style.charStyle().fontSize() / 10.0);
-						current.yPos -= currasce * (DropLines-1);
-//						qDebug() << QString("after dropcaps: y=%1").arg(current.yPos);
-						tcli.setPoint(0, QPoint(qRound(hl->glyph.xoffset), qRound(maxDY-DropLines*currasce)));
-						tcli.setPoint(1, QPoint(qRound(maxDX), qRound(maxDY-DropLines*currasce)));
-					}
-				}
-				tcli.setPoint(2, QPoint(qRound(maxDX), qRound(maxDY)));
-				tcli.setPoint(3, QPoint(qRound(hl->glyph.xoffset), qRound(maxDY)));
+					current.yPos = adjustToBaselineGrid (current, this, OwnPage);
+				//FIXME: use current.colLeft instead of xOffset?
+				tcli.setPoint(0, QPoint (qRound(hl->glyph.xoffset), qRound(maxDY - DropLines * spacing)));
+				tcli.setPoint(1, QPoint (qRound(maxDX), qRound(maxDY - DropLines * spacing)));
+				tcli.setPoint(2, QPoint (qRound(maxDX), qRound(maxDY)));
+				tcli.setPoint(3, QPoint (qRound(hl->glyph.xoffset), qRound(maxDY)));
 				// #6821 : the following two lines are causing bad text flow around drop caps 
 				// in some case, discarding them put more emphasis on user control of line spacing
 				/*cm = QRegion(pf2.map(tcli));
@@ -1815,13 +1791,8 @@
 						assert( a < itemText.length() );
 						hl = itemText.item(a);
 						style = itemText.paragraphStyle(a);
-						if (style.lineSpacingMode() == ParagraphStyle::AutomaticLineSpacing)
-						{
-							style.setLineSpacing(style.charStyle().font().height(style.charStyle().fontSize() / 10.0));
-//							qDebug() << QString("auto linespacing: %1").arg(style.lineSpacing());
-						}
-						else if (style.lineSpacingMode() == ParagraphStyle::BaselineGridLineSpacing)
-							style.setLineSpacing(m_Doc->guidesPrefs().valueBaselineGrid);
+			      style.setLineSpacing (calculateLineSpacing (style, this));
+
 						current.itemsInLine = a - current.line.firstItem + 1;
 //						qDebug() << QString("style outs pos %1: %2 (%3)").arg(a).arg(style.alignment()).arg(style.parent());
 //						qDebug() << QString("style <@%6: %1 -- %2, %4/%5 char: %3").arg(style.leftMargin()).arg(style.rightMargin())
@@ -1905,13 +1876,7 @@
 						{
 							hl = itemText.item(a);
 							style = itemText.paragraphStyle(a);
-							if (style.lineSpacingMode() == ParagraphStyle::AutomaticLineSpacing)
-							{
-								style.setLineSpacing(style.charStyle().font().height(style.charStyle().fontSize() / 10.0));
-//								qDebug() << QString("auto linespacing: %1").arg(style.lineSpacing());
-							}
-							else if (style.lineSpacingMode() == ParagraphStyle::BaselineGridLineSpacing)
-								style.setLineSpacing(m_Doc->guidesPrefs().valueBaselineGrid);
+			        style.setLineSpacing (calculateLineSpacing (style, this));
 						}
 						current.breakLine(itemText, style, firstLineOffset(), a);
 //						qDebug() << QString("style no break pos %1: %2 (%3)").arg(a).arg(style.alignment()).arg(style.parent());
@@ -1949,10 +1914,7 @@
 //								qDebug() << QString("layout: next lower2 grid=%1 x %2").arg(style.lineSpacingMode() == ParagraphStyle::BaselineGridLineSpacing).arg(m_Doc->typographicSettings.valueBaseGrid);
 //								qDebug() << QString("nextline: y=%1+%2").arg(current.yPos).arg(style.lineSpacingMode() == ParagraphStyle::BaselineGridLineSpacing? m_Doc->typographicSettings.valueBaseGrid : style.lineSpacing());
 
-								if (style.lineSpacingMode() == ParagraphStyle::BaselineGridLineSpacing)
-									current.yPos += m_Doc->guidesPrefs().valueBaselineGrid;
-								else
-									current.yPos += style.lineSpacing();
+								current.yPos += calculateLineSpacing (style, this);
 								if (current.isEndOfCol(desc) && (current.column+1 == Cols))
 								{
 									goNoRoom = true;
@@ -2020,26 +1982,10 @@
 						}
 //						qDebug() << QString("layout: next lower3 grid=%1 x %2").arg(style.lineSpacingMode() == ParagraphStyle::BaselineGridLineSpacing).arg(m_Doc->typographicSettings.valueBaseGrid);
 
-
-						if (style.lineSpacingMode() == ParagraphStyle::BaselineGridLineSpacing)
-						{
-//							qDebug() << QString("next line (grid): y=%1+%2").arg(current.yPos).arg(m_Doc->typographicSettings.valueBaseGrid);
-
-							current.yPos += m_Doc->guidesPrefs().valueBaselineGrid;
-						}
-						else
-						{
-//							qDebug() << QString("next line (auto): y=%1+%2").arg(current.yPos).arg(style.lineSpacing());
-							//#5845 : use next paragraph line spacing for switching to next paragraph instead of current one
-							//current.yPos += style.lineSpacing();
-							const  ParagraphStyle& pStyle = itemText.paragraphStyle(a+1);
-							double lineSpacing = pStyle.lineSpacing();
-							if (pStyle.lineSpacingMode() == ParagraphStyle::AutomaticLineSpacing)
-								lineSpacing = pStyle.charStyle().font().height(pStyle.charStyle().fontSize() / 10.0);
-							else if (pStyle.lineSpacingMode() == ParagraphStyle::BaselineGridLineSpacing)
-								lineSpacing = m_Doc->guidesPrefs().valueBaselineGrid;
-							current.yPos += lineSpacing;
-						}
+//						qDebug() << QString("next line (auto): y=%1+%2").arg(current.yPos).arg(style.lineSpacing());
+						//#5845 : use next paragraph line spacing for switching to next paragraph instead of current one
+						const  ParagraphStyle& pStyle = itemText.paragraphStyle(a+1);
+		        current.yPos += calculateLineSpacing (pStyle, this);
 						if (current.hasDropCap)
 						{
 							++DropLinesCount;
@@ -2122,7 +2068,14 @@
 				if (current.line.firstItem > current.line.lastItem)
 					; //qDebug() << QString("layout: empty line %1 - %2").arg(current.line.firstItem).arg(current.line.lastItem);
 				else if (current.itemsInLine > 0)
+				{
 					itemText.appendLine(current.line);
+					if (moveLinesFromPreviousFrame ()) 
+					{
+						layout ();    // line moving ensures that this won't be an endless loop
+						return;
+					}
+				}
 				current.startLine(a+1);
 				outs = false;
 				if (goNoRoom)
@@ -2139,6 +2092,7 @@
 					if (current.column < Cols)
 					{
 						current.nextColumn(asce);
+						// manual break - we do NOT call adjustParagraphEndings () here
 					}
 					else
 					{
@@ -2210,6 +2164,10 @@
 		
 		if (current.itemsInLine > 0) {
 			itemText.appendLine(current.line);
+			if (moveLinesFromPreviousFrame ()) {
+				layout ();  // line moving ensures that this won't be an endless loop
+				return;
+			}
 		}
 	}
 	MaxChars = itemText.length();
@@ -2230,6 +2188,9 @@
 NoRoom:     
 //	pf2.end();
 	invalid = false;
+
+	adjustParagraphEndings ();
+
 	PageItem_TextFrame * next = dynamic_cast<PageItem_TextFrame*>(NextBox);
 	if (next != NULL)
 	{
@@ -2493,18 +2454,8 @@
 				if (charStyle.effects() & ScStyle_DropCap)
 				{
 					const ParagraphStyle& style(itemText.paragraphStyle(a));
-					if (style.lineSpacingMode() == ParagraphStyle::BaselineGridLineSpacing)
-						chs = qRound(10 * ((m_Doc->guidesPrefs().valueBaselineGrid * (style.dropCapLines()-1) + (charStyle.font().ascent(style.charStyle().fontSize() / 10.0))) / charStyle.font().realCharHeight(chstr0, 10)));
-					else
-					{
-						if (style.lineSpacingMode() == ParagraphStyle::FixedLineSpacing)
-							chs = qRound(10 * ((style.lineSpacing() * (style.dropCapLines()-1)+(charStyle.font().ascent(style.charStyle().fontSize() / 10.0))) / charStyle.font().realCharHeight(chstr0, 10)));
-						else
-						{
-							double currasce = charStyle.font().height(style.charStyle().fontSize() / 10.0);
-							chs = qRound(10 * ((currasce * (style.dropCapLines()-1)+(charStyle.font().ascent(style.charStyle().fontSize() / 10.0))) / charStyle.font().realCharHeight(chstr0, 10)));
-						}
-					}
+					double spacing = calculateLineSpacing (style, this);
+					chs = qRound(10 * ((spacing * (style.dropCapLines()-1) + (charStyle.font().ascent(style.charStyle().fontSize() / 10.0))) / charStyle.font().realCharHeight(chstr0, 10)));
 				}
 				if (chstr0 == SpecialChars::TAB)
 					tabCc++;
Index: scribus/pageitem_textframe.h
===================================================================
--- scribus/pageitem_textframe.h	(revision 16830)
+++ scribus/pageitem_textframe.h	(working copy)
@@ -98,9 +98,17 @@
 	virtual bool createInfoGroup(QFrame *, QGridLayout *);
 	virtual void applicableActions(QStringList& actionList);
 	virtual QString infoDescription();
-	
+	// Move incomplete lines from the previous frame if needed.
+	bool moveLinesFromPreviousFrame ();
+	void adjustParagraphEndings ();
+
 private:
 	bool cursorBiasBackward;
+	// If the last paragraph had to be split, this is how many lines of the paragraph are in this frame.
+	// Used for orphan/widow control
+	int incompleteLines;
+	// This holds the line splitting positions
+	QList<int> incompletePositions;
 
 	void setShadow();
 	QString currentShadow;
Index: scribus/pdflib_core.cpp
===================================================================
--- scribus/pdflib_core.cpp	(revision 16830)
+++ scribus/pdflib_core.cpp	(working copy)
@@ -4811,7 +4811,7 @@
 				const QChar ch = hl->ch;
 				const CharStyle& chstyle(ite->itemText.charStyle(d));
 				const ParagraphStyle& pstyle(ite->itemText.paragraphStyle(d));
-				if ((ch == SpecialChars::PARSEP) || (ch == QChar(10)) || (ch == SpecialChars::LINEBREAK) || (ch == SpecialChars::FRAMEBREAK) || (ch == SpecialChars::COLBREAK))
+				if (SpecialChars::isBreak(ch, true) || (ch == QChar(10)))
 					continue;
 				if (chstyle.effects() & ScStyle_SuppressSpace)
 					continue;
@@ -4921,7 +4921,7 @@
 			const QChar ch = hl->ch;
 			const CharStyle& chstyle(ite->itemText.charStyle(d));
 			const ParagraphStyle& pstyle(ite->itemText.paragraphStyle(d));
-			if ((ch == SpecialChars::PARSEP) || (ch == QChar(10)) || (ch == SpecialChars::LINEBREAK) || (ch == SpecialChars::FRAMEBREAK) || (ch == SpecialChars::COLBREAK))
+			if (SpecialChars::isBreak(ch, true) || (ch == QChar(10)))
 				continue;
 			if (chstyle.effects() & ScStyle_SuppressSpace)
 				continue;
Index: scribus/plugins/fileloader/scribus150format/scribus150format.cpp
===================================================================
--- scribus/plugins/fileloader/scribus150format/scribus150format.cpp	(revision 16832)
+++ scribus/plugins/fileloader/scribus150format/scribus150format.cpp	(working copy)
@@ -2117,6 +2117,22 @@
 	if (attrs.hasAttribute(MaxGlyphExtend))
 		newStyle.setMaxGlyphExtension(attrs.valueAsDouble(MaxGlyphExtend));
 	
+	static const QString KeepLinesStart("KeepLinesStart");
+	if (attrs.hasAttribute(KeepLinesStart))
+		newStyle.setKeepLinesStart(attrs.valueAsInt(KeepLinesStart));
+
+	static const QString KeepLinesEnd("KeepLinesEnd");
+	if (attrs.hasAttribute(KeepLinesEnd))
+		newStyle.setKeepLinesEnd(attrs.valueAsInt(KeepLinesEnd));
+
+	static const QString KeepWithNext("KeepWithNext");
+	if (attrs.hasAttribute(KeepWithNext))
+		newStyle.setKeepWithNext(attrs.valueAsInt(KeepWithNext));
+
+	static const QString KeepTogether("KeepTogether");
+	if (attrs.hasAttribute(KeepTogether))
+		newStyle.setKeepTogether(attrs.valueAsInt(KeepTogether));
+
 	readCharacterStyleAttrs( doc, attrs, newStyle.charStyle());
 
 	//	newStyle.tabValues().clear();
@@ -3532,6 +3548,14 @@
 		pstyle.setRightMargin(attrs.valueAsDouble("rightMargin"));
 	if (attrs.hasAttribute("firstIndent"))
 		pstyle.setFirstIndent(attrs.valueAsDouble("firstIndent"));
+	if (attrs.hasAttribute("keepLinesStart"))
+		pstyle.setKeepLinesStart(attrs.valueAsInt("keepLinesStart"));
+	if (attrs.hasAttribute("keepLinesEnd"))
+		pstyle.setKeepLinesEnd(attrs.valueAsInt("keepLinesEnd"));
+	if (attrs.hasAttribute("keepWithNext"))
+		pstyle.setKeepWithNext(attrs.valueAsBool("keepWithNext"));
+	if (attrs.hasAttribute("keepTogether"))
+		pstyle.setKeepTogether(attrs.valueAsBool("keepTogether"));
 	currItem->itemText.setDefaultStyle(pstyle);
 
 	if (attrs.hasAttribute("PSTYLE"))
Index: scribus/plugins/fileloader/scribus150format/scribus150format_save.cpp
===================================================================
--- scribus/plugins/fileloader/scribus150format/scribus150format_save.cpp	(revision 16830)
+++ scribus/plugins/fileloader/scribus150format/scribus150format_save.cpp	(working copy)
@@ -619,6 +619,14 @@
 		docu.writeAttribute("MinGlyphShrink", style.minGlyphExtension());
 	if ( ! style.isInhMaxGlyphExtension())
 		docu.writeAttribute("MaxGlyphExtend", style.maxGlyphExtension());
+	if ( ! style.isInhKeepLinesStart())
+		docu.writeAttribute("KeepLinesStart", style.keepLinesStart());
+	if ( ! style.isInhKeepLinesEnd())
+		docu.writeAttribute("KeepLinesEnd", style.keepLinesEnd());
+	if ( ! style.isInhKeepWithNext())
+		docu.writeAttribute("KeepWithNext", style.keepWithNext());
+	if ( ! style.isInhKeepTogether())
+		docu.writeAttribute("KeepTogether", style.keepTogether());
 
 	if ( ! style.shortcut().isEmpty() )
 		docu.writeAttribute("PSHORTCUT", style.shortcut()); // shortcuts won't be inherited
@@ -1850,64 +1858,73 @@
 		docu.writeAttribute("PICART", item->imageShown() ? 1 : 0);
 		docu.writeAttribute("SCALETYPE", item->ScaleType ? 1 : 0);
 		docu.writeAttribute("RATIO", item->AspectRatio ? 1 : 0);
-		if ( ! item->itemText.defaultStyle().charStyle().isInhFillColor())
-			docu.writeAttribute("TXTFILL",item->itemText.defaultStyle().charStyle().fillColor());
-		if ( ! item->itemText.defaultStyle().charStyle().isInhStrokeColor())
-			docu.writeAttribute("TXTSTROKE",item->itemText.defaultStyle().charStyle().strokeColor());
-		if ( ! item->itemText.defaultStyle().charStyle().isInhStrokeShade())
-			docu.writeAttribute("TXTSTRSH",item->itemText.defaultStyle().charStyle().strokeShade());
-		if ( ! item->itemText.defaultStyle().charStyle().isInhFillShade())
-			docu.writeAttribute("TXTFILLSH",item->itemText.defaultStyle().charStyle().fillShade());
-		if ( ! item->itemText.defaultStyle().charStyle().isInhScaleH())
-			docu.writeAttribute("TXTSCALE",item->itemText.defaultStyle().charStyle().scaleH() / 10.0);
-		if ( ! item->itemText.defaultStyle().charStyle().isInhScaleV())
-			docu.writeAttribute("TXTSCALEV",item->itemText.defaultStyle().charStyle().scaleV() / 10.0);
-		if ( ! item->itemText.defaultStyle().charStyle().isInhBaselineOffset())
-			docu.writeAttribute("TXTBASE",item->itemText.defaultStyle().charStyle().baselineOffset() / 10.0);
-		if ( ! item->itemText.defaultStyle().charStyle().isInhShadowXOffset())
-			docu.writeAttribute("TXTSHX",item->itemText.defaultStyle().charStyle().shadowXOffset() / 10.0);
-		if ( ! item->itemText.defaultStyle().charStyle().isInhShadowYOffset())
-			docu.writeAttribute("TXTSHY",item->itemText.defaultStyle().charStyle().shadowYOffset() / 10.0);
-		if ( ! item->itemText.defaultStyle().charStyle().isInhOutlineWidth())
-			docu.writeAttribute("TXTOUT",item->itemText.defaultStyle().charStyle().outlineWidth() / 10.0);
-		if ( ! item->itemText.defaultStyle().charStyle().isInhUnderlineOffset())
-			docu.writeAttribute("TXTULP",item->itemText.defaultStyle().charStyle().underlineOffset() / 10.0);
-		if ( ! item->itemText.defaultStyle().charStyle().isInhUnderlineWidth())
-			docu.writeAttribute("TXTULW",item->itemText.defaultStyle().charStyle().underlineWidth() / 10.0);
-		if ( ! item->itemText.defaultStyle().charStyle().isInhStrikethruOffset())
-			docu.writeAttribute("TXTSTP",item->itemText.defaultStyle().charStyle().strikethruOffset() / 10.0);
-		if ( ! item->itemText.defaultStyle().charStyle().isInhStrikethruWidth())
-			docu.writeAttribute("TXTSTW",item->itemText.defaultStyle().charStyle().strikethruWidth() / 10.0);
-		if ( ! item->itemText.defaultStyle().charStyle().isInhTracking())
-			docu.writeAttribute("TXTKERN",item->itemText.defaultStyle().charStyle().tracking() / 10.0);
-		if ( ! item->itemText.defaultStyle().charStyle().isInhWordTracking())
-			docu.writeAttribute("wordTrack",item->itemText.defaultStyle().charStyle().wordTracking());
-		if ( ! item->itemText.defaultStyle().isInhMinWordTracking())
-			docu.writeAttribute("MinWordTrack", item->itemText.defaultStyle().minWordTracking());
-		if ( ! item->itemText.defaultStyle().isInhMinGlyphExtension())
-			docu.writeAttribute("MinGlyphShrink", item->itemText.defaultStyle().minGlyphExtension());
-		if ( ! item->itemText.defaultStyle().isInhMaxGlyphExtension())
-			docu.writeAttribute("MaxGlyphExtend", item->itemText.defaultStyle().maxGlyphExtension());
-		if ( ! item->itemText.defaultStyle().isInhOpticalMargins())
-			docu.writeAttribute("OpticalMargins", item->itemText.defaultStyle().opticalMargins());
-		if ( ! item->itemText.defaultStyle().isInhHyphenationMode())
-			docu.writeAttribute("HyphenationMode", item->itemText.defaultStyle().hyphenationMode());
-		if ( ! item->itemText.defaultStyle().isInhLeftMargin() )
-			docu.writeAttribute("leftMargin", item->itemText.defaultStyle().leftMargin());
-		if ( ! item->itemText.defaultStyle().isInhRightMargin())
-			docu.writeAttribute("rightMargin", item->itemText.defaultStyle().rightMargin());
-		if ( ! item->itemText.defaultStyle().isInhFirstIndent())
-			docu.writeAttribute("firstIndent", item->itemText.defaultStyle().firstIndent());
-		if ( ! item->itemText.defaultStyle().isInhLineSpacing())
-			docu.writeAttribute("LINESP",item->itemText.defaultStyle().lineSpacing());
-		if ( ! item->itemText.defaultStyle().isInhLineSpacingMode())
-			docu.writeAttribute("LINESPMode", item->itemText.defaultStyle().lineSpacingMode());
-		if ( ! item->itemText.defaultStyle().charStyle().isInhFont())
-			docu.writeAttribute("IFONT",item->itemText.defaultStyle().charStyle().font().scName());
-		if ( ! item->itemText.defaultStyle().charStyle().isInhFontSize())
-			docu.writeAttribute("ISIZE",item->itemText.defaultStyle().charStyle().fontSize() / 10.0 );
-		if ( ! item->itemText.defaultStyle().charStyle().isInhLanguage())
-			docu.writeAttribute("LANGUAGE", item->itemText.defaultStyle().charStyle().language());
+		const ParagraphStyle& dStyle(item->itemText.defaultStyle());
+		if ( ! dStyle.charStyle().isInhFillColor())
+			docu.writeAttribute("TXTFILL", dStyle.charStyle().fillColor());
+		if ( ! dStyle.charStyle().isInhStrokeColor())
+			docu.writeAttribute("TXTSTROKE", dStyle.charStyle().strokeColor());
+		if ( ! dStyle.charStyle().isInhStrokeShade())
+			docu.writeAttribute("TXTSTRSH", dStyle.charStyle().strokeShade());
+		if ( ! dStyle.charStyle().isInhFillShade())
+			docu.writeAttribute("TXTFILLSH", dStyle.charStyle().fillShade());
+		if ( ! dStyle.charStyle().isInhScaleH())
+			docu.writeAttribute("TXTSCALE", dStyle.charStyle().scaleH() / 10.0);
+		if ( ! dStyle.charStyle().isInhScaleV())
+			docu.writeAttribute("TXTSCALEV", dStyle.charStyle().scaleV() / 10.0);
+		if ( ! dStyle.charStyle().isInhBaselineOffset())
+			docu.writeAttribute("TXTBASE", dStyle.charStyle().baselineOffset() / 10.0);
+		if ( ! dStyle.charStyle().isInhShadowXOffset())
+			docu.writeAttribute("TXTSHX", dStyle.charStyle().shadowXOffset() / 10.0);
+		if ( ! dStyle.charStyle().isInhShadowYOffset())
+			docu.writeAttribute("TXTSHY", dStyle.charStyle().shadowYOffset() / 10.0);
+		if ( ! dStyle.charStyle().isInhOutlineWidth())
+			docu.writeAttribute("TXTOUT", dStyle.charStyle().outlineWidth() / 10.0);
+		if ( ! dStyle.charStyle().isInhUnderlineOffset())
+			docu.writeAttribute("TXTULP", dStyle.charStyle().underlineOffset() / 10.0);
+		if ( ! dStyle.charStyle().isInhUnderlineWidth())
+			docu.writeAttribute("TXTULW", dStyle.charStyle().underlineWidth() / 10.0);
+		if ( ! dStyle.charStyle().isInhStrikethruOffset())
+			docu.writeAttribute("TXTSTP", dStyle.charStyle().strikethruOffset() / 10.0);
+		if ( ! dStyle.charStyle().isInhStrikethruWidth())
+			docu.writeAttribute("TXTSTW", dStyle.charStyle().strikethruWidth() / 10.0);
+		if ( ! dStyle.charStyle().isInhTracking())
+			docu.writeAttribute("TXTKERN", dStyle.charStyle().tracking() / 10.0);
+		if ( ! dStyle.charStyle().isInhWordTracking())
+			docu.writeAttribute("wordTrack", dStyle.charStyle().wordTracking());
+		if ( ! dStyle.isInhMinWordTracking())
+			docu.writeAttribute("MinWordTrack", dStyle.minWordTracking());
+		if ( ! dStyle.isInhMinGlyphExtension())
+			docu.writeAttribute("MinGlyphShrink", dStyle.minGlyphExtension());
+		if ( ! dStyle.isInhMaxGlyphExtension())
+			docu.writeAttribute("MaxGlyphExtend", dStyle.maxGlyphExtension());
+		if ( ! dStyle.isInhOpticalMargins())
+			docu.writeAttribute("OpticalMargins", dStyle.opticalMargins());
+		if ( ! dStyle.isInhHyphenationMode())
+			docu.writeAttribute("HyphenationMode", dStyle.hyphenationMode());
+		if ( ! dStyle.isInhLeftMargin() )
+			docu.writeAttribute("leftMargin", dStyle.leftMargin());
+		if ( ! dStyle.isInhRightMargin())
+			docu.writeAttribute("rightMargin", dStyle.rightMargin());
+		if ( ! dStyle.isInhFirstIndent())
+			docu.writeAttribute("firstIndent", dStyle.firstIndent());
+		if ( ! dStyle.isInhLineSpacing())
+			docu.writeAttribute("LINESP", dStyle.lineSpacing());
+		if ( ! dStyle.isInhLineSpacingMode())
+			docu.writeAttribute("LINESPMode", dStyle.lineSpacingMode());
+		if ( ! dStyle.isInhKeepLinesStart())
+			docu.writeAttribute("KeepLinesStart", dStyle.keepLinesStart());
+		if ( ! dStyle.isInhKeepLinesEnd())
+			docu.writeAttribute("KeepLinesEnd", dStyle.keepLinesEnd());
+		if ( ! dStyle.isInhKeepWithNext())
+			docu.writeAttribute("KeepWithNext", dStyle.keepWithNext());
+		if ( ! dStyle.isInhKeepTogether())
+			docu.writeAttribute("KeepTogether", dStyle.keepTogether());
+		if ( ! dStyle.charStyle().isInhFont())
+			docu.writeAttribute("IFONT", dStyle.charStyle().font().scName());
+		if ( ! dStyle.charStyle().isInhFontSize())
+			docu.writeAttribute("ISIZE", dStyle.charStyle().fontSize() / 10.0 );
+		if ( ! dStyle.charStyle().isInhLanguage())
+			docu.writeAttribute("LANGUAGE", dStyle.charStyle().language());
 	}
 	if (item->asTextFrame() || item->asPathText())
 	{
Index: scribus/scribus.cpp
===================================================================
--- scribus/scribus.cpp	(revision 16830)
+++ scribus/scribus.cpp	(working copy)
@@ -1107,7 +1107,7 @@
 		doc->currentStyle.charStyle().setStyle( currItem->currentCharStyle() );
 		emit TextStyle(doc->currentStyle);
 		// to go: (av)
-		propertiesPalette->textPal->updateCharStyle(doc->currentStyle.charStyle());
+		propertiesPalette->textPal->updateStyle(doc->currentStyle);
 	}
 }
 
@@ -2843,7 +2843,7 @@
 			propertiesPalette->textPal->displayCharStyle(doc->currentStyle.charStyle().parent());
 			emit TextStyle(doc->currentStyle);
 			// to go: (av)
-			propertiesPalette->textPal->updateCharStyle(doc->currentStyle.charStyle());
+			propertiesPalette->textPal->updateStyle(doc->currentStyle);
 			setStyleEffects(doc->currentStyle.charStyle().effects());
 		}
 
@@ -2909,7 +2909,7 @@
 			propertiesPalette->textPal->displayCharStyle(doc->currentStyle.charStyle().parent());
 			emit TextStyle(doc->currentStyle);
 			// to go: (av)
-			propertiesPalette->textPal->updateCharStyle(doc->currentStyle.charStyle());
+			propertiesPalette->textPal->updateStyle(doc->currentStyle);
 			setStyleEffects(doc->currentStyle.charStyle().effects());
 		}
 		break;
Index: scribus/styles/paragraphstyle.attrdefs.cxx
===================================================================
--- scribus/styles/paragraphstyle.attrdefs.cxx	(revision 16830)
+++ scribus/styles/paragraphstyle.attrdefs.cxx	(working copy)
@@ -35,5 +35,8 @@
 ATTRDEF(bool, hasDropCap, HasDropCap, false)
 ATTRDEF(double, dropCapOffset, DropCapOffset, 0.0)
 ATTRDEF(bool, useBaselineGrid, UseBaselineGrid, false)
+ATTRDEF(int, keepLinesStart, KeepLinesStart, 0)
+ATTRDEF(int, keepLinesEnd, KeepLinesEnd, 0)
+ATTRDEF(bool, keepWithNext, KeepWithNext, false)
+ATTRDEF(bool, keepTogether, KeepTogether, false)
 
-
Index: scribus/text/storytext.h
===================================================================
--- scribus/text/storytext.h	(revision 16830)
+++ scribus/text/storytext.h	(working copy)
@@ -288,7 +288,21 @@
 			lastFrameItem = qMax(lastFrameItem, ls.lastItem);
 		}
 	}
-	
+
+	// Remove the last line from the list. Used when we need to backtrack on the layouting.
+	void removeLastLine ()
+	{
+		if (m_lines.isEmpty()) return;
+		LineSpec last = m_lines.takeLast ();
+		if (m_lines.isEmpty()) {
+			clearLines();
+			return;
+		}
+		// fix lastFrameItem
+		if (lastFrameItem != last.lastItem) return;
+		lastFrameItem = m_lines.last().lastItem;
+	}
+
 	void clearLines() 
 	{ 
 		m_lines.clear(); 
Index: scribus/ui/propertiespalette_text.cpp
===================================================================
--- scribus/ui/propertiespalette_text.cpp	(revision 16832)
+++ scribus/ui/propertiespalette_text.cpp	(working copy)
@@ -20,6 +20,7 @@
 #include "propertywidget_advanced.h"
 #include "propertywidget_distance.h"
 #include "propertywidget_flop.h"
+#include "propertywidget_flow.h"
 #include "propertywidget_pathtext.h"
 #include "propertywidget_optmargins.h"
 #include "propertywidget_textcolor.h"
@@ -71,6 +72,9 @@
 	flopBox = new PropertyWidget_Flop(textTree);
 	flopItem = textTree->addWidget( tr("First Line Offset"), flopBox);
 
+	flowBox = new PropertyWidget_Flow(textTree);
+	flowItem = textTree->addWidget( tr("Text Flow"), flowBox);
+
 	distanceWidgets = new PropertyWidget_Distance(textTree);
     distanceItem = textTree->addWidget( tr("Columns & Text Distances"), distanceWidgets);
 
@@ -165,6 +169,7 @@
 	colorWidgets->setDoc(m_doc);
 	distanceWidgets->setDoc(m_doc);
 	flopBox->setDoc(m_doc);
+	flowBox->setDoc(m_doc);
 	optMargins->setDoc(m_doc);
 	pathTextWidgets->setDoc(m_doc);
 
@@ -196,6 +201,7 @@
 	colorWidgets->setDoc(0);
 	distanceWidgets->setDoc(0);
 	flopBox->setDoc(0);
+	flowBox->setDoc(0);
 	optMargins->setDoc(0);
 
 	m_haveItem = false;
@@ -362,6 +368,7 @@
 	if (m_item->asPathText())
 	{
 		flopItem->setHidden(true);
+		flowItem->setHidden(true);
 		distanceItem->setHidden(true);
 		pathTextItem->setHidden(false);
 		pathTextWidgets->pathTextType->setCurrentIndex(m_item->textPathType);
@@ -373,12 +380,14 @@
 	else if (m_item->asTextFrame())
 	{
 		flopItem->setHidden(false);
+		flowItem->setHidden(false);
 		distanceItem->setHidden(false);
 		pathTextItem->setHidden(true);
 	}
 	else
 	{
 		flopItem->setHidden(false);
+		flowItem->setHidden(false);
 		distanceItem->setHidden(false);
 		pathTextItem->setHidden(true);
 	}
@@ -562,6 +571,7 @@
 
 	advancedWidgets->updateStyle(newCurrent);
 	colorWidgets->updateStyle(newCurrent);
+	flowBox->updateStyle (newCurrent);
 
 	displayFontFace(charStyle.font().scName());
 	displayFontSize(charStyle.fontSize());
@@ -912,6 +922,7 @@
 	colorWidgetsItem->setText(0, tr("Color & Effects"));
 	advancedWidgetsItem->setText(0, tr("Advanced Settings"));
 	flopItem->setText(0, tr("First Line Offset"));
+	flowItem->setText(0, tr("Text Flow"));
     distanceItem->setText(0, tr("Columns & Text Distances"));
 	optMarginsItem->setText(0, tr("Optical Margins"));
 	pathTextItem->setText(0, tr("Path Text Properties"));
@@ -932,6 +943,7 @@
 	colorWidgets->languageChange();
 	distanceWidgets->languageChange();
 	flopBox->languageChange();
+	flowBox->languageChange();
 	optMargins->languageChange();
 	pathTextWidgets->languageChange();
 
Index: scribus/ui/propertiespalette_text.h
===================================================================
--- scribus/ui/propertiespalette_text.h	(revision 16830)
+++ scribus/ui/propertiespalette_text.h	(working copy)
@@ -26,6 +26,7 @@
 class PropertyWidget_Advanced;
 class PropertyWidget_Distance;
 class PropertyWidget_Flop;
+class PropertyWidget_Flow;
 class PropertyWidget_OptMargins;
 class PropertyWidget_PathText;
 class PropertyWidget_TextColor;
@@ -150,6 +151,9 @@
 
 	PropertyWidget_Flop* flopBox;
 	QTreeWidgetItem* flopItem;
+
+	PropertyWidget_Flow* flowBox;
+	QTreeWidgetItem* flowItem;
 	
 	PropertyWidget_PathText* pathTextWidgets;
 	QTreeWidgetItem* pathTextItem;
Index: scribus/ui/propertywidget_flow.cpp
===================================================================
--- scribus/ui/propertywidget_flow.cpp	(revision 0)
+++ scribus/ui/propertywidget_flow.cpp	(revision 0)
@@ -0,0 +1,107 @@
+/*
+For general Scribus (>=1.3.2) copyright and licensing information please refer
+to the COPYING file provided with the program. Following this notice may exist
+a copyright and/or license notice that predates the release of Scribus 1.3.2
+for which a new license (GPL+exception) is in place.
+*/
+
+#include "propertywidget_flow.h"
+
+#include "scribus.h"
+
+PropertyWidget_Flow::PropertyWidget_Flow(QWidget* parent) : QFrame(parent)
+{
+	setupUi(this);
+
+	setFrameStyle(QFrame::Box | QFrame::Plain);
+	setLineWidth(1);
+	layout()->setAlignment( Qt::AlignTop );
+
+	languageChange();
+}
+
+void PropertyWidget_Flow::connectSignals()
+{
+	connect(keepLinesStart, SIGNAL(valueChanged(int)), this, SLOT(handleKeepLinesStart()));
+	connect(keepLinesEnd, SIGNAL(valueChanged(int)), this, SLOT(handleKeepLinesEnd()));
+	connect(keepTogether, SIGNAL(stateChanged(int)), this, SLOT(handleKeepTogether()));
+	connect(keepWithNext, SIGNAL(stateChanged(int)), this, SLOT(handleKeepWithNext()));
+}
+
+void PropertyWidget_Flow::disconnectSignals()
+{
+	disconnect(keepLinesStart, SIGNAL(valueChanged(int)), this, SLOT(handleKeepLinesStart()));
+	disconnect(keepLinesEnd, SIGNAL(valueChanged(int)), this, SLOT(handleKeepLinesEnd()));
+	disconnect(keepTogether, SIGNAL(stateChanged(int)), this, SLOT(handleKeepTogether()));
+	disconnect(keepWithNext, SIGNAL(stateChanged(int)), this, SLOT(handleKeepWithNext()));
+}
+
+void PropertyWidget_Flow::handleKeepLinesStart()
+{
+	if (!m_doc) return;
+	ParagraphStyle newStyle;
+	newStyle.setKeepLinesStart (keepLinesStart->value());
+	m_doc->itemSelection_ApplyParagraphStyle(newStyle);
+}
+
+void PropertyWidget_Flow::handleKeepLinesEnd()
+{
+	if (!m_doc) return;
+	ParagraphStyle newStyle;
+	newStyle.setKeepLinesEnd (keepLinesEnd->value());
+	m_doc->itemSelection_ApplyParagraphStyle(newStyle);
+}
+
+void PropertyWidget_Flow::handleKeepTogether()
+{
+	if (!m_doc) return;
+	ParagraphStyle newStyle;
+	newStyle.setKeepTogether (keepTogether->isChecked());
+	m_doc->itemSelection_ApplyParagraphStyle(newStyle);
+}
+
+void PropertyWidget_Flow::handleKeepWithNext()
+{
+	if (!m_doc) return;
+	ParagraphStyle newStyle;
+	newStyle.setKeepWithNext (keepWithNext->isChecked());
+	m_doc->itemSelection_ApplyParagraphStyle(newStyle);
+}
+
+void PropertyWidget_Flow::updateStyle(const ParagraphStyle& newCurrent)
+{
+	disconnectSignals ();
+  keepLinesStart->setValue (newCurrent.keepLinesStart());
+  keepLinesEnd->setValue (newCurrent.keepLinesEnd());
+	keepTogether->setChecked (newCurrent.keepTogether());
+	keepWithNext->setChecked (newCurrent.keepWithNext());
+	connectSignals ();
+}
+
+void PropertyWidget_Flow::changeEvent(QEvent *e)
+{
+	if (e->type() == QEvent::LanguageChange)
+	{
+		languageChange();
+		return;
+	}
+	QWidget::changeEvent(e);
+}
+
+void PropertyWidget_Flow::languageChange()
+{
+	keepLabelStart->setText (tr ("Don't separate first"));
+	keepLabelEnd->setText (tr ("Don't separate last"));
+	keepLinesStart->setSuffix (tr (" lines"));
+	keepLinesEnd->setSuffix (tr (" lines"));
+	keepTogether->setText (tr ("Do not split paragraph"));
+	keepWithNext->setText (tr ("Keep with next paragraph"));
+
+	keepLinesStart->setToolTip ("<qt>" + tr ("Ensure that first lines of a paragraph won't end up separated from the rest (known as widow/orphan control)") + "</qt>");
+	keepLinesEnd->setToolTip ("<qt>" + tr ("Ensure that last lines of a paragraph won't end up separated from the rest (known as widow/orphan control)") + "</qt>");
+	keepLabelStart->setToolTip (keepLinesStart->toolTip());
+	keepLabelEnd->setToolTip (keepLinesEnd->toolTip());
+	keepTogether->setToolTip ("<qt>" + tr ("If checked, ensures that the paragraph won't be split across multiple pages or columns") + "</qt>");
+	keepWithNext->setToolTip ("<qt>" + tr ("If checked, automatically moves the paragraph to the next column or page if the next paragraph isn't on the same page or column") + "</qt>");
+}
+
Index: scribus/ui/propertywidget_flow.h
===================================================================
--- scribus/ui/propertywidget_flow.h	(revision 0)
+++ scribus/ui/propertywidget_flow.h	(revision 0)
@@ -0,0 +1,41 @@
+/*
+For general Scribus (>=1.3.2) copyright and licensing information please refer
+to the COPYING file provided with the program. Following this notice may exist
+a copyright and/or license notice that predates the release of Scribus 1.3.2
+for which a new license (GPL+exception) is in place.
+*/
+#ifndef PROPERTYWIDGET_FLOW_H
+#define PROPERTYWIDGET_FLOW_H
+
+#include "ui_propertywidget_flowbase.h"
+
+#include "propertywidgetbase.h"
+
+class ParagraphStyle;
+
+class PropertyWidget_Flow : public QFrame, public Ui::PropertyWidget_FlowBase, public PropertyWidgetBase
+{
+	Q_OBJECT
+
+public:
+
+	PropertyWidget_Flow(QWidget* parent);
+	~PropertyWidget_Flow() {};
+
+	virtual void changeEvent(QEvent *e);
+
+  void updateStyle(const ParagraphStyle& newCurrent);
+public slots:
+
+	void languageChange();
+	void handleKeepLinesStart();
+	void handleKeepLinesEnd();
+	void handleKeepTogether();
+	void handleKeepWithNext();
+
+private:
+	void connectSignals();
+	void disconnectSignals();
+};
+
+#endif
Index: scribus/ui/propertywidget_flowbase.ui
===================================================================
--- scribus/ui/propertywidget_flowbase.ui	(revision 0)
+++ scribus/ui/propertywidget_flowbase.ui	(revision 0)
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>PropertyWidget_FlowBase</class>
+ <widget class="QFrame" name="PropertyWidget_FlowBase">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>211</width>
+    <height>130</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <layout class="QGridLayout" name="gridLayout" columnstretch="0,1">
+   <item row="0" column="0">
+    <widget class="QLabel" name="keepLabelStart">
+     <property name="toolTip">
+      <string>Ensure that first lines of a paragraph won't end up separated from the rest (known as widow/orphan control)</string>
+     </property>
+     <property name="text">
+      <string>Don't separate first</string>
+     </property>
+    </widget>
+   </item>
+   <item row="0" column="1">
+    <widget class="QSpinBox" name="keepLinesStart">
+     <property name="suffix">
+      <string> lines</string>
+     </property>
+     <property name="maximum">
+      <number>10</number>
+     </property>
+    </widget>
+   </item>
+   <item row="1" column="0">
+    <widget class="QLabel" name="keepLabelEnd">
+     <property name="text">
+      <string>Don't separate last</string>
+     </property>
+    </widget>
+   </item>
+   <item row="1" column="1">
+    <widget class="QSpinBox" name="keepLinesEnd">
+     <property name="suffix">
+      <string> lines</string>
+     </property>
+     <property name="maximum">
+      <number>10</number>
+     </property>
+    </widget>
+   </item>
+   <item row="2" column="0" colspan="2">
+    <widget class="QCheckBox" name="keepTogether">
+     <property name="text">
+      <string>Do not split paragraph</string>
+     </property>
+    </widget>
+   </item>
+   <item row="3" column="0" colspan="2">
+    <widget class="QCheckBox" name="keepWithNext">
+     <property name="text">
+      <string>Keep with next paragraph</string>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
Index: scribus/ui/smpstylewidget.cpp
===================================================================
--- scribus/ui/smpstylewidget.cpp	(revision 16830)
+++ scribus/ui/smpstylewidget.cpp	(working copy)
@@ -106,8 +106,15 @@
 	minGlyphExtLabel->setToolTip(minGlyphExtSpin->toolTip());
 	maxGlyphExtSpin->setToolTip(tr("Maximum extension of glyphs"));
 	maxGlyphExtLabel->setToolTip(maxGlyphExtSpin->toolTip());
-	
 
+	keepLinesStart->setToolTip ("<qt>" + tr ("Ensure that first lines of a paragraph won't end up separated from the rest (known as widow/orphan control)") + "</qt>");
+	keepLinesEnd->setToolTip ("<qt>" + tr ("Ensure that last lines of a paragraph won't end up separated from the rest (known as widow/orphan control)") + "</qt>");
+	keepLabelStart->setToolTip (keepLinesStart->toolTip());
+	keepLabelEnd->setToolTip (keepLinesEnd->toolTip());
+	keepTogether->setToolTip ("<qt>" + tr ("If checked, ensures that the paragraph won't be split across multiple pages or columns") + "</qt>");
+	keepWithNext->setToolTip ("<qt>" + tr ("If checked, automatically moves the paragraph to the next column or page if the next paragraph isn't on the same page or column") + "</qt>");
+
+
 /***********************************/
 /*      End Tooltips               */
 /***********************************/
@@ -140,6 +147,13 @@
 	glyphExtensionLabel->setText(tr("Glyph Extension"));
 	minGlyphExtLabel->setText(tr("Min:", "Glyph Extension"));
 	maxGlyphExtLabel->setText(tr("Max:", "Glyph Extension"));
+
+	keepLabelStart->setText (tr ("Don't separate first"));
+	keepLabelEnd->setText (tr ("Don't separate last"));
+	keepLinesStart->setSuffix (tr (" lines"));
+	keepLinesEnd->setSuffix (tr (" lines"));
+	keepTogether->setText (tr ("Do not split paragraph"));
+	keepWithNext->setText (tr ("Keep with next paragraph"));
 }
 
 void SMPStyleWidget::unitChange(double oldRatio, double newRatio, int unitIndex)
@@ -236,6 +250,15 @@
 
 		tabList_->setRightIndentValue(pstyle->rightMargin() * unitRatio, pstyle->isInhRightMargin());
 		tabList_->setParentRightIndent(parent->rightMargin() * unitRatio);
+
+		keepLinesStart->setValue (pstyle->keepLinesStart(), pstyle->isInhKeepLinesStart());
+		keepLinesEnd->setValue (pstyle->keepLinesEnd(), pstyle->isInhKeepLinesEnd());
+		keepTogether->setChecked (pstyle->keepTogether(), pstyle->isInhKeepTogether());
+		keepWithNext->setChecked (pstyle->keepWithNext(), pstyle->isInhKeepWithNext());
+		keepLinesStart->setParentValue (parent->keepLinesStart());
+		keepLinesEnd->setParentValue (parent->keepLinesEnd());
+		keepTogether->setParentValue (parent->keepTogether());
+		keepWithNext->setParentValue (parent->keepWithNext());
 	}
 	else
 	{
@@ -260,7 +283,11 @@
 		tabList_->setLeftIndentValue(pstyle->leftMargin() * unitRatio);
 		tabList_->setFirstLineValue(pstyle->firstIndent() * unitRatio);
 		tabList_->setRightIndentValue(pstyle->rightMargin() * unitRatio);
-		
+
+		keepLinesStart->setValue (pstyle->keepLinesStart());
+		keepLinesEnd->setValue (pstyle->keepLinesEnd());
+		keepTogether->setChecked (pstyle->keepTogether());
+		keepWithNext->setChecked (pstyle->keepWithNext());
 	}
 
 	lineSpacing_->setEnabled(pstyle->lineSpacingMode() == ParagraphStyle::FixedLineSpacing);
Index: scribus/ui/smpstylewidget.ui
===================================================================
--- scribus/ui/smpstylewidget.ui	(revision 16830)
+++ scribus/ui/smpstylewidget.ui	(working copy)
@@ -1,47 +1,48 @@
-<ui version="4.0" >
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
  <class>SMPStyleWidget</class>
- <widget class="QWidget" name="SMPStyleWidget" >
-  <property name="geometry" >
+ <widget class="QWidget" name="SMPStyleWidget">
+  <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
     <width>714</width>
-    <height>533</height>
+    <height>580</height>
    </rect>
   </property>
-  <layout class="QVBoxLayout" >
+  <layout class="QVBoxLayout">
    <item>
-    <widget class="QTabWidget" name="tabWidget" >
-     <property name="currentIndex" >
+    <widget class="QTabWidget" name="tabWidget">
+     <property name="currentIndex">
       <number>0</number>
      </property>
-     <widget class="QWidget" name="tab" >
-      <attribute name="title" >
+     <widget class="QWidget" name="tab">
+      <attribute name="title">
        <string>Properties</string>
       </attribute>
-      <layout class="QVBoxLayout" name="verticalLayout_4" >
+      <layout class="QVBoxLayout" name="verticalLayout_4">
        <item>
-        <layout class="QHBoxLayout" >
-         <property name="spacing" >
+        <layout class="QHBoxLayout">
+         <property name="spacing">
           <number>5</number>
          </property>
-         <property name="margin" >
+         <property name="margin">
           <number>0</number>
          </property>
          <item>
-          <widget class="QLabel" name="parentLabel" >
-           <property name="text" >
+          <widget class="QLabel" name="parentLabel">
+           <property name="text">
             <string>Based On:</string>
            </property>
-           <property name="wordWrap" >
+           <property name="wordWrap">
             <bool>false</bool>
            </property>
           </widget>
          </item>
          <item>
-          <widget class="QComboBox" name="parentCombo" >
-           <property name="sizePolicy" >
-            <sizepolicy vsizetype="Fixed" hsizetype="Minimum" >
+          <widget class="QComboBox" name="parentCombo">
+           <property name="sizePolicy">
+            <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
              <horstretch>5</horstretch>
              <verstretch>0</verstretch>
             </sizepolicy>
@@ -51,49 +52,49 @@
         </layout>
        </item>
        <item>
-        <layout class="QHBoxLayout" name="horizontalLayout_5" >
+        <layout class="QHBoxLayout" name="horizontalLayout_5">
          <item>
-          <layout class="QVBoxLayout" name="verticalLayout_2" >
+          <layout class="QVBoxLayout" name="verticalLayout_2">
            <item>
-            <widget class="QGroupBox" name="distancesBox" >
-             <property name="sizePolicy" >
-              <sizepolicy vsizetype="Minimum" hsizetype="Preferred" >
+            <widget class="QGroupBox" name="distancesBox">
+             <property name="sizePolicy">
+              <sizepolicy hsizetype="Preferred" vsizetype="Minimum">
                <horstretch>0</horstretch>
                <verstretch>0</verstretch>
               </sizepolicy>
              </property>
-             <property name="font" >
+             <property name="font">
               <font>
                <weight>50</weight>
                <bold>false</bold>
               </font>
              </property>
-             <property name="title" >
+             <property name="title">
               <string>Distances and Alignment</string>
              </property>
-             <layout class="QVBoxLayout" >
+             <layout class="QVBoxLayout">
               <item>
-               <layout class="QHBoxLayout" >
+               <layout class="QHBoxLayout">
                 <item>
-                 <widget class="QLabel" name="lineSpacingLabel" >
-                  <property name="maximumSize" >
+                 <widget class="QLabel" name="lineSpacingLabel">
+                  <property name="maximumSize">
                    <size>
                     <width>22</width>
                     <height>22</height>
                    </size>
                   </property>
-                  <property name="text" >
+                  <property name="text">
                    <string>TextLabel</string>
                   </property>
-                  <property name="buddy" >
+                  <property name="buddy">
                    <cstring>lineSpacingMode_</cstring>
                   </property>
                  </widget>
                 </item>
                 <item>
-                 <widget class="SMScComboBox" name="lineSpacingMode_" >
-                  <property name="sizePolicy" >
-                   <sizepolicy vsizetype="Fixed" hsizetype="MinimumExpanding" >
+                 <widget class="SMScComboBox" name="lineSpacingMode_">
+                  <property name="sizePolicy">
+                   <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
                     <horstretch>0</horstretch>
                     <verstretch>0</verstretch>
                    </sizepolicy>
@@ -101,21 +102,21 @@
                  </widget>
                 </item>
                 <item>
-                 <widget class="SMScrSpinBox" name="lineSpacing_" >
-                  <property name="minimum" >
+                 <widget class="SMScrSpinBox" name="lineSpacing_">
+                  <property name="minimum">
                    <number>1</number>
                   </property>
-                  <property name="maximum" >
+                  <property name="maximum">
                    <number>300</number>
                   </property>
                  </widget>
                 </item>
                 <item>
                  <spacer>
-                  <property name="orientation" >
+                  <property name="orientation">
                    <enum>Qt::Horizontal</enum>
                   </property>
-                  <property name="sizeHint" stdset="0" >
+                  <property name="sizeHint" stdset="0">
                    <size>
                     <width>40</width>
                     <height>20</height>
@@ -126,36 +127,36 @@
                </layout>
               </item>
               <item>
-               <layout class="QHBoxLayout" >
+               <layout class="QHBoxLayout">
                 <item>
-                 <widget class="QLabel" name="spaceAboveLabel" >
-                  <property name="maximumSize" >
+                 <widget class="QLabel" name="spaceAboveLabel">
+                  <property name="maximumSize">
                    <size>
                     <width>22</width>
                     <height>22</height>
                    </size>
                   </property>
-                  <property name="text" >
+                  <property name="text">
                    <string>TextLabel</string>
                   </property>
-                  <property name="buddy" >
+                  <property name="buddy">
                    <cstring>spaceAbove_</cstring>
                   </property>
                  </widget>
                 </item>
                 <item>
-                 <widget class="SMScrSpinBox" name="spaceAbove_" >
-                  <property name="maximum" >
+                 <widget class="SMScrSpinBox" name="spaceAbove_">
+                  <property name="maximum">
                    <number>300</number>
                   </property>
                  </widget>
                 </item>
                 <item>
                  <spacer>
-                  <property name="orientation" >
+                  <property name="orientation">
                    <enum>Qt::Horizontal</enum>
                   </property>
-                  <property name="sizeHint" stdset="0" >
+                  <property name="sizeHint" stdset="0">
                    <size>
                     <width>40</width>
                     <height>20</height>
@@ -166,36 +167,36 @@
                </layout>
               </item>
               <item>
-               <layout class="QHBoxLayout" >
+               <layout class="QHBoxLayout">
                 <item>
-                 <widget class="QLabel" name="spaceBelowLabel" >
-                  <property name="maximumSize" >
+                 <widget class="QLabel" name="spaceBelowLabel">
+                  <property name="maximumSize">
                    <size>
                     <width>22</width>
                     <height>22</height>
                    </size>
                   </property>
-                  <property name="text" >
+                  <property name="text">
                    <string>TextLabel</string>
                   </property>
-                  <property name="buddy" >
+                  <property name="buddy">
                    <cstring>spaceBelow_</cstring>
                   </property>
                  </widget>
                 </item>
                 <item>
-                 <widget class="SMScrSpinBox" name="spaceBelow_" >
-                  <property name="maximum" >
+                 <widget class="SMScrSpinBox" name="spaceBelow_">
+                  <property name="maximum">
                    <number>300</number>
                   </property>
                  </widget>
                 </item>
                 <item>
                  <spacer>
-                  <property name="orientation" >
+                  <property name="orientation">
                    <enum>Qt::Horizontal</enum>
                   </property>
-                  <property name="sizeHint" stdset="0" >
+                  <property name="sizeHint" stdset="0">
                    <size>
                     <width>40</width>
                     <height>20</height>
@@ -206,11 +207,11 @@
                </layout>
               </item>
               <item>
-               <layout class="QHBoxLayout" >
+               <layout class="QHBoxLayout">
                 <item>
-                 <widget class="SMAlignSelect" native="1" name="alignement_" >
-                  <property name="sizePolicy" >
-                   <sizepolicy vsizetype="Minimum" hsizetype="Minimum" >
+                 <widget class="SMAlignSelect" name="alignement_" native="true">
+                  <property name="sizePolicy">
+                   <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
                     <horstretch>0</horstretch>
                     <verstretch>0</verstretch>
                    </sizepolicy>
@@ -219,10 +220,10 @@
                 </item>
                 <item>
                  <spacer>
-                  <property name="orientation" >
+                  <property name="orientation">
                    <enum>Qt::Horizontal</enum>
                   </property>
-                  <property name="sizeHint" stdset="0" >
+                  <property name="sizeHint" stdset="0">
                    <size>
                     <width>211</width>
                     <height>20</height>
@@ -236,45 +237,45 @@
             </widget>
            </item>
            <item>
-            <widget class="QGroupBox" name="dropCapsBox" >
-             <property name="sizePolicy" >
-              <sizepolicy vsizetype="Minimum" hsizetype="Preferred" >
+            <widget class="QGroupBox" name="dropCapsBox">
+             <property name="sizePolicy">
+              <sizepolicy hsizetype="Preferred" vsizetype="Minimum">
                <horstretch>0</horstretch>
                <verstretch>0</verstretch>
               </sizepolicy>
              </property>
-             <property name="title" >
+             <property name="title">
               <string>Drop Caps</string>
              </property>
-             <property name="checkable" >
+             <property name="checkable">
               <bool>true</bool>
              </property>
-             <property name="checked" >
+             <property name="checked">
               <bool>true</bool>
              </property>
-             <layout class="QVBoxLayout" >
+             <layout class="QVBoxLayout">
               <item>
-               <layout class="QHBoxLayout" >
+               <layout class="QHBoxLayout">
                 <item>
-                 <layout class="QVBoxLayout" >
+                 <layout class="QVBoxLayout">
                   <item>
-                   <layout class="QHBoxLayout" >
+                   <layout class="QHBoxLayout">
                     <item>
-                     <widget class="QLabel" name="label" >
-                      <property name="text" >
+                     <widget class="QLabel" name="label">
+                      <property name="text">
                        <string>&amp;Lines:</string>
                       </property>
-                      <property name="buddy" >
+                      <property name="buddy">
                        <cstring>dropCapLines_</cstring>
                       </property>
                      </widget>
                     </item>
                     <item>
-                     <widget class="SMSpinBox" name="dropCapLines_" >
-                      <property name="minimum" >
+                     <widget class="SMSpinBox" name="dropCapLines_">
+                      <property name="minimum">
                        <number>2</number>
                       </property>
-                      <property name="maximum" >
+                      <property name="maximum">
                        <number>20</number>
                       </property>
                      </widget>
@@ -282,23 +283,23 @@
                    </layout>
                   </item>
                   <item>
-                   <layout class="QHBoxLayout" >
+                   <layout class="QHBoxLayout">
                     <item>
-                     <widget class="QLabel" name="label_2" >
-                      <property name="text" >
+                     <widget class="QLabel" name="label_2">
+                      <property name="text">
                        <string>Distance from Text:</string>
                       </property>
-                      <property name="buddy" >
+                      <property name="buddy">
                        <cstring>dropCapOffset_</cstring>
                       </property>
                      </widget>
                     </item>
                     <item>
-                     <widget class="SMScrSpinBox" name="dropCapOffset_" >
-                      <property name="minimum" >
+                     <widget class="SMScrSpinBox" name="dropCapOffset_">
+                      <property name="minimum">
                        <number>-3000</number>
                       </property>
-                      <property name="maximum" >
+                      <property name="maximum">
                        <number>3000</number>
                       </property>
                      </widget>
@@ -309,10 +310,10 @@
                 </item>
                 <item>
                  <spacer>
-                  <property name="orientation" >
+                  <property name="orientation">
                    <enum>Qt::Horizontal</enum>
                   </property>
-                  <property name="sizeHint" stdset="0" >
+                  <property name="sizeHint" stdset="0">
                    <size>
                     <width>40</width>
                     <height>20</height>
@@ -322,28 +323,88 @@
                 </item>
                </layout>
               </item>
+              <item>
+               <widget class="QToolButton" name="parentDropCapButton">
+                <property name="enabled">
+                 <bool>true</bool>
+                </property>
+                <property name="text">
+                 <string>Use Parent Value</string>
+                </property>
+               </widget>
+              </item>
              </layout>
             </widget>
            </item>
            <item>
-            <widget class="QToolButton" name="parentDropCapButton" >
-             <property name="enabled" >
-              <bool>true</bool>
+            <widget class="QGroupBox" name="textFlowBox">
+             <property name="title">
+              <string>Text Flow</string>
              </property>
-             <property name="text" >
-              <string>Use Parent Value</string>
-             </property>
+             <layout class="QGridLayout" name="gridLayout_2">
+              <item row="0" column="0">
+               <widget class="QLabel" name="keepLabelStart">
+                <property name="toolTip">
+                 <string>Ensure that first lines of a paragraph won't end up separated from the rest (known as widow/orphan control)</string>
+                </property>
+                <property name="text">
+                 <string>Don't separate first</string>
+                </property>
+               </widget>
+              </item>
+              <item row="0" column="1">
+               <widget class="SMSpinBox" name="keepLinesStart">
+                <property name="suffix">
+                 <string> lines</string>
+                </property>
+                <property name="maximum">
+                 <number>10</number>
+                </property>
+               </widget>
+              </item>
+              <item row="1" column="0">
+               <widget class="QLabel" name="keepLabelEnd">
+                <property name="text">
+                 <string>Don't separate last</string>
+                </property>
+               </widget>
+              </item>
+              <item row="1" column="1">
+               <widget class="SMSpinBox" name="keepLinesEnd">
+                <property name="suffix">
+                 <string> lines</string>
+                </property>
+                <property name="maximum">
+                 <number>10</number>
+                </property>
+               </widget>
+              </item>
+              <item row="2" column="0" colspan="2">
+               <widget class="SMCheckBox" name="keepTogether">
+                <property name="text">
+                 <string>Do not split paragraph</string>
+                </property>
+               </widget>
+              </item>
+              <item row="3" column="0" colspan="2">
+               <widget class="SMCheckBox" name="keepWithNext">
+                <property name="text">
+                 <string>Keep with next paragraph</string>
+                </property>
+               </widget>
+              </item>
+             </layout>
             </widget>
            </item>
            <item>
             <spacer>
-             <property name="orientation" >
+             <property name="orientation">
               <enum>Qt::Vertical</enum>
              </property>
-             <property name="sizeType" >
+             <property name="sizeType">
               <enum>QSizePolicy::MinimumExpanding</enum>
              </property>
-             <property name="sizeHint" stdset="0" >
+             <property name="sizeHint" stdset="0">
               <size>
                <width>272</width>
                <height>0</height>
@@ -354,67 +415,67 @@
           </layout>
          </item>
          <item>
-          <layout class="QVBoxLayout" name="verticalLayout_3" >
+          <layout class="QVBoxLayout" name="verticalLayout_3">
            <item>
-            <widget class="QGroupBox" name="groupBox" >
-             <property name="font" >
+            <widget class="QGroupBox" name="groupBox">
+             <property name="font">
               <font>
                <weight>50</weight>
                <bold>false</bold>
               </font>
              </property>
-             <property name="title" >
+             <property name="title">
               <string>Optical Margins</string>
              </property>
-             <layout class="QFormLayout" name="formLayout" >
-              <property name="fieldGrowthPolicy" >
+             <layout class="QFormLayout" name="formLayout">
+              <property name="fieldGrowthPolicy">
                <enum>QFormLayout::AllNonFixedFieldsGrow</enum>
               </property>
-              <item row="4" column="1" >
-               <layout class="QHBoxLayout" name="horizontalLayout_4" >
+              <item row="4" column="1">
+               <layout class="QHBoxLayout" name="horizontalLayout_4">
                 <item>
-                 <widget class="QPushButton" name="optMarginDefaultButton" >
-                  <property name="text" >
+                 <widget class="QPushButton" name="optMarginDefaultButton">
+                  <property name="text">
                    <string>Reset to Default</string>
                   </property>
                  </widget>
                 </item>
                 <item>
-                 <widget class="QPushButton" name="optMarginParentButton" >
-                  <property name="text" >
+                 <widget class="QPushButton" name="optMarginParentButton">
+                  <property name="text">
                    <string>Use Parent Value</string>
                   </property>
                  </widget>
                 </item>
                </layout>
               </item>
-              <item row="0" column="1" >
-               <widget class="SMRadioButton" name="optMarginRadioNone" >
-                <property name="text" >
+              <item row="0" column="1">
+               <widget class="SMRadioButton" name="optMarginRadioNone">
+                <property name="text">
                  <string>None</string>
                 </property>
-                <property name="checked" >
+                <property name="checked">
                  <bool>true</bool>
                 </property>
                </widget>
               </item>
-              <item row="1" column="1" >
-               <widget class="SMRadioButton" name="optMarginRadioBoth" >
-                <property name="text" >
+              <item row="1" column="1">
+               <widget class="SMRadioButton" name="optMarginRadioBoth">
+                <property name="text">
                  <string>Both Sides</string>
                 </property>
                </widget>
               </item>
-              <item row="2" column="1" >
-               <widget class="SMRadioButton" name="optMarginRadioLeft" >
-                <property name="text" >
+              <item row="2" column="1">
+               <widget class="SMRadioButton" name="optMarginRadioLeft">
+                <property name="text">
                  <string>Left Only</string>
                 </property>
                </widget>
               </item>
-              <item row="3" column="1" >
-               <widget class="SMRadioButton" name="optMarginRadioRight" >
-                <property name="text" >
+              <item row="3" column="1">
+               <widget class="SMRadioButton" name="optMarginRadioRight">
+                <property name="text">
                  <string>Right Only</string>
                 </property>
                </widget>
@@ -423,56 +484,56 @@
             </widget>
            </item>
            <item>
-            <widget class="QGroupBox" name="groupBox_2" >
-             <property name="enabled" >
+            <widget class="QGroupBox" name="groupBox_2">
+             <property name="enabled">
               <bool>true</bool>
              </property>
-             <property name="sizePolicy" >
-              <sizepolicy vsizetype="Minimum" hsizetype="Preferred" >
+             <property name="sizePolicy">
+              <sizepolicy hsizetype="Preferred" vsizetype="Minimum">
                <horstretch>0</horstretch>
                <verstretch>0</verstretch>
               </sizepolicy>
              </property>
-             <property name="minimumSize" >
+             <property name="minimumSize">
               <size>
                <width>0</width>
                <height>163</height>
               </size>
              </property>
-             <property name="title" >
+             <property name="title">
               <string>Advanced Settings</string>
              </property>
-             <layout class="QGridLayout" name="gridLayout" >
-              <item row="0" column="0" >
-               <layout class="QHBoxLayout" name="horizontalLayout" >
-                <property name="sizeConstraint" >
+             <layout class="QGridLayout" name="gridLayout">
+              <item row="0" column="0">
+               <layout class="QHBoxLayout" name="horizontalLayout">
+                <property name="sizeConstraint">
                  <enum>QLayout::SetDefaultConstraint</enum>
                 </property>
-                <property name="topMargin" >
+                <property name="topMargin">
                  <number>0</number>
                 </property>
-                <property name="bottomMargin" >
+                <property name="bottomMargin">
                  <number>0</number>
                 </property>
                 <item>
-                 <widget class="QLabel" name="minSpaceLabel" >
-                  <property name="text" >
+                 <widget class="QLabel" name="minSpaceLabel">
+                  <property name="text">
                    <string>TextLabel</string>
                   </property>
-                  <property name="buddy" >
+                  <property name="buddy">
                    <cstring>minSpaceSpin</cstring>
                   </property>
                  </widget>
                 </item>
                 <item>
-                 <widget class="SMScrSpinBox" name="minSpaceSpin" >
-                  <property name="minimumSize" >
+                 <widget class="SMScrSpinBox" name="minSpaceSpin">
+                  <property name="minimumSize">
                    <size>
                     <width>0</width>
                     <height>32</height>
                    </size>
                   </property>
-                  <property name="baseSize" >
+                  <property name="baseSize">
                    <size>
                     <width>0</width>
                     <height>0</height>
@@ -481,11 +542,11 @@
                  </widget>
                 </item>
                 <item>
-                 <spacer name="horizontalSpacer" >
-                  <property name="orientation" >
+                 <spacer name="horizontalSpacer">
+                  <property name="orientation">
                    <enum>Qt::Horizontal</enum>
                   </property>
-                  <property name="sizeHint" stdset="0" >
+                  <property name="sizeHint" stdset="0">
                    <size>
                     <width>40</width>
                     <height>20</height>
@@ -495,30 +556,30 @@
                 </item>
                </layout>
               </item>
-              <item row="1" column="0" >
-               <widget class="QLabel" name="glyphExtensionLabel" >
-                <property name="text" >
+              <item row="1" column="0">
+               <widget class="QLabel" name="glyphExtensionLabel">
+                <property name="text">
                  <string>TextLabel</string>
                 </property>
                </widget>
               </item>
-              <item row="2" column="0" >
-               <layout class="QHBoxLayout" name="horizontalLayout_6" >
+              <item row="2" column="0">
+               <layout class="QHBoxLayout" name="horizontalLayout_6">
                 <item>
-                 <layout class="QHBoxLayout" name="horizontalLayout_2" >
+                 <layout class="QHBoxLayout" name="horizontalLayout_2">
                   <item>
-                   <widget class="QLabel" name="minGlyphExtLabel" >
-                    <property name="text" >
+                   <widget class="QLabel" name="minGlyphExtLabel">
+                    <property name="text">
                      <string>TextLabel</string>
                     </property>
-                    <property name="buddy" >
+                    <property name="buddy">
                      <cstring>minGlyphExtSpin</cstring>
                     </property>
                    </widget>
                   </item>
                   <item>
-                   <widget class="SMScrSpinBox" name="minGlyphExtSpin" >
-                    <property name="minimumSize" >
+                   <widget class="SMScrSpinBox" name="minGlyphExtSpin">
+                    <property name="minimumSize">
                      <size>
                       <width>0</width>
                       <height>32</height>
@@ -529,20 +590,20 @@
                  </layout>
                 </item>
                 <item>
-                 <layout class="QHBoxLayout" name="horizontalLayout_3" >
+                 <layout class="QHBoxLayout" name="horizontalLayout_3">
                   <item>
-                   <widget class="QLabel" name="maxGlyphExtLabel" >
-                    <property name="text" >
+                   <widget class="QLabel" name="maxGlyphExtLabel">
+                    <property name="text">
                      <string>TextLabel</string>
                     </property>
-                    <property name="buddy" >
+                    <property name="buddy">
                      <cstring>maxGlyphExtSpin</cstring>
                     </property>
                    </widget>
                   </item>
                   <item>
-                   <widget class="SMScrSpinBox" name="maxGlyphExtSpin" >
-                    <property name="minimumSize" >
+                   <widget class="SMScrSpinBox" name="maxGlyphExtSpin">
+                    <property name="minimumSize">
                      <size>
                       <width>0</width>
                       <height>32</height>
@@ -559,13 +620,13 @@
            </item>
            <item>
             <spacer>
-             <property name="orientation" >
+             <property name="orientation">
               <enum>Qt::Vertical</enum>
              </property>
-             <property name="sizeType" >
+             <property name="sizeType">
               <enum>QSizePolicy::Expanding</enum>
              </property>
-             <property name="sizeHint" stdset="0" >
+             <property name="sizeHint" stdset="0">
               <size>
                <width>270</width>
                <height>16</height>
@@ -578,23 +639,23 @@
         </layout>
        </item>
        <item>
-        <widget class="QGroupBox" name="tabsBox" >
-         <property name="sizePolicy" >
-          <sizepolicy vsizetype="Expanding" hsizetype="Preferred" >
+        <widget class="QGroupBox" name="tabsBox">
+         <property name="sizePolicy">
+          <sizepolicy hsizetype="Preferred" vsizetype="Expanding">
            <horstretch>0</horstretch>
            <verstretch>0</verstretch>
           </sizepolicy>
          </property>
-         <property name="title" >
+         <property name="title">
           <string>Tabulators and Indentation</string>
          </property>
-         <layout class="QVBoxLayout" >
+         <layout class="QVBoxLayout">
           <item>
-           <layout class="QHBoxLayout" >
+           <layout class="QHBoxLayout">
             <item>
-             <widget class="SMTabruler" native="1" name="tabList_" >
-              <property name="sizePolicy" >
-               <sizepolicy vsizetype="MinimumExpanding" hsizetype="MinimumExpanding" >
+             <widget class="SMTabruler" name="tabList_" native="true">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
                 <horstretch>0</horstretch>
                 <verstretch>0</verstretch>
                </sizepolicy>
@@ -603,10 +664,10 @@
             </item>
             <item>
              <spacer>
-              <property name="orientation" >
+              <property name="orientation">
                <enum>Qt::Horizontal</enum>
               </property>
-              <property name="sizeHint" stdset="0" >
+              <property name="sizeHint" stdset="0">
                <size>
                 <width>40</width>
                 <height>20</height>
@@ -620,11 +681,11 @@
         </widget>
        </item>
        <item>
-        <spacer name="verticalSpacer" >
-         <property name="orientation" >
+        <spacer name="verticalSpacer">
+         <property name="orientation">
           <enum>Qt::Vertical</enum>
          </property>
-         <property name="sizeHint" stdset="0" >
+         <property name="sizeHint" stdset="0">
           <size>
            <width>20</width>
            <height>0</height>
@@ -634,34 +695,34 @@
        </item>
       </layout>
      </widget>
-     <widget class="QWidget" name="TabPage" >
-      <attribute name="title" >
+     <widget class="QWidget" name="TabPage">
+      <attribute name="title">
        <string>Ch&amp;aracter Style</string>
       </attribute>
-      <layout class="QVBoxLayout" >
-       <property name="spacing" >
+      <layout class="QVBoxLayout">
+       <property name="spacing">
         <number>5</number>
        </property>
-       <property name="margin" >
+       <property name="margin">
         <number>0</number>
        </property>
        <item>
-        <widget class="QFrame" name="characterBox" >
-         <property name="enabled" >
+        <widget class="QFrame" name="characterBox">
+         <property name="enabled">
           <bool>true</bool>
          </property>
-         <property name="frameShape" >
+         <property name="frameShape">
           <enum>QFrame::NoFrame</enum>
          </property>
-         <property name="frameShadow" >
+         <property name="frameShadow">
           <enum>QFrame::Plain</enum>
          </property>
-         <property name="lineWidth" >
+         <property name="lineWidth">
           <number>0</number>
          </property>
-         <layout class="QGridLayout" >
-          <item row="0" column="0" >
-           <widget class="SMCStyleWidget" native="1" name="cpage" />
+         <layout class="QGridLayout">
+          <item row="0" column="0">
+           <widget class="SMCStyleWidget" name="cpage" native="true"/>
           </item>
          </layout>
         </widget>
@@ -672,7 +733,7 @@
    </item>
   </layout>
  </widget>
- <layoutdefault spacing="5" margin="5" />
+ <layoutdefault spacing="5" margin="5"/>
  <customwidgets>
   <customwidget>
    <class>SMScComboBox</class>
@@ -712,6 +773,11 @@
    <extends>QRadioButton</extends>
    <header>ui/smradiobutton.h</header>
   </customwidget>
+  <customwidget>
+   <class>SMCheckBox</class>
+   <extends>QCheckBox</extends>
+   <header>ui/smcheckbox.h</header>
+  </customwidget>
  </customwidgets>
  <resources/>
  <connections/>
Index: scribus/ui/smtextstyles.cpp
===================================================================
--- scribus/ui/smtextstyles.cpp	(revision 16830)
+++ scribus/ui/smtextstyles.cpp	(working copy)
@@ -516,6 +516,11 @@
 	connect(pwidget_->dropCapLines_, SIGNAL(valueChanged(int)), this, SLOT(slotDropCapLines(int)));
 	connect(pwidget_->dropCapOffset_, SIGNAL(valueChanged(double)), this, SLOT(slotDropCapOffset()));
 
+	connect(pwidget_->keepLinesStart, SIGNAL(valueChanged(int)), this, SLOT(handleKeepLinesStart()));
+	connect(pwidget_->keepLinesEnd, SIGNAL(valueChanged(int)), this, SLOT(handleKeepLinesEnd()));
+	connect(pwidget_->keepTogether, SIGNAL(stateChanged(int)), this, SLOT(handleKeepTogether()));
+	connect(pwidget_->keepWithNext, SIGNAL(stateChanged(int)), this, SLOT(handleKeepWithNext()));
+
 	connect(pwidget_->tabList_, SIGNAL(tabsChanged()), this, SLOT(slotTabRuler()));
 	connect(pwidget_->tabList_, SIGNAL(mouseReleased()), this, SLOT(slotTabRuler()));
 	connect(pwidget_->tabList_->left_, SIGNAL(valueChanged(double)), this, SLOT(slotLeftIndent()));
@@ -591,6 +596,11 @@
 	disconnect(pwidget_->parentCombo, SIGNAL(activated(const QString&)),
 			this, SLOT(slotParentChanged(const QString&)));
 
+	disconnect(pwidget_->keepLinesStart, SIGNAL(valueChanged(int)), this, SLOT(handleKeepLinesStart()));
+	disconnect(pwidget_->keepLinesEnd, SIGNAL(valueChanged(int)), this, SLOT(handleKeepLinesEnd()));
+	disconnect(pwidget_->keepTogether, SIGNAL(stateChanged(int)), this, SLOT(handleKeepTogether()));
+	disconnect(pwidget_->keepWithNext, SIGNAL(stateChanged(int)), this, SLOT(handleKeepWithNext()));
+
 	disconnect(pwidget_->tabList_, SIGNAL(tabsChanged()), this, SLOT(slotTabRuler()));
 	disconnect(pwidget_->tabList_->left_, SIGNAL(valueChanged(double)), this, SLOT(slotLeftIndent()));
 	disconnect(pwidget_->tabList_->right_, SIGNAL(valueChanged(double)), this, SLOT(slotRightIndent()));
@@ -901,6 +911,82 @@
 	}
 }
 
+void SMParagraphStyle::handleKeepLinesStart()
+{
+	if (pwidget_->keepLinesStart->useParentValue())
+		for (int i = 0; i < selection_.count(); ++i)
+			selection_[i]->resetKeepLinesStart();
+	else 
+	{
+		int value = pwidget_->keepLinesStart->value();
+		for (int i = 0; i < selection_.count(); ++i)
+			selection_[i]->setKeepLinesStart (value);
+	}
+	
+	if (!selectionIsDirty_)
+	{
+		selectionIsDirty_ = true;
+		emit selectionDirty();
+	}
+}
+
+void SMParagraphStyle::handleKeepLinesEnd()
+{
+	if (pwidget_->keepLinesEnd->useParentValue())
+		for (int i = 0; i < selection_.count(); ++i)
+			selection_[i]->resetKeepLinesEnd();
+	else 
+	{
+		int value = pwidget_->keepLinesEnd->value();
+		for (int i = 0; i < selection_.count(); ++i)
+			selection_[i]->setKeepLinesEnd (value);
+	}
+	
+	if (!selectionIsDirty_)
+	{
+		selectionIsDirty_ = true;
+		emit selectionDirty();
+	}
+}
+
+void SMParagraphStyle::handleKeepTogether()
+{
+	if (pwidget_->keepTogether->useParentValue())
+		for (int i = 0; i < selection_.count(); ++i)
+			selection_[i]->resetKeepTogether();
+	else 
+	{
+		bool value = pwidget_->keepTogether->isChecked();
+		for (int i = 0; i < selection_.count(); ++i)
+			selection_[i]->setKeepTogether (value);
+	}
+	
+	if (!selectionIsDirty_)
+	{
+		selectionIsDirty_ = true;
+		emit selectionDirty();
+	}
+}
+
+void SMParagraphStyle::handleKeepWithNext()
+{
+	if (pwidget_->keepWithNext->useParentValue())
+		for (int i = 0; i < selection_.count(); ++i)
+			selection_[i]->resetKeepWithNext();
+	else 
+	{
+		bool value = pwidget_->keepWithNext->isChecked();
+		for (int i = 0; i < selection_.count(); ++i)
+			selection_[i]->setKeepWithNext (value);
+	}
+	
+	if (!selectionIsDirty_)
+	{
+		selectionIsDirty_ = true;
+		emit selectionDirty();
+	}
+}
+
 void SMParagraphStyle::slotTabRuler()
 {
 	if (pwidget_->tabList_->useParentTabs())
Index: scribus/ui/smtextstyles.h
===================================================================
--- scribus/ui/smtextstyles.h	(revision 16830)
+++ scribus/ui/smtextstyles.h	(working copy)
@@ -84,6 +84,10 @@
 	void slotMinSpace();
 	void slotMinGlyphExt();
 	void slotMaxGlyphExt();
+	void handleKeepLinesStart();
+	void handleKeepLinesEnd();
+	void handleKeepTogether();
+	void handleKeepWithNext();
 	void slotTabRuler();
 	void slotLeftIndent();
 	void slotRightIndent();
Index: win32/vc9/Scribus.vcproj
===================================================================
--- win32/vc9/Scribus.vcproj	(revision 16830)
+++ win32/vc9/Scribus.vcproj	(working copy)
@@ -1313,6 +1313,10 @@
 				>
 			</File>
 			<File
+				RelativePath="..\..\scribus\ui\propertywidget_flow.cpp"
+				>
+			</File>
+			<File
 				RelativePath="..\..\scribus\ui\propertywidget_optmargins.cpp"
 				>
 			</File>
@@ -5732,6 +5736,24 @@
 				</FileConfiguration>
 			</File>
 			<File
+				RelativePath="..\..\scribus\ui\propertywidget_flow.h"
+				>
+				<FileConfiguration
+					Name="Debug-cairo|Win32"
+					>
+					<Tool
+						Name="moc.exe"
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release-cairo|Win32"
+					>
+					<Tool
+						Name="moc.exe"
+					/>
+				</FileConfiguration>
+			</File>
+			<File
 				RelativePath="..\..\scribus\ui\propertywidget_optmargins.h"
 				>
 				<FileConfiguration
@@ -8890,6 +8912,10 @@
 				>
 			</File>
 			<File
+				RelativePath="..\..\scribus\ui\moc_propertywidget_flow.cpp"
+				>
+			</File>
+			<File
 				RelativePath="..\..\scribus\ui\moc_propertywidget_optmargins.cpp"
 				>
 			</File>
@@ -9515,6 +9541,10 @@
 				>
 			</File>
 			<File
+				RelativePath="..\..\scribus\ui\propertywidget_flowbase.ui"
+				>
+			</File>
+			<File
 				RelativePath="..\..\scribus\ui\propertywidget_optmarginsbase.ui"
 				>
 			</File>
@@ -9587,6 +9617,10 @@
 				>
 			</File>
 			<File
+				RelativePath="..\..\scribus\ui\ui_propertywidget_flowbase.h"
+				>
+			</File>
+			<File
 				RelativePath="..\..\scribus\ui\unicodesearch.ui"
 				>
 			</File>
scribus-textflow-r16832.patch (112,399 bytes)   

jghali

2011-09-14 21:16

administrator   ~0026872

Last edited: 2011-09-14 21:22

I uploaded an updated patch vs r16832, only one minor modification vs first patch in scribusformat150_save.cpp.

jghali

2011-09-18 20:56

administrator   ~0026886

Committed! Thanks! I did an additionnal modification : i renamed most *_flow class names and variables to _orphans as _flow is not very meaning full. The caption for those options in style manager and properties palette has also been renamed from "text flow" to "orphans and widows".

mecirt

2011-09-19 09:16

reporter   ~0026889

Excellent, thank you! The 'widows and orphans' caption isn't entirely accurate, as the 'keep with next paragraph' option is unrelated to these, but it should be good enough.

Issue History

Date Modified Username Field Change
2011-08-28 18:22 mecirt New Issue
2011-08-28 18:22 mecirt File Added: scribus-textflow.patch
2011-08-28 21:19 Vladimir Savic Note Added: 0026809
2011-08-28 21:20 cezaryece Note Added: 0026810
2011-08-29 08:06 mecirt File Added: propertywidget_flowbase.ui
2011-08-29 08:08 mecirt Note Added: 0026811
2011-08-30 05:16 cezaryece Note Added: 0026813
2011-09-02 09:26 StefanM Note Added: 0026822
2011-09-02 09:26 StefanM Tag Attached: text frames
2011-09-02 09:26 StefanM Tag Attached: typography
2011-09-12 21:18 jghali Note Added: 0026858
2011-09-14 21:14 jghali File Added: scribus-textflow-r16832.patch
2011-09-14 21:16 jghali Note Added: 0026872
2011-09-14 21:22 jghali Note Edited: 0026872
2011-09-18 20:56 jghali Note Added: 0026886
2011-09-18 20:56 jghali Status new => resolved
2011-09-18 20:56 jghali Fixed in Version => 1.5.0svn
2011-09-18 20:56 jghali Resolution open => fixed
2011-09-18 20:56 jghali Assigned To => jghali
2011-09-19 09:16 mecirt Note Added: 0026889
2011-09-28 18:32 cbradney Status resolved => closed
2015-09-17 20:08 Kunda Category Story Editor / Text Frames => Story Ed/Txt Frames
2015-09-17 20:12 Kunda Category Story Ed/Txt Frames => Story Editor / Text Frames