Example #1
0
BOOL CDRFilter::AddAttributesToArrowheadNode(NodeRenderableBounded *N, DocColour *Col, INT32 LineWidth,
		LineCapType Cap, JointType Join)
{
	if(IS_A(N, NodeGroup))
	{
		// go through all the members of the group setting their attributes
		Node *pNode;

		pNode = N->FindFirstChild();

		while(pNode != 0)
		{
			if(IS_A(pNode, NodePath))
				AddAttributesToArrowheadPath((NodePath *)pNode, Col, LineWidth, Cap, Join);
			
			pNode = pNode->FindNext();
		}
	} else if(IS_A(N, NodePath))
	{
		// simply apply the attributes to this path
		AddAttributesToArrowheadPath((NodePath *)N, Col, LineWidth, Cap, Join);
	} else {
		ERROR3("Something unexpected passed to AddAttributesToArrowheadNode");
	}

	return TRUE;
}
Example #2
0
void NodeSimpleShape::PolyCopyNodeContents(NodeRenderable* pNodeCopy)
{
	ENSURE(pNodeCopy, "Trying to copy a node's contents into a NULL node");
	ENSURE(IS_A(pNodeCopy, NodeSimpleShape), "PolyCopyNodeContents given wrong dest node type");

	if (IS_A(pNodeCopy, NodeSimpleShape))
		CopyNodeContents((NodeSimpleShape*)pNodeCopy);
}
Example #3
0
void Chapter::PolyCopyNodeContents(NodeRenderable* pNodeCopy)
{
	ENSURE(pNodeCopy, "Trying to copy a node's contents into a NULL node");
	ENSURE(IS_A(pNodeCopy, Chapter), "PolyCopyNodeContents given wrong dest node type");

	if (IS_A(pNodeCopy, Chapter))
		CopyNodeContents((Chapter*)pNodeCopy);
}
Example #4
0
void NodeAnimatingBitmap::PolyCopyNodeContents(NodeRenderable* pNodeCopy)
{
	ENSURE(pNodeCopy, "Trying to copy a node's contents into a NULL node");
	ENSURE(IS_A(pNodeCopy, NodeAnimatingBitmap), "PolyCopyNodeContents given wrong dest node type");

	if (IS_A(pNodeCopy, NodeAnimatingBitmap))
		CopyNodeContents((NodeAnimatingBitmap*)pNodeCopy);
}
Example #5
0
void TemplateAttribute::PolyCopyNodeContents(NodeRenderable* pNodeCopy)
{
	ENSURE(pNodeCopy, "Trying to copy a node's contents into a NULL node");
	ENSURE(IS_A(pNodeCopy, TemplateAttribute), "PolyCopyNodeContents given wrong dest node type");

	if (IS_A(pNodeCopy, TemplateAttribute))
		CopyNodeContents((TemplateAttribute*)pNodeCopy);
}
Example #6
0
void AttrWebAddress::PolyCopyNodeContents(NodeRenderable* pNodeCopy)
{
	ENSURE(pNodeCopy, "Trying to copy a node's contents into a NULL node");
	ENSURE(IS_A(pNodeCopy, AttrWebAddress), "PolyCopyNodeContents given wrong dest node type");

	if (IS_A(pNodeCopy, AttrWebAddress))
		CopyNodeContents((AttrWebAddress*)pNodeCopy);
}
Example #7
0
OpState	OpBreakAtPoints::GetState(String_256* UIDescription, OpDescriptor*)
{

	OpState OpSt;
	String_256 DisableReason; 

   	OpSt.Greyed = FALSE;
	BOOL FoundSelected = FALSE;

	// Go through the selection until we find a selected point

	SelRange* Selected = GetApplication()->FindSelection();
	Node* pNode = Selected->FindFirst();

	while (pNode)
	{
		if (IS_A(pNode,NodePath) || IS_A(pNode,NodeBlendPath))
		{
			NodePath* pNodePath = (NodePath*)pNode;
			INT32 NumSplinters = pNodePath->InkPath.NumSplinters();

			if (NumSplinters > 0)
			{
				// We need to ask the effected nodes if they (and their parents) can handle this node being replaced
				ObjChangeFlags cFlags;

				if (NumSplinters > 1)
					cFlags.MultiReplaceNode = TRUE;	// Node will be replaced with more than one node.
				else
					cFlags.ReplaceNode = TRUE;		// Node will be replaced with one node only.

				String_32 optokenstring(OPTOKEN_BREAKATPOINTS);
				ObjChangeParamWithToken ObjChange(OBJCHANGE_STARTING,cFlags,pNodePath,NULL,&optokenstring);

				// Will the node allow this op to happen?
				if (pNodePath->AllowOp(&ObjChange,FALSE))
				{
					FoundSelected = TRUE;
					break;
				}
			}
		}
		pNode = Selected->FindNext(pNode);
	}

	// The operation is disabled if there are no complex paths selected

	if (!FoundSelected)
	{
		OpSt.Greyed = TRUE;
		DisableReason = String_256(_R(IDS_NEEDS_SELECTED_POINT));
		*UIDescription = DisableReason;
	}
	
	return(OpSt);   
}
Example #8
0
UINT32 GalleryLineDragInfo::GetCursorID(DragTarget* pDragTarget)
{
	if (pDragTarget && pDragTarget->IS_KIND_OF(ViewDragTarget))
	{
		PageDropInfo PageDropInfo;
		((ViewDragTarget*)pDragTarget)->GetDropInfo(&PageDropInfo);

		NodeRenderableInk* pObjectHit 	= PageDropInfo.pObjectHit;
		ObjectDragTarget TargetHit 		= PageDropInfo.TargetHit;

		if (IS_A(pAttr, AttrStartArrow) || IS_A(pAttr, AttrEndArrow))
		{
			if (pObjectHit && pObjectHit->IS_KIND_OF(NodePath))
			{
				Path* pPath = &((NodePath*)pObjectHit)->InkPath;
				BOOL IsStart;
				if (DropStartOrEndArrow(pPath, PageDropInfo.DropPos, &IsStart))
				{
					TargetHit = IsStart ? STARTCOL_TARGET : ENDCOL_TARGET;
				}
			}
		}

		ClickModifiers ClickMods = ClickModifiers::GetClickModifiers();
		BOOL IsInside = ClickMods.Constrain;

		if (!IsInside && pObjectHit && pObjectHit->IsCompound())
		{
			TargetHit = MANY_TARGET;
		}

		switch (TargetHit)
		{
			case FILL_TARGET:
				return IsInside ? _R(IDC_DROPINSIDEONLINE) : _R(IDC_CANDROPONLINE);
			case LINE_TARGET:
				return IsInside ? _R(IDC_DROPINSIDEONLINE) : _R(IDC_CANDROPONLINE);
			case STARTCOL_TARGET:
				return IsInside ? _R(IDC_DROPINSIDEONFILLSTART) : _R(IDC_CANDROPONFILLSTART);
			case ENDCOL_TARGET:
				return IsInside ? _R(IDC_DROPINSIDEONFILLEND) : _R(IDC_CANDROPONFILLEND);
			case MANY_TARGET:
				return IsInside ? _R(IDC_DROPINSIDEONLINE) : _R(IDC_CANDROPONLINE);

			case NO_TARGET:
				return _R(IDC_CANDROPONPAGE);
			default:
				break;
		};

		return _R(IDC_CANDROPONPAGE);
	}

	return _R(IDC_CANTDROP);
}
Example #9
0
BOOL OpBackground::GetPageColour(Spread *pSpread, KernelBitmap **ppOutBitmap, 
														DocColour **ppOutColour)
{
	ERROR2IF(pSpread == NULL,FALSE,"OpBackground::GetPageColour Bad params error!");
	ERROR2IF(ppOutBitmap == NULL || ppOutColour == NULL,FALSE,"OpBackground::GetPageColour Bad params error!");

	// Search for our special page background layer
	Layer* pFoundLayer = pSpread->FindFirstPageBackgroundLayer();
	if (pFoundLayer == NULL)
		return FALSE;

	// search for our page node
	NodeRegularShape *pNode = DoFindPageRectangle(pSpread, pFoundLayer);

	if (!pNode)
		return FALSE;

	// find the fill attribute applied to the page
	NodeAttribute *pAppliedAttr = NULL;
	pNode->FindAppliedAttribute(CC_RUNTIME_CLASS(AttrFillGeometry),&pAppliedAttr);

	if (pAppliedAttr != NULL)
	{
		if (IS_A(pAppliedAttr, AttrFlatColourFill)) // flat colour fill?
		{
			// get the colour attribute
			ColourFillAttribute *pColAttr = (ColourFillAttribute *)(pAppliedAttr->GetAttributeValue());

			// set the colour pointer to the doc colour, and the bitmap pointer to NULL
			*ppOutBitmap = NULL;
			*ppOutColour = pColAttr->GetStartColour();

		}
		else if (IS_A(pAppliedAttr, AttrBitmapColourFill)) // bitmap fill
		{

			// set the colour pointer to NULL, and the bitmap pointer to the kernel bitmap
			*ppOutBitmap = ((AttrFillGeometry *)pAppliedAttr)->GetBitmap();
			*ppOutColour = NULL;
		}
		else
			return FALSE;
	}
	else
		return FALSE;

	return TRUE;
}
Example #10
0
NodeAttribute* GalleryLineDragInfo::MakeStartOrEndArrow(NodeAttribute* pArrowAttr, BOOL Start)
{
	ArrowRec Arrow;
	NodeAttribute* NewAttr;

	if (IS_A(pArrowAttr, AttrStartArrow))
	{
		Arrow = ((AttrStartArrow*)pArrowAttr)->Value.StartArrow;
	}
	else
	{
		Arrow = ((AttrEndArrow*)pArrowAttr)->Value.EndArrow;
	}

	if (Start)
	{
		NewAttr = new AttrStartArrow();	
		((AttrStartArrow*)NewAttr)->Value.StartArrow = Arrow;
	}
	else
	{
		NewAttr = new AttrEndArrow();	
		((AttrEndArrow*)NewAttr)->Value.EndArrow = Arrow;
	}

	delete pArrowAttr;
	return NewAttr;
}
Example #11
0
/*
 * set_cheapest
 *	  Find the minimum-cost paths from among a relation's paths,
 *	  and save them in the rel's cheapest-path fields.
 *
 * This is normally called only after we've finished constructing the path
 * list for the rel node.
 *
 * If we find two paths of identical costs, try to keep the better-sorted one.
 * The paths might have unrelated sort orderings, in which case we can only
 * guess which might be better to keep, but if one is superior then we
 * definitely should keep it.
 */
