bool pt_PieceTable::_deleteObjectWithNotify(PT_DocPosition dpos,
											   pf_Frag_Object * pfo, UT_uint32 fragOffset,
											   UT_uint32 length,
											   pf_Frag_Strux * pfs,
											   pf_Frag ** ppfEnd, UT_uint32 * pfragOffsetEnd,
											   bool bAddChangeRec)
{
	// create a change record for this change and put it in the history.

	UT_return_val_if_fail (pfs,false);
	UT_return_val_if_fail (length == pfo->getLength(), false);
	UT_return_val_if_fail (fragOffset == 0,false);

	PT_BlockOffset blockOffset = _computeBlockOffset(pfs,pfo) + fragOffset;

	PX_ChangeRecord_Object * pcr
		= new PX_ChangeRecord_Object(PX_ChangeRecord::PXT_DeleteObject,
									 dpos, pfo->getIndexAP(), pfo->getXID(), pfo->getObjectType(),
									 blockOffset, pfo->getField(),pfo);
	UT_return_val_if_fail (pcr, false);

	// actually remove the fragment from the list and delete it.

	_deleteObject(pfo,ppfEnd,pfragOffsetEnd);

	if (bAddChangeRec)
		m_history.addChangeRecord(pcr);
	m_pDocument->notifyListeners(pfs,pcr);
	if (!bAddChangeRec)
		delete pcr;
	
	return true;
}
Beispiel #2
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;
}
Beispiel #3
0
bool pt_PieceTable::_fmtChangeSpanWithNotify(PTChangeFmt ptc,
											 pf_Frag_Text * pft, UT_uint32 fragOffset,
											 PT_DocPosition dpos,
											 UT_uint32 length,
											 const gchar ** attributes,
											 const gchar ** properties,
											 pf_Frag_Strux * pfs,
											 pf_Frag ** ppfNewEnd,
											 UT_uint32 * pfragOffsetNewEnd,
											 bool bRevisionDelete)
{
	// create a change record for this change and put it in the history.

	if (length == 0)					// TODO decide if this is an error.
	{
		UT_DEBUGMSG(("_fmtChangeSpanWithNotify: length==0\n"));
		SETP(ppfNewEnd, pft->getNext());
		SETP(pfragOffsetNewEnd, 0);
		return true;
	}

	UT_return_val_if_fail (fragOffset+length <= pft->getLength(), false);

	PT_AttrPropIndex indexNewAP;
	PT_AttrPropIndex indexOldAP = pft->getIndexAP();
	bool bMerged;
	if(attributes && properties && (attributes[0] == NULL) && (properties[0] == NULL))
	{
	    //
	    // Clear out all attributes/properties and set to the first index
	    //
	    bMerged = true;
	    indexNewAP = 0;
	}
	else
	  bMerged = m_varset.mergeAP(ptc,indexOldAP,attributes,properties,&indexNewAP,getDocument());

	UT_ASSERT_HARMLESS(bMerged);

	if (indexOldAP == indexNewAP)		// the requested change will have no effect on this fragment.
	{
		if (fragOffset+length == pft->getLength())
		{
			SETP(ppfNewEnd, pft->getNext());
			SETP(pfragOffsetNewEnd, 0);
		}
		else
		{
			SETP(ppfNewEnd, pft);
			SETP(pfragOffsetNewEnd, fragOffset+length);
		}

		return true;
	}

	// we do this before the actual change because various fields that
	// we need may be blown away during the change.  we then notify all
	// listeners of the change.

	PT_BlockOffset blockOffset = _computeBlockOffset(pfs,pft) + fragOffset;

	PX_ChangeRecord_SpanChange * pcr
		= new PX_ChangeRecord_SpanChange(PX_ChangeRecord::PXT_ChangeSpan,
										 dpos, indexOldAP,indexNewAP,
										 m_varset.getBufIndex(pft->getBufIndex(),fragOffset),
										 length,blockOffset,bRevisionDelete);
	UT_return_val_if_fail (pcr,false);
	bool bResult = _fmtChangeSpan(pft,fragOffset,length,indexNewAP,ppfNewEnd,pfragOffsetNewEnd);

	// add record to history.  we do not attempt to coalesce these.
	m_history.addChangeRecord(pcr);
	m_pDocument->notifyListeners(pfs,pcr);

	return bResult;
}
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;
}
Beispiel #5
0
bool pt_PieceTable::_realInsertObject(PT_DocPosition dpos,
									PTObjectType pto,
									const gchar ** attributes,
									const gchar ** properties )
{

	// 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.
	UT_UTF8String sProps;
	UT_sint32 i = 0;
	sProps.clear();
	if(properties != NULL)
	{
	    for(i=0;(properties[i] != NULL);i+=2)
	    {
		UT_DEBUGMSG(("Object: szProps = |%s| \n",properties[i]));
		sProps +=properties[i];
		sProps += ":";
		sProps += properties[i+1];
		if(properties[i+2] != NULL)
		{
		    sProps += ";";
		}
	    }
	}
	UT_GenericVector<const gchar*>  Atts;
	Atts.clear();
	if(attributes)
	{
		for(i=0; attributes[i] != 0; i++)
		{
		    Atts.addItem(attributes[i]);
		}
	}
	if(sProps.size() > 0)
	{
	    Atts.addItem("props");
	    Atts.addItem(sProps.utf8_str());
	}
	PT_AttrPropIndex indexAP;
	if (!m_varset.storeAP(&Atts,&indexAP))
		return 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);

	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);
	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);

	return true;
}