Example #1
0
bool pt_PieceTable::_realInsertObject(PT_DocPosition dpos,
									PTObjectType pto,
									const gchar ** attributes,
									const gchar ** properties,  pf_Frag_Object ** ppfo)
{
	UT_ASSERT_HARMLESS((pto == PTO_Math) || (pto == PTO_Embed) || (properties == NULL));

	// dpos == 1 seems to be generally bad. - plam
	// I'm curious about how often it happens.  Please mail me if it does!
	UT_ASSERT_HARMLESS(dpos > 1);

	// TODO currently we force the caller to pass in the attr/prop.
	// TODO this is probably a good thing for Images, but might be
	// TODO bogus for things like Fields.

	UT_return_val_if_fail (m_pts==PTS_Editing, false);

	// store the attributes and properties and get an index to them.
	PT_AttrPropIndex apiOld = 0, indexAP;

	pf_Frag * pf = NULL;
	PT_BlockOffset fragOffset = 0;
	bool bFound = getFragFromPosition(dpos,&pf,&fragOffset);
	UT_return_val_if_fail (bFound,false);
	pf_Frag_Strux * pfs = NULL;
	bool bFoundStrux = _getStruxFromFrag(pf,&pfs);

	UT_return_val_if_fail (bFoundStrux,false);
	if(isEndFootnote((pf_Frag *) pfs))
	{
		bFoundStrux = _getStruxFromFragSkip((pf_Frag *)pfs,&pfs);
	}
	UT_return_val_if_fail (bFoundStrux, false);
	
	apiOld = _chooseIndexAP(pf,fragOffset);

	if (!m_varset.mergeAP(PTC_AddFmt, apiOld, attributes, properties, &indexAP, m_pDocument))
		return false;

	// get the fragment at the given document position.

	PT_BlockOffset blockOffset = _computeBlockOffset(pfs,pf) + fragOffset;
        pf_Frag_Object * pfo = NULL;
	if (!_insertObject(pf,fragOffset,pto,indexAP,pfo))
		return false;

	// create a change record, add it to the history, and notify
	// anyone listening.

	PX_ChangeRecord_Object * pcr
		= new PX_ChangeRecord_Object(PX_ChangeRecord::PXT_InsertObject,
									 dpos,indexAP, pfo->getXID(), pto,blockOffset,
                                     pfo->getField(),reinterpret_cast<PL_ObjectHandle>(pfo));
	UT_return_val_if_fail (pcr,false);

	m_history.addChangeRecord(pcr);
	m_pDocument->notifyListeners(pfs,pcr);
        *ppfo = pfo;
	return true;
}
PT_AttrPropIndex pt_PieceTable::_chooseIndexAP(pf_Frag * pf, PT_BlockOffset fragOffset)
{
	// decide what indexAP to give an insertSpan inserting at the given
	// position in the document [pf,fragOffset].
	// try to get it from the current fragment.

	if (pf->getType() == pf_Frag::PFT_FmtMark)
	{
		pf_Frag_FmtMark * pffm = static_cast<pf_Frag_FmtMark *>(pf);
		return pffm->getIndexAP();
	}

	if ((pf->getType() == pf_Frag::PFT_Text) && (fragOffset > 0))
	{
		// if we are inserting at the middle or end of a text fragment,
		// we take the A/P of this text fragment.

		pf_Frag_Text * pft = static_cast<pf_Frag_Text *>(pf);
		return pft->getIndexAP();
	}

	// we are not looking forward at a text fragment or
	// we are at the beginning of a text fragment.
	// look to the previous fragment to see what to do.

	pf_Frag * pfPrev = pf->getPrev();
	switch (pfPrev->getType())
	{
	case pf_Frag::PFT_Text:
		{
			// if we immediately follow another text fragment, we
			// take the A/P of that fragment.

			pf_Frag_Text * pftPrev = static_cast<pf_Frag_Text *>(pfPrev);
			return pftPrev->getIndexAP();
		}

	case pf_Frag::PFT_Strux:
		{
			// if we immediately follow a block (paragraph),
			// (and don't have a FmtMark (tested earlier) at
			// the beginning of the block), look to the right.

			if (pf->getType() == pf_Frag::PFT_Text)
			{
				// we take the A/P of this text fragment.

				pf_Frag_Text * pft = static_cast<pf_Frag_Text *>(pf);
				return pft->getIndexAP();
			}

			// we can't find anything, just use the default.

			return 0;
		}

	case pf_Frag::PFT_Object:
		{
			// if we immediately follow an object, then we may or may not
			// want to use the A/P of the object.  for an image, it is
			// probably not correct to use it (and we should probably use
			// the text to the right of the image).  for a field, it may
			// be a valid to use the A/P of the object.

			pf_Frag_Object * pfo = static_cast<pf_Frag_Object *>(pfPrev);
			switch (pfo->getObjectType())
			{
			case PTO_Image:
				return _chooseIndexAP(pf->getPrev(),pf->getPrev()->getLength());

			case PTO_Field:
			case PTO_Math:
			case PTO_Embed:
				return pfo->getIndexAP();

			// TODO: determine what we want to do about these guys
			case PTO_Bookmark:
			case PTO_Hyperlink:
			case PTO_RDFAnchor:
				return 0;

			default:
				UT_ASSERT_HARMLESS(0);
				return 0;
			}
		}

	case pf_Frag::PFT_FmtMark:
		{
			// TODO i'm not sure this is possible

			pf_Frag_FmtMark * pffm = static_cast<pf_Frag_FmtMark *>(pfPrev);
			return pffm->getIndexAP();
		}

	default:
		UT_ASSERT_HARMLESS(0);
		return 0;
	}
}
bool pt_PieceTable::_realInsertSpan(PT_DocPosition dpos,
									const UT_UCSChar * p,
									UT_uint32 length,
									const gchar ** attributes,
									const gchar ** properties,
									fd_Field * pField,
									bool bAddChangeRec)
{
	// insert character data into the document at the given position.

	UT_return_val_if_fail (m_pts==PTS_Editing, false);

	// get the fragment at the given document position.

	pf_Frag * pf = NULL;
	PT_BlockOffset fragOffset = 0;
	bool bFound = getFragFromPosition(dpos,&pf,&fragOffset);
	UT_return_val_if_fail (bFound,false);


	// append the text data to the end of the current buffer.

	PT_BufIndex bi;
	if (!m_varset.appendBuf(p,length,&bi))
		return false;

	pf_Frag_Strux * pfs = NULL;
	bool bFoundStrux = _getStruxFromFrag(pf,&pfs);
	UT_return_val_if_fail (bFoundStrux,false);
	if(isEndFootnote((pf_Frag *)pfs))
	{
		bFoundStrux = _getStruxFromFragSkip((pf_Frag *) pfs,&pfs);
	}
	UT_return_val_if_fail (pfs,false);
	if(pfs->getStruxType() == PTX_EndFrame)
	{
		bFoundStrux = _getStruxFromFragSkip((pf_Frag *) pfs,&pfs);
	}
	// we just did a getFragFromPosition() which gives us the
	// the thing *starting* at that position.  if we have a
	// fragment boundary at that position, it's sort of arbitrary
	// whether we treat this insert as a prepend to the one we just found
	// or an append to the previous one (when it's a text frag).
	// in the normal case, we want the Attr/Prop of a character
	// insertion to take the AP of the thing to the immediate
	// left (seems to be what MS-Word and MS-WordPad do).  It's also
	// useful when the user hits the BOLD button (without a)
	// selection) and then starts typing -- ideally you'd like
	// all of the text to have bold not just the first.  therefore,
	// we will see if we are on a text-text boundary and backup
	// (and thus appending) to the previous.

	bool bNeedGlob = false;
	PT_AttrPropIndex indexAP = 0;

	if ( (fragOffset==0) && (pf->getPrev()) )
	{
		bool bRightOfFmtMark = (pf->getPrev()->getType() == pf_Frag::PFT_FmtMark);
		if (bRightOfFmtMark)
		{
			// if we're just to the right of a _FmtMark, we want to replace
			// it with a _Text frag with the same attr/prop (we
			// only used the _FmtMark to remember a toggle format
			// before we had text for it).

			pf_Frag_FmtMark * pfPrevFmtMark = static_cast<pf_Frag_FmtMark *>(pf->getPrev());
			indexAP = pfPrevFmtMark->getIndexAP();

			if (_lastUndoIsThisFmtMark(dpos))
			{
				// if the last thing in the undo history is the insertion of this
				// _FmtMark, then let's remember the indexAP, do an undo, and then
				// insert the text.  this way the only thing remaining in the undo
				// is the insertion of this text (with no globbing around it).  then
				// a user-undo will undo all of the coalesced text back to this point
				// and leave the insertion point as if the original InsertFmtMark
				// had never happened.
				//
				// we don't allow consecutive FmtMarks, but the undo may be a
				// changeFmtMark and thus just re-change the mark frag rather
				// than actually deleting it.  so we loop here to get back to
				// the original insertFmtMark (this is the case if the user hit
				// BOLD then ITALIC then UNDERLINE then typed a character).

				do { undoCmd(); } while (_lastUndoIsThisFmtMark(dpos));
			}
			else
			{
				// for some reason, something else has happened to the document
				// since this _FmtMark was inserted (perhaps it was one that we
				// inserted when we did a paragraph break and inserted several
				// to remember the current inline formatting).
				//
				// here we have to do it the hard way and use a glob and an
				// explicit deleteFmtMark.  note that this messes up the undo
				// coalescing.  that is, if the user starts typing at this
				// position and then hits UNDO, we will erase all of the typing
				// except for the first character.  the second UNDO, will erase
				// the first character and restores the current FmtMark.  if the
				// user BACKSPACES instead of doing the second UNDO, both the
				// first character and the FmtMark would be gone.
				//
				// TODO decide if we like this...
				// NOTE this causes BUG#431.... :-)

				bNeedGlob = true;
				beginMultiStepGlob();
				_deleteFmtMarkWithNotify(dpos,pfPrevFmtMark,pfs,&pf,&fragOffset);
			}

			// we now need to consider pf invalid, since the fragment list may have
			// been coalesced as the FmtMarks were deleted.  let's recompute them
			// but with a few shortcuts.

			bFound = getFragFromPosition(dpos,&pf,&fragOffset);
			UT_return_val_if_fail (bFound, false);

			bFoundStrux = _getStruxFromFrag(pf,&pfs);
			UT_return_val_if_fail (bFoundStrux,false);
			if(isEndFootnote((pf_Frag *)pfs))
			{
				bFoundStrux = _getStruxFromFragSkip((pf_Frag *)pfs,&pfs);
			}
			UT_return_val_if_fail (bFoundStrux, false);
			xxx_UT_DEBUGMSG(("Got FragStrux at Pos %d \n",pfs->getPos()));

			// with the FmtMark now gone, we make a minor adjustment so that we
			// try to append text to the previous rather than prepend to the current.
			// this makes us consistent with other places in the code.

			if ( (fragOffset==0) && (pf->getPrev()) && (pf->getPrev()->getType() == pf_Frag::PFT_Text) && pf->getPrev()->getField()== NULL )
			{
				// append to the end of the previous frag rather than prepend to the current one.
				pf = pf->getPrev();
				fragOffset = pf->getLength();
			}
		}
		else if (pf->getPrev()->getType() == pf_Frag::PFT_Text && pf->getPrev()->getField()==NULL)
		{
			pf_Frag_Text * pfPrevText = static_cast<pf_Frag_Text *>(pf->getPrev());
			indexAP = pfPrevText->getIndexAP();

			// append to the end of the previous frag rather than prepend to the current one.
			pf = pf->getPrev();
			fragOffset = pf->getLength();
		}
		else
		{
			indexAP = _chooseIndexAP(pf,fragOffset);
			// PLAM: This is the list of field attrs that should not inherit
			// PLAM: to the span following a field.
			const gchar * pFieldAttrs[12];
			pFieldAttrs[0] = "type";  pFieldAttrs[1] = NULL;
			pFieldAttrs[2] = "param"; pFieldAttrs[3] = NULL;
			pFieldAttrs[4] = "name";  pFieldAttrs[5] = NULL;
			pFieldAttrs[6] = "endnote-id"; pFieldAttrs[7] = NULL;
			pFieldAttrs[8] = NULL;   pFieldAttrs[9] = NULL;
			pFieldAttrs[10] = NULL;   pFieldAttrs[11] = NULL;
			
			const PP_AttrProp * pAP = NULL;
			
			if (!getAttrProp(indexAP, &pAP))
				return false;
			
			if (pAP->areAnyOfTheseNamesPresent(pFieldAttrs, NULL))
			{
				// We do not want to inherit a char style from a field.
				pFieldAttrs[8] = "style";
				PP_AttrProp * pAPNew = pAP->cloneWithElimination(pFieldAttrs, NULL);
				if (!pAPNew)
					return false;
				pAPNew->markReadOnly();
				
				if (!m_varset.addIfUniqueAP(pAPNew, &indexAP))
					return false;
			}
		}
	}
	else
	{
		// is existing fragment a field? If so do nothing
		// Or should we display a message to the user?

		if(pf->getField() != NULL)
		{
		       return false;
		}

		indexAP = _chooseIndexAP(pf,fragOffset);
	}
	PT_BlockOffset blockOffset = _computeBlockOffset(pfs,pf) + fragOffset;
	PX_ChangeRecord_Span * pcr = NULL;

	if(attributes || properties)
	{
		// we need to add the attrs and props passed to us ...
		PT_AttrPropIndex indexNewAP;
		bool bMerged;
		bMerged = m_varset.mergeAP(PTC_AddFmt,indexAP,attributes,properties,&indexNewAP,getDocument());
		UT_ASSERT_HARMLESS( bMerged );

		if(bMerged)
			indexAP = indexNewAP;
	}
	
	if (!_insertSpan(pf,bi,fragOffset,length,indexAP,pField))
	{
		if (bNeedGlob)
			endMultiStepGlob();
		return false;
	}

	// note: because of coalescing, pf should be considered invalid at this point.
	// create a change record, add it to the history, and notify
	// anyone listening.

	pcr = new PX_ChangeRecord_Span(PX_ChangeRecord::PXT_InsertSpan,
								   dpos,indexAP,bi,length,
								   blockOffset, pField);
	UT_return_val_if_fail (pcr, false);
	
	pcr->setDocument(m_pDocument);
	bool canCoalesce = _canCoalesceInsertSpan(pcr);
	if (!bAddChangeRec || (canCoalesce && !m_pDocument->isCoalescingMasked()))
	{
		if (canCoalesce)
			m_history.coalesceHistory(pcr);
		
		m_pDocument->notifyListeners(pfs,pcr);
		delete pcr;
	}
	else
	{
		m_history.addChangeRecord(pcr);
		m_pDocument->notifyListeners(pfs,pcr);
	}

	if (bNeedGlob)
		endMultiStepGlob();	
	return true;
}