void
set_cheapest(rel_optmz_info_n *parent_rel)
{
	struct list* pathlist = parent_rel->pathlist;
	struct list_cell* p;
	path_n* cheapest_startup_path;
	path_n* cheapest_total_path;

	ASSERT(IS_A(parent_rel, RelOptInfo));

	if (pathlist == NIL)
		elog(ERROR, "could not devise a query plan for the given query");

	cheapest_startup_path = cheapest_total_path = (path_n *) linitial(pathlist);

	for_each_cell(p, lnext(list_head(pathlist))) {
		path_n* path = (path_n *) lfirst(p);
		int cmp;

		cmp = cmp_path_costs(cheapest_startup_path, path, STARTUP_COST);
		if (cmp > 0
			|| (cmp == 0
				&& cmp_pathkeys(cheapest_startup_path->pathkeys,
					path->pathkeys) == PATHKEYS_BETTER2))
			cheapest_startup_path = path;

		cmp = cmp_path_costs(cheapest_total_path, path, TOTAL_COST);
		if (cmp > 0 ||
			(cmp == 0 &&
				cmp_pathkeys(cheapest_total_path->pathkeys,
					path->pathkeys) == PATHKEYS_BETTER2))
			cheapest_total_path = path;
	}
// expand partial ref (it is ensured to be leaf by toposort), and eliminate nil rules
static void _expand_lex_def(struct ContextMap* context_map, Val name) {
  Val curr;
  bool found = ContextMap.find(context_map, name, &curr);
  assert(found);
  Val res = VAL_NIL;

  for (; curr != VAL_NIL; curr = TAIL(curr)) {
    if (IS_A(HEAD(curr), "RefPartialContext")) {
      Val ref_name = AT(HEAD(curr), 0);
      Val child_nodes;
      bool found = ContextMap.find(context_map, ref_name, &child_nodes);
      if (!found) {
        COMPILE_ERROR("partial context not found: %.*s", (int)nb_string_byte_size(ref_name), nb_string_ptr(ref_name));
      }
      for (Val child_curr = child_nodes; child_curr != VAL_NIL; child_curr = TAIL(child_curr)) {
        if (child_curr != VAL_NIL) {
          res = nb_cons_new(child_curr, res);
        }
      }
    }
  }

  // yes it allocs more, but necessary to keep the order consistent
  ContextMap.insert(context_map, name, nb_cons_reverse(res));
}
Example #13
0
void OpNudge::PerformMergeProcessing()
{
    // Obtain a pointer to the operation history for the current document
    OperationHistory* pOpHist = &pOurDoc->GetOpHistory();

    // Ensure that we are the last operation added to the operation history
    // Note cannot be an ERROR2 cos this function cannot fail.
    ERROR3IF(pOpHist->FindLastOp() != this, "Last Op should be this op");

    // OK lets see if the operation performed before this was an OpNudge operation
    Operation* pPrevOp = pOpHist->FindPrevToLastOp();

    if (pPrevOp != NULL)   // Check if there was a previous op
    {

        if (IS_A(pPrevOp, OpNudge))
        {
            // Yes it was
            // We can merge this op with pPrevOp if they both apply to the same set of objects
            // This will be TRUE is their SelectionStates are the same.
            RestoreSelectionsAction* pRestoreSelAct = (RestoreSelectionsAction*)
                    GetUndoActionList()->FindActionOfClass(CC_RUNTIME_CLASS(RestoreSelectionsAction));
            ERROR3IF(pRestoreSelAct == NULL, "This op should have a RestoreSelectionsAction");
            SelectionState* ThisOpsSelection = pRestoreSelAct->GetSelState();

            pRestoreSelAct = (RestoreSelectionsAction*)
                             pPrevOp->GetUndoActionList()->FindActionOfClass(CC_RUNTIME_CLASS(RestoreSelectionsAction));
            ERROR3IF(pRestoreSelAct == NULL, "OpNudge op should have a RestoreSelectionsAction");
            SelectionState* LastOpsSelection = pRestoreSelAct->GetSelState();

            if ((*ThisOpsSelection) == (*LastOpsSelection))
            {

                // scan to see if either of these ops hides a node
                // if either do then we cannot merge them together
                // this can happen if perhaps the extending definitions
                // were changed by the nudge (sjk 27-7-00)
                if (DoesActionListHideNodes(pPrevOp) || DoesActionListHideNodes(this) )
                    return;

                // this op can be merged into the previous op, we simply need to combine the
                // TransformNodeActions
                TransformNodeAction* pTransNdAct = (TransformNodeAction*)
                                                   GetUndoActionList()->FindActionOfClass(CC_RUNTIME_CLASS(TransformNodeAction));
                ERROR3IF(pTransNdAct == NULL, "This op should have a TransformNodeAction");

                TransformNodeAction* pLastOpsTransNdAct = (TransformNodeAction*)
                        pPrevOp->GetUndoActionList()->FindActionOfClass(CC_RUNTIME_CLASS(TransformNodeAction));
                ERROR3IF(pLastOpsTransNdAct == NULL,"OpNudgeOp should have a TransformNodeAction");

                pLastOpsTransNdAct->CombineWith(pTransNdAct);

                // This op is no longer required, so let's vape it
                pOpHist->DeleteLastOp();
            }
        }
    }
    return;
}
Example #14
0
BOOL GalleryLineDragInfo::OnPageDrop(ViewDragTarget* pDragTarget)
{
	PageDropInfo PageDropInfo;
	((ViewDragTarget*)pDragTarget)->GetDropInfo(&PageDropInfo);
	NodeRenderableInk* pObjectHit = PageDropInfo.pObjectHit;

	NodeAttribute* Attrib = SourceItem->CreateNewAttribute(IsAnAdjustDrag());

	if (Attrib == NULL)
		return FALSE;

	if (pObjectHit && (IS_A(Attrib, AttrStartArrow) || IS_A(Attrib, AttrEndArrow)))
	{
		if (!pObjectHit->IS_KIND_OF(NodePath))
		{
			delete Attrib;
			return FALSE;
		}

		Path* pPath = &((NodePath*)pObjectHit)->InkPath;
		BOOL IsStart;

		if (!DropStartOrEndArrow(pPath, PageDropInfo.DropPos, &IsStart))
		{
			delete Attrib;
			return FALSE;
		}

		Attrib = MakeStartOrEndArrow(Attrib, IsStart);
	}

	if (pObjectHit) 
	{
		// Hit a Line Object, so apply attribute to it
		AttributeManager::ApplyAttribToNode(pObjectHit, Attrib);
	}
	else
	{
		// Didn't hit anything, so just set the current attribute
		AttributeManager::AttributeSelected(Attrib);
	}

	return TRUE;
}
Example #15
0
/*
 * If we have a COUNT, and our input is a sort_pl node, notify it that it can
 * use bounded sort.  Also, if our input is a merge_append_pl, we can apply the
 * same bound to any Sorts that are direct children of the merge_append_pl,
 * since the merge_append_pl surely need read no more than that many tuples from
 * any one input.  We also have to be prepared to look through a result_pl,
 * since the planner might stick one atop merge_append_pl for projection purposes.
 *
 * This is a bit of a kluge, but we don't have any more-abstract way of
 * communicating between the two nodes; and it doesn't seem worth trying
 * to invent one without some more examples of special communication needs.
 *
 * Note: it is the responsibility of nodeSort.c to react properly to
 * changes of these parameters.  If we ever do redesign this, it'd be a
 * good idea to integrate this signaling with the parameter-change mechanism.
 */
static void
pass_down_bound(limit_ps *node, plan_state_n *child_node)
{
	if (IS_A(child_node, SortState)) {
		sort_ss  *sortState;
		int64 tuples_needed;

		sortState = (sort_ss *) child_node;
		tuples_needed = node->count + node->offset;

		/* negative test checks for overflow in sum */
		if (node->noCount || tuples_needed < 0) {
			/* make sure flag gets reset if needed upon rescan */
			sortState->bounded = false;
		} else {
			sortState->bounded = true;
			sortState->bound = tuples_needed;
		}
	} else if (IS_A(child_node, MergeAppendState)) {
		merge_append_ps *maState;
		int i;

		maState = (merge_append_ps *) child_node;
		for (i = 0; i < maState->ms_nplans; i++)
			pass_down_bound(node, maState->mergeplans[i]);
	} else if (IS_A(child_node, ResultState)) {
		/*
		 * An extra consideration here is that if the result_pl is projecting a
		 * targetlist that contains any SRFs, we can't assume that every input
		 * tuple generates an output tuple, so a sort_pl underneath might need to
		 * return more than N tuples to satisfy LIMIT N. So we cannot use
		 * bounded sort.
		 *
		 * If result_pl supported qual checking, we'd have to punt on seeing a
		 * qual, too.  Note that having a resconstantqual is not a
		 * showstopper: if that fails we're not getting any rows at all.
		 */
		if (OUTER_PLAN_STATE(child_node) &&
			!expr_returns_set((node_n *) child_node->plan->targetlist))
			pass_down_bound(node, OUTER_PLAN_STATE(child_node));
	}
}
Example #16
0
/*
 * xfrm_tgt_list()
 * Turns a list of ResTarget's into a list of TargetEntry's.
 *
 * At this point, we don't care whether we are doing SELECT, INSERT,
 * or UPDATE; we just transform the given expressions (the "val" fields).
 */
struct list* xfrm_tgt_list(parse_state_s* pstate, struct list* targetlist)
{
	struct list *p_target = NIL;
	struct list_cell *o_target;

	foreach(o_target, targetlist) {
		ResTarget *res = (ResTarget *) lfirst(o_target);

		/*
		 * Check for "something.*".  Depending on the complexity of the
		 * "something", the star could appear as the last field in ColumnRef,
		 * or as the last indirection item in A_Indirection.
		 */
		if (IS_A(res->val, ColumnRef)) {
			column_ref_n *cref;

			cref = (column_ref_n *) res->val;
			if (IS_A(llast(cref->fields), A_Star)) {
				/* It is something.*, expand into multiple items */
				p_target = list_concat(p_target, ExpandColumnRefStar(pstate, cref, true));
				continue;
			}
		} else if (IS_A(res->val, A_Indirection)) {
			A_Indirection *ind;

			ind = (A_Indirection*) res->val;
			if (IS_A(llast(ind->indirection), A_Star)) {
				/* It is something.*, expand into multiple items */
				p_target = list_concat(p_target, ExpandIndirectionStar(pstate, ind, true));
				continue;
			}
		}

		/*
		 * Not "something.*", so transform as a single expression
		 */
		p_target = lappend(p_target, xfrm_tgt_entry(pstate, res->val, NULL, res->name, false));
	}
Example #17
0
BOOL Node::OptimiseAttributes()
{
	Node* Current= this->FindFirstDepthFirst(); 

	while (Current != NULL)
	{
		if (Current->IsCompound())
		{
			ENSURE(Current->IsKindOf(CC_RUNTIME_CLASS(NodeRenderableInk)), "Compound should be a NodeRenderableInk");
			// It's a compound node, so factor out common attributes
			// We don't want to attribute inside moulds (as yet)
			if ( !((IS_A(Current, NodeMouldGroup)) || (IS_A(Current, NodeMoulder))) )
			{
				if (!((NodeRenderableInk*)Current)->FactorOutCommonChildAttributes())
				{
					return FALSE; 
				}
			}
		}
		Current = Current->FindNextDepthFirst(this); 
	}

	return TRUE; 
}
Example #18
0
BOOL WebAddressAttribute::Blend(BlendAttrParam *pBlendParam)
{
	// Check entry param
	ERROR3IF(pBlendParam == NULL,"NULL entry param");
	if (pBlendParam == NULL) return FALSE;

	WebAddressAttribute* pOtherWebAttr = (WebAddressAttribute*) pBlendParam->GetOtherAttrVal();
	
	// Check that the other line attr val is not NULL, and is a WebAddressAttribute
	ERROR3IF(pOtherWebAttr == NULL,"NULL other attr val");
	ERROR3IF(!IS_A(pOtherWebAttr,WebAddressAttribute),"other attr val not a Web Address attr");
	if (pOtherWebAttr == NULL || !IS_A(pOtherWebAttr,WebAddressAttribute)) return FALSE;

	// If both attributes do not have a URL then we must return FALSE. 
	// Bug fix - Ranbirr 17/03/98
	if(!this->HasURL() && !pOtherWebAttr->HasURL())
		return FALSE;

	// Get a new WebAddressAttribute to hold the blended version, (return FALSE if this fails)
	WebAddressAttribute* pBlendedWebAttr = new WebAddressAttribute;
	if (pBlendedWebAttr == NULL) return FALSE;

	//If the blend ratio is less than 0.5, we use this WebAddressAttribute.
	//Otherwise, we use the other WebAddressAttribute
	double dRatio=pBlendParam->GetBlendRatio();

	if (dRatio<0.5)
		*pBlendedWebAttr=*this;
	else
		*pBlendedWebAttr=*pOtherWebAttr;

	// Store the ptr to the new blended line width attr val
	pBlendParam->SetBlendedAttrVal(pBlendedWebAttr);

	return TRUE;
}
Example #19
0
BOOL BitmapPreviewData::ExportBrowserTypeBitmap(KernelBitmap *pBmp,PathName *pOutFile)
{
	ERROR3IF(pBmp == NULL, "NULL param passed in ExportPreviewBitmap");
	ERROR3IF(pOutFile == NULL, "NULL param passed in ExportPreviewBitmap");
	if ((pBmp == NULL) || (pOutFile == NULL))
		return FALSE;
	
	// create a disk file
	CCDiskFile TempDiskFile(1024, FALSE, TRUE);
	
	// Find the gif preview filter for export (so we don't get an options dialog box)
	Filter *pFilter = Filter::GetFirst();
	while (pFilter != NULL)
	{
		if (IS_A(pFilter,PreviewFilterGIF))
			// This is the filter!
			break;
		
		// Try the next filter
		pFilter = Filter::GetNext(pFilter);
	}
	
	if (pFilter == NULL)
		return FALSE;  // filter not found
	
	// get the preview bitmap filter, so no dialog box is displayed
	PreviewFilterGIF *pGIFFilter = (PreviewFilterGIF *)pFilter;
	
	BOOL ok = TRUE;
	
	// create an operation for the export
	SelOperation *pOp = new SelOperation;
	if (pOp != NULL)
	{
		ok = pGIFFilter->DoExportBitmap(pOp, &TempDiskFile, pOutFile, pBmp);
		
		// delete the created operation
		delete pOp;
	}
	else
		return FALSE;
	
	// close the file 
	if (TempDiskFile.isOpen())
		TempDiskFile.close();
	
	return ok;
}
Example #20
0
/********************************************************************************************

>	BOOL OpNudge::DoesActionListHideNodes(OpNudge * pOp)

	Author:		Simon_Knight (Xara Group Ltd) <*****@*****.**>
	Created:	27/7/00
	Inputs:		ptr to the (nudge) operation
	Outputs:	-
	Returns:	TRUE if the action list for the op does a HideNodesAction
	Purpose:	To disallow the merging of nudge operations if a HideNodesAction was added
				to this nudge op, as this wouldn't then undo correctly.
	Errors:		-
	SeeAlso:	-

********************************************************************************************/
BOOL OpNudge::DoesActionListHideNodes(Operation * pOp)
{
    ActionList * pActions = pOp->GetUndoActionList();
    ListItem* CurrentAction = pActions->GetHead();

    while (CurrentAction != NULL) // For each action in the list
    {
        if (IS_A(CurrentAction, HideNodeAction))
            return TRUE;

        // Get next action
        CurrentAction = pActions->GetNext(CurrentAction);
    }

    return FALSE;
}
Example #21
0
/* --------------------------------
 *		exec_drop_single_tupslot
 *
 *		Release a struct tupslot made with make_single_tupslot.
 *		DON'T use this on a slot that's part of a tuple table list!
 * --------------------------------
 */
void
exec_drop_single_tupslot(struct tupslot *slot)
{
	/* This should match exec_reset_tupslot's processing of one slot */
	ASSERT(IS_A(slot, TupleTableSlot));
	exec_clear_tuple(slot);
	if (slot->tts_tupleDescriptor)
		RELEASE_TUPLE_DESC(slot->tts_tupleDescriptor);

	if (slot->tts_values)
		pfree(slot->tts_values);

	if (slot->tts_isnull)
		pfree(slot->tts_isnull);

	pfree(slot);
}
Example #22
0
void NodeMouldGroup::Transform( TransformBase& Trans )
{
	// there are only two types of transform that dont alter the
	// relationship of the bounding box with respect to its objects.
	// These are the only ones I can allow through otherwise the 
	// mould will shear.
	// Unfortunately Scales don't seem to work too well either
	// so we're gonna have to put up with simple translations only
	// ok I've commented this lot out for now until someone complains
	// about it... Moving the source objects about is a bit of a pain
	// as during undo the final position of the object is weird.
	// (See bug 4630 for instance)

	if (IS_A(&Trans, Trans2DMatrix))
	{
		Trans2DMatrix* t = (Trans2DMatrix*)(&Trans);
		Matrix m = t->GetMatrix();
		
		if (m.IsTranslation())
			NodeRenderableInk::TransformChildren(Trans);			
	}
}
Example #23
0
	/*
	 * Transform the subscript expressions.
	 */
	foreach(idx, indirection) {
		A_Indices *ai = (A_Indices *) lfirst(idx);
		node_n *subexpr;

		ASSERT(IS_A(ai, A_Indices));
		if (isSlice) {
			if (ai->lidx) {
				subexpr = xfrm_expr(pstate, ai->lidx);
				/* If it's not int4 already, try to coerce */
				subexpr = coerce_to_target_type(pstate, subexpr, expr_type(subexpr), INT4OID, -1,
					COERCION_ASSIGNMENT, COERCE_IMPLICIT_CAST, -1);
				if (subexpr == NULL)
					ereport(ERROR, (
					errcode(E_DATATYPE_MISMATCH),
					errmsg("array subscript must have type integer"),
				 	parser_errpos(pstate, expr_location(ai->lidx))));
			} else {
				/* Make a constant 1 */
				subexpr = (node_n *) makeConst(INT4OID, -1, INVALID_OID, sizeof(int32), INT32_TO_D(1),
					false, true);
					/* pass by value */
			}

			lowerIndexpr = lappend(lowerIndexpr, subexpr);
		}

		subexpr = xfrm_expr(pstate, ai->uidx);

		/* If it's not int4 already, try to coerce */
		subexpr = coerce_to_target_type(pstate, subexpr, expr_type(subexpr), INT4OID, -1, 
			COERCION_ASSIGNMENT, COERCE_IMPLICIT_CAST, -1);
		if (subexpr == NULL)
			ereport(ERROR, (
			errcode(E_DATATYPE_MISMATCH),
			errmsg("array subscript must have type integer"),
			parser_errpos(pstate, expr_location(ai->uidx))));

		upperIndexpr = lappend(upperIndexpr, subexpr);
	}
Example #24
0
BOOL SGLineDragTarget::ProcessEvent(DragEventType Event, DragInformation *pDragInfo,
										OilCoord *pMousePos, KeyPress* pKeyPress)
{
	if (!pDragInfo->IsKindOf(CC_RUNTIME_CLASS(GalleryLineDragInfo)))
		return(FALSE);

	SGDisplayNode *DraggedNode = NULL;
	BOOL IsSimpleBitmapDrag = TRUE;

	if (IS_A(pDragInfo, GalleryLineDragInfo))
	{
		DraggedNode = ((GalleryLineDragInfo *)pDragInfo)->GetDraggedLineAttr();
	}

	if (DraggedNode != NULL)
	{
		switch(Event)
		{
			case DRAGEVENT_COMPLETED:
				HandleDragCompleted((SuperGallery *) TargetDialog,
									DraggedNode, pMousePos, IsSimpleBitmapDrag);
				return(TRUE);


			case DRAGEVENT_MOUSESTOPPED:
			case DRAGEVENT_MOUSEMOVED:
			case DRAGEVENT_MOUSEIDLE:
				// Call a subroutine to work out and set our current cursor shape
				return(DetermineCursorShape((SuperGallery *) TargetDialog,
											DraggedNode, pMousePos));
			default:
				break;
		}
	}

	// Otherwise, we aren't interested in the event, so we don't claim it
	return(FALSE);
}
Example #25
0
/* --------------------------------
 *		exec_reset_tupslot
 *
 *		This releases any resources (buffer pins, tupdesc refcounts)
 *		held by the tuple table, and optionally releases the memory
 *		occupied by the tuple table data structure.
 *		It is expected that this routine be called by EndPlan().
 * --------------------------------
 */
void
exec_reset_tupslot(
	struct list *tupleTable,	/* tuple table */
	bool shouldFree)		/* true if we should free memory */
{
	struct list_cell* lc;

	foreach(lc, tupleTable) {
		struct tupslot *slot = (struct tupslot *) lfirst(lc);

		/* Sanity checks */
		ASSERT(IS_A(slot, TupleTableSlot));

		/* Always release resources and reset the slot to empty */
		exec_clear_tuple(slot);
		if (slot->tts_tupleDescriptor) {
			RELEASE_TUPLE_DESC(slot->tts_tupleDescriptor);
			slot->tts_tupleDescriptor = NULL;
		}

		/* If shouldFree, release memory occupied by the slot itself */
		if (shouldFree) {
			if (slot->tts_values)
				pfree(slot->tts_values);

			if (slot->tts_isnull)
				pfree(slot->tts_isnull);

			pfree(slot);
		}
	}

	/* If shouldFree, release the list structure */
	if (shouldFree)
		list_free(tupleTable);
}
Example #26
0
void SelectionState::Restore(BOOL RestoreSelBlobs, BOOL RemoveBlobs) 	     	
{
	DeselectAll(RemoveBlobs); // Deselect all selections

	#ifdef _DEBUG
	UINT32 NumRestored = 0; 	 
	#endif

	 
    // ----------------------------------------------------------------------------------
	// Restore all nodes in the SelNdList
	
	UINT32 i; 

	for (i=0; i < NumNd; i++)
	{
		// Only NodeRenderableInk nodes should be selected
		ENSURE(SelNdList[i]->IsKindOf(CC_RUNTIME_CLASS(NodeRenderableInk)), 
			   "Node to be selected is not a NodeRenderableInk");
			    
		// Ensure node to be selected is not already selected                                
		//ENSURE(!(SelNdList[i]->IsSelected()), "Invalid selected node"); 

		// Select the node 
		SelNdList[i]->SetSelected(TRUE); 
		#ifdef _DEBUG
		NumRestored++; 	 
		#endif	
	}
	   
	// ----------------------------------------------------------------------------------
	// Restore all nodes in the SelNdRngList	  

	Node* Current; 
	   
	for (i=0; i < NumNdRng; i++) 
	{  
		// A SelNdRng shold represent at least two contiguous selected nodes
		ENSURE(SelNdRngList[i].NumSelected >= 2, "Number of nodes in SelNdRng < 2");    

		UINT32 NumNodesSelected = 0; 
		Current = SelNdRngList[i].FirstNode; // First node in range 
		
		// Select the next SelNdRng->NumSelected nodes
		do  
		{   
			ENSURE((Current != NULL), "Node in a SelNdRng is NULL, Has tree changed ?" );  
			// Only NodeRenderableInk nodes should be selected   
			
			ENSURE(Current->IsKindOf(CC_RUNTIME_CLASS(NodeRenderableInk)), 
				   "Selected node is not a NodeRenderableInk"); 

			// [Phil, 11/10/2005] The Current node may be a Caret node
			// because Carets are included in the selection, even when
			// a sub-selection of characters is present, so that attributes
			// optimise correctly within the text story
			// However, the Record function treats them as single selected
			// nodes, not part of a contiguous run of selected nodes to
			// allow for them moving around...
			// So we should ignore carets here
			if (!IS_A(Current, CaretNode))
			{
				// Ensure SelNode not already selected  
				ENSURE(!(Current->IsSelected()), "Invalid selected node");

				Current->SetSelected(TRUE); 	// Set the nodes selected flag
				NumNodesSelected++;
#ifdef _DEBUG
				NumRestored++; 	 
#endif	
			}
			Current = Current->FindNextNonHidden();  
		}
		while (NumNodesSelected != SelNdRngList[i].NumSelected); 
	}

#if !defined(EXCLUDE_FROM_RALPH)
	// Only restore the blobs if any selections have been restored
	if (((NumNdRng != 0) || (NumNd !=0)) && RestoreSelBlobs)	// We need to restore the selection blobs 
	{
		// Find the current selections 
		SelRange* pSel; 
		pSel = GetApplication()->FindSelection();
 
		Current = pSel->FindFirst(); // The first selected node 

		Spread *pSpread = NULL;
		if (Current != NULL)
			pSpread = Current->FindParentSpread();

//		ENSURE(pSpread != NULL, "First selected node does not have a parent spread");
		// It's a legal state to not find a selected node - the layers may all be locked.
		if (pSpread == NULL)
		{
			AttrFillGeometry::LastRenderedMesh = NULL;
			return;
		}

		// Go get the blob manager
		BlobManager* BlobMgr = GetApplication()->GetBlobManager();
		ENSURE( BlobMgr!=NULL, "Blob Manger was not there when we needed him!");
		if (BlobMgr==NULL)
			return;

	    Current = pSel->FindFirst();   // The first selected node 

		while (Current != NULL)
		{
			ENSURE(Current->IsSelected(), "Node not selected"); 
			ENSURE(Current->IsKindOf(CC_RUNTIME_CLASS(NodeRenderable)), 
					"Selected Node not a NodeRenderable"); 

			// Tell the node to add selection blobs
			BlobMgr->RenderMyBlobsOn(NULL, pSpread, (NodeRenderable*)Current);
			
			Current = pSel->FindNext(Current); 	// Get next selected node 
		}

		Tool* pTool = Tool::GetCurrent();
			
		// Get the tool to remove all its blobs before we deselect the nodes.
		// Only do this if the current tool dosent update itself on sel changed messages
		if (pSpread!=NULL && pTool!=NULL && !pTool->AreToolBlobsRenderedOnSelection())
			pTool->RenderToolBlobs(pSpread,NULL);

		AttrFillGeometry::LastRenderedMesh = NULL;
	}
#endif
	
	#ifdef _DEBUG
	//if (IsUserName("Simon"))
	//	TRACE( _T(" Num Restored = %lu\n"), NumRestored);  
	#endif	
	 
}  
Example #27
0
BOOL SelectionState::Record() 	     	 
{   
	// Make sure that Record has not been called before     
	ENSURE(((SelNdList == NULL) && (SelNdRngList == NULL)), "SelectionState::Record called twice"); 
	
	// Find the current selections 
	SelRange* pSel; 
	pSel = GetApplication()->FindSelection();

	Node* StartRange;

	UINT32 SelNdRngListIndex = 0; 
	UINT32 SelNdListIndex = 0; 

	UINT32 NumSelectedNodes = pSel->Count(); 

	if (NumSelectedNodes != 0)
	{

		// At this point we don't know exactly how much memory to allocate for the SelNdRngList
		// and SelNdList. So we allocate two temporary arrays which are more than big enough to
		// store the selection state. When we have completed recording the selection state into 
		// these temporary arrays, we know how big the SelNdRngList and SelNdList should
		// be. so we can allocate memory for these and then copy the data from the temporary 
		// arrays into them.  

		SelNdRng* SelNdRngListTmp = new SelNdRng[NumSelectedNodes];  
	

		if (SelNdRngListTmp == NULL)
		{
			return FALSE; 
		}
	 
		Node** SelNdListTmp = new Node*[NumSelectedNodes]; 		  

		if (SelNdListTmp == NULL)
		{
			delete[] SelNdRngListTmp; 	// Tidy up 
			return FALSE; 
		}

		// Get the first selected node in the tree
		Node * Current = pSel->FindFirst(); 
		Node* Last; 

		BYTE NumAdjacentSel; 			  // Number of contiguous selected nodes   
	
		#ifdef _DEBUG   
		UINT32 NumSel = 0; 
		#endif
	
		// always use the selection object to determine next node to store... this fixes bug #10775 whereby
		// selected Bevel,Contour & Shadow objects were not being restored on Undo
		while (Current != NULL)
		{   
			// At this point Current will always point to the next selected node which needs 
			// recording.      
			// Only NodeRenderableInk nodes should be selected
			ENSURE(Current->IsKindOf(CC_RUNTIME_CLASS(NodeRenderableInk)), 
					"A non NodeRenderableInk node is selected"); 
		
			NumAdjacentSel = 1;    
		
			#ifdef _DEBUG   
			NumSel++;  
			#endif
		        
			StartRange = Current; 
			Last = Current;  
			Current = pSel->FindNext(Current); //next from selection
		
			// if we have at least two contiguous nodes, store as a range
			if (Current && AreContiguous(Last, Current) && !IS_A(Last, CaretNode) && !IS_A(Current, CaretNode))
			{
				SelNdRngListTmp[SelNdRngListIndex].FirstNode = StartRange;   
				do
				{
					if (IS_A(Current, CaretNode))
					{
						// Give Caret it's own selection record but don't break the contiguous range
						SelNdListTmp[SelNdListIndex] = Current;
						SelNdListIndex++; 
						#ifdef _DEBUG   
						NumSel++;  
						#endif
					}
					else
					{
						NumAdjacentSel++; 
						#ifdef _DEBUG   
						NumSel++;  
						#endif
					}
					Last = Current; 
					Current = pSel->FindNext(Current);
	                if (Current == NULL)
	                   	break; 
				} while ((AreContiguous(Last,Current)) && (NumAdjacentSel < 255));      
				// Either there are no more contiguous selected nodes or 
				// we have hit the maximum number of selected nodes that a SelNdRng can 
				// represent.  
				SelNdRngListTmp[SelNdRngListIndex].NumSelected = NumAdjacentSel; 
				SelNdRngListIndex++;
			}
			else  // Store node in the SelNdLst 
			{
				ERROR3IF(StartRange==NULL, "Trying to add NULL pointer to SelNdList\n");
				SelNdListTmp[SelNdListIndex] = StartRange; 
				SelNdListIndex++; 
			}    
		}       
		ERROR3IF(NumSel!=NumSelectedNodes,"Incorrect selection state stored!");
	
		NumNd = SelNdListIndex; 
		NumNdRng = SelNdRngListIndex; 

		if (SelNdRngListIndex != 0) 
		{
			// We have created at least one SelNdRange
	 		SelNdRngList = new SelNdRng[NumNdRng];  
			if (SelNdRngList == NULL)			// Out of memory 
			{
				// Delete the two temporary lists
				delete [] SelNdRngListTmp; 
				delete [] SelNdListTmp; 
				return FALSE; 
			}
		
			// Copy the SelNdRngListTmp to the SelNdRngList  
			memcpy(SelNdRngList, SelNdRngListTmp, sizeof(SelNdRng)*NumNdRng); 
		}

		delete[] SelNdRngListTmp; // No longer required
	
	
		if (SelNdListIndex != 0)
		{
			SelNdList = new Node*[NumNd]; 
			if (SelNdList == NULL)				 // Out of memory 
			{
				delete [] SelNdListTmp; 
				if (SelNdRngList != NULL)		 // We allocated the SelNdRng list
				{
					delete [] SelNdRngList; 
				}
				return FALSE; 
			}
			// copy the SelNdListTmp to the SelNdList 
			memcpy(SelNdList, SelNdListTmp, sizeof(Node*)*NumNd); 
		} 

		delete[] SelNdListTmp; // No longer required
	 
		#ifdef _DEBUG   
		//if (IsUserName("Simon"))
			//TRACE( _T("Number of nodes selected = %lu\n"), NumSel); 
		#endif
	}
	return (TRUE);  
}     
Example #28
0
datum_t
connectby_text(PG_FUNC_ARGS)
{
	char	   *relname = text_to_cstring(ARG_TEXT_PP(0));
	char	   *key_fld = text_to_cstring(ARG_TEXT_PP(1));
	char	   *parent_key_fld = text_to_cstring(ARG_TEXT_PP(2));
	char	   *start_with = text_to_cstring(ARG_TEXT_PP(3));
	int			max_depth = ARG_INT32(4);
	char	   *branch_delim = NULL;
	bool		show_branch = false;
	bool		show_serial = false;
	return_set_info_n *rsinfo = (return_set_info_n *) fcinfo->resultinfo;
	struct tuple *	tupdesc;
	AttInMetadata *attinmeta;
	struct mctx * per_query_ctx;
	struct mctx * oldcontext;

	/* check to see if caller supports us returning a tuplestore */
	if (rsinfo == NULL || !IS_A(rsinfo, ReturnSetInfo))
		ereport(ERROR,
				(errcode(E_FEATURE_NOT_SUPPORTED),
				 errmsg("set-valued function called in context that cannot accept a set")));
	if (!(rsinfo->allowedModes & SFRM_Materialize) ||
		rsinfo->expectedDesc == NULL)
		ereport(ERROR,
				(errcode(E_FEATURE_NOT_SUPPORTED),
				 errmsg("materialize mode required, but it is not " \
						"allowed in this context")));

	if (fcinfo->nargs == 6)
	{
		branch_delim = text_to_cstring(ARG_TEXT_PP(5));
		show_branch = true;
	}
	else
		/* default is no show, tilde for the delimiter */
		branch_delim = pstrdup("~");

	per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
	oldcontext = mctx_switch(per_query_ctx);

	/* get the requested return tuple description */
	tupdesc = tupdesc_copy(rsinfo->expectedDesc);

	/* does it meet our needs */
	validateConnectbyTupleDesc(tupdesc, show_branch, show_serial);

	/* OK, use it then */
	attinmeta = TupleDescGetAttInMetadata(tupdesc);

	/* OK, go to work */
	rsinfo->returnMode = SFRM_Materialize;
	rsinfo->setResult = connectby(relname,
								  key_fld,
								  parent_key_fld,
								  NULL,
								  branch_delim,
								  start_with,
								  max_depth,
								  show_branch,
								  show_serial,
								  per_query_ctx,
							  rsinfo->allowedModes & SFRM_Materialize_Random,
								  attinmeta);
	rsinfo->setDesc = tupdesc;

	mctx_switch(oldcontext);

	/*
	 * SFRM_Materialize mode expects us to return a NULL Datum. The actual
	 * tuples are in our tuplestore and passed back through rsinfo->setResult.
	 * rsinfo->setDesc is set to the tuple description that we actually used
	 * to build our tuples with, so the caller can verify we did what it was
	 * expecting.
	 */
	return (datum_t) 0;
}
Example #29
0
/*
 *	Parse a function call
 *
 *	For historical reasons, Postgres tries to treat the notations tab.col
 *	and col(tab) as equivalent: if a single-argument function call has an
 *	argument of complex type and the (unqualified) function name matches
 *	any attribute of the type, we take it as a column projection.  Conversely
 *	a function of a single complex-type argument can be written like a
 *	column reference, allowing functions to act like computed columns.
 *
 *	Hence, both cases come through here.  The is_column parameter tells us
 *	which syntactic construct is actually being dealt with, but this is
 *	intended to be used only to deliver an appropriate error message,
 *	not to affect the semantics.  When is_column is true, we should have
 *	a single argument (the putative table), unqualified function name
 *	equal to the column name, and no aggregate or variadic decoration.
 *	Also, when is_column is true, we return NULL on failure rather than
 *	reporting a no-such-function error.
 *
 *	The argument expressions (in fargs) must have been transformed already.
 *	But the agg_order expressions, if any, have not been.
 */
node_n*
ParseFuncOrColumn(
	parse_state_s* pstate,
	struct list *funcname,
	struct list *fargs,
	struct list *agg_order,
	bool agg_star,
	bool agg_distinct,
	bool func_variadic,
	WindowDef* over,
	bool is_column,
	int location)
{
	oid_t rettype;
	oid_t funcid;
	struct list_cell *l;
	struct list_cell *nextl;
	node_n *first_arg = NULL;
	int nargs;
	int nargsplusdefs;
	oid_t actual_arg_types[FUNC_MAX_ARGS];
	oid_t *declared_arg_types;
	struct list *argnames;
	struct list *argdefaults;
	node_n *retval;
	bool retset;
	int nvargs;
	func_code_e fdresult;

	/*
	 * Most of the rest of the parser just assumes that functions do not have
	 * more than FUNC_MAX_ARGS parameters.  We have to test here to protect
	 * against array overruns, etc.  Of course, this may not be a function,
	 * but the test doesn't hurt.
	 */
	if (list_length(fargs) > FUNC_MAX_ARGS) {
		ereport(ERROR, (
		errcode(E_TOO_MANY_ARGUMENTS),
		errmsg_plural("cannot pass more than %d argument to a function",
			"cannot pass more than %d arguments to a function",
			FUNC_MAX_ARGS, FUNC_MAX_ARGS),
		parser_errpos(pstate, location)));
	}

	/*
	 * Extract arg type info in preparation for function lookup.
	 *
	 * If any arguments are param_xp markers of type VOID, we discard them from
	 * the parameter list.  This is a hack to allow the JDBC driver to not
	 * have to distinguish "input" and "output" parameter symbols while
	 * parsing function-call constructs.  We can't use foreach() because we
	 * may modify the list ...
	 */
	nargs = 0;
	for (l = list_head(fargs); l != NULL; l = nextl) {
		node_n *arg;
		oid_t argtype;

		arg = lfirst(l);
		argtype = expr_type(arg);
		nextl = lnext(l);
		if (argtype == VOIDOID 
			&& IS_A(arg, Param)
			&& !is_column) {
			fargs = list_delete_ptr(fargs, arg);
			continue;
		}

		actual_arg_types[nargs++] = argtype;
	}

	/*
	 * Check for named arguments; if there are any, build a list of names.
	 *
	 * We allow mixed notation (some named and some not), but only with all
	 * the named parameters after all the unnamed ones.  So the name list
	 * corresponds to the last N actual parameters and we don't need any extra
	 * bookkeeping to match things up.
	 */
	argnames = NIL;
	foreach(l, fargs) {
		node_n *arg;

		arg = lfirst(l);
		if (IS_A(arg, NamedArgExpr)) {
			named_arg_xp *na = (named_arg_xp *) arg;
			struct list_cell *lc;

			/* Reject duplicate arg names */
			foreach(lc, argnames) {
				if (strcmp(na->name, (char *)lfirst(lc)) == 0)
					ereport(ERROR, (
					errcode(E_SYNTAX_ERROR),
					errmsg("argument name \"%s\" used more than once", na->name),
					parser_errpos(pstate, na->location)));
			}

			argnames = lappend(argnames, na->name);
		} else {
			if (argnames != NIL)
Example #30
0
BOOL CDRFilter::AddArrowheadsToPath(DWORD StartArrow, DWORD EndArrow, DocColour *Col, INT32 LineWidth,
			LineCapType Cap, JointType Join)
{
	if(!IS_A(pMadeNode, NodePath))
		return TRUE;			// don't apply arrowheads to non path things

	NodePath *pPath = (NodePath *)pMadeNode;

	// check that the path actaully needs some arrow heads
	if(!DoesPathNeedArrowheads(pPath) || (StartArrow == 0 && EndArrow == 0))
		return TRUE;
	
	// get pointers to nodes of the arrow heads to copy
	NodeRenderableBounded *StartA = 0;
	NodeRenderableBounded *EndA = 0;
	INT32 StartDistance;
	INT32 EndDistance;
	if(StartArrow != 0)
	{
		BOOL NotPresent;
		StartA = Arrowheads.GetConvertedNode(StartArrow, &StartDistance, &NotPresent);

		if(StartA == 0 && NotPresent == FALSE)
			return FALSE;	// error occurred
	}
	if(EndArrow != 0)
	{
		BOOL NotPresent;
		EndA = Arrowheads.GetConvertedNode(EndArrow, &EndDistance, &NotPresent);

		if(EndA == 0 && NotPresent == FALSE)
			return FALSE;	// error occurred
	}

	// check that enough arrowheads were found to do something with
	if(StartA == 0 && EndA == 0)
		return TRUE;

	// get rid of any dash patterns
	DashRec NoDash;
	NoDash = SD_SOLID;
	SetDashPattern(NoDash);

	// get some info about the path
	DocCoord *Coords = pPath->InkPath.GetCoordArray();
	PathVerb *Verbs = pPath->InkPath.GetVerbArray();
	INT32 NCoords = pPath->InkPath.GetNumCoords();
	INT32 SubPathStart = -1; 		// the number of the coord the sub path starts on
	INT32 SubPathEnd = -1;			// the number of the coord the sub path ends on

	// set up a pointers for the nodes we're about to create - it points to the made
	// at first, but shuffles on as nodes are copied. You see, we have to copy nodes which
	// are attached to something
	NodeRenderableBounded *ThisNode = pPath;

	// work out the trim distances
	double StartTrimDistance;
	double EndTrimDistance;
	if(StartA != 0)
		StartTrimDistance = ((double)LineWidth / (double)cdrfARROWHEAD_LINEWIDTH) * (double)StartDistance;
	if(EndA != 0)
		EndTrimDistance = ((double)LineWidth / (double)cdrfARROWHEAD_LINEWIDTH) * (double)EndDistance;

	// run through all the coords
	INT32 c;
	for(c = 0; c < NCoords; c++)
	{
		UINT32 Verb = Verbs[c] & ~PT_CLOSEFIGURE;
		BOOL CloseHere = ((Verbs[c] & PT_CLOSEFIGURE) != 0)?TRUE:FALSE;
		
		// if the verb is a close figure, this sub path is close, so we can't add arrowheads
		// to it. Invalidate the path start to avoid getting any arrowheads
		if(CloseHere)
			SubPathStart = -1;
		
		// if we get a new moveto and the sub path start is valid then we have a applying arrowheads
		// situation
		if((Verb == PT_MOVETO || ((c == (NCoords - 1)) && !CloseHere)) && c != 0)
		{
			// set the sub path end
			if(Verb == PT_MOVETO)
			{
				// if it's a moveto, then the sub path end was at the previous coord
				SubPathEnd = c - 1;
			} else {
				// if it wasn't, then it's this one (end of path)
				SubPathEnd = c;
			}
		}

		// have we got a valid start path marker?

		if(SubPathStart != -1 && SubPathEnd != -1)
		{
			// OK, now we need to trim the sub path
			if(StartA != 0)
			{
				// trim the start of the path if the next element is a line
				if((Verbs[SubPathStart + 1] & ~PT_CLOSEFIGURE) == PT_LINETO)
				{
					double dx, dy;
					dx = Coords[SubPathStart + 1].x - Coords[SubPathStart].x;
					dy = Coords[SubPathStart + 1].y - Coords[SubPathStart].y;
					double len = sqrt((dx * dx) + (dy * dy));
					// if the resulting path line leaves enough room, trim it
					if((len - StartTrimDistance) >= 32.0)
					{
						double factor = StartTrimDistance / len;
						dx *= factor;
						Coords[SubPathStart].x += (INT32)dx;
						dy *= factor;
						Coords[SubPathStart].y += (INT32)dy;
					}
				}
				
				// now pop an arrowhead on the resulting point

				// first, make a copy of our node
				if(!StartA->CopyNode(ThisNode, NEXT))
					return FALSE;	// error!

				ThisNode = (NodeRenderableBounded *)ThisNode->FindNext();
				ERROR3IF(ThisNode == 0, "CopyNode returned a zero path but didn't complain");
				ERROR3IF(ThisNode->IsKindOf(CC_RUNTIME_CLASS(NodeRenderableBounded)) == FALSE, "copied thing isn't a renderable bounded node");

				// transform it
				if(!TransformArrowhead(ThisNode, &Coords[SubPathStart], &Coords[SubPathStart + 1],
						TRUE, LineWidth, StartDistance))
					return FALSE;

				// and apply attributes to it
				if(!AddAttributesToArrowheadNode(ThisNode, Col, LineWidth, Cap, Join))
					return FALSE;		
			}

			// and the same for the end path
			if(EndA != 0)
			{
				if((Verbs[SubPathEnd] & ~PT_CLOSEFIGURE) == PT_LINETO)
				{
					double dx, dy;
					dx = Coords[SubPathEnd].x - Coords[SubPathEnd - 1].x;
					dy = Coords[SubPathEnd].y - Coords[SubPathEnd - 1].y;
					double len = sqrt((dx * dx) + (dy * dy));
					// if the resulting path line leaves enough room, trim it
					if((len - EndTrimDistance) >= 32.0)
					{
						double factor = EndTrimDistance / len;
						dx *= factor;
						Coords[SubPathEnd].x += (INT32)dx;
						dy *= factor;
						Coords[SubPathEnd].y += (INT32)dy;
					}
				}
				
				// now pop an arrowhead on the resulting point

				// first, make a copy of our node
				if(!EndA->CopyNode(ThisNode, NEXT))
					return FALSE;	// error!

				ThisNode = (NodeRenderableBounded *)ThisNode->FindNext();
				ERROR3IF(ThisNode == 0, "CopyNode returned a zero path but didn't complain");
				ERROR3IF(ThisNode->IsKindOf(CC_RUNTIME_CLASS(NodeRenderableBounded)) == FALSE, "copied thing isn't a renderable bounded node");

				// transform it
				if(!TransformArrowhead(ThisNode, &Coords[SubPathEnd], &Coords[SubPathEnd - 1],
						FALSE, LineWidth, EndDistance))
					return FALSE;

				// and apply attributes to it
				if(!AddAttributesToArrowheadNode(ThisNode, Col, LineWidth, Cap, Join))
					return FALSE;				
			}
		
		}
			
		// set the new sub path start marker and invalidate the end point marker
		if(Verb == PT_MOVETO)
		{
			SubPathStart = c;
			SubPathEnd = -1;
		}
	}


	// ensure that the bounding rectangle is updated
	pPath->InvalidateBoundingRect();

	// get a new group node
	NodeGroup *pGroup = new NodeGroup;
	if(pGroup == 0)
		return FALSE;
	
	// attach everything to it
	pMadeNode->InsertChainSimple(pGroup, FIRSTCHILD);

	// and make the made node the group
	pMadeNode = pGroup;
	
	return TRUE;
}