⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 sequence.cpp

📁 支持Unicode及Uniscribe的多语言输入的文本编辑器源码。
💻 CPP
📖 第 1 页 / 共 2 页
字号:
{
	span *		sptr;
	size_w		spanindex;
	size_t		modbuf_offset;
	span_range	newspans;
	size_w		insoffset;

	if(index > sequence_length)
		return false;

	// find the span that the insertion starts at
	if((sptr = spanfromindex(index, &spanindex)) == 0)
		return false;

	// ensure there is room in the modify buffer...
	// allocate a new buffer if necessary and then invalidate span cache
	// to prevent a span using two buffers of data
	if(!import_buffer(buf, length, &modbuf_offset))
		return false;

	debug("Inserting: idx=%d len=%d %.*s\n", index, length, length, buf);

	clearstack(redostack);
	insoffset = index - spanindex;

	// special-case #1: inserting at the end of a prior insertion, at a span-boundary
	if(insoffset == 0 && can_optimize(act, index))
	{
		// simply extend the last span's length
		span_range *event = undostack.back();
		sptr->prev->length	+= length;
		event->length		+= length;
	}
	// general-case #1: inserting at a span boundary?
	else if(insoffset == 0)
	{
		//
		// Create a new undo event; because we are inserting at a span
		// boundary there are no spans to replace, so use a "span boundary"
		//
		span_range *oldspans = initundo(index, length, act);
		oldspans->spanboundary(sptr->prev, sptr);
		
		// allocate new span in the modify buffer
		newspans.append(new span(
			modbuf_offset, 
			length, 
			modifybuffer_id)
			);
		
		// link the span into the sequence
		swap_spanrange(oldspans, &newspans);
	}
	// general-case #2: inserting in the middle of a span
	else
	{
		//
		//	Create a new undo event and add the span
		//  that we will be "splitting" in half
		//
		span_range *oldspans = initundo(index, length, act);
		oldspans->append(sptr);

		//	span for the existing data before the insertion
		newspans.append(new span(
							sptr->offset, 
							insoffset, 
							sptr->buffer)
						);

		// make a span for the inserted data
		newspans.append(new span(
							modbuf_offset, 
							length, 
							modifybuffer_id)
						);

		// span for the existing data after the insertion
		newspans.append(new span(
							sptr->offset + insoffset, 
							sptr->length - insoffset, 
							sptr->buffer)
						);

		swap_spanrange(oldspans, &newspans);
	}

	sequence_length += length;

	return true;
}

//
//	sequence::insert
//
//	Insert a buffer into the sequence at the specified position.
//	Consecutive insertions are optimized into a single event
//
bool sequence::insert (size_w index, const seqchar *buf, size_w length)
{
	if(insert_worker(index, buf, length, action_insert))
	{
		record_action(action_insert, index + length);
		return true;
	}
	else
	{
		return false;
	}
}

//
//	sequence::insert
//
//	Insert specified character-value into sequence
//
bool sequence::insert (size_w index, const seqchar val)
{
	return insert(index, &val, 1);
}

//
//	sequence::deletefromsequence
//
//	Remove + delete the specified *span* from the sequence
//
void sequence::deletefromsequence(span **psptr)
{
	span *sptr = *psptr;
	sptr->prev->next = sptr->next;
	sptr->next->prev = sptr->prev;

	memset(sptr, 0, sizeof(span));
	delete sptr;
	*psptr = 0;
}

//
//	sequence::erase_worker
//
bool sequence::erase_worker (size_w index, size_w length, action act)
{
	span		*sptr;
	span_range	 oldspans;
	span_range	 newspans;
	span_range	*event;
	size_w		 spanindex;
	size_w		 remoffset;
	size_w		 removelen;
	bool		 append_spanrange;	

	debug("Erasing: idx=%d len=%d\n", index, length);

	// make sure we stay within the range of the sequence
	if(length == 0 || length > sequence_length || index > sequence_length - length)
		return false;

	// find the span that the deletion starts at
	if((sptr = spanfromindex(index, &spanindex)) == 0)
		return false;

	// work out the offset relative to the start of the *span*
	remoffset = index - spanindex;
	removelen = length;

	//
	//	can we optimize?
	//
	//	special-case 1: 'forward-delete'
	//	erase+replace operations will pass through here
	//
	if(index == spanindex && can_optimize(act, index))
	{
		event = stackback(undostack, act == action_replace ? 1 : 0);
		event->length	+= length;
		append_spanrange = true;

		if(frag2 != 0)
		{
			if(length < frag2->length)
			{
				frag2->length	-= length;
				frag2->offset	+= length;
				sequence_length -= length;
				return true;
			}
			else
			{
				if(act == action_replace)
					stackback(undostack, 0)->last = frag2->next;

				removelen	-= sptr->length;
				sptr = sptr->next;
				deletefromsequence(&frag2);
			}
		}
	}
	//
	//	special-case 2: 'backward-delete'
	//	only erase operations can pass through here
	//
	else if(index + length == spanindex + sptr->length && can_optimize(action_erase, index+length))
	{
		event = undostack.back();
		event->length	+= length;
		event->index	-= length;
		append_spanrange = false;

		if(frag1 != 0)
		{
			if(length < frag1->length)
			{
				frag1->length	-= length;
				frag1->offset	+= 0;
				sequence_length -= length;
				return true;
			}
			else
			{
				removelen -= frag1->length;
				deletefromsequence(&frag1);
			}
		}
	}
	else
	{
		append_spanrange = true;
		frag1 = frag2 = 0;

		if((event = initundo(index, length, act)) == 0)
			return false;
	}

	//
	//	general-case 2+3
	//
	clearstack(redostack);

	// does the deletion *start* mid-way through a span?
	if(remoffset != 0)
	{
		// split the span - keep the first "half"
		newspans.append(new span(sptr->offset, remoffset, sptr->buffer));
		frag1 = newspans.first;
		
		// have we split a single span into two?
		// i.e. the deletion is completely within a single span
		if(remoffset + removelen < sptr->length)
		{
			// make a second span for the second half of the split
			newspans.append(new span(
							sptr->offset + remoffset + removelen, 
							sptr->length - remoffset - removelen, 
							sptr->buffer)
							);

			frag2 = newspans.last;
		}

		removelen -= min(removelen, (sptr->length - remoffset));

		// archive the span we are going to replace
		oldspans.append(sptr);
		sptr = sptr->next;	
	}

	// we are now on a proper span boundary, so remove
	// any further spans that the erase-range encompasses
	while(removelen > 0 && sptr != tail)
	{
		// will the entire span be removed?
		if(removelen < sptr->length)
		{
			// split the span, keeping the last "half"
			newspans.append(new span(
						sptr->offset + removelen, 
						sptr->length - removelen, 
						sptr->buffer)
						);

			frag2 = newspans.last;
		}

		removelen -= min(removelen, sptr->length);

		// archive the span we are replacing
		oldspans.append(sptr);
		sptr = sptr->next;
	}

	// for replace operations, update the undo-event for the
	// insertion so that it knows about the newly removed spans
	if(act == action_replace && !oldspans.boundary)
		stackback(undostack, 0)->last = oldspans.last->next;

	swap_spanrange(&oldspans, &newspans);
	sequence_length -= length;

	if(append_spanrange)
		event->append(&oldspans);
	else
		event->prepend(&oldspans);

	return true;
}

//
//	sequence::erase 
//
//  "removes" the specified range of data from the sequence. 
//
bool sequence::erase (size_w index, size_w len)
{
	if(erase_worker(index, len, action_erase))
	{
		record_action(action_erase, index);
		return true;
	}
	else
	{
		return false;
	}
}

//
//	sequence::erase
//
//	remove single character from sequence
//
bool sequence::erase (size_w index)
{
	return erase(index, 1);
}

