📄 singleedit.h
字号:
/*
Copyright (c) 1999, kSet Lab
Author: Konstantin Bukreev
E-mail: konstantin@mail.primorye.ru
Created: 12.10.99 22:59:42
Version: 1.0.1
implementation of the buffer for single line editor box
*/
#ifndef _SingleEdit_80a721e9_7432_11d3_9285_0080adb811c5
#define _SingleEdit_80a721e9_7432_11d3_9285_0080adb811c5
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
#include <deque>
#include <malloc.h>
#include "convert.h"
#ifdef _UNICODE
#define _CF_TEXT CF_UNICODETEXT
#else
#define _CF_TEXT CF_TEXT
#endif
template <class _char>
class _sedit
{
public:
typedef unsigned int _position;
struct _sink
{
void virtual onchange() = 0;
};
private:
enum {MIN_SIZE = 12};
typedef _sedit<_char> T;
class _stack;
class _undo
{
protected:
T* m_pT;
const _position m_caret;
const _position m_sel;
public:
_undo (T* p, _position caret, _position sel) : m_pT(p), m_caret(caret), m_sel(sel) {}
virtual ~_undo() {}
virtual void Run(_stack* pstack) = 0;
};
class _undo_delete : public _undo
{
_char* m_str;
public:
_undo_delete (T* p, _position caret, _position sel, _char* str, int size) : _undo(p, caret, sel)
{
m_str = new _char [size + 1];
memcpy(m_str, str, size * sizeof _char);
m_str[size] = _char(0);
}
~_undo_delete()
{
delete [] m_str;
}
void Run(_stack* pstack)
{
m_pT->caret(m_caret);
m_pT->insert(m_str, pstack);
}
};
class _undo_insert : public _undo
{
public:
_undo_insert (T* p, _position caret, _position sel) : _undo(p, caret, sel)
{
}
~_undo_insert()
{
}
void Run(_stack* pstack)
{
m_pT->caret(m_caret);
m_pT->select(m_sel);
m_pT->del(pstack);
}
};
friend class _undo_delete;
friend class _undo_insert;
typedef std::deque <_undo*> _deque;
class _stack : protected _deque
{
public:
_undo* top () const {return back();}
virtual void push (_undo* x) {push_back(x);}
void pop () {pop_back();}
bool empty() const {return _deque::empty();}
};
class _undostack : public _stack
{
int m_size;
public:
_undostack () : m_size(0)
{
}
virtual ~ _undostack ()
{
free();
}
void push(_undo* x)
{
_stack::push(x);
recheck();
}
void set_stack_size(int size)
{
m_size = size;
recheck();
}
void free()
{
while(!empty())
erase_top();
}
private:
void recheck()
{
while (size() > m_size)
erase_down();
}
void erase_top()
{
delete top();
pop();
}
void erase_down()
{
delete front();
pop_front();
};
};
typedef _undostack _redostack;
class _undo_composite : public _stack, public _undo
{
public:
_undo_composite() : _undo(0,0,0) {}
~_undo_composite()
{
while(!empty())
{
delete top();
pop();
}
}
void Run(_stack* pstack)
{
_undo_composite* pundo = 0;
if (pstack)
pundo = new _undo_composite;
while(!empty())
{
_undo* p = top();
p->Run(pundo);
delete p;
pop();
}
if (pstack && pundo)
pstack->push(pundo);
}
};
public:
class _error
{
};
_sedit ();
_sedit (_char*, int, _sink*);
~_sedit ();
//read-write properties
void text(_char*) throw(_error);
const _char* text() const throw();
void caret(_position, bool shift = false) throw(_error);
_position caret() const throw();
void select(_position) throw(_error);
_position select() const throw();
void enabled(bool) throw();
bool enabled(void) const throw();
void sink (_sink* p) throw();
_sink* sink (void) const throw();
//read properties
bool canundo() const throw();
bool canredo() const throw();
int length () const throw();
bool isselect() const throw();
//methods
void reset_undo() throw();
void empty () throw();
void undo () throw(_error);
void redo () throw(_error);
void insert (_char*) throw(_error);
void insert (_char) throw(_error);
void del () throw(_error);
bool left (bool shift = false) throw();
bool right (bool shift = false) throw();
void cut () throw(_error);
void copy () throw(_error);
void paste () throw(_error);
void set_stack_size(int size)throw();
void select_word() throw();
private:
void alloc(int size, bool bSave = false) throw(_error);
void free() throw();
int optimal_size(int) throw();
void delete_select(_stack*) throw(_error);
void normalize() throw();
void insert (_char*, _stack*) throw(_error);
void del (_stack*) throw(_error);
_char* m_pbuffer;
_position m_sel; //select
_position m_caret; //input position
int m_len; //size of data in buffer
int m_size; //size of allocate memory
bool m_benabled;
_undostack m_undo;
_redostack m_redo;
_sink* m_psink;
};
///////////////////////////////////////////////////////////////////////
//implement
//ctor/dtor
template <class _char>
_sedit<_char>::_sedit ()
{
m_size = m_sel = m_caret = m_len = 0;
m_benabled = true;
m_pbuffer = 0;
alloc(0, false);
m_pbuffer[0] = _char(0);
m_psink = 0;
}
template <class _char>
_sedit<_char>::_sedit (_char* txt, int stack_size, _sink* psink)
{
m_size = m_sel = m_caret = m_len = 0;
m_benabled = true;
m_pbuffer = 0;
m_psink = 0;
insert(txt, 0);
set_stack_size(stack_size);
sink(psink);
}
template <class _char>
_sedit<_char>::~_sedit ()
{
free();
}
//undo/redo stack support
template <class _char>
void _sedit<_char>::reset_undo() throw()
{
m_undo.free();
m_redo.free();
}
template <class _char>
void _sedit<_char>::set_stack_size(int size) throw()
{
m_undo.set_stack_size(size);
m_redo.set_stack_size(size);
}
//utilites
template <class _char>
int _sedit<_char>::optimal_size(int size)
{
if (size <= MIN_SIZE)
return MIN_SIZE;
int x = 16;
while (x < size)
x = x << 1;
return x;
};
template <class _char>
void _sedit<_char>::alloc(int size, bool bSave) throw(_error)
{
size = optimal_size (size + 1); //for null terminate
if (size == m_size)
return;
_char* p = m_pbuffer;
__try {m_pbuffer = new _char[size];}
__except(1) {m_pbuffer = p; throw _error();}
if (p)
{
if (bSave)
memcpy(m_pbuffer, p, m_len * sizeof _char);
delete [] p;
}
m_size = size;
}
template <class _char>
void _sedit<_char>::free() throw()
{
if (m_pbuffer)
delete [] m_pbuffer;
m_caret = m_sel = m_size = m_len = 0;
m_pbuffer = 0;
}
template <class _char>
void _sedit<_char>::normalize() throw()
{
if (m_caret > m_sel)
{
_position temp = m_caret;
m_caret = m_sel;
m_sel = temp;
}
}
template <class _char>
void _sedit<_char>::delete_select(_stack* pstack) throw(_error)
{
_ASSERTE((m_sel >= m_caret));
if (pstack)
pstack->push(new _undo_delete(this, m_caret, m_sel, &m_pbuffer[m_caret], m_sel - m_caret));
_position move = m_len - m_sel;
memcpy(&m_pbuffer[m_caret], &m_pbuffer[m_sel], move * sizeof _char);
m_len -= m_sel - m_caret;
m_sel = m_caret;
}
template <class _char>
void _sedit<_char>::del(_stack* pstack) throw(_error)
{
if (m_caret == m_len)
return;
if (m_sel == m_caret)
m_sel = m_caret + 1;
delete_select(pstack);
alloc(m_len, true);
if (m_psink) m_psink->onchange();
}
template <class _char>
void _sedit<_char>::insert(_char* str, _stack* pstack) throw(_error)
{
if (!str)
throw _error();
_undo_composite* pundo = 0;
if (pstack)
pundo = new _undo_composite;
if (m_sel != m_caret)
{
normalize();
delete_select(pundo);
}
int len = _ex_util::_convert::_strlen(str);
_position old = m_caret;
m_sel = m_caret = m_caret + len;
m_len += len;
alloc(m_len, true);
if (m_caret < m_len)
memcpy(&m_pbuffer[m_caret], &m_pbuffer[old], (m_len - m_caret) * sizeof _char);
memcpy(&m_pbuffer[old], str, len * sizeof _char);
if (pstack && pundo)
{
pundo->push(new _undo_insert (this, old, m_caret));
pstack->push(pundo);
}
if (m_psink) m_psink->onchange();
}
//properties
template <class _char>
void _sedit<_char>::enabled(bool value) throw()
{
m_benabled = value;
}
template <class _char>
bool _sedit<_char>::enabled(void) const throw()
{
return m_benabled;
}
template <class _char>
void _sedit<_char>::sink(_sink* p) throw()
{
m_psink = p;
}
template <class _char>
_sedit<_char>::_sink* _sedit<_char>::sink(void) const throw()
{
return m_psink;
}
template <class _char>
void _sedit<_char>::text (_char* str) throw(_error)
{
if (!str)
throw _error();
caret(0);
select(length());
insert(str, 0);
}
template <class _char>
const _char* _sedit<_char>::text () const throw()
{
m_pbuffer[m_len] = _char(0);
return m_pbuffer;
}
template <class _char>
void _sedit<_char>::caret(_position caret, bool shift) throw(_error)
{
if ((caret < 0) || (caret > m_len))
throw _error();
m_caret = caret;
if (!shift)
m_sel = m_caret;
}
template <class _char>
_sedit<_char>::_position _sedit<_char>::caret() const throw()
{
return m_caret;
}
template <class _char>
void _sedit<_char>::select (_position sel) throw(_error)
{
if ((sel < 0) || (sel > m_len))
throw _error();
m_sel = sel;
}
template <class _char>
_sedit<_char>::_position _sedit<_char>::select () const throw()
{
return m_sel;
}
template <class _char>
bool _sedit<_char>::canundo() const throw()
{
if (!m_benabled) return false;
return !m_undo.empty();
}
template <class _char>
bool _sedit<_char>::canredo() const throw()
{
if (!m_benabled) return false;
return !m_redo.empty();
}
template <class _char>
int _sedit<_char>::length () const throw()
{
return m_len;
}
template <class _char>
bool _sedit<_char>::isselect() const throw()
{
return (m_caret != m_sel);
}
//methods
template <class _char>
void _sedit<_char>::select_word() throw()
{
m_sel = m_caret;
while (m_caret && m_pbuffer[m_caret - 1] != _char(' '))
m_caret--;
while (m_sel < m_len && m_pbuffer[m_sel] != _char(' '))
m_sel++;
}
template <class _char>
void _sedit<_char>::empty () throw()
{
free();
}
template <class _char>
void _sedit<_char>::undo() throw(_error)
{
if (m_undo.empty())
return;
_undo* p = m_undo.top();
p->Run(&m_redo);
delete p;
m_undo.pop();
}
template <class _char>
void _sedit<_char>::redo() throw(_error)
{
if (m_redo.empty())
return;
_undo* p = m_redo.top();
p->Run(&m_undo);
delete p;
m_redo.pop();
}
template <class _char>
void _sedit<_char>::insert(_char* ch) throw(_error)
{
if (!m_benabled) return;
m_redo.free();
insert(ch, &m_undo);
}
template <class _char>
void _sedit<_char>::insert(_char ch) throw(_error)
{
if (!m_benabled) return;
m_redo.free();
_char p[2];
p[0] = ch; p[1] = _char(0);
insert(p, &m_undo);
}
template <class _char>
void _sedit<_char>::del() throw(_error)
{
if (!m_benabled) return;
m_redo.free();
normalize();
del(&m_undo);
}
template <class _char>
bool _sedit<_char>::left(bool shift) throw()
{
if (0 == m_caret)
return false;
m_caret--;
if (!shift)
m_sel = m_caret;
return true;
}
template <class _char>
bool _sedit<_char>::right(bool shift) throw()
{
if (m_len == m_caret)
return false;
m_caret++;
if (!shift)
m_sel = m_caret;
return true;
}
template <class _char>
void _sedit<_char>::copy() throw(_error)
{
if (m_sel == m_caret) return;
bool bResult = false;
if (OpenClipboard(0))
{
EmptyClipboard();
normalize();
int len = m_sel - m_caret;
HGLOBAL hMemory = GlobalAlloc(GHND, (len + 1) * sizeof _char);
if (hMemory)
{
void* p = GlobalLock(hMemory);
if (p)
{
_char* ptext = (_char*)&text()[m_caret];
_ex_util::_convert::make((TCHAR*)p, ptext, len);
//((TCHAR*)p)[len] = TCHAR(0); because GMEM_ZEROINIT in GHND include
GlobalUnlock(hMemory);
bResult = (SetClipboardData(_CF_TEXT, hMemory) != 0);
}
}
CloseClipboard();
}
if (!bResult) throw _error();
}
template <class _char>
void _sedit<_char>::cut () throw(_error)
{
if (!m_benabled) return;
copy();
del(&m_undo);
}
template <class _char>
void _sedit<_char>::paste() throw(_error)
{
if (!m_benabled) return;
_char* buffer = 0;
int len = 0;
if (OpenClipboard(0))
{
HANDLE hMemory = GetClipboardData(_CF_TEXT);
if (hMemory)
{
TCHAR* p = (TCHAR*)GlobalLock(hMemory);
if (p)
{
len = _ex_util::_convert::_strlen(p);
buffer = (_char*)_alloca((len + 1) * sizeof _char);
_ex_util::_convert::make(buffer, p, len);
GlobalUnlock(hMemory);
}
}
CloseClipboard ();
}
if (!buffer) throw _error();
buffer[len] = _char(0);
insert(buffer, &m_undo);
}
#endif //_SingleEdit_80a721e9_7432_11d3_9285_0080adb811c5
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -