- notepad2 컴파일 삽질기 3 : undo/redo가 정상동작하도록 수정 (버그패치)
- 컴퓨터야그/notepad2
- 2008. 12. 3. 20:10
step3. undo/redo가 정상동작하도록 수정
앞의 두 포스팅과 달리, 이번 3번에서 수정되는 내용은 우아한 수정과는 거리가 멉니다.
private로 선언된 클래스를 굳이 public으로 끌어올리는 것 외에도 좀 지저분한 수정이 곳곳에서 자행(?)됩니다.
하지만, 그래도 꼭 직접 수정해봐야겠단 일념으로 수정을 했습니다.
1. Editor.h
2. CellBuffer.h
3. Document.h
4. CellBuffer.cxx
5. Editor.cxx
6. ScintillaWin.cxx
이렇게 수정하고 컴파일해보면 한글 입력 및 한글을 포함한 undo/redo가 정상적으로 동작하는 것을 볼 수 있습니다.
그리고, 이런 글에는 언제나 붙는 말씀입니다만...
이 패치로 발생하는 어떠한 손해 내지는 피해에 대해서도 저는 책임지지 않습니다.
덧. 다음 두 가지 버그를 수정했습니다.
- 블럭 선택이 된 상태에서 undo/redo 기능이 정상동작하지 않는 버그 수정
- 한글 입력 중 포커스를 빼았겼다 다시 포커스를 가져오면 윈도우 IME의 한글입력창이 나타나는 버그 수정
(수정 내용이 작아서 별도 포스팅은 하지 않고 내용을 수정했습니다)
앞의 두 포스팅과 달리, 이번 3번에서 수정되는 내용은 우아한 수정과는 거리가 멉니다.
private로 선언된 클래스를 굳이 public으로 끌어올리는 것 외에도 좀 지저분한 수정이 곳곳에서 자행(?)됩니다.
하지만, 그래도 꼭 직접 수정해봐야겠단 일념으로 수정을 했습니다.
Scintilla는 편집은 Editor 클래스에서, undo/redo는 Cellbuffer 클래스에서 처리하는 구조인데, 편집기를 담당하는 Editor 클래스에서 이를 직접 제어하도록 수정했습니다.
(이런 식으로 패치하면 안돼요. 엉엉엉)
1. Editor.h
Editor 클래스에 앞의 포스팅에서 추가한 inComposion 외에 2개의 bool 변수를 더 추가합니다.
역할이 조금씩 다르기 때문에 이렇게 지정했는데, 막상 끝내고 보니 지저분해지는 근본적인 원인 중 하나가 되더군요.
역할이 조금씩 다르기 때문에 이렇게 지정했는데, 막상 끝내고 보니 지저분해지는 근본적인 원인 중 하나가 되더군요.
class Editor : public DocWatcher {
bool inComposition;
bool bCompositioning;
bool bCompletingComposition;
2. CellBuffer.h
undo/redo를 처리하는 부분을 외부에서 제어할 수 있도록 수정합니다.
UndoHistory 클래스의 선언부에 아래와 같은 내용을 추가합니다.
다음으로 CellBuffer 클래스의 선언부에서 UndoHistory uh; 의 상속형태를 private: 에서 public: 으로 바꿔줍니다.
UndoHistory 클래스의 선언부에 아래와 같은 내용을 추가합니다.
class UndoHistory {
protected:
bool bEnabled;
public:
void EnableUndoAction(bool bEnable);
다음으로 CellBuffer 클래스의 선언부에서 UndoHistory uh; 의 상속형태를 private: 에서 public: 으로 바꿔줍니다.
class CellBuffer {를
private:
UndoHistory uh;
class CellBuffer {와 같이 말이죠.
public:
UndoHistory uh;
private:
3. Document.h
Document 클래스 역시 위와 같은 이유에서 CellBuffer cb; 의 상속형태를 private: 에서 public: 으로 바꿔줍니다.
class Document : PerLine {를
private:
CellBuffer cb;
class Document : PerLine {와 같이 말입니다.
public:
CellBuffer cb;
private:
4. CellBuffer.cxx
UndoHistory 클래스의 생성자에 아래와 같은 한 줄을 추가합니다.
그리고, AppendAction(), BeginUndoAction(), EndUndoAction()의 맨앞에 if (!bEnabled) return; 를 추가합니다.
마지막으로 Undo 여부를 제어하는 메쏘드를 추가합니다.
이것으로 외부에서 CellBuffer를 손댈 수 있게 되었습니다.
UndoHistory::UndoHistory() {
bEnabled = true;
그리고, AppendAction(), BeginUndoAction(), EndUndoAction()의 맨앞에 if (!bEnabled) return; 를 추가합니다.
void UndoHistory::AppendAction(actionType at, int position, char *data, int lengthData, bool &startSequence) {
if (!bEnabled) return;
EnsureUndoRoom(); // 여기부터는 원래의 메쏘드
void UndoHistory::BeginUndoAction() {
if (!bEnabled) return;
EnsureUndoRoom(); // 여기부터는 원래의 메쏘드
void UndoHistory::EndUndoAction() {
if (!bEnabled) return;
PLATFORM_ASSERT(undoSequenceDepth > 0); // 여기부터는 원래의 메쏘드
마지막으로 Undo 여부를 제어하는 메쏘드를 추가합니다.
void UndoHistory::EnableUndoAction(bool bEnable)
{
bEnabled = bEnable;
}
이것으로 외부에서 CellBuffer를 손댈 수 있게 되었습니다.
5. Editor.cxx
이제부터는 외부에서 직접 CellBuffer를 손대어 한글 조립시 적절하게 undo를 enable/disable시킬 수 있도록 수정합니다.
우선, AddCharUTF()를 대폭 수정해서 아래 내용으로 바꿔치기합니다.
다음으로, sptr_t Editor::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam)의 switch-case 에서 case 일부를 수정합니다.
두 case문 모두, 맨앞에 pdoc->cb.uh.EnableUndoAction(true); 한 줄을 추가한 것입니다.
우선, AddCharUTF()를 대폭 수정해서 아래 내용으로 바꿔치기합니다.
void Editor::AddCharUTF(char *s, unsigned int len, bool treatAsDBCS) {
bool wasSelection = currentPos != anchor;
ClearSelection();
static bool charReplaceAction = false;
if (inOverstrike && !wasSelection && !RangeContainsProtected(currentPos, currentPos + 1)) {
bool bOneMore = false;
if (!inComposition)
{
if (currentPos < (pdoc->Length())) {
if (!IsEOLChar(pdoc->CharAt(currentPos))) {
charReplaceAction = true;
pdoc->BeginUndoAction();
pdoc->DelChar(currentPos);
}
}
if (bCompositioning) pdoc->cb.uh.EnableUndoAction(false);
}
else if (bCompletingComposition)
{
if (len>2)
{
len--;
bOneMore = true;
}
pdoc->cb.uh.EnableUndoAction(true);
}
else if (!bCompositioning) pdoc->cb.uh.EnableUndoAction(true);
if (pdoc->InsertString(currentPos, s, len)) {
SetEmptySelection(currentPos + len);
}
if (charReplaceAction && (!bCompositioning || (inComposition && !bCompositioning))) {
pdoc->cb.uh.EnableUndoAction(true);
pdoc->EndUndoAction();
charReplaceAction = false;
}
if (bCompletingComposition && bOneMore)
{
if (currentPos < (pdoc->Length())) {
if (!IsEOLChar(pdoc->CharAt(currentPos))) {
charReplaceAction = true;
pdoc->BeginUndoAction();
pdoc->DelChar(currentPos);
}
}
if (pdoc->InsertString(currentPos, s+2, 1)) {
SetEmptySelection(currentPos + 1);
}
if (charReplaceAction) {
pdoc->EndUndoAction();
charReplaceAction = false;
}
}
bCompletingComposition = false;
}
else if (pdoc->InsertString(currentPos, s, len)) {
SetEmptySelection(currentPos + len);
}
// If in wrap mode rewrap current line so EnsureCaretVisible has accurate information
if (wrapState != eWrapNone) {
AutoSurface surface(this);
if (surface) {
WrapOneLine(surface, pdoc->LineFromPosition(currentPos));
}
SetScrollBars();
}
EnsureCaretVisible();
// Avoid blinking during rapid typing:
ShowCaretAtCurrentPosition();
if (!caretSticky) {
SetLastXChosen();
}
if (treatAsDBCS) {
NotifyChar((static_cast<unsigned char>(s[0]) << 8) |
static_cast<unsigned char>(s[1]));
} else {
int byte = static_cast<unsigned char>(s[0]);
if ((byte < 0xC0) || (1 == len)) {
// Handles UTF-8 characters between 0x01 and 0x7F and single byte
// characters when not in UTF-8 mode.
// Also treats \0 and naked trail bytes 0x80 to 0xBF as valid
// characters representing themselves.
} else {
// Unroll 1 to 3 byte UTF-8 sequences. See reference data at:
// http://www.cl.cam.ac.uk/~mgk25/unicode.html
// http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt
if (byte < 0xE0) {
int byte2 = static_cast<unsigned char>(s[1]);
if ((byte2 & 0xC0) == 0x80) {
// Two-byte-character lead-byte followed by a trail-byte.
byte = (((byte & 0x1F) << 6) | (byte2 & 0x3F));
}
// A two-byte-character lead-byte not followed by trail-byte
// represents itself.
} else if (byte < 0xF0) {
int byte2 = static_cast<unsigned char>(s[1]);
int byte3 = static_cast<unsigned char>(s[2]);
if (((byte2 & 0xC0) == 0x80) && ((byte3 & 0xC0) == 0x80)) {
// Three-byte-character lead byte followed by two trail bytes.
byte = (((byte & 0x0F) << 12) | ((byte2 & 0x3F) << 6) |
(byte3 & 0x3F));
}
// A three-byte-character lead-byte not followed by two trail-bytes
// represents itself.
}
}
NotifyChar(byte);
}
}
다음으로, sptr_t Editor::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam)의 switch-case 에서 case 일부를 수정합니다.
두 case문 모두, 맨앞에 pdoc->cb.uh.EnableUndoAction(true); 한 줄을 추가한 것입니다.
case SCI_UNDO:
pdoc->cb.uh.EnableUndoAction(true);
Undo();
SetLastXChosen();
break;
case SCI_PAGEDOWNRECTEXTEND:
case SCI_SELECTIONDUPLICATE:
pdoc->cb.uh.EnableUndoAction(true);
return KeyCommand(iMessage);
6. ScintillaWin.cxx
sptr_t HandleComposition(uptr_t wParam, sptr_t lParam) 메쏘드를 아래 내용으로 교체합니다.
마지막으로, sptr_t ScintillaWin::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam)의 switch-case에서 WM_SETFOCUS:에 한 줄을 추가합니다.
추가된 결과는 아래와 같습니다.
sptr_t ScintillaWin::HandleComposition(uptr_t wParam, sptr_t lParam) {
#ifdef __DMC__
// Digital Mars compiler does not include Imm library
return 0;
#else
if (inOverstrike)
{
ClearSelection();
if(lParam & GCS_COMPSTR)
{
if(inComposition) DelCharBack(false);
bCompositioning = true;
inComposition = !(AddImeCompositionString(GCS_COMPSTR) == 0);
}
else if(lParam & GCS_RESULTSTR)
{
if(inComposition) DelCharBack(false);
bCompositioning = false;
AddImeCompositionString(GCS_RESULTSTR);
inComposition = FALSE;
}
}
else
{
ClearSelection();
pdoc->cb.uh.EnableUndoAction(false);
if(lParam & GCS_COMPSTR)
{
if(inComposition) DelCharBack(false);
inComposition = !(AddImeCompositionString(GCS_COMPSTR) == 0);
}
else if(lParam & GCS_RESULTSTR)
{
if(inComposition) DelCharBack(false);
pdoc->cb.uh.EnableUndoAction(true);
AddImeCompositionString(GCS_RESULTSTR);
inComposition = FALSE;
}
}
MoveCompositionWindow();
UpdateSystemCaret();
return 0;
#endif
}
마지막으로, sptr_t ScintillaWin::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam)의 switch-case에서 WM_SETFOCUS:에 한 줄을 추가합니다.
추가된 결과는 아래와 같습니다.
case WM_SETFOCUS:
if (inComposition) PostMessage(this->MainHWND(), WM_IME_ENDCOMPOSITION, 0, 0); //추가된 줄
SetFocusState(true);
RealizeWindowPalette(false);
DestroySystemCaret();
이렇게 수정하고 컴파일해보면 한글 입력 및 한글을 포함한 undo/redo가 정상적으로 동작하는 것을 볼 수 있습니다.
그리고, 이런 글에는 언제나 붙는 말씀입니다만...
이 패치로 발생하는 어떠한 손해 내지는 피해에 대해서도 저는 책임지지 않습니다.
덧. 다음 두 가지 버그를 수정했습니다.
- 블럭 선택이 된 상태에서 undo/redo 기능이 정상동작하지 않는 버그 수정
- 한글 입력 중 포커스를 빼았겼다 다시 포커스를 가져오면 윈도우 IME의 한글입력창이 나타나는 버그 수정
(수정 내용이 작아서 별도 포스팅은 하지 않고 내용을 수정했습니다)
'컴퓨터야그 > notepad2' 카테고리의 다른 글
notepad2 컴파일 삽질기 부록#2 : context-menu 추가 업그레이드 (5) | 2008.12.30 |
---|---|
notepad2 컴파일 삽질기 부록#1 : context-menu 추가 (7) | 2008.12.29 |
notepad2 컴파일 삽질기 4 : Visual C++ 6.0 SP6 컴파일 (21) | 2008.12.06 |
notepad2 컴파일 삽질기 2 : IME 메시지를 처리하도록 수정 (2) | 2008.11.30 |
notepad2 컴파일 삽질기 1 : 기본적인 오류 수정 (10) | 2008.11.30 |
Recent comment