//
//	sequence::replace
//
//	A 'replace' (or 'overwrite') is a combination of erase+inserting
//  (first we erase a section of the sequence, then insert a new block
//  in it's place). 
//
//	Doing this as a distinct operation (erase+insert at the 
//  same time) is really complicated, so I just make use of the existing 
//  sequence::erase and sequence::insert and combine them into action. We
//	need to play with the undo stack to combine them in a 'true' sense.
//
bool sequence::replace(size_w index, const seqchar *buf, size_w length, size_w erase_length)
{
	size_t remlen = 0;

	debug("Replacing: idx=%d len=%d %.*s\n", index, length, length, buf);

	// make sure operation is within allowed range
	if(index > sequence_length || MAX_SEQUENCE_LENGTH - index < length)
		return false;

	// for a "replace" which will overrun the sequence, make sure we 
	// only delete up to the end of the sequence
	remlen = min(sequence_length - index, erase_length);

	// combine the erase+insert actions together
	group();

	// first of all remove the range
	if(remlen > 0 && index < sequence_length && !erase_worker(index, remlen, action_replace))
	{
		ungroup();
		return false;
	}
	
	// then insert the data
	if(insert_worker(index, buf, length, action_replace))
	{
		ungroup();
		record_action(action_replace, index + length);
		return true;
	}
	else
	{
		// failed...cleanup what we have done so far
		ungroup();
		record_action(action_invalid, 0);

		span_range *range = undostack.back();
		undostack.pop_back();
		restore_spanrange(range, true);
		delete range;

		return false;
	}
}

//
//	sequence::replace
//
//	overwrite with the specified buffer
//
bool sequence::replace (size_w index, const seqchar *buf, size_w length)
{
	return replace(index, buf, length, length);
}

//
//	sequence::replace
//
//	overwrite with a single character-value
//
bool sequence::replace (size_w index, const seqchar val)
{
	return replace(index, &val, 1);
}

//
//	sequence::append
//
//	very simple wrapper around sequence::insert, just inserts at
//  the end of the sequence
//
bool sequence::append (const seqchar *buf, size_w length)
{
	return insert(size(), buf, length);
}

//
//	sequence::append
//
//	append a single character to the sequence
//
bool sequence::append (const seqchar val)
{
	return append(&val, 1);
}

//
//	sequence::clear
//
//	empty the entire sequence, clear undo/redo history etc
//
bool sequence::clear ()
{
	span *sptr, *tmp;
	
	// delete all spans in the sequence
	for(sptr = head->next; sptr != tail; sptr = tmp)
	{
		tmp = sptr->next;
		delete sptr;
	}

	// re-link the head+tail
	head->next = tail;
	tail->prev = head;

	// delete everything in the undo/redo stacks
	clearstack(undostack);
	clearstack(redostack);

	// delete all memory-buffers
	for(size_t i = 0; i < buffer_list.size(); i++)
	{
		delete[] buffer_list[i]->buffer;
		delete   buffer_list[i];
	}

	buffer_list.clear();
	sequence_length = 0;
	return true;
}

//
//	sequence::render
//
//	render the specified range of data (index, len) and store in 'dest'
//
//	Returns number of chars copied into destination
//
size_w sequence::render(size_w index, seqchar *dest, size_w length) const
{
	size_w spanoffset = 0;
	size_w total = 0;
	span  *sptr;

	// find span to start rendering at
	if((sptr = spanfromindex(index, &spanoffset)) == 0)
		return false;

	// might need to start mid-way through the first span
	spanoffset = index - spanoffset;

	// copy each span's referenced data in succession
	while(length && sptr != tail)
	{
		size_w copylen   = min(sptr->length - spanoffset, length);
		seqchar *source  = buffer_list[sptr->buffer]->buffer;

		memcpy(dest, source + sptr->offset + spanoffset, copylen * sizeof(seqchar));
		
		dest	+= copylen;
		length	-= copylen;
		total	+= copylen;

		sptr = sptr->next;
		spanoffset = 0;
	}

	return total;
}

//
//	sequence::peek
//
//	return single element at specified position in the sequence
//
seqchar sequence::peek(size_w index) const
{
	seqchar   value;
	return render(index, &value, 1) ? value : 0;
}

//
//	sequence::poke
//
//	modify single element at specified position in the sequence
//
bool sequence::poke(size_w index, seqchar value) 
{
	return replace(index, &value, 1);
}

//
//	sequence::operator[] const
//
//	readonly array access
//
seqchar sequence::operator[] (size_w index) const
{
	return peek(index);
}

//
//	sequence::operator[] 
//
//	read/write array access
//
sequence::ref sequence::operator[] (size_w index)
{
	return ref(this, index);
}

//
//	sequence::breakopt
//
//	Prevent subsequent operations from being optimized (coalesced) 
//  with the last.
//
void sequence::breakopt()
{
	lastaction = action_invalid;
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -