Пример #1
0
LONG	MnTextView::OnMsg_EM_SETSEL(long nStart, long nEnd)
{
	if (nStart == -1)
	{
		nStart = m_nSelectionEnd;
		nEnd = nStart;
	}

	CHAR_POS cp_start;
	CHAR_POS cp_end;
	if (FilePosToCharPos(nStart, &cp_start) && FilePosToCharPos(nEnd, &cp_end))
	{
		m_visualLineView.setSelection(cp_start, cp_end);
	}
		
	InvalidateRange(m_nSelectionStart, m_nSelectionEnd);
	m_nSelectionStart = nStart;
	m_nSelectionEnd = nEnd;
	//m_nCursorOffset = nEnd;
	FilePosToCharPos(m_nSelectionEnd, &m_CurrentCharPos);
	updateCaretPos(m_CurrentCharPos);

	InvalidateRange(m_nSelectionStart, m_nSelectionEnd);
	return 0;
}
Пример #2
0
LONG TextViewBase::OnLButtonDblClick(UINT nFlags, int mx, int my)
{
	// remove any existing selection
	InvalidateRange(m_nSelectionStart, m_nSelectionEnd);


	ULONG lineno, fileoff;
	int   xpos;

	// map the mouse-coordinates to a real file-offset-coordinate
	MouseCoordToFilePos(mx, my, &lineno, &fileoff, &xpos);
	//m_nAnchorPosX = m_nCaretPosX;

	// move selection-start to start of word
	MoveWordStart();
	m_nSelectionStart = m_nSelectionEnd;// m_nCursorOffset;

	// move selection-end to end of word
	MoveWordEnd();
	//m_nSelectionEnd = m_nCursorOffset;

	// update caret position
	InvalidateRange(m_nSelectionStart, m_nSelectionEnd);
	//UpdateCaretOffset(m_nCursorOffset, TRUE, &m_nCaretPosX, &m_nCurrentLine);
	//m_nAnchorPosX = m_nCaretPosX;
	RepositionCaret();

	NotifyParent(TVN_CURSOR_CHANGE);
	NotifyParent(TVN_SELECTION_CHANGED);

	return 0;
}
Пример #3
0
void
DataView::UpdateFromEditor(BMessage *message)
{
	if (fData == NULL)
		return;

	BAutolock locker(fEditor);

	fFileSize = fEditor.FileSize();

	// get the range of the changes

	int32 start = 0, end = fDataSize - 1;
	off_t offset, size;
	if (message != NULL
		&& message->FindInt64("offset", &offset) == B_OK
		&& message->FindInt64("size", &size) == B_OK) {
		if (offset > fOffset + (off_t)fDataSize
			|| offset + (off_t)size < fOffset) {
			// the changes are not within our scope, so we can ignore them
			return;
		}

		if (offset > fOffset)
			start = offset - fOffset;
		if (offset + (off_t)size < fOffset + (off_t)fDataSize)
			end = offset + size - fOffset;
	}

	if (fOffset + (off_t)fDataSize > fFileSize)
		fSizeInView = fFileSize - fOffset;
	else
		fSizeInView = fDataSize;

	const uint8 *data;
	if (fEditor.GetViewBuffer(&data) == B_OK)
		// ToDo: copy only the relevant part
		memcpy(fData, data, fDataSize);

	InvalidateRange(start, end);

	// we notify our selection listeners also if the
	// data in the selection has changed
	if (start <= fEnd && end >= fStart) {
		BMessage update;
		update.AddInt64("start", fStart);
		update.AddInt64("end", fEnd);
		SendNotices(kDataViewSelection, &update);
	}
}
Пример #4
0
// Sets the selected region.  Note that this does not mean that any of this
// region will actually become visible to the user unless it is within the
// current display (see SetScroll).  To let the user see (at least some of)
// the region use DisplayCaret().  If p1 == p2 then this is the same as
// calling SetCaret().  The boolean parameter base1 (default false) determines
// how basepos_ (base address for selections) is set -- if base1 is false
// then basepos_ is the smaller of p1 and p2, if true the basepos_ becomes p1.
void CScrView::SetSel(CPointAp p1, CPointAp p2, bool base1)
{
	// Save current selection
	CPointAp start = caretpos_;
	CPointAp end = selpos_;

	// Check/get valid caret locations and move selection there
	ValidateCaret(p2);
	ValidateCaret(p1);

	caret_hide();
	if (p1.y < p2.y || (p1.y == p2.y && p1.x < p2.x))
	{
		basepos_ = caretpos_ = p1;
		selpos_ = p2;
	}
	else
	{
		basepos_ = caretpos_ = p2;
		selpos_ = p1;
	}
	if (base1) basepos_ = p1;

	// Cause prev. selection to be redrawn (unselected) and new selection drawn (selected)
	if (start != caretpos_ || end != selpos_)
	{
		// We invalidate everything - TODO this should be optimised to only do the changed bit
		InvalidateRange(start, end);
		InvalidateRange(caretpos_, selpos_);
		// Alternative (whole selection range) invalidation
		InvalidateRange(start, end, true);
		InvalidateRange(caretpos_, selpos_, true);
	}

	caret_show();
}
Пример #5
0
NS_IMETHODIMP nsTreeSelection::ClearRange(PRInt32 aStartIndex, PRInt32 aEndIndex)
{
  nsresult rv = SetCurrentIndex(aEndIndex);
  if (NS_FAILED(rv))
    return rv;

  if (mFirstRange) {
    PRInt32 start = aStartIndex < aEndIndex ? aStartIndex : aEndIndex;
    PRInt32 end = aStartIndex < aEndIndex ? aEndIndex : aStartIndex;

    mFirstRange->RemoveRange(start, end);

    if (mTree)
      mTree->InvalidateRange(start, end);
  }
  
  return NS_OK;
}
Пример #6
0
void CScrView::OnSelUpdate(CPoint point)
{
	// Keep the last drag position so we only invalidate what has changed.
	CPointAp last;
	ASSERT(basepos_ == caretpos_ || basepos_ == selpos_);
	if (caretpos_ == basepos_)
		last = selpos_;
	else
		last = caretpos_;

	CRect cli;                          // Display area (client coords)
	GetDisplayRect(&cli);
	CRectAp disp = ConvertFromDP(cli);  // Display area ("cli") in our coords
	CPointAp pp = ConvertFromDP(point); // Mouse point ("point") in our coords

	// Get display rect shrunk to only contain whole rows/columns
	CRectAp rr(disp);
	rr.top  = ((rr.top - 1)/line_.cy + 1)*line_.cy;
	rr.left = ((rr.left- 1)/line_.cx + 1)*line_.cx;
	rr.bottom = (rr.bottom/line_.cy)*line_.cy;
	rr.right  = (rr.right /line_.cx)*line_.cx;

	// Test if we are about to scroll or very close to edge
	if (!rr.PtInRect(pp))
	{
		// TODO: We probably should handle vertical and horizontal auto-scrolling
		// separately and not allow both at the same time, to avoid confusion.

		CPointAp newpos(-1, -1);  // New scroll position (our coords)
		static clock_t last_clock = 0;

		if (pp.x < rr.left)
		{
			newpos.x = scrollpos_.x - line_.cx;
		}
		else if (pp.x >= rr.right)
		{
			newpos.x = scrollpos_.x + line_.cx;
		}

		if (pp.y < rr.top)
		{
			// Use clock to ensure scrolling does not get faster with more mouse move events
			clock_t curr_clock = clock();
			clock_t diff_clock = curr_clock - last_clock;
			if (diff_clock > CLOCKS_PER_SEC/5) diff_clock = CLOCKS_PER_SEC/5;

			// Scroll speed depends on distance mouse is above window
//            int diff_pixel = ((disp.top - pp.y)*(disp.top - pp.y))/10;
			int diff_pixel = int(disp.top - pp.y);
			int autoscroll = (int)pow((double)diff_pixel, autoscroll_accel_/10.0);

			__int64 to_move = 0;                        // How much to scroll up

			// Always scroll so that top of text line is at top of window
			if (disp.top < rr.top) to_move = line_.cy - (rr.top - disp.top);

			// If enough time has passed or mouse is far enough above the window...
			if (disp.top > pp.y && diff_clock * autoscroll >= CLOCKS_PER_SEC/5)
			{
				// Scroll according to time and distance
				to_move += line_.cy*((diff_clock * autoscroll)/(CLOCKS_PER_SEC/5));
				last_clock = curr_clock;
			}
			newpos.y = scrollpos_.y - to_move;
			if (newpos.y < 0)
				newpos.y = 0;
		}
		else if (pp.y >= rr.bottom)
		{
			// See above comments (same but for bottom of window not top)
			clock_t curr_clock = clock();
			clock_t diff_clock = curr_clock - last_clock;
			if (diff_clock > CLOCKS_PER_SEC/5) diff_clock = CLOCKS_PER_SEC/5;
			int diff_pixel = int(pp.y - disp.bottom);
			int autoscroll = (int)pow((double)diff_pixel, autoscroll_accel_/10.0);

			__int64 to_move = 0;
			if (disp.bottom > rr.bottom) to_move = line_.cy - (disp.bottom - rr.bottom);
			if (disp.bottom < pp.y && diff_clock * autoscroll >= CLOCKS_PER_SEC/5)
			{
				to_move += line_.cy*((diff_clock * autoscroll)/(CLOCKS_PER_SEC/5));
				last_clock = curr_clock;
			}
			newpos.y = scrollpos_.y + to_move;
			if (newpos.y < 0)
				newpos.y = 0;
		}

		SetScroll(newpos, TRUE);
	}
	
	// Set end of selection based on this point
	CPoint ptmp = point;                         // Get pp based on new value of "point"

	// Don't allow selection to extend outside window
	if (ptmp.y < cli.top) ptmp.y = cli.top;
	else if (ptmp.y >= cli.bottom) ptmp.y = cli.bottom - 1;

	if (ptmp.x < cli.left) ptmp.x = cli.left;
	else if (ptmp.x >= cli.right) ptmp.x = cli.right - 1;

	// Work out new selection
	pp = ConvertFromDP(ptmp);
	ValidateCaret(pp, FALSE);

	if (pp.y < basepos_.y || (pp.y == basepos_.y && pp.x < basepos_.x))
	{
		caretpos_ = pp;
		selpos_ = basepos_;
	}
	else
	{
		caretpos_ = basepos_;
		selpos_ = pp;
	}

	// Allow derived class to invalidate the whole selection
	if (pp.y != last.y || pp.x != last.x)
		InvalidateRange(caretpos_, selpos_, true);

	// Invalidate everything that's changed
	if (pp.y < last.y || (pp.y == last.y && pp.x < last.x))
		InvalidateRange(pp, last);
	else if (pp.y != last.y || pp.x != last.x)
		InvalidateRange(last, pp);
}
Пример #7
0
//
//	Process keyboard-navigation keys
//
LONG TextViewBase::OnKeyDown(UINT nKeyCode, UINT nFlags)
{
	bool fCtrlDown	= IsKeyPressed(VK_CONTROL);
	bool fShiftDown	= IsKeyPressed(VK_SHIFT);
	BOOL fAdvancing = FALSE;
	long oldCursorOffset = m_nSelectionEnd;
	//
	//	Process the key-press. Cursor movement is different depending
	//	on if <ctrl> is held down or not, so act accordingly
	//
	switch(nKeyCode)
	{
	case VK_SHIFT: case VK_CONTROL:
		return 0;

	case 'a': case 'A':
		
		{
			if(fCtrlDown)
				SelectAll();
		}
		return 0;
	// CTRL+Z undo
	case 'z': case 'Z':
		
		if(fCtrlDown && Undo())
			/*NotifyParent(TVN_CHANGED);*/

		return 0;

	// CTRL+Y redo
	case 'y': case 'Y':
		
		if(fCtrlDown && Redo()) 
			//NotifyParent(TVN_CHANGED);

		return 0;
	// CTRL+C copy
	case 'c': case 'C':
	{
		//if(fCtrlDown && Redo()) 
		//	NotifyParent(TVN_CHANGED);
		if(fCtrlDown)
			return OnCopy();
		else
			break;
	}

	case 'x': case 'X':
		{
		if(fCtrlDown)
			return OnCut();
		else
			break;
		}
	case 'v': case 'V':
		{
		if(fCtrlDown)
			return OnPaste();
		else
			break;
		}

	
	// Change insert mode / clipboard copy&paste
	case VK_INSERT:

		if(fCtrlDown)
		{
			OnCopy();
			NotifyParent(TVN_CHANGED);
		}
		else if(fShiftDown)
		{
			OnPaste();
			NotifyParent(TVN_CHANGED);
		}
		else
		{
			if(m_nEditMode == MODE_INSERT)
				m_nEditMode = MODE_OVERWRITE;

			else if(m_nEditMode == MODE_OVERWRITE)
				m_nEditMode = MODE_INSERT;

			NotifyParent(TVN_EDITMODE_CHANGE);
		}

		return 0;

	case VK_DELETE:

		if(m_nEditMode != MODE_READONLY)
		{
			if(fShiftDown)
				OnCut();
			else
				ForwardDelete();

			NotifyParent(TVN_CHANGED);
		}
		return 0;

	case VK_BACK:

		if(m_nEditMode != MODE_READONLY)
		{
			BackDelete();
			fAdvancing = FALSE;

			NotifyParent(TVN_CHANGED);
		}
		return 0;

	case VK_LEFT:

		if(fCtrlDown)	MoveWordPrev();
		else			MoveCharPrev();

		fAdvancing = FALSE;
		break;

	case VK_RIGHT:
		
		if(fCtrlDown)	MoveWordNext();
		else			MoveCharNext();
			
		fAdvancing = TRUE;
		break;

	case VK_UP:
		if(fCtrlDown)	Scroll(0, -1);
		else			MoveLineUp(1);
		break;

	case VK_DOWN:
		if(fCtrlDown)	Scroll(0, 1);
		else			MoveLineDown(1);
		break;

	case VK_PRIOR:
		if(!fCtrlDown)	MovePageUp();
		break;

	case VK_NEXT:
		if(!fCtrlDown)	MovePageDown();
		break;

	case VK_HOME:
		if(fCtrlDown)	MoveFileStart();
		else			MoveLineStart(m_nCurrentLine);
		break;

	case VK_END:
		if(fCtrlDown)	MoveFileEnd();
		else			MoveLineEnd(m_nCurrentLine);
		break;

	default:
		return 0;
	}

	// Extend selection if <shift> is down
	if(fShiftDown)
	{		
		InvalidateRange(m_nSelectionEnd, oldCursorOffset);
		//m_nSelectionEnd	= m_nCursorOffset;
	}
	// Otherwise clear the selection
	else
	{
		if(m_nSelectionStart != m_nSelectionEnd)
			InvalidateRange(m_nSelectionStart, m_nSelectionEnd);

		//m_nSelectionEnd		= m_nCursorOffset;
		m_nSelectionStart = m_nSelectionEnd;
	}

	// update caret-location (xpos, line#) from the offset
	//UpdateCaretOffset(m_nCursorOffset, fAdvancing, &m_nCaretPosX, &m_nCurrentLine);
	CHAR_POS cp;
	FilePosToCharPos(m_nSelectionEnd, &cp);
	m_nCurrentLine_D = cp.logLine;
	
	// maintain the caret 'anchor' position *except* for up/down actions
	if(nKeyCode != VK_UP && nKeyCode != VK_DOWN)
	{
		//m_nAnchorPosX = m_nCaretPosX;

		// scroll as necessary to keep caret within viewport
		//ScrollToPosition(m_nCaretPosX, m_nCurrentLine);
	}
	else
	{
		// scroll as necessary to keep caret within viewport
		if(!fCtrlDown);
			//ScrollToPosition(m_nCaretPosX, m_nCurrentLine);
	}

	NotifyParent(TVN_CURSOR_CHANGE);

	return 0;
}
Пример #8
0
//
//	TextView::EnterText
//
//	Import the specified text into the TextView at the current
//	cursor position, replacing any text-selection in the process
//
ULONG TextViewBase::EnterText(const TCHAR *szText, ULONG nLength)
{
#ifdef _DEBUG
	wchar_t db_string[200];
	wsprintf(db_string, L"EnterText  m_nSelectionEnd=%d\n", m_nSelectionEnd);
	OutputDebugString(db_string);
#endif
	ULONG selstart	= min(m_nSelectionStart, m_nSelectionEnd);
	ULONG selend	= max(m_nSelectionStart, m_nSelectionEnd);

	BOOL  fReplaceSelection = (selstart == selend) ? FALSE : TRUE;
	ULONG erase_len			= nLength;
	ULONG line_offset		= 0, line_length = 0;
	bool		result		= m_pTextDoc->lineinfo_from_lineno(m_nCurrentLine_D, &line_offset, &line_length, NULL, NULL);
	if(m_nCurrentLine_D != 0 && !result)
	{
		int stop = 1;
		throw MnException();
	}

	switch(m_nEditMode)
	{
	case MODE_READONLY:
		return 0;

	case MODE_INSERT:

		// if there is a selection then remove it
		if(fReplaceSelection)
		{
			// group this erase with the insert/replace operation
			m_pTextDoc->m_seq.group();
			m_pTextDoc->erase_text(selstart, selend-selstart);
			m_nSelectionEnd = selstart;
		}

		if(!m_pTextDoc->insert_text(m_nSelectionEnd, szText, nLength))
			return 0;

		if(fReplaceSelection)
			m_pTextDoc->m_seq.ungroup();
	
		break;

	case MODE_OVERWRITE:

		if(fReplaceSelection)
		{
			erase_len = selend - selstart;
			m_nSelectionEnd = selstart;
		}
		else
		{
			//ULONG lastPosition;

			ULONG lineoff;
			ULONG length_CRLF;
			/*
			USPCACHE *uspCache	= GetUspCache(0, m_nCurrentLine, &lineoff);*/
			GetOffsetAndLength_CRLF( m_nCurrentLine_D, &lineoff, &length_CRLF);


			// single-character overwrite - must behave like 'forward delete'
			// and remove a whole character-cluster (i.e. maybe more than 1 char)
			if(nLength == 1)
			{
				ULONG oldpos = m_nSelectionEnd;
				MoveCharNext();
				erase_len = m_nSelectionEnd - oldpos;
				m_nSelectionEnd = oldpos;
			}

			// if we are at the end of a line (just before the CRLF) then we must
			// not erase any text - instead we act like a regular insertion
			if (m_nSelectionEnd == lineoff + length_CRLF)
				erase_len = 0;

			
		}

		if(!m_pTextDoc->replace_text(m_nSelectionEnd, szText, nLength, erase_len))
			return 0;
		
		break;

	default:
		return 0;
	}

	// update cursor+selection positions
	m_nSelectionEnd += nLength;
	m_nSelectionStart = m_nSelectionEnd;
	//m_nSelectionEnd   = m_nCursorOffset;
	

	// we altered the document, recalculate line+scrollbar information

	

	//RefreshWindow();
	
	int a1 = '\n';
	int a2 = '\r';
	//ULONG offset, len;
	

	if(nLength == 1 && !fReplaceSelection &&  line_length < MAX_LINE_LENGTH - 1)
	{
		
		FilePosToCharPos(m_nSelectionEnd, &m_CurrentCharPos);
		if(szText[0] =='\n' )
		{
			wchar_t debug[50];
			wsprintf(debug, L"newline m_nCursorOffset%d", m_nSelectionEnd);
			OutputDebugString(debug);
			Smeg(TRUE);
		}
		else if(szText[0] =='\r') 
		{

		}
		else
		{
			m_pTextDoc->init_linebuffer();

			ResetLineCache( m_nCurrentLine_D);
			InvalidateRange(m_nSelectionEnd - 1, line_offset + line_length + 1);
			RepositionCaret();

		}
	}else
	{
		//ResetLineCache( );
		Smeg(TRUE);
	}
	
	NotifyParent(TVN_CURSOR_CHANGE);

	return nLength;
}
Пример #9
0
BOOL TextViewBase::BackDelete()
{
	ULONG selstart	= min(m_nSelectionStart, m_nSelectionEnd);
	ULONG selend	= max(m_nSelectionStart, m_nSelectionEnd);
	ULONG length	= selend - selstart;
	bool  nead_Smeg	= false;

	ULONG line_offset, line_length, line_no;

	m_pTextDoc->lineinfo_from_offset(selstart, &line_no, &line_offset, &line_length, NULL, NULL);
	//m_pTextDoc->lineinfo_from_lineno(m_nCurrentLine_D, &line_offset, &line_length, NULL, NULL);
		
	// if there's a selection then delete it
	if(selstart != selend)
	{
		if(selstart + length >= line_length + line_offset)
		{
			nead_Smeg = true;
		}

		m_pTextDoc->erase_text(selstart, selend-selstart);
		m_nSelectionEnd = selstart;
		m_pTextDoc->m_seq.breakopt();
	}
	// otherwise do a back-delete
	else if (m_nSelectionEnd > 0)
	{
		//m_nCursorOffset--;
		ULONG oldpos = m_nSelectionEnd;
		MoveCharPrev();
		length = oldpos - m_nSelectionEnd;

		if (m_nSelectionEnd < line_offset)
		{
			nead_Smeg = true;
		}
		m_pTextDoc->erase_text(m_nSelectionEnd, length);
	}

	m_nSelectionStart = m_nSelectionEnd;
	//m_nSelectionEnd   = m_nCursorOffset;

	//FilePosToCharPos(m_nCursorOffset, &m_CurrentCharPos);

	if(nead_Smeg)
	{
		Smeg(FALSE);
	}else
	{
		int		old_offset = m_nSelectionEnd;

		m_pTextDoc->init_linebuffer();
		ResetLineCache( m_nCurrentLine_D);
	
		//FilePosToCharPos(m_nCursorOffset, &m_CurrentCharPos);

		MoveLineStart(0, false);
		InvalidateRange((m_nSelectionEnd ? m_nSelectionEnd - 1 : 0), line_offset + line_length);
		m_nSelectionEnd = old_offset;
		RepositionCaret();
		
		
	}
#ifdef _DEBUG
	wchar_t db_string[200];
	wsprintf(db_string, L"m_nSelectionEnd=%d\n", m_nSelectionEnd);
	OutputDebugString(db_string);
#endif
	return TRUE;
}
Пример #10
0
BOOL TextViewBase::ForwardDelete()
{

	ULONG selstart	= min(m_nSelectionStart, m_nSelectionEnd);
	ULONG selend	= max(m_nSelectionStart, m_nSelectionEnd);
	ULONG length	= selend - selstart;
	bool  nead_Smeg	= false;

	ULONG line_offset, line_length, line_no;

	m_pTextDoc->lineinfo_from_offset(selstart, &line_no, &line_offset, &line_length,NULL, NULL);

	if(selstart != selend)
	{
		if(selstart + length >= line_length + line_offset)
		{
			nead_Smeg = true;
		}

		m_pTextDoc->erase_text(selstart, length);
		//m_nCursorOffset = selstart;
		m_pTextDoc->m_seq.breakopt();
	}
	else
	{

		ULONG oldpos = m_nSelectionEnd;
		
		MoveCharNext();
		length = m_nSelectionEnd - oldpos;

		m_pTextDoc->erase_text(oldpos, length);
		m_nSelectionEnd = oldpos;
		

		if(oldpos + length >= line_length + line_offset)
		{
			nead_Smeg = true;
		}
		//else
		//	m_pTextDoc->erase_text(m_nCursorOffset, 1);
	}

	m_nSelectionStart = selstart;
	m_nSelectionEnd = selstart;

	FilePosToCharPos(selstart, &m_CurrentCharPos);


	if(nead_Smeg)
	{
		Smeg(FALSE);
	}else
	{
		m_pTextDoc->init_linebuffer();
		if(m_pTextDoc->lineinfo_from_lineno(m_nCurrentLine_D, &line_offset, &line_length ,NULL,NULL))
		{
			ResetLineCache( m_nCurrentLine_D);
			InvalidateRange(min(m_nSelectionEnd - 1, 0), line_offset + line_length);
			RepositionCaret();
		}
		
	}
	
	return TRUE;
}
Пример #11
0
LRESULT HexView::OnKeyDown(UINT nVirtualKey, UINT nRepeatCount, UINT nFlags)
{
	BOOL	fForceUpdate = FALSE;
	bool	fCtrlDown	 = IsKeyDown(VK_CONTROL);
	bool	fShiftDown	 = IsKeyDown(VK_SHIFT);
	size_w  oldoffset    = m_nCursorOffset;

	fForceUpdate = !IsKeyDown(VK_SHIFT);

	if(nVirtualKey == VK_CONTROL || nVirtualKey == VK_SHIFT || nVirtualKey == VK_MENU)
		return 0;

	switch(nVirtualKey)
	{
	case VK_ESCAPE:
		fForceUpdate = TRUE;
		break;

	case VK_INSERT:
		
		if(fCtrlDown)
		{
			OnCopy();
		}
		else if(fShiftDown)
		{
			OnPaste();
		}
		else
		{
			if(m_nEditMode == HVMODE_INSERT)
				m_nEditMode = HVMODE_OVERWRITE;

			else if(m_nEditMode == HVMODE_OVERWRITE)
				m_nEditMode = HVMODE_INSERT;

			NotifyParent(HVN_EDITMODE_CHANGE);
		}

		return 0;

	case 'z': case 'Z':
		
		m_nSubItem = 0;

		if(fCtrlDown)
			Undo();

		return 0;

	// CTRL+Y redo
	case 'y': case 'Y':
		
		m_nSubItem = 0;

		if(fCtrlDown)
			Redo();

		return 0;

	case VK_DELETE:
		
		// can only erase when in Insert mode
		if(m_nEditMode == HVMODE_INSERT || 
			CheckStyle(HVS_ALWAYSDELETE) && 
			(m_nEditMode == HVMODE_INSERT || m_nEditMode == HVMODE_OVERWRITE) && 
			SelectionSize() == 0)
		{
			ForwardDelete();
		}
		else if(m_nEditMode != HVMODE_READONLY)
		{
			BYTE b[] = { 0 };
			FillData(b, 1, SelectionSize());
		}

		return 0;

	case VK_BACK:
		
		// can only erase when in Insert mode
		if(m_nEditMode == HVMODE_INSERT || 
			CheckStyle(HVS_ALWAYSDELETE) && 
			(m_nEditMode == HVMODE_INSERT || m_nEditMode == HVMODE_OVERWRITE))
		{
			BackDelete();			
		}
		else
		{
			//PostMessage(m_hWnd, WM_KEYDOWN, 
			INPUT inp = { INPUT_KEYBOARD };
			inp.ki.wVk = VK_LEFT;
			SendInput(1, &inp, sizeof(inp));
		}

		return 0;

	case VK_LEFT:

		//if ctrl held down, then scroll the viewport around!
		if(IsKeyDown(VK_CONTROL))
		{
			PostMessage(m_hWnd, WM_HSCROLL, SB_LINEUP, 0L);
			return 0;
		}

		if(m_nCursorOffset > 0) 
			m_nCursorOffset--;

		m_fCursorAdjustment = FALSE;
		break;

	case VK_RIGHT:

		//if ctrl held down, then scroll the viewport around!
		if(IsKeyDown(VK_CONTROL))
		{
			PostMessage(m_hWnd, WM_HSCROLL, SB_LINEDOWN, 0L);
			return 0;
		}
		
		if(m_nCursorOffset < m_pDataSeq->size()) 
		{
			m_nCursorOffset++;

			if(m_nCursorOffset == m_pDataSeq->size() &&  m_pDataSeq->size() % m_nBytesPerLine == 0)
				m_fCursorAdjustment = TRUE;
			else
				m_fCursorAdjustment = FALSE;
		}

		break;

	case VK_UP:

		//if ctrl held down, then scroll the viewport around!
		if(IsKeyDown(VK_CONTROL))
		{
			PostMessage(m_hWnd, WM_VSCROLL, SB_LINEUP, 0L);
			return 0;
		}

		if(m_nCursorOffset > (unsigned)m_nBytesPerLine) 
			m_nCursorOffset -= m_nBytesPerLine;

		break;

	case VK_DOWN:

		//if ctrl held down, then scroll the viewport around!
		if(IsKeyDown(VK_CONTROL))
		{
			PostMessage(m_hWnd, WM_VSCROLL, SB_LINEDOWN, 0L);
			return 0;
		}
		
		m_nCursorOffset += min((size_w)m_nBytesPerLine, m_pDataSeq->size() - m_nCursorOffset);

		// if in the last partial line, don't go to end of file, rather
		// stay at "bottom" of file/window
		if(m_nCursorOffset >= m_pDataSeq->size() && !m_fCursorAdjustment)
		{		
			// test if in a partial line 
			if(	oldoffset % m_nBytesPerLine < m_pDataSeq->size() % m_nBytesPerLine  ||
				m_pDataSeq->size() % m_nBytesPerLine == 0)
			{
				m_nCursorOffset = oldoffset;
				fForceUpdate = TRUE;
			}
		}
	
		break;

	case VK_HOME:

		//if ctrl held down, then scroll the viewport around!
		if(fCtrlDown)
		{
			m_nCursorOffset = 0;
			PostMessage(m_hWnd, WM_VSCROLL, SB_TOP, 0L);
		}
		else
		{
			if(m_fCursorAdjustment && m_nCursorOffset > 0)
				m_nCursorOffset--;

			m_nCursorOffset -= m_nCursorOffset % m_nBytesPerLine;
		}

		m_fCursorAdjustment = FALSE;
		break;

	case VK_END:
		
		if(IsKeyDown(VK_CONTROL))
		{
			m_nCursorOffset = m_pDataSeq->size();
		
			if(m_nCursorOffset % m_nBytesPerLine == 0)
				m_fCursorAdjustment = TRUE;

			PostMessage(m_hWnd, WM_VSCROLL, SB_BOTTOM, 0L);
		}
		else
		{
			// if not already at very end of line
			if(m_fCursorAdjustment == FALSE)
			{
				if(m_pDataSeq->size() - m_nBytesPerLine >= m_nCursorOffset
					&& m_pDataSeq->size() >= m_nBytesPerLine)
				{
					m_nCursorOffset += 
						m_nBytesPerLine - (m_nCursorOffset % m_nBytesPerLine);
					
					m_fCursorAdjustment = TRUE;
				}
				else
				{
					m_nCursorOffset += m_pDataSeq->size()-m_nCursorOffset;
				}
			}

			if(m_nCursorOffset >= m_pDataSeq->size() && m_pDataSeq->size() % m_nBytesPerLine == 0)
				m_fCursorAdjustment = TRUE;
		}

		break;

	case VK_PRIOR:		// pageup

		m_nCursorOffset -= min(m_nCursorOffset, (size_w)m_nBytesPerLine * m_nWindowLines);

		break;

	case VK_NEXT:		// pagedown

		m_nCursorOffset += min(m_pDataSeq->size() - m_nCursorOffset, (size_w)m_nBytesPerLine * m_nWindowLines);

		if(m_nCursorOffset >= m_pDataSeq->size() && m_pDataSeq->size() % m_nBytesPerLine == 0)
		{
			m_fCursorAdjustment = TRUE;
		}

		break;

	case VK_TAB:

		m_nWhichPane ^= 1;
		fForceUpdate = TRUE;
		
		if(m_ColourList[HVC_SELECTION] != m_ColourList[HVC_SELECTION2])
		{
			InvalidateRange(m_nSelectionStart, m_nSelectionEnd);
		}
		break;

	default:
		// don't know what this key is, so exit
		return 0;
	}

	m_nSubItem = 0;

	if(m_nCursorOffset != oldoffset || fForceUpdate)
	{
		// SHIFT key being held down?
		if(IsKeyDown(VK_SHIFT))
		{
			// extend the selection
			m_nSelectionEnd = m_nCursorOffset;
			InvalidateRange(oldoffset, m_nSelectionEnd);
		}
		else if(nVirtualKey != VK_TAB)
		{
			// clear any selection
			if(m_nSelectionEnd != m_nSelectionStart)
				InvalidateRange(m_nSelectionEnd, m_nSelectionStart);

			m_nSelectionEnd   = m_nCursorOffset;
			m_nSelectionStart = m_nCursorOffset;
		}

		ScrollToCaret();
		NotifyParent(HVN_CURSOR_CHANGE);

		if(nVirtualKey == VK_NEXT || nVirtualKey == VK_PRIOR)
		{
			RefreshWindow();
		}
	}

	return 0;
}