void AP_UnixDialog_Paragraph::_populateWindowData(void)
{

	// alignment option menu
	UT_ASSERT(m_listAlignment);
	XAP_comboBoxSetActiveFromIntCol(GTK_COMBO_BOX(m_listAlignment), 1, 
									(gint) _getMenuItemValue(id_MENU_ALIGNMENT));

	// indent and paragraph margins
	UT_ASSERT(m_spinbuttonLeft);
	gtk_entry_set_text(GTK_ENTRY(m_spinbuttonLeft),
					   (const gchar *) _getSpinItemValue(id_SPIN_LEFT_INDENT));

	UT_ASSERT(m_spinbuttonRight);
	gtk_entry_set_text(GTK_ENTRY(m_spinbuttonRight),
					   (const gchar *) _getSpinItemValue(id_SPIN_RIGHT_INDENT));

	UT_ASSERT(m_spinbuttonBy);
	gtk_entry_set_text(GTK_ENTRY(m_spinbuttonBy),
					   (const gchar *) _getSpinItemValue(id_SPIN_SPECIAL_INDENT));

	UT_ASSERT(m_listSpecial);
	XAP_comboBoxSetActiveFromIntCol(GTK_COMBO_BOX(m_listSpecial), 1,
								(gint) _getMenuItemValue(id_MENU_SPECIAL_INDENT));

	// spacing
	UT_ASSERT(m_spinbuttonLeft);
	gtk_entry_set_text(GTK_ENTRY(m_spinbuttonBefore),
					   (const gchar *) _getSpinItemValue(id_SPIN_BEFORE_SPACING));

	UT_ASSERT(m_spinbuttonRight);
	gtk_entry_set_text(GTK_ENTRY(m_spinbuttonAfter),
					   (const gchar *) _getSpinItemValue(id_SPIN_AFTER_SPACING));

	UT_ASSERT(m_spinbuttonAt);
	gtk_entry_set_text(GTK_ENTRY(m_spinbuttonAt),
					   (const gchar *) _getSpinItemValue(id_SPIN_SPECIAL_SPACING));

	UT_ASSERT(m_listLineSpacing);
	XAP_comboBoxSetActiveFromIntCol(GTK_COMBO_BOX(m_listLineSpacing), 1,
								(gint) _getMenuItemValue(id_MENU_SPECIAL_SPACING));

	// set the check boxes
	// TODO : handle tri-state boxes !!!

	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(GTK_CHECK_BUTTON(m_checkbuttonWidowOrphan)),
								 (_getCheckItemValue(id_CHECK_WIDOW_ORPHAN) == check_TRUE));
	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(GTK_CHECK_BUTTON(m_checkbuttonKeepLines)),
								 (_getCheckItemValue(id_CHECK_KEEP_LINES) == check_TRUE));
	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(GTK_CHECK_BUTTON(m_checkbuttonPageBreak)),
								 (_getCheckItemValue(id_CHECK_PAGE_BREAK) == check_TRUE));
	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(GTK_CHECK_BUTTON(m_checkbuttonSuppress)),
								 (_getCheckItemValue(id_CHECK_SUPPRESS) == check_TRUE));
	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(GTK_CHECK_BUTTON(m_checkbuttonHyphenate)),
								 (_getCheckItemValue(id_CHECK_NO_HYPHENATE) == check_TRUE));
	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(GTK_CHECK_BUTTON(m_checkbuttonKeepNext)),
								 (_getCheckItemValue(id_CHECK_KEEP_NEXT) == check_TRUE));
	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(GTK_CHECK_BUTTON(m_checkbuttonDomDirection)),
								 (_getCheckItemValue(id_CHECK_DOMDIRECTION) == check_TRUE));
}
void AP_Dialog_Paragraph::_setSpinItemValue(tControl item, const gchar * value,
											tOperation op /* = op_UICHANGE */)
{
	UT_return_if_fail (item <= m_vecProperties.getItemCount() && value);

	sControlData * pItem = _getPropertyItem (item);
	UT_return_if_fail (pItem);

	// some spinbuttons have special data requirements
	switch(item)
	{
	case id_SPIN_LEFT_INDENT:
	case id_SPIN_RIGHT_INDENT:
	case id_SPIN_SPECIAL_INDENT:
		pItem->setData (reinterpret_cast<const gchar *>(UT_reformatDimensionString (m_dim, value)));
		break;

	case id_SPIN_BEFORE_SPACING:
	case id_SPIN_AFTER_SPACING:
		{
			/* NOTE : line spacing can't be negative, so take absolute value:
			 */
			const char * abs_value = UT_reformatDimensionString (DIM_PT, _makeAbsolute (value));
			pItem->setData (reinterpret_cast<const gchar *>(abs_value));
		}
		break;

	case id_SPIN_SPECIAL_SPACING:
		if (_getMenuItemValue (id_MENU_SPECIAL_SPACING) == spacing_MULTIPLE)
			{
				const char * abs_value = UT_reformatDimensionString (DIM_none, _makeAbsolute (value), ".2");
				pItem->setData (reinterpret_cast<const gchar *>(abs_value));
			}
		else
			{
				const char * abs_value = UT_reformatDimensionString (DIM_PT, _makeAbsolute (value));
				pItem->setData (reinterpret_cast<const gchar *>(abs_value));
			}
		break;

	default:
		/* all others get a simple string copy to the static member
		 */
		pItem->setData (value);
	}

	if ((op == op_UICHANGE) || (op == op_SYNC))
		pItem->changed (true);

	// for UI-driven changes, may need to sync other controls
	if (op == op_UICHANGE)
		_syncControls(item);
}
void AP_Dialog_Paragraph::_syncControls(tControl changed, bool /*bAll  = false */)
{
	if(changed == id_SPIN_LEFT_INDENT)
	{
		// need to check the limits
		// cannot go past left page margin.
		// TODO : is there a minimum text width?

		double leftPageMargin = UT_convertToDimension(m_pageLeftMargin, m_dim);
		double rightIndent = UT_convertToDimension(_getSpinItemValue(id_SPIN_RIGHT_INDENT), m_dim);

		if(-UT_convertToDimension(_getSpinItemValue(id_SPIN_LEFT_INDENT), m_dim) >
					leftPageMargin)
		{
			_setSpinItemValue(id_SPIN_LEFT_INDENT,
									(const gchar *)UT_formatDimensionString(m_dim, -leftPageMargin),
									op_SYNC);
		}

		// nor past pagesize - rightIndent on right.
  		if(UT_convertDimensionless(_getSpinItemValue(id_SPIN_LEFT_INDENT)) >
						UT_convertInchesToDimension(m_iMaxWidth, m_dim) - rightIndent)
  		{
  			_setSpinItemValue(id_SPIN_LEFT_INDENT,
  									(const gchar *)UT_convertInchesToDimensionString(m_dim, m_iMaxWidth - rightIndent),
  									op_SYNC);
  		}
	}

	if(changed == id_SPIN_RIGHT_INDENT)
	{
		// need to check the limits
		// cannot go past right page margin.

		double rightPageMargin = UT_convertToDimension(m_pageRightMargin, m_dim);
		double leftIndent = UT_convertToDimension(_getSpinItemValue(id_SPIN_LEFT_INDENT), m_dim);

		if(-UT_convertToDimension(_getSpinItemValue(id_SPIN_RIGHT_INDENT), m_dim) >
					rightPageMargin)
		{
			_setSpinItemValue(id_SPIN_RIGHT_INDENT,
									(const gchar *)UT_formatDimensionString(m_dim, -rightPageMargin),
									op_SYNC);
		}

		// nor can we force text left past pagesize, minus left margin
  		if(UT_convertDimensionless(_getSpinItemValue(id_SPIN_RIGHT_INDENT)) >
						UT_convertInchesToDimension(m_iMaxWidth, m_dim) - leftIndent)
  		{
  			_setSpinItemValue(id_SPIN_RIGHT_INDENT,
  									(const gchar *)UT_convertInchesToDimensionString(m_dim, m_iMaxWidth - leftIndent),
  									op_SYNC);
  		}
	}

	if (changed == id_MENU_SPECIAL_INDENT || changed == id_SPIN_SPECIAL_INDENT)
	{
		double dDefault = 0.0;
		bool bDefault = true;

		double sign = -1.0;
		if (_getMenuItemValue(id_MENU_SPECIAL_INDENT) == indent_FIRSTLINE)
		  sign = +1.0;

		if (changed == id_MENU_SPECIAL_INDENT)
		{
			switch(_getMenuItemValue(id_MENU_SPECIAL_INDENT))
			{
			case indent_NONE:
				dDefault = 0.0;
				break;

			case indent_FIRSTLINE:
			case indent_HANGING:
				// only change to default if existing value is zero
				dDefault = UT_convertDimensionless(_getSpinItemValue(id_SPIN_SPECIAL_INDENT));
				if (dDefault == 0)
				{
					bDefault = false;
				}
				else
				{
					dDefault = 0.5;
				}
				break;

			default:
				UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN);
				break;
			}

			if (bDefault)
			{
				if (m_dim != DIM_IN)
					dDefault = UT_convertInchesToDimension(dDefault, m_dim);

				const gchar* szNew = UT_convertInchesToDimensionString(m_dim, dDefault, ".1");

				_setSpinItemValue(id_SPIN_SPECIAL_INDENT, szNew, op_SYNC);
			}
		}
		else /* (changed == id_SPIN_SPECIAL_INDENT) */
		{
			switch(_getMenuItemValue(id_MENU_SPECIAL_INDENT))
			{
			case indent_NONE:
				_setMenuItemValue(id_MENU_SPECIAL_INDENT, indent_FIRSTLINE, op_SYNC);
				break;

			default:
				break;
			}
		}

		// if spin contains a negative number, we flip the direction of the indent.
		double val = UT_convertDimensionless(_getSpinItemValue(id_SPIN_SPECIAL_INDENT));
		if (val < 0)
		{
			sign = -sign;

			// sometimes this appears to have no effect. why?
			if (_getMenuItemValue(id_MENU_SPECIAL_INDENT) == indent_FIRSTLINE)
				_setMenuItemValue(id_MENU_SPECIAL_INDENT, indent_HANGING, op_SYNC);
			else if (_getMenuItemValue(id_MENU_SPECIAL_INDENT) == indent_HANGING)
				_setMenuItemValue(id_MENU_SPECIAL_INDENT, indent_FIRSTLINE, op_SYNC);

			const gchar* szNew = UT_convertInchesToDimensionString(m_dim, -val, ".1");
			_setSpinItemValue(id_SPIN_SPECIAL_INDENT, szNew, op_SYNC);
		}

		// sanity check.

		double leftIndent =
		UT_convertToDimension(_getSpinItemValue(id_SPIN_LEFT_INDENT), m_dim);

		double effectiveLeftMargin = leftIndent + (UT_convertToDimension
		  (_getSpinItemValue(id_SPIN_SPECIAL_INDENT), m_dim) * sign);

		double leftPageMargin = UT_convertToDimension(m_pageLeftMargin, m_dim);
		double rightIndent = UT_convertToDimension(_getSpinItemValue(id_SPIN_RIGHT_INDENT), m_dim);

		if(-effectiveLeftMargin > leftPageMargin)
		{
			_setSpinItemValue(id_SPIN_SPECIAL_INDENT,
									(const gchar *)UT_formatDimensionString(m_dim, -leftPageMargin),
									op_SYNC);
		} 

  		if(effectiveLeftMargin >
			UT_convertInchesToDimension(m_iMaxWidth, m_dim) - rightIndent)
  		{
  			_setSpinItemValue(id_SPIN_SPECIAL_INDENT,
  									(const gchar *)UT_convertInchesToDimensionString(m_dim, m_iMaxWidth - rightIndent),
  									op_SYNC);
  		}
	}

	if (changed == id_SPIN_SPECIAL_SPACING)
	{
		switch(_getMenuItemValue(id_MENU_SPECIAL_SPACING))
		{
		case spacing_SINGLE:
		case spacing_ONEANDHALF:
		case spacing_DOUBLE:
			_setMenuItemValue(id_MENU_SPECIAL_SPACING, spacing_MULTIPLE, op_SYNC);
			break;

		default:
			break;
		}
	}

	if (changed == id_MENU_SPECIAL_SPACING)
	{
		UT_Dimension dimOld = UT_determineDimension(_getSpinItemValue(id_SPIN_SPECIAL_SPACING), DIM_none);

		switch(_getMenuItemValue(id_MENU_SPECIAL_SPACING))
		{
		case spacing_SINGLE:
			_setSpinItemValue(id_SPIN_SPECIAL_SPACING, "1.0", op_SYNC);
			break;

		case spacing_ONEANDHALF:
			_setSpinItemValue(id_SPIN_SPECIAL_SPACING, "1.5", op_SYNC);
			break;

		case spacing_DOUBLE:
			_setSpinItemValue(id_SPIN_SPECIAL_SPACING, "2.0", op_SYNC);
			break;

		case spacing_ATLEAST:
		case spacing_EXACTLY:
			// only change to default if not dimensioned
			if (dimOld == DIM_none)
				_setSpinItemValue(id_SPIN_SPECIAL_SPACING, "12pt", op_SYNC);
			break;

		case spacing_MULTIPLE:
			// only change to default if dimensioned
			if (dimOld != DIM_none)
				_setSpinItemValue(id_SPIN_SPECIAL_SPACING, "1.0", op_SYNC);
			break;

		default:
			UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN);
			break;
		}
	}

	// the preview needs to suck in the changed data (to cache it
	// for subsequent draws)
	UT_BidiCharType iDir;
	if(_getCheckItemValue(id_CHECK_DOMDIRECTION) == check_TRUE)
		iDir = UT_BIDI_RTL;
	else if(_getCheckItemValue(id_CHECK_DOMDIRECTION) == check_FALSE)
		iDir = UT_BIDI_LTR;
	else
	{
		// nothing given -- default to LTR
		UT_DEBUGMSG(("AP_Dialog_Paragraph::_syncControls: no value of dom-dir,"
					 " defaulting to LTR\n"));
		iDir = UT_BIDI_LTR;
	}
	
		
	m_paragraphPreview->setFormat(m_pageLeftMargin,
									m_pageRightMargin,
									(AP_Dialog_Paragraph::tAlignState) _getMenuItemValue(id_MENU_ALIGNMENT),
									_getSpinItemValue(id_SPIN_SPECIAL_INDENT),
									(AP_Dialog_Paragraph::tIndentState) _getMenuItemValue(id_MENU_SPECIAL_INDENT),
									_getSpinItemValue(id_SPIN_LEFT_INDENT),
									_getSpinItemValue(id_SPIN_RIGHT_INDENT),
									_getSpinItemValue(id_SPIN_BEFORE_SPACING),
									_getSpinItemValue(id_SPIN_AFTER_SPACING),
									_getSpinItemValue(id_SPIN_SPECIAL_SPACING),
									(AP_Dialog_Paragraph::tSpacingState) _getMenuItemValue(id_MENU_SPECIAL_SPACING),
								  iDir);

	m_paragraphPreview->queueDraw();
}
void AP_Dialog_Paragraph::_doSpin(tControl edit, UT_sint32 amt)
{
	UT_ASSERT_HARMLESS(amt); // zero makes no sense

	// get current value from member
	const gchar* szOld = _getSpinItemValue(edit);
	double d = UT_convertDimensionless(szOld);

	// figure out which dimension and units to spin in
	UT_Dimension dimSpin = m_dim;
	double dSpinUnit = SPIN_INCR_PT;
	double dMin = 0.0;
	bool bMin = false;

	switch (edit)
	{
	case id_SPIN_SPECIAL_INDENT:
		dMin = 0.0;
		bMin = true;
		// fall through
	case id_SPIN_LEFT_INDENT:
	case id_SPIN_RIGHT_INDENT:
		dimSpin = m_dim;
		switch (dimSpin)
		{
		case DIM_IN:	dSpinUnit = SPIN_INCR_IN;	break;
		case DIM_CM:	dSpinUnit = SPIN_INCR_CM;	break;
		case DIM_PI:	dSpinUnit = SPIN_INCR_PI;	break;
		case DIM_PT:	dSpinUnit = SPIN_INCR_PT;	break;
		default:
			UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN);
			break;
		}
		break;

	case id_SPIN_BEFORE_SPACING:
	case id_SPIN_AFTER_SPACING:
		dimSpin = DIM_PT;
		dSpinUnit = 6.0;
		dMin = 0.0;
		bMin = true;
		break;

	case id_SPIN_SPECIAL_SPACING:
		switch(_getMenuItemValue(id_MENU_SPECIAL_SPACING))
		{
		case spacing_SINGLE:
		case spacing_ONEANDHALF:
		case spacing_DOUBLE:
			_setMenuItemValue(id_MENU_SPECIAL_SPACING, spacing_MULTIPLE);
			// fall through
		case spacing_MULTIPLE:
			dimSpin = DIM_none;
			dSpinUnit = 0.1;
			dMin = 0.5;
			bMin = true;
			break;

		case spacing_EXACTLY:
			dMin = 1;
			// fall through
		case spacing_ATLEAST:
			dimSpin = DIM_PT;
			dSpinUnit = SPIN_INCR_PT;
			bMin = true;
			break;

		default:
			UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN);
			break;
		}
		break;

	default:
		UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN);
		break;
	}

	// figure out spin precision, too
	const char * szPrecision = ".1";
	if ((dimSpin == DIM_PT) ||
		(dimSpin == DIM_PI))
		szPrecision = ".0";



	// if needed, switch unit systems and round off
	UT_Dimension dimOld = UT_determineDimension(szOld, dimSpin);

	if (dimOld != dimSpin)
	{
		double dInches = UT_convertToInches(szOld);
		d = UT_convertInchesToDimension(dInches, dimSpin);
	}

	// value is now in desired units, so change it
	d += (dSpinUnit * amt);
	if (bMin)
	{
		// some spinners clamp at bottom of range
		if (d < dMin)
			d = dMin;
	}
	const gchar* szNew = UT_formatDimensionString(dimSpin, d, szPrecision);

	_setSpinItemValue(edit, szNew);
}
bool AP_Dialog_Paragraph::getDialogData(const gchar **& pProps)
{
	UT_Vector v;

	struct propPair
	{
		gchar * prop;
		gchar * val;
	};

	propPair * p;

	// only do this if the control has a proper value
	if (_wasChanged(id_MENU_ALIGNMENT)  && _getMenuItemValue(id_MENU_ALIGNMENT))
	{
		ALLOC_PROP_PAIR(p);
		p->prop = g_strdup("text-align");

		switch (_getMenuItemValue(id_MENU_ALIGNMENT))
		{
			case align_LEFT:
				p->val = g_strdup("left");
				break;
			case align_CENTERED:
				p->val = g_strdup("center");
				break;
			case align_RIGHT:
				p->val = g_strdup("right");
				break;
			case align_JUSTIFIED:
				p->val = g_strdup("justify");
				break;
			default:
				UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN);
		}
		v.addItem(p);
	}


	if (_wasChanged(id_CHECK_DOMDIRECTION))
	{
		ALLOC_PROP_PAIR(p);
		p->prop = g_strdup("dom-dir");

		if (_getCheckItemValue(id_CHECK_DOMDIRECTION) == check_TRUE)
			p->val = g_strdup("rtl");
		else
			p->val = g_strdup("ltr");

		v.addItem(p);

	}


	if (_wasChanged(id_SPIN_LEFT_INDENT))
	{
		ALLOC_PROP_PAIR(p);
		p->prop = g_strdup("margin-left");
		p->val = g_strdup(_getSpinItemValue(id_SPIN_LEFT_INDENT));

		v.addItem(p);
	}

	if (_wasChanged(id_SPIN_RIGHT_INDENT))
	{
		ALLOC_PROP_PAIR(p);
		p->prop = g_strdup("margin-right");
		p->val = g_strdup(_getSpinItemValue(id_SPIN_RIGHT_INDENT));

		v.addItem(p);
	}

	// TODO : The logic here might not be bulletproof.  If the user triggers
	// TODO : a change in the TYPE of special indent (hanging, first line,
	// TODO : none), we will always save what's in the box as a property.
	// TODO : One could make it smarter with a stronger contract with
	// TODO : the platform interfaces.

	if (_getMenuItemValue(id_MENU_SPECIAL_INDENT) &&
		(_wasChanged(id_MENU_SPECIAL_INDENT) || _wasChanged(id_SPIN_SPECIAL_INDENT)))
	{
		ALLOC_PROP_PAIR(p);
		p->prop = g_strdup("text-indent");

		tIndentState i = (tIndentState) _getMenuItemValue(id_MENU_SPECIAL_INDENT);

		if (i == indent_NONE)
			p->val = g_strdup(UT_convertInchesToDimensionString(m_dim, 0));
		else if (i == indent_FIRSTLINE)
			p->val = g_strdup(_getSpinItemValue(id_SPIN_SPECIAL_INDENT));
		else if (i == indent_HANGING)
		{
			// we have to flip the sign for "hanging" indents to a negative quantity for
			// storage in the document as a text-indent

			// get with no dimension
			UT_Dimension dim = UT_determineDimension(_getSpinItemValue(id_SPIN_SPECIAL_INDENT));
			double val = UT_convertDimensionless(_getSpinItemValue(id_SPIN_SPECIAL_INDENT));
			// Convert to inches
			val = UT_convertDimToInches(val, dim);

			// flip sign
			val = val * (double) -1;

			// store the reconstructed
			p->val = g_strdup(UT_convertInchesToDimensionString(dim, val));
		}

		v.addItem(p);
	}

	if (_wasChanged(id_SPIN_BEFORE_SPACING))
	{
		ALLOC_PROP_PAIR(p);
		p->prop = g_strdup("margin-top");
		p->val = g_strdup(_getSpinItemValue(id_SPIN_BEFORE_SPACING));

		v.addItem(p);
	}

	if (_wasChanged(id_SPIN_AFTER_SPACING))
	{
		ALLOC_PROP_PAIR(p);
		p->prop = g_strdup("margin-bottom");
		p->val = g_strdup(_getSpinItemValue(id_SPIN_AFTER_SPACING));

		v.addItem(p);
	}

	// TODO : The logic here might not be bulletproof.  If the user triggers
	// TODO : a change in the TYPE of special indent (single, double, etc.)
	// TODO : we will always save what's in the box as a property.
	// TODO : One could make it smarter with a stronger contract with
	// TODO : the platform interfaces.

	if(_getMenuItemValue(id_MENU_SPECIAL_SPACING) &&
		 (_wasChanged(id_MENU_SPECIAL_SPACING) || _wasChanged(id_SPIN_SPECIAL_SPACING)))
	{
		ALLOC_PROP_PAIR(p);
		p->prop = g_strdup("line-height");

		// normal spacings (single, 1.5, double) are just simple numbers.
		// "at least" needs a "+" at the end of the number (no units).
		// "exactly" simply has units.
		// "multiple" has no units.

		gchar * pTmp = NULL;
		const gchar * pString = _getSpinItemValue(id_SPIN_SPECIAL_SPACING);
		UT_uint32 nSize = 0;

		switch(_getMenuItemValue(id_MENU_SPECIAL_SPACING))
		{
		case spacing_SINGLE:
			p->val = g_strdup("1.0");
			break;
		case spacing_ONEANDHALF:
			p->val = g_strdup("1.5");
			break;
		case spacing_DOUBLE:
			p->val = g_strdup("2.0");
			break;
		case spacing_ATLEAST:
			nSize = strlen(pString);
			pTmp = (gchar *) UT_calloc(nSize + 2, sizeof(gchar));
			UT_return_val_if_fail (pTmp, false);

			strncpy(pTmp, pString, nSize);
			// stick a '+' at the end
			pTmp[nSize] = '+';
			p->val = g_strdup(pTmp);
			FREEP(pTmp);
			break;

		case spacing_EXACTLY:
			// fallthrough
		case spacing_MULTIPLE:
			// both these cases either do or don't have units associated with them.
			// the platform dialog code takes care of that.
			p->val = g_strdup(pString);
			break;
		default:
			UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN);
		}
		v.addItem(p);
	}

	// NOTE : "orphans" and "widows" hold a number specifying the number
	// NOTE : of lines to consider an orphaned or widowed piece of text.
	// NOTE : If they're both 0 they're off.  If either is greater than
	// NOTE : 0, then some form of control is in effect.  If the property
	// NOTE : is not set, they're indeterminate.

	if (_wasChanged(id_CHECK_WIDOW_ORPHAN))
	{
		// TODO : fix this!  we stomp on both properties (setting them
		// TODO : to "2"s or "0"s) if the one check box was ever
		// TODO : changed

		{
			ALLOC_PROP_PAIR(p);
			p->prop = g_strdup("orphans");

			if (_getCheckItemValue(id_CHECK_WIDOW_ORPHAN) == check_TRUE)
				p->val = g_strdup("2");
			else
				p->val = g_strdup("0");

			v.addItem(p);
		}

		{
			ALLOC_PROP_PAIR(p);
			p->prop = g_strdup("widows");

			if (_getCheckItemValue(id_CHECK_WIDOW_ORPHAN) == check_TRUE)
				p->val = g_strdup("2");
			else
				p->val = g_strdup("0");

			v.addItem(p);
		}
	}

	if (_wasChanged(id_CHECK_KEEP_LINES))
	{
		ALLOC_PROP_PAIR(p);
		p->prop = g_strdup("keep-together");

		if (_getCheckItemValue(id_CHECK_KEEP_LINES) == check_TRUE)
			p->val = g_strdup("yes");
		else
			p->val = g_strdup("no");

		v.addItem(p);
	}

	if (_wasChanged(id_CHECK_KEEP_NEXT))
	{
		ALLOC_PROP_PAIR(p);
		p->prop = g_strdup("keep-with-next");

		if (_getCheckItemValue(id_CHECK_KEEP_NEXT) == check_TRUE)
			p->val = g_strdup("yes");
		else
			p->val = g_strdup("no");

		v.addItem(p);
	}

	// TODO : add these to PP_Property (pp_Property.cpp) !!!
	/*
	  m_pageBreakBefore;
	  m_suppressLineNumbers;
	  m_noHyphenate;
	*/

	// export everything in the array
	UT_uint32 count = v.getItemCount()*2 + 1;

	const gchar ** newprops = (const gchar **) UT_calloc(count, sizeof(gchar *));
	if (!newprops)
		return false;

	const gchar ** newitem = newprops;

	UT_uint32 i = v.getItemCount();

	while (i > 0)
	{
		p = (propPair *) v.getNthItem(i-1);
		i--;

		newitem[0] = p->prop;
		newitem[1] = p->val;
		newitem += 2;
	}

	// DO purge the vector's CONTENTS, which are just propPair structs
	UT_VECTOR_FREEALL(propPair *, v);

	// DO NOT purge the propPair's CONTENTS, because they will be pointed to
	// by the pointers we just copied into memory typed (gchar **)

	pProps = newprops;

	return true;
}
void AP_UnixDialog_Paragraph::_syncControls(tControl changed, bool bAll /* = false */)
{
	// let parent sync any member variables first
	AP_Dialog_Paragraph::_syncControls(changed, bAll);

	// sync the display

	// 1.  link the "hanging indent by" combo and spinner
	if (bAll || (changed == id_SPIN_SPECIAL_INDENT))
	{
		// typing in the control can change the associated combo
		if (_getMenuItemValue(id_MENU_SPECIAL_INDENT) == indent_FIRSTLINE)
		{
			XAP_comboBoxSetActiveFromIntCol(GTK_COMBO_BOX(m_listSpecial),1,
										(gint) _getMenuItemValue(id_MENU_SPECIAL_INDENT));
		}
	}
	if (bAll || (changed == id_MENU_SPECIAL_INDENT))
	{
		switch(_getMenuItemValue(id_MENU_SPECIAL_INDENT))
		{
		case indent_NONE:
			// clear the spin control
			gtk_entry_set_text(GTK_ENTRY(m_spinbuttonBy), "");
			gtk_widget_set_sensitive(m_spinbuttonBy, FALSE);
			break;

		default:
			// set the spin control
			gtk_widget_set_sensitive(m_spinbuttonBy, TRUE);
			gtk_entry_set_text(GTK_ENTRY(m_spinbuttonBy), _getSpinItemValue(id_SPIN_SPECIAL_INDENT));
			break;
		}
	}

	// 2.  link the "line spacing at" combo and spinner

	if (bAll || (changed == id_SPIN_SPECIAL_SPACING))
	{
		// typing in the control can change the associated combo
		if (_getMenuItemValue(id_MENU_SPECIAL_SPACING) == spacing_MULTIPLE)
		{
			XAP_comboBoxSetActiveFromIntCol(GTK_COMBO_BOX(m_listLineSpacing),1,
										(gint) _getMenuItemValue(id_MENU_SPECIAL_SPACING));
		}
	}
	if (bAll || (changed == id_MENU_SPECIAL_SPACING))
	{
		switch(_getMenuItemValue(id_MENU_SPECIAL_SPACING))
		{
		case spacing_SINGLE:
		case spacing_ONEANDHALF:
		case spacing_DOUBLE:
			// clear the spin control
			gtk_entry_set_text(GTK_ENTRY(m_spinbuttonAt), "");
			gtk_widget_set_sensitive(m_spinbuttonAt, FALSE);
			break;

		default:
			// set the spin control
			gtk_widget_set_sensitive(m_spinbuttonAt, TRUE);
			gtk_entry_set_text(GTK_ENTRY(m_spinbuttonAt), _getSpinItemValue(id_SPIN_SPECIAL_SPACING));
			break;
		}
	}

	// 3.  move results of _doSpin() back to screen

	if (!bAll)
	{
		// spin controls only sync when spun
		switch (changed)
		{
		case id_SPIN_LEFT_INDENT:
			gtk_entry_set_text(GTK_ENTRY(m_spinbuttonLeft), 	_getSpinItemValue(id_SPIN_LEFT_INDENT));
			break;
		case id_SPIN_RIGHT_INDENT:
			gtk_entry_set_text(GTK_ENTRY(m_spinbuttonRight), 	_getSpinItemValue(id_SPIN_RIGHT_INDENT));
			break;
		case id_SPIN_SPECIAL_INDENT:
			gtk_entry_set_text(GTK_ENTRY(m_spinbuttonBy), 		_getSpinItemValue(id_SPIN_SPECIAL_INDENT));
			break;
		case id_SPIN_BEFORE_SPACING:
			gtk_entry_set_text(GTK_ENTRY(m_spinbuttonBefore), 	_getSpinItemValue(id_SPIN_BEFORE_SPACING));
			break;
		case id_SPIN_AFTER_SPACING:
			gtk_entry_set_text(GTK_ENTRY(m_spinbuttonAfter), 	_getSpinItemValue(id_SPIN_AFTER_SPACING));
			break;
		case id_SPIN_SPECIAL_SPACING:
			gtk_entry_set_text(GTK_ENTRY(m_spinbuttonAt), 		_getSpinItemValue(id_SPIN_SPECIAL_SPACING));
			break;
		default:
			break;
		}
	}
}
void AP_Win32Dialog_Paragraph::_syncControls(tControl changed, bool bAll /* = false */)
{
	// let parent sync any member variables first

	AP_Dialog_Paragraph::_syncControls(changed, bAll);

	// sync the display

	// 1.  link the "hanging indent by" combo and spinner

	if (bAll || (changed == id_SPIN_SPECIAL_INDENT))
	{
		// typing in the control can change the associated combo
		if (_getMenuItemValue(id_MENU_SPECIAL_INDENT) == indent_FIRSTLINE)
		{
			HWND h = GetDlgItem(m_hwndSpacing, AP_RID_DIALOG_PARA_COMBO_HANG);
			SendMessageW(h, CB_SETCURSEL, (WPARAM) _getMenuItemValue(id_MENU_SPECIAL_INDENT), 0);
		}
	}
	if (bAll || (changed == id_MENU_SPECIAL_INDENT))
	{
		switch(_getMenuItemValue(id_MENU_SPECIAL_INDENT))
		{
		case indent_NONE:
			// clear the spin control
			SetDlgItemText(m_hwndSpacing, AP_RID_DIALOG_PARA_EDIT_BY, NULL);
			break;

		default:
			// set the spin control
			SetDlgItemText(m_hwndSpacing, AP_RID_DIALOG_PARA_EDIT_BY, _getSpinItemValue(id_SPIN_SPECIAL_INDENT));
			break;
		}
	}

	// 2.  link the "line spacing at" combo and spinner

	if (bAll || (changed == id_SPIN_SPECIAL_SPACING))
	{
		// typing in the control can change the associated combo
		if (_getMenuItemValue(id_MENU_SPECIAL_SPACING) == spacing_MULTIPLE)
		{
			HWND h = GetDlgItem(m_hwndSpacing, AP_RID_DIALOG_PARA_COMBO_LEAD);
			SendMessageW(h, CB_SETCURSEL, (WPARAM) _getMenuItemValue(id_MENU_SPECIAL_SPACING), 0);
		}
	}
	if (bAll || (changed == id_MENU_SPECIAL_SPACING))
	{
		switch(_getMenuItemValue(id_MENU_SPECIAL_SPACING))
		{
		case spacing_SINGLE:
		case spacing_ONEANDHALF:
		case spacing_DOUBLE:
			// clear the spin control
			SetDlgItemText(m_hwndSpacing, AP_RID_DIALOG_PARA_EDIT_AT, NULL);
			break;

		default:
			// set the spin control
			SetDlgItemText(m_hwndSpacing, AP_RID_DIALOG_PARA_EDIT_AT, _getSpinItemValue(id_SPIN_SPECIAL_SPACING));
			break;
		}
	}

	// 3.  move results of _doSpin() back to screen

	if (!bAll)
	{
		// spin controls only sync when spun
		switch (changed)
		{
		_syncSPIN(m_hwndSpacing, PARA_EDIT_LEFT,	id_SPIN_LEFT_INDENT)
		_syncSPIN(m_hwndSpacing, PARA_EDIT_RIGHT,	id_SPIN_RIGHT_INDENT)
		_syncSPIN(m_hwndSpacing, PARA_EDIT_BY,		id_SPIN_SPECIAL_INDENT)
		_syncSPIN(m_hwndSpacing, PARA_EDIT_BEFORE,	id_SPIN_BEFORE_SPACING)
		_syncSPIN(m_hwndSpacing, PARA_EDIT_AFTER,	id_SPIN_AFTER_SPACING)
		_syncSPIN(m_hwndSpacing, PARA_EDIT_AT,		id_SPIN_SPECIAL_SPACING)
		default:
			break;
		}
	}
}
BOOL AP_Win32Dialog_Paragraph::_onInitTab(HWND hWnd, WPARAM wParam, LPARAM lParam)
{
	const XAP_StringSet * pSS = m_pApp->getStringSet();

	UT_Win32LocaleString str;

	// position ourselves w.r.t. containing tab

	RECT r;
	GetClientRect(m_hwndTab, &r);
	TabCtrl_AdjustRect(m_hwndTab, FALSE, &r);
	SetWindowPos(hWnd, HWND_TOP, r.left, r.top, 0, 0, SWP_NOSIZE);

	// remember which window is which tab

	TabParam * pTP = (TabParam *) lParam;
	switch (pTP->which)
	{
	case AP_RID_DIALOG_PARA_TAB1:		// first tab
		{
			m_hwndSpacing = hWnd;

			// Hide Bidi Check Box unless required
			{
				HWND hwndBidi = GetDlgItem(hWnd, AP_RID_DIALOG_PARA_CHECK_BIDI);
				ShowWindow(hwndBidi,SW_HIDE);
				ShowWindow(hwndBidi,SW_SHOW);
			}

			// localize controls
			_DS(PARA_TEXT_ALIGN,		DLG_Para_LabelAlignment);
			_DS(PARA_TEXT_INDENT,		DLG_Para_LabelIndentation);
			_DS(PARA_TEXT_LEFT,			DLG_Para_LabelLeft);
			_DS(PARA_TEXT_RIGHT,		DLG_Para_LabelRight);
			_DS(PARA_TEXT_HANG,			DLG_Para_LabelSpecial);
			_DS(PARA_TEXT_BY,			DLG_Para_LabelBy);
			_DS(PARA_TEXT_SPACING,		DLG_Para_LabelSpacing);
			_DS(PARA_TEXT_BEFORE,		DLG_Para_LabelBefore);
			_DS(PARA_TEXT_AFTER,		DLG_Para_LabelAfter);
			_DS(PARA_TEXT_LEAD,			DLG_Para_LabelLineSpacing);
			_DS(PARA_TEXT_AT,			DLG_Para_LabelAt);
			_DS(PARA_CHECK_BIDI,		DLG_Para_DomDirection);

			// populate fixed choices
			{
				HWND hwndAlign = GetDlgItem(hWnd, AP_RID_DIALOG_PARA_COMBO_ALIGN);
				// insert the empty value (for multi-para selections with different state)
				SendMessageW(hwndAlign, CB_ADDSTRING, 0, (LPARAM) L"");
				_CAS(hwndAlign, DLG_Para_AlignLeft);
				_CAS(hwndAlign, DLG_Para_AlignCentered);
				_CAS(hwndAlign, DLG_Para_AlignRight);
				_CAS(hwndAlign, DLG_Para_AlignJustified);
				SendMessageW(hwndAlign, CB_SETCURSEL, (WPARAM) _getMenuItemValue(id_MENU_ALIGNMENT), 0);

				HWND hwndHang = GetDlgItem(hWnd, AP_RID_DIALOG_PARA_COMBO_HANG);
				SendMessageW(hwndHang, CB_ADDSTRING, 0, (LPARAM) L"");
				_CAS(hwndHang, DLG_Para_SpecialNone);
				_CAS(hwndHang, DLG_Para_SpecialFirstLine);
				_CAS(hwndHang, DLG_Para_SpecialHanging);
				SendMessageW(hwndHang, CB_SETCURSEL, (WPARAM) _getMenuItemValue(id_MENU_SPECIAL_INDENT), 0);

				HWND hwndLead = GetDlgItem(hWnd, AP_RID_DIALOG_PARA_COMBO_LEAD);
				SendMessageW(hwndLead, CB_ADDSTRING, 0, (LPARAM) L"");
				_CAS(hwndLead, DLG_Para_SpacingSingle);
				_CAS(hwndLead, DLG_Para_SpacingHalf);
				_CAS(hwndLead, DLG_Para_SpacingDouble);
				_CAS(hwndLead, DLG_Para_SpacingAtLeast);
				_CAS(hwndLead, DLG_Para_SpacingExactly);
				_CAS(hwndLead, DLG_Para_SpacingMultiple);
				SendMessageW(hwndLead, CB_SETCURSEL, (WPARAM) _getMenuItemValue(id_MENU_SPECIAL_SPACING), 0);
			}

			// set initial state
			_SST(PARA_EDIT_LEFT,	id_SPIN_LEFT_INDENT);
			_SST(PARA_EDIT_RIGHT,	id_SPIN_RIGHT_INDENT);
			_SST(PARA_EDIT_BY,		id_SPIN_SPECIAL_INDENT);
			_SST(PARA_EDIT_BEFORE,	id_SPIN_BEFORE_SPACING);
			_SST(PARA_EDIT_AFTER,	id_SPIN_AFTER_SPACING);
			_SST(PARA_EDIT_AT,		id_SPIN_SPECIAL_SPACING);
			_CDB(PARA_CHECK_BIDI,	id_CHECK_DOMDIRECTION);
		}
		break;

	case AP_RID_DIALOG_PARA_TAB2:		// second tab
		{
			m_hwndBreaks = hWnd;

			// localize controls
			_DS(PARA_TEXT_PAGE,			DLG_Para_LabelPagination);
			_DS(PARA_CHECK_WIDOW,		DLG_Para_PushWidowOrphanControl);
			_DS(PARA_CHECK_NEXT,		DLG_Para_PushKeepWithNext);
			_DS(PARA_CHECK_TOGETHER,	DLG_Para_PushKeepLinesTogether);
			_DS(PARA_CHECK_BREAK,		DLG_Para_PushPageBreakBefore);
			_DS(PARA_CHECK_SUPPRESS,	DLG_Para_PushSuppressLineNumbers);
			_DS(PARA_CHECK_NOHYPHEN,	DLG_Para_PushNoHyphenate);

			// set initial state
			_CDB(PARA_CHECK_WIDOW,		id_CHECK_WIDOW_ORPHAN);
			_CDB(PARA_CHECK_NEXT,		id_CHECK_KEEP_NEXT);
			_CDB(PARA_CHECK_TOGETHER,	id_CHECK_KEEP_LINES);
			_CDB(PARA_CHECK_BREAK,		id_CHECK_PAGE_BREAK);
			_CDB(PARA_CHECK_SUPPRESS,	id_CHECK_SUPPRESS);
			_CDB(PARA_CHECK_NOHYPHEN,	id_CHECK_NO_HYPHENATE);
		}
		break;

	default:
		UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN);
		break;
	}

	// the following are common to each tab

	_DS(PARA_TEXT_PREVIEW,		DLG_Para_LabelPreview);

	if (!m_pPreviewWidget)
	{
		// for XP purposes, life is simplest if we only have one preview
		// widget which "floats" above both tabs.  to get the window
		// parentage right, we use the dimensions and location of the
		// owner-draw control on the tab to position *another* dummy
		// window which is parented by the main dialog instead.

		HWND hwndChild = GetDlgItem(hWnd, AP_RID_DIALOG_PARA_PREVIEW);
		HWND hwndFloater = GetDlgItem(m_hwndDlg, AP_RID_DIALOG_PARA_PREVIEW);

		RECT r2;
		GetWindowRect(hwndChild, &r2);

		POINT pt;
		pt.x = r2.left;
		pt.y = r2.top;
		ScreenToClient(m_hwndDlg, &pt);

		SetWindowPos(hwndFloater, HWND_TOP, pt.x, pt.y,
					 r2.right - r2.left, r2.bottom - r2.top, SWP_NOREDRAW);

		// use this floater window as a parent to the widget that we create
		// here and thus have complete control of.

		m_pPreviewWidget = new XAP_Win32PreviewWidget(static_cast<XAP_Win32App *>(m_pApp),
													  hwndFloater,
													  0);

		// instantiate the XP preview object using the win32 preview widget (window)
		// we just created.  we seem to have a mish-mash of terms here, sorry.

		UT_uint32 w,h;
		m_pPreviewWidget->getWindowSize(&w,&h);

		_createPreviewFromGC(m_pPreviewWidget->getGraphics(),w,h);
		m_pPreviewWidget->setPreview(m_paragraphPreview); // we need this to call draw() on WM_PAINTs
//		_updatePreview();
	}

	return 1;							// 1 == we did not call SetFocus()
}