
#include <vcl.h>
#pragma hdrstop                                                                     

#include "GEdit.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TGEdit_Form *GEdit_Form;
//---------------------------------------------------------------------------


//[2007.09.27.목][생성!!!]{------------------------------------------------------
//""edit_edit" 를 가지고 한줄만 편집할수 있는 TEdit 와 같은 컨트롤을 만든것이다.
//이곳에 기록된 모든 날짜는 무시한다.
//}------------------------------------------------------------------------------


//[클립보드 접근용]
//{
    #include  <clipbrd.hpp>

//}


#include "..\gui_part.h"
#include "ide_library.h"

//[2010.03.09.화][추가!!!]
#include "vmos_def.h"

//[2018.06.23.토][추가!!!]
#include "msg_queue.h"
#include "event.h"


//[2018.08.25.토][추가!!!]
#include "gui.h"

//[정의]
//{
    //출력 시작 위치.
    #define  d_X_width    0

    //편집 용량.(1K Byte)
    //한줄만 편집하므로 1K 정도면 충분하다.
    #define  d_BUF_size   1024

//}


//[참조대상]
//{
    //[2012.05.01.화][수정!!!]{-----------------------------------------
    //자세한 내용은 <GPART_GFindDialog_GetPtr2()> 함수안의 설명을 참고.
    #define  _GVmos    (*((TGVmos_Form **)_vpGVmos))
    //}-----------------------------------------------------------------

//}


//[전역변수]
//{
    //[2018.10.13.토][추가!!!]{---------------------------------
    //모체가 없는 경우는 독립창으로 보고 true 로 설정한다.
    //해당 변수이름으로 검색하여 해당 부분을 확인한다.
    //
    //[주의!!!]*************************************************
    //<CreateParams()> 이 <TGxxx_Form()> 보다 먼저 실행되므로
    //반드시 전역변수로 선언한다.
    //생성시에만 일시적으로 사용하므로 [전역변수] 도 무관.
    //
    //실제 설정은 <Gxxx_Create()> 에서 [모체] 유무에 따라 설정.
    //}---------------------------------------------------------

    bool  _indpt_GEdit = false; //(기본값:flase)

//}


void __fastcall TGEdit_Form::CreateParams(TCreateParams &Params)
{
  //[2018.10.13.토][생성!!!]{--------------------------------------------------------------
  //<TForm> 을 기본하는 [그림부품] 을 완전히 독립적인 동작을 할수 있도록 처리.
  //이는 <Application> 에 묶여서 같이 작업표시줄로 사라지거나 하는 문제를 방지하기 위한것.
  //
  //[폼디자이너] 를 개발하면서 필요시 추가하는 것으로 기존에 <GForm> 에만 적용하였으나
  //[폼디자이너] 에서는 <GEdit> 등 모든 [그림부품] 이 독립적으로 화면에 나올수 있으므로
  //모든 [그림부품] 에도 반영한다.
  //
  //[주의!!!]******************************************************************************
  //이부분에서는 <_this_> 관련 처리를 해서는 안된다.
  //반드시 실행되어야 한다.
  //
  //[주의!!!][2018.10.13.토][추가!!!]******************************************************
  //[독립폼] 인 경우 다른 [그림부품] 을 [모체] 로 지정해도 무시되고  바탕화면에 나온다.
  //즉, [모체] 적용이 안되는 것에 주의.
  //}--------------------------------------------------------------------------------------

  TForm::CreateParams(Params);

  if(_indpt_GEdit == true)
  {
    //독립창으로 설정된 경우만 처리한다.
    Params.ExStyle   = WS_EX_APPWINDOW;
    Params.WndParent = GetDesktopWindow();

  }

}

__fastcall TGEdit_Form::TGEdit_Form(TComponent* Owner)
        : TForm(Owner)
{
  //[개체존재:생성]{**************
  //[2012.05.01.화][추가!!!]
  //개체가 생성되었음을 알려준다.
  _this_ = true;
  //}*****************************

  //[사용구분 초기화]
  //{
      //[2012.05.05.금][추가!!!]{----------------
      //반드시 처음에는 <BCB:false> 로 설정한다.
      //}----------------------------------------

      _GUse = false;

  //}

  //[폼디자인:모드]
  //{
      //[2018.06.24.일][추가!!!]
      _GFormDesignMode = false;

  //}

  //[생성자 초기화]
  //{
      //[2010.03.09.화][추가!!!]{---------
      //[생성자] 위치 정보를 초기화 한다.
      //}---------------------------------

      _GPart = NULL;

  //}

  //[이벤트 사용 유/무]
  //{
      //[2018.06.23.토][추가!!!]{------------
      //처음에는 기본적으로 사용되지 않는다.
      //}------------------------------------

      _evtKDown  = false;
      _evtKUp    = false;
      _evtKEnter = false;

      //[2018.07.03.화][추가!!!]
      _evtKEsc   = false;

  //}

  //[OnResize:비활성화][폼디자인:모드]
  //{
      //[2018.06.24.일][추가!!!]{------------------------------------------------------------
      //[폼디자이너] 관련 추가를 하면서 <OnResize> 를 활성화 했는데, 그림부품 생성시
      //아래 { this->Width  = 50; } 부분에서 크기 조정 중에도 발생한다.
      //그러나, 이때는 아직 <_bmp_> 가 생성되기 전이라 내부적으로 <_Shape()> 시에 오류 발생.
      //따라서, 이 부분에서 <OnResize:NULL> 로 초기화 해 놓고, <_bmp_> 생성후 연결한다.
      //이 그림부품의 내부 요소가 완전히 생성된 후 [활성화] 시킨다.
      //}------------------------------------------------------------------------------------

      this->OnResize = NULL;

  //}

  //[속성]
  //{
      //[기본화면]
      //{
          //[2010.02.13.토][추가!!!]
          _vpGVmos = NULL;

          //[2012.05.01.화][수정!!!]{-----------------------------------------
          //자세한 내용은 <GPART_GFindDialog_GetPtr2()> 함수안의 설명을 참고.
          GPART_GVmos_GetPtr2((TGVmos_Form ***)(&_vpGVmos));
          //}-----------------------------------------------------------------

      //}

  //}

  //[form]
  //{
      this->Left     = 0;
      this->Top      = 0;
      this->Width    = 100;
      this->Height   = 16;

      this->Color    = clWhite;

  //}

  //[bmp]
  //{
      //실제 그림 영역을 생성한다.
      _bmp_ = new Graphics::TBitmap;

      //그림 영역 크기 설정.
      _bmp_->Width       = this->Width;
      _bmp_->Height      = this->Height;

      //24bit bitmap 으로 지정한다.
      _bmp_->PixelFormat = pf24bit;

  //}

  //[배경화면]
  //{
      //실제 그림 영역을 생성한다.
      _bmp_back_ = new Graphics::TBitmap;

      _bmp_back_->Width  = _bmp_->Width;
      _bmp_back_->Height = _bmp_->Height;

      //바탕색으로 지운다.
      _bmp_back_->Canvas->Brush->Color = d_Color_Back;
      _bmp_back_->Canvas->FillRect(_bmp_back_->Canvas->ClipRect);

  //}

  //[글꼴 설정]
  //{
      //[기본 끌꼴 등록]{------------------------------------------------
      //모든 프로그램에서 기본적으로 사용하는 [system 글꼴] 을 등록한다.
      FONTPACK_SetDefault(&_font);
      //}----------------------------------------------------------------

      _font.margin_size   = d_X_width + 2 - 1;

      _font.offset_left   = 0;
      _font.offset_right  = 0;
      _font.offset_top    = 0;
      _font.offset_bottom = 0;

      //[2010.01.26.화][추가!!!]{---
      _font.en_offset_left  = 0;
      _font.en_offset_right = 0;
      //}---------------------------

      //[한글/영문] 의 대표 [문자폭] 을 구하고 동시에 [행높이] 값도 같이 구한다.
      //이는 한번만 구해 놓으면 되는 것으로 배경을 지우기 전에 먼저 처리한다.
      _EDIT_Get_Kor_Eng_Moonja_Width();

      //[수평스크롤] 을 위한 문자폭은 [영문] 폭으로 설정한다.
      _word_width = _font_eng_width;

  //}

  //[주 저장소]
  //{
      //편집용량은 기본적으로 1K 로 설정한다.
      //한줄만 사용되므로 길이가 길 필요는 없다.
      //_editbuf = new char [d_BUF_size];
      //[동적생성단위적용][2021.01.24.일][수정]
      _editbuf = new char [UnitX(1024, d_BUF_size)];

      _editbuf[0] = '\0';

  //}

  //[임시저장소]
  //{
      //[2007.04.09.월][추가!!!]
      //_editbuf[] 와 같은 크기로 생성한다.
      //_tempbuf = new char [d_BUF_size];
      //[동적생성단위적용][2021.01.24.일][수정]
      _tempbuf = new char [UnitX(1024, d_BUF_size)];

      _tempbuf[0] = '\0';

  //}

  //[edit 변수]
  //{
      //[문서 기준 원점]
      //{
          //[2022.01.06.목][수정]
          //문자가 너무 왼쪽 테두리에 붙이 않도록 <+2> 정도 간격을 추가한다.
          //--------------------------------v
          _text_org_x = _font.margin_size + 2;
          _text_org_y = 2;

          //[2022.06.13.월][추가]
          _text_org_y_ofs = 0;

      //}

      _moonja       = 0;
      _ime_TkorFeng = false;

      _caret_x = 0;
      _caret_y = 0;

      //[관리변수]
      //{
          //[입력지정자] 가 위치해야 하므로 행은 무조건 한개는 있어야 한다.
          _linecount = 1;

          //처음에는 문서가 없다. (단, '\0' 이 있으므로 1 로 설정한다.)
          _filesize = 1;

          _linestartindex = 0;
          _lineendindex   = 0;

          _linetop_linenumber = 0;
          _linetop_startindex = 0;

          _caretindex = 0;

          _row = 0;
          _col = 0;

          _scroll_h_position = 0;

      //}

      //기본적으로 문서는 출력되어야 한다.
      _editbuf_text_on  = true;
      _edit_caretpos_on = true;
      _edit_paste_ing   = false;
      _editbuf_text_no  = false;

      //[선택영역]
      //{
          //[선택영역] 을 초기화 한다.
          //둘중 하나라도 -1 인 경우는 [선택영역] 이 존재하지 않는것이다.
          _select_start.caret_index = -1;
          _select_start.caret_x     = 0;
          _select_start.caret_y     = 0;

          _select_end.caret_index   = -1;
          _select_end.caret_x       = 0;
          _select_end.caret_y       = 0;

          //기본적으로 [선택영역] 은 형성되지 않은 상태이다.
          _select_ing = false;

      //}

      //[마우스] 는 기본적으로 눌린 상태는 아니다.
      _mouse_down  = false;
      _shift_down  = false;
      _scroll_ing  = false;

  //}

  //[컨트롤 그리기]
  //{
      //컨트롤을 그려서 모양을 만든다.
      _Shape();

  //}

  //[캐럿생성]
  //{
      _caret         = new TGCaret_Form(this);
      _caret->Parent = this;

      //[기본설정]
      //{
          _caret->Width  = 2;
          _caret->Height = 12;
          _caret->Left   = _font.margin_size;
          _caret->Top    = 2;

      //}

      //[입력지정자] 의 기본 위치를 지정한다.
      _EDIT_CaretPos(_caret_x, _caret_y);

      //일단 처음에는 보이지 않도록 만든다.
      _caret->Hide();

  //}

  //[속성]
  //{
      //[테두리]
      //{
          _borderColor = d_Color_Part;

      //}

      //[바탕]
      //{
          //[2010.02.06.토][추가!!!]
          _backColor = d_Color_Back;

      //}

      //[글꼴]
      //{
          //[2010.02.06.토][추가!!!]
          _fontColor = d_Color_Font;
          _fontStyle = d_FONTSTYLE_default;

      //}

      //[풍선도움말]
      //{
          //[2010.02.06.토][추가!!!]

          //처음에는 사용하지 않는다.
          _balloonHelpUse = false;

          //문자열을 초기화 한다.
          _balloonHelpStr = NULL;

          //[2010.01.28.목][추가!!!]
          _GBalloonBox    = NULL;

          //[공용풍선 도움말 위치 얻기]
          //{
              //[2010.01.28.목][추가!!!]{------------------------
              //[공용풍선도움말] 의 위치를 얻는다.
              //
              //[2012.05.01.화][중요수정!!!]---------------------
              //<전역변수:_GPART_GBalloonBox> 자체주소를 얻는다.
              //}------------------------------------------------

              GPART_GBalloonBox_GetPtr2(&_GBalloonBox);

          //}

      //}

  //}

  //[빠른목록]
  //{
      _popupMenu         = new TGList_Form(this);

      //[2010.03.09.화][수정!!!]{------------------------------------------------------
      //전체 화면을 대상으로 한다.
      //[빠른목록] 이나 [풍선도움말], [귀뜸말] 등은 전체화면을 대상으로 하는것이 좋다.
      //이렇게 해야 어떤 상황에서도 정확하게 위치를 지정할수 있다.
      _popupMenu->Parent = NULL;//_GVmos->_baseForm;
      //}------------------------------------------------------------------------------

      //<GList> 를 [빠른목록] 으로 사용하므로 처음에 보이지 않게 만들어야 한다.
      _popupMenu->Visible = false;

      //[빠른메뉴] 로 사용하도록 설정한다.
      _popupMenu->_MenuMode(true);

      _popupMenu->_AutoWidth(true);
      _popupMenu->_AutoHeight(true);

      _popupMenu->_BorderColor(_borderColor);
      _popupMenu->_BackColor(_backColor);
      _popupMenu->_FontColor(_fontColor);

      _popupMenu->_FontSelColor(d_Color_FontM);
      _popupMenu->_FontSelBackColor(_borderColor);

      _popupMenu->_FontMouseSelColor(d_Color_FontM);
      _popupMenu->_FontMouseSelBackColor(_borderColor);

      //[항목 설정]
      //{
          //각 항목은 간단하게 [행단위] 로 구분된다.
          _popupMenu->_ItemStr(" 복사 \r\n"
                               " 잘라내기 \r\n"
                               " 붙이기 \r\n"
                               " 삭제 \r\n"
                               " 모두선택 ");

      //}

      //[동작함수 등록]
      //{
          _popupMenu->_EVENT_ItemSelect = _EVENT_ItemSelect_popupMenu;

      //}

  //}

  //[동작 함수 초기화]
  //{
      //[2009.04.10.금][추가!!!]
      _EVENT_KeyEnter = NULL;

      //[2022.06.13.월][추가]{-----
      _EVENT_KeyDown_Esc   = NULL;
      _EVENT_Change        = NULL;
      _EVENT_KeyDown_Left  = NULL;
      _EVENT_KeyDown_Right = NULL;
      _EVENT_KeyDown_Up    = NULL;
      _EVENT_KeyDown_Down  = NULL;
      _EVENT_KeyDown_Home  = NULL;
      _EVENT_KeyDown_End   = NULL;
      //}--------------------------

  //}

  //[OnResize:활성화][폼디자인:모드]
  //{
      //[2018.06.24.일][추가!!!]{-------
      //모든 구성이 생성된 후에 활성화.
      //}-------------------------------

      this->OnResize = FormResize;

  //}

  //[<폼디자인:모드> 적용 인가?][폼디자인:모드]
  //{
      //[2018.06.24.일][추가!!!]{------------------------------------------------------------------
      //[GVmos:폼디자인모드==true] 인 경우는 생성되는 모든 그림부품은 [폼디자인:모드] 로 생성된다.
      //}------------------------------------------------------------------------------------------

      if(GVmos_FormDesignMode() == true)
      {
        _FormDesignMode(true);

      }

  //}

}

__fastcall TGEdit_Form::~TGEdit_Form(void)
{
  //[2012.04.30.월][수정!!!]{---------------
  //안전을 위하여 선언한 역순으로 삭제한다.
  //}---------------------------------------

  if(_balloonHelpStr != NULL) { delete [] _balloonHelpStr; _balloonHelpStr = NULL; }

  if(_popupMenu != NULL) { delete _popupMenu; _popupMenu = NULL; }

  if(_caret != NULL) { delete _caret; _caret = NULL; }

  if(_tempbuf != NULL) { delete [] _tempbuf; _tempbuf = NULL; }

  if(_editbuf != NULL) { delete [] _editbuf; _editbuf = NULL; }

  if(_bmp_back_ != NULL) { delete _bmp_back_; _bmp_back_ = NULL; }

  if(_bmp_ != NULL) { delete _bmp_; _bmp_ = NULL; }

  //[생성자 정보 초기화]
  //{
      //[2010.03.09.화][추가!!!]
      GEdit_CreatorInit(_GPart);

  //}

  //[개체존재:생성]{**************
  //[2012.05.01.화][추가!!!]
  //개체가 생성되었음을 알려준다.
  _this_ = false;
  //}*****************************

}

//[서브클래싱]------------------------------------------------------------------

void __fastcall TGEdit_Form::WndProc(Messages::TMessage  &Message)
{
  //[2006.12.12.화][생성!!!]{-----------------------------------------------------
  //TForm 의 경우는 바탕을 칠하고 OnPaint() 를 발생하게 되므로 실제 _Draw() 를
  //OnPaint() 에서 사용하는 경우는 크기가 변경되는 경우 바로 칠해지지 않아서
  //하얀색 바탕이 그대로 나오게 되어 보기 좋지 않게 된다.
  //따라서 TForm 자체의 바탕을 그리는 WM_ERASEBKGND 에서 바로 _Draw() 를 사용하여
  //그리고 OnPaint() 는 처리하지 않는다.
  //C++Builder 와 같이 완벽하게 처리되는 깜박임이 전혀 없는 화면을 만들수 있다.
  //}-----------------------------------------------------------------------------

  //[VAR]
  //{
      bool  caret_viewalways = false;

  //}

  //[개체존재:확인{*****************
  //[2012.05.01.화][추가!!!]
  //개체가 이미 해제된 뒤라면 종료.
  if(_this_ != true) goto lbEnd;
  //}*******************************

  switch(Message.Msg)
  {
    case WM_ERASEBKGND :
    {
      _Draw();

      //[!]{----------------------------------------------------------------------------
      //바로 죵료하지 않으면 WM_ERASEBKGND 메세지가 다음 TForm::WndProc(Message); 에서
      //다시 처리 되어 TForm->Color 로 칠해지게 되어 _Draw() 로 그린 것이 다 지워지므로
      //바로 종료한다.
      //}-------------------------------------------------------------------------------

      //[2018.02.25.일][추가!!!]{---------------------------------------------
      //코드 일관성을 위하여 <editor_edit> 와 동일하게 넣어준다(차이는 없음).
      Message.Result = true;
      //}---------------------------------------------------------------------
      
      return;

    } break;

    case WM_CHAR :
    {
      //[영문] 인 경우만 처리한다.
      if(_ime_TkorFeng == false)
      {
        _moonja = (Message.WParam & 0xff);

        //0x20 ~ 0x7e 까지만 눈에 보이는 문자이므로 이 범위의 문자만 처리한다.
        if((0x20 <= _moonja) && (_moonja <= 0x7e))
        {
          //[문자출력]{------------------
          _CARET_MOONJA_Insert(_moonja);
          //}----------------------------

          //[동작함수]
          //{
              //[2022.06.13.월][추가]
              if(_EVENT_Change != NULL) { _EVENT_Change(); }

          //}

        }

      }

      //[2018.02.25.일][추가!!!]{---------------------------------------------
      //코드 일관성을 위하여 <editor_edit> 와 동일하게 넣어준다(차이는 없음).
      Message.Result = true;
      //}---------------------------------------------------------------------

      return;
      
    } break;

    case WM_IME_STARTCOMPOSITION :
    {
      _ime_TkorFeng = true;

      //[2018.02.25.일][추가!!!]{---------------------------------------------
      //코드 일관성을 위하여 <editor_edit> 와 동일하게 넣어준다(차이는 없음).
      Message.Result = true;
      //}---------------------------------------------------------------------
      
      return;

    } break;

    case WM_IME_COMPOSITION :
    {
      //[!]{----------------------------------------------------------------
      //실제 WM_IME_COMPOSITION 에서 한글의 조합을 알수 있고 처리할수 있다.
      //실제로 문자를 얻을수 있는 부분이다.
      //}-------------------------------------------------------------------

      //[현재 상태 알기]
      //{
          if     (Message.LParam & GCS_RESULTSTR)
          {
            //[완성된상태][2018.02.21.수][수정!!!]{-----------------------------------
            //완성된 문자는 <_moonja[]>, <_imeBuffer[]> 에 2바이트 값으로 들어 있으나
            //<_imeBuffer[]> 는 [윈도우/Linux:wine] 모두 사용 가능 하고
            //<_moonja[]> 는 [윈도우] 만 사용 가능하므로 <_imeBuffer[]> 를 사용.
            //}-----------------------------------------------------------------------

            //[한글 1 문자 출력]
            //{
                //IME 핸들 얻기.
                _imeHimc = ImmGetContext(this->Handle);

                //IME 버퍼 안의 문자수 얻기.
                _imeBufcount = ImmGetCompositionString(_imeHimc, GCS_RESULTSTR, NULL, 0);

                //저장된 문자열 얻기.
                ImmGetCompositionString(_imeHimc, GCS_RESULTSTR, _imeBuffer, _imeBufcount);

                //IME 핸들 해제.
                ImmReleaseContext(this->Handle, _imeHimc);

                _imeBuffer[_imeBufcount] = '\0';

                //[wine 지원]
                //{
                    //[2018.02.14.수][추가!!!]{-----------------------------------------------------
                    //[Linux:wine] 에서는 [한글모드] 에서 [한글/숫자/기호] 등이 전부
                    //2바이트로 넘어 온다.
                    //[Linux:UTF-8] 인 경우도 [한글] 은 [완성형한글:코드] 로 변환되어 넘어오는데
                    //이 코드값은 <_moonja(유니코드2바이트값)> 에 들어 있지 않고 <_imeBuffer[]> 에
                    //들어있다.
                    //따라서, <_imeBuffer[]> 값의 2바이트 코드를 사용해야 [Linux:wine] 에서 정상적인
                    //[한글] 을 출력할수 있다.
                    //이 경우 일반 [윈도우/Linux:wine] 모두 정확한 [한글] 출력이 된다.
                    //}------------------------------------------------------------------------------

                    //[한글] 2바이트 값 합성.
                    _moonja = ((_imeBuffer[0] << 8)&0xff00) | (_imeBuffer[1]&0x00ff);

                    //[한글] 1 문자 출력.
                    _CARET_MOONJA_Insert(_moonja);

                    //[동작함수]
                    //{
                        //[2022.06.13.월][추가]
                        if(_EVENT_Change != NULL) { _EVENT_Change(); }

                    //}

                //}

            //}
            
            //[한글 조합이 기호등 문자입력으로 완성된 경우 처리]
            //{
                //[2008.02.13.수][추가!!!]{--------------------------------------------------
                //한글이 조합중일때 숫자, 기호등이 눌리면 자동으로 한글이 완성되고 눌려진
                //숫자, 기호가 뒤이어 버퍼에 저장된다.
                //이미 한글 부분은 _moonja 값으로 출력되었으므로 뒤에 붙어 있는 숫자, 기호만
                //읽어서 출력해 주면 된다.
                //IME 버퍼에 2 개가 저장돼 있다면 이미 출력된 한글 부분이므로 무시하고
                //3개 인 경우는 이 3번째 문자만 추가로 출력해주면 된다.
                //}--------------------------------------------------------------------------

                if(_imeBufcount > 2)
                {
                  //이 경우는 맨 뒤의 문자 1 개를 추가로 출력하면 된다.
                  _CARET_MOONJA_Insert(_imeBuffer[2]);

                  //[동작함수]
                  //{
                      //[2022.06.13.월][추가]
                      if(_EVENT_Change != NULL) { _EVENT_Change(); }

                  //}

                }

            //}
            
          }
          else if(Message.LParam & GCS_COMPSTR)
          {
            //[조합중인 상태]{------------------------------------------------------------
            //아직 문자가 완성되지 않고 조합중이다.
            //조함중인경우는 [입력지정자] 모양을 정사각형으로 바꾸고 여기에 출력해 준다.
            //이는 어떤 문자가 조합중인지 상태를 보여주기 위한 것이다.
            //
            //[2018.02.21.수][수정!!!]
            //<Message.WParam> 의 조합중인 문자값은 [윈도우] 에서만 정확하고 [Linux:wine]
            //에서는 [유니코드] 가 들어가서 표시에 아무 의미가 없다.
            //<_imeBuffer[]> 에는 [조합중] 인 문자가 들어 있지 않으므로 일단, [윈도우] 만
            //지원되도록 한다.
            //}---------------------------------------------------------------------------

            //[한글 문자 얻기]{-------------------------
            //현재 입력되고 있는 [한글:문자] 를 얻는다.
            _moonja = (Message.WParam & 0xffff);
            //}-----------------------------------------

            //[입력지정자 문자출력]{---------------------------------------
            //한글이 조합중인 경우는 현재 문자를 [입력지정자] 에 표시한다.
            _caret->_Shape_Composition(_moonja);
            //}------------------------------------------------------------

          }

      //}

      //[2018.02.25.일][추가!!!]{---------------------------------------------
      //코드 일관성을 위하여 <editor_edit> 와 동일하게 넣어준다(차이는 없음).
      Message.Result = true;
      //}---------------------------------------------------------------------

      return;

    } break;

    case WM_IME_ENDCOMPOSITION :
    {
      _ime_TkorFeng = false;

      //[기본 입력지정자]
      //{
          //------------------------------------------
          //다시 기본 [입력지정자] 모양으로 복구한다.
          _caret->_Shape_Default();
          //------------------------------------------
      //}

      //[2018.02.25.일][추가!!!]{---------------------------------------------
      //코드 일관성을 위하여 <editor_edit> 와 동일하게 넣어준다(차이는 없음).
      Message.Result = true;
      //}---------------------------------------------------------------------

      return;

    } break;

    case WM_IME_NOTIFY :
    {
      //[!]{----------------------------------------------------------------
      //[한/영] 전환키가 눌린 경우 발생한다.
      //한번 늘릴때 마다 3 번 발생하는데 현재는 이 부분에 처리할 것은 없다.
      //}-------------------------------------------------------------------

    } break;

    case WM_GETDLGCODE :
    {
      //[2006.12.21.목][중요!!!]
      //{
          //---------------------------------------------------------------------
          //[탭] 키와 [화살표] 키는 기본적으로 지원하지 않으므로 이 프로그램에서
          //이 키들을 사용한다는 것을 OS 에 알려주어야 사용할수 있다.
          Message.Result = DLGC_WANTARROWS | DLGC_WANTTAB;

          //반드시 여기서 종료해야 한다. 그렇지 않은 경우는 유효하지 않게 된다.
          return;
          //---------------------------------------------------------------------
      //}

    } break;

    case WM_SETFOCUS :
    {
      //[입력지정자 보이기]
      //{
          //[!]{-------------------------------------------------
          //[입력지정자] 를 보인다.
          //
          //[2012.04.27.금][수정!!!]-----------------------------
          //[입력지정자] 를 바로 보이지 않고 [타이머:1주기] 뒤에
          //보여주는 <_Show2()> 함수를 사용.
          //}----------------------------------------------------

          _caret->_Show2();

      //}

    } break;

    case WM_KILLFOCUS :
    {
      //[입력지정자]
      //{
          //[2007.09.28.금][추가!!!]
          //{
              //[2007.10.01.월][추가!!!]{-----------------------------------------------
              //만일 [빠른목록] 을 보이는 경우는 [복사/붙이기] 등을 처리해야 하므로
              //계속 선택영역을 유지할수 있게 [입력지정자] 를 이동해서는 안된다.
              //
              //[2012.08.21.화][중요수정!!!]--------------------------------------------
              //[맷돌C] 로 [G.M.RC.BASiC-IDE] 를 새로 만들던중 [알림창:한줄편집기] 에서
              //[선택영역] 지정후 [빠른목록] 을 보일때 [선택영역] 이 [해제] 되는 문제가
              //발생하여 [선택영역] 이 존재하는 경우는 [입력지정자:위치이동] 을 하지
              //않는 것으로 수정.
              //}-----------------------------------------------------------------------

              if(!((_select_start.caret_index >= 0) && (_select_end.caret_index >= 0)))
              {
                //[2012.04.27.금][추가!!!]{-------------------------------------
                //[입력지정자] 를 먼저 감춘 상태로 보이지 않게 처리하도록 한다.
                //이는 잔상이 남는 경우를 방지하기 위한것.
                //}-------------------------------------------------------------

                //감추기.
                _caret->Hide();

                //보이지 않는 상태에서 처리.
                _caret->_Stealth(true);
                //{
                    //촛점을 잃은 경우는 항상 첫문자를 보이게 만든다.
                    _KEY_Home(caret_viewalways);

                //}
                _caret->_Stealth(false);

              }

          //}

          //다시 [입력지정자] 를 감춘다.
          _caret->_Hide();

      //}

    } break;

    case WM_KEYDOWN :
    {
      //[VMOS 이벤트 처리]
      //{
          //[2018.06.23.토][추가!!!]{------------
          //[그림부품] 자체의 이벤트를 처리한다.
          //}------------------------------------

          if((_evtKDown == true) && (Event_Global_Check() == true) &&
             //[2018.07.10.화][추가!!!]
             //[폼디자인:모드] 에서는 이벤트 금지(설정용으로만 사용).
             (_GFormDesignMode != true))
          {
            //[GUI 알림문]{------------------------------------------------------------------------------------
            //먼저 [알림문 저장소:GUI] 에 [알림문:64비트] 를 기록한다.
            GuiMsgQ_Write(GPART_GuiEvtMsg_Make_int((((DTU2 *)_GPart)[5]), d_GuiMsg_KDown, 0, Message.WParam));
            //[VMOS 알림문]------------------------------------------------------------------------------------
            //[알림문 저장소:VMOS] 에 기본 [알림문] 을 기록한다.
            MsgQ_Write(d_PcEvtMsg_Gui);
            //}------------------------------------------------------------------------------------------------

          }

      //}
     
      switch(Message.WParam)
      {
        case VK_INSERT :
        {
          if     ((bool)GetAsyncKeyState(VK_SHIFT) == true)
          {
            //[Shift + Insert] 조합.
            //...
          }
          else if((bool)GetAsyncKeyState(VK_CONTROL) == true)
          {
            //[Conotrl + Insert] 조합.
            //...
          }
          else
          {
            //[Insert] 만 눌린 경우.

            //이전에는 [편집모드] 를 전환하는데 사용되었으나 현재는 단지 [밀어쓰기(끼워넣기)]
            //만 지원하므로 여기서는 아무 처리도 하지 않는다.
          }

          return;
        } break;

        //[캐럿 이동 관련]
        //{
            case VK_UP :
            {
              //[동작함수]
              //{
                  //[2022.06.13.월][추가]
                  if(_EVENT_KeyDown_Up != NULL) { _EVENT_KeyDown_Up(); }

              //}

            } break;

            case VK_DOWN :
            {
              //[동작함수]
              //{
                  //[2022.06.13.월][추가]
                  if(_EVENT_KeyDown_Down != NULL) { _EVENT_KeyDown_Down(); }

              //}

            } break;

            case VK_LEFT :
            {
              //[입력지정자 항상 보이기]
              //{
                  //키는 항상 눈에 보이는 위치에 있어야 한다.
                  _editbuf_text_no = true;
                  //{
                      caret_viewalways = _CARET_ViewAlways();

                  //}
                  _editbuf_text_no = false;

              //}

              _KEY_Left(caret_viewalways);

              //[동작함수]
              //{
                  //[2022.06.13.월][추가]
                  if(_EVENT_KeyDown_Left != NULL) { _EVENT_KeyDown_Left(); }

              //}

              return;
            } break;

            case VK_RIGHT :
            {
              //[입력지정자 항상 보이기]
              //{
                  //키는 항상 눈에 보이는 위치에 있어야 한다.
                  _editbuf_text_no = true;
                  //{
                      caret_viewalways = _CARET_ViewAlways();

                  //}
                  _editbuf_text_no = false;

              //}

              //[선택영역] 처리도 수반해야 한다.
              _KEY_Right(caret_viewalways, true);

              //[동작함수]
              //{
                  //[2022.06.13.월][추가]
                  if(_EVENT_KeyDown_Right != NULL) { _EVENT_KeyDown_Right(); }

              //}

              return;
            } break;

            case VK_HOME :
            {
              //[2007.04.09.월][추가!!!]
              //[Ctrl-Key] 가 눌린 경우는 별도 편집 처리를 수행한다.
              if((bool)GetAsyncKeyState(VK_CONTROL) == true)
              {
                if(_shift_down == true) return; 

              }
              else
              {
                //[입력지정자 항상 보이기]
                //{
                    //키는 항상 눈에 보이는 위치에 있어야 한다.
                    _editbuf_text_no = true;
                    //{
                        caret_viewalways = _CARET_ViewAlways();

                    //}
                    _editbuf_text_no = false;

                //}

                _KEY_Home(caret_viewalways);

              }

              //[동작함수]
              //{
                  //[2022.06.13.월][추가]
                  if(_EVENT_KeyDown_Home != NULL) { _EVENT_KeyDown_Home(); }

              //}

              return;
            } break;

            case VK_END :
            {
              //[2007.04.09.월][추가!!!]
              //[Ctrl-Key] 가 눌린 경우는 별도 편집 처리를 수행한다.
              if((bool)GetAsyncKeyState(VK_CONTROL) == true)
              {
                if(_shift_down == true) return;

              }
              else
              {
                //[입력지정자 항상 보이기]
                //{
                    //키는 항상 눈에 보이는 위치에 있어야 한다.
                    _editbuf_text_no = true;
                    //{
                        caret_viewalways = _CARET_ViewAlways();

                    //}
                    _editbuf_text_no = false;

                //}

                _KEY_End(caret_viewalways);

              }

              //[동작함수]
              //{
                  //[2022.06.13.월[추가]
                  if(_EVENT_KeyDown_End != NULL) { _EVENT_KeyDown_End(); }

              //}

              return;
            } break;

            case VK_PRIOR : //[PageUp]
            {

            } break;

            case VK_NEXT : //[PageDown]
            {

            } break;

        //}

        case VK_ESCAPE : //Esc.
        {
          //[VMOS 이벤트 처리]
          //{
              //[2018.07.03.화][추가!!!]{----------------------
              //[그림부품] 자체의 이벤트를 처리한다.
              //
              //[2018.10.17.수][수정!!!]***********************
              //새로 추가된 <Msg:d_GuiMsg_KEsc> 로 직접 처리.
              //별도 이벤트 이므로 <KeyDown> 과는 별도로 동작.
              //}----------------------------------------------

              if((_evtKEsc == true) && (Event_Global_Check() == true) &&
                 //[2018.07.10.화][추가!!!]
                 //[폼디자인:모드] 에서는 이벤트 금지(설정용으로만 사용).
                 (_GFormDesignMode != true))
              {
                //[GUI 알림문]{-------------------------------------------------------------------------------
                //먼저 [알림문 저장소:GUI] 에 [알림문:64비트] 기록.
                GuiMsgQ_Write(GPART_GuiEvtMsg_Make_int((((DTU2 *)_GPart)[5]), d_GuiMsg_KEsc, 0, VK_ESCAPE));
                //[VMOS 알림문]-------------------------------------------------------------------------------
                //[알림문 저장소:VMOS] 에 기본 [알림문] 기록.
                MsgQ_Write(d_PcEvtMsg_Gui);
                //}-------------------------------------------------------------------------------------------

              }

          //}

          //[동작함수:_EVENT_KeyDown_Esc]
          //{
              //[2022.06.13.월][추가]
              if(_EVENT_KeyDown_Esc != NULL) { _EVENT_KeyDown_Esc(); }

          //}

          return;
        } break;

        case VK_TAB : //Tab.
        {

          return;
        } break;

        case VK_RETURN : //Enter.
        {
          //한줄만 있는 [편집기] 이므로 아무 동작도 하지 않아야 한다.

          //[동작 함수 호출]
          //{
              //[2009.04.10.금][추가!!!]{----
              //해당 [동작함수] 를 호출한다.
              //}----------------------------

              if(_EVENT_KeyEnter != NULL) { _EVENT_KeyEnter(); }

          //}

          //[VMOS 이벤트 처리]
          //{
              //[2018.06.23.토][추가!!!]{-----------------------
              //[그림부품] 자체의 이벤트를 처리한다.
              //
              //[2018.10.17.수][수정!!!]************************
              //새로 추가된 <Msg:d_GuiMsg_KEnter> 로 직접 처리.
              //별도 이벤트 이므로 <KeyDown> 과는 별도로 동작.
              //}-----------------------------------------------

              if((_evtKEnter == true) && (Event_Global_Check() == true) &&
                 //[2018.07.10.화][추가!!!]
                 //[폼디자인:모드] 에서는 이벤트 금지(설정용으로만 사용).
                 (_GFormDesignMode != true))
              {
                //[GUI 알림문]{--------------------------------------------------------------------------------
                //먼저 [알림문 저장소:GUI] 에 [알림문:64비트] 기록.
                GuiMsgQ_Write(GPART_GuiEvtMsg_Make_int((((DTU2 *)_GPart)[5]), d_GuiMsg_KEnter, 0, VK_RETURN));
                //[VMOS 알림문]--------------------------------------------------------------------------------
                //[알림문 저장소:VMOS] 에 기본 [알림문] 기록.
                MsgQ_Write(d_PcEvtMsg_Gui);
                //}--------------------------------------------------------------------------------------------

              }

          //}

        } break;

        case VK_BACK : //Back.
        {
          _KEY_Backspace();

          //[동작함수]
          //{
              //[2022.06.13.월][추가]
              if(_EVENT_Change != NULL) { _EVENT_Change(); }

          //}

          return;
        } break;

        case VK_DELETE : //Del.
        {
          _KEY_Delete();

          //[동작함수]
          //{
              //[2022.06.13.월][추가]
              if(_EVENT_Change != NULL) { _EVENT_Change(); }

          //}

          return;
        } break;

        case VK_SHIFT :
        {
          _shift_down = true;

          return;
        } break;

        default :
        {
          //[편집용 단축키]
          //{
              //[2007.03.22.목][추가!!!]{----------------------------------
              //[편집기] 에서 사용되는 [단축키] 를 처리한다.
              //이미 만들어져 있는 [간편기능항목] 의 함수를 빌려 사용한다.
              //}----------------------------------------------------------

              //다른 <키> 와의 조합은 안되므로 오직 <Ctrl> 만 허용해야 한다.
              if(((bool)GetAsyncKeyState(VK_CONTROL) == true) && //<Ctrl-Key> 이어야 하고
                 ((bool)GetAsyncKeyState(VK_MENU) != true))      //<Alt-Key>  는 아니어야 한다.
              {
                switch(Message.WParam & 0xff)
                {
                  case 'X' : case 'x' : //[잘라내기] <Ctrl + X>
                  {
                    _CLIPBOARD_Cut();

                    //[동작함수]
                    //{
                        //[2022.06.13.월][추가]
                        if(_EVENT_Change != NULL) { _EVENT_Change(); }

                    //}

                  } break;

                  case 'C' : case 'c' : //[복    사] <Ctrl + C>
                  {
                    _CLIPBOARD_Copy();

                  } break;

                  case 'V' : case 'v' : //[붙 이 기] <Ctrl + V>
                  {
                    _CLIPBOARD_Paste();

                    //[동작함수]
                    //{
                        //[2022.06.13.월][추가]
                        if(_EVENT_Change != NULL) { _EVENT_Change(); }

                    //}

                  } break;

                  case 'A' : case 'a' : //[전체선택] <Ctrl + A>
                  {
                    //[2018.02.24.토][추가!!!]{-------
                    //그동안 빼먹고 안넣은 것 넣는다.
                    //[전체선택] 수행.
                    //}-------------------------------

                    _EDIT_SelectAll();

                    //[동작함수]
                    //{
                        //[2022.06.13.월][추가]{-----------------
                        //[전체선택] 의 경우는 [키:End] 로 취급.
                        //}--------------------------------------

                        if(_EVENT_KeyDown_End != NULL) { _EVENT_KeyDown_End(); }

                    //}

                  } break;

                }

            //}

          }

          return;
        } break;

      }

      return;
    } break;

    case WM_KEYUP :
    {
      //[VMOS 이벤트 처리]
      //{
          //[2018.06.23.토][추가!!!]{------------
          //[그림부품] 자체의 이벤트를 처리한다.
          //}------------------------------------

          if((_evtKUp == true) && (Event_Global_Check() == true) &&
             //[2018.07.10.화][추가!!!]
             //[폼디자인:모드] 에서는 이벤트 금지(설정용으로만 사용).
             (_GFormDesignMode != true))
          {
            //[GUI 알림문]{----------------------------------------------------------------------------------
            //먼저 [알림문 저장소:GUI] 에 [알림문:64비트] 를 기록한다.
            GuiMsgQ_Write(GPART_GuiEvtMsg_Make_int((((DTU2 *)_GPart)[5]), d_GuiMsg_KUp, 0, Message.WParam));
            //[VMOS 알림문]----------------------------------------------------------------------------------
            //[알림문 저장소:VMOS] 에 기본 [알림문] 을 기록한다.
            MsgQ_Write(d_PcEvtMsg_Gui);
            //}----------------------------------------------------------------------------------------------

          }

      //}

      switch(Message.WParam)
      {
        case VK_SHIFT :
        {
          //[shift-key]
          //{
              //[TEST]
              //{
                  if((_select_start.caret_index >= 0) && (_select_end.caret_index >= 0))
                  {
                    //현재 [선택영역] 을 보여주기.
                    //_DEBUG_SelectTextView();

                  }

              //}

              //[선택영역 관련 초기화][2007.02.10.토]
              //{
                  //<shift> 가 올라온 경우는 키로 만드는 [선택영역] 생성이 끝난 것으로 본다.
                  _select_ing = false;
                  _shift_down = false;

              //}

          //}

        }

      }

      return;
    } break;

    case WM_SYSKEYDOWN : //[Alt] 와 결합된 시스템키 down.
    {

      return;
    } break;

    case WM_SYSKEYUP : //[Alt] 와 결합된 시스템키 up.
    {

      return;
    } break;

    case  CM_MOUSEENTER :
    {
      //[2008.02.03.일][추가!!!]
      //[풍선도움말] 을 설정해 준다.
      if((_balloonHelpUse == true) && ((*_GBalloonBox) != NULL) &&
         //[2010.03.09.화][추가!!!]
         //[편집기] 에 촛점이 있는 경우는 작업중이므로 [풍선도움말] 을 보이지 않는다.
         (this->Focused() != true) &&
         //[2018.07.12.목][추가!!!]
         //[폼디자인:모드] 에서는 사용하지 않는다.
         (_GFormDesignMode != true))
      {
        //[2010.03.09.화][중요수정!!!]{------------------------------------------------
        //[빠른목록] 이 이미 보여지고 있는 경우 [풍선도움말] 을 보이면 [촛점문제] 로
        //심각한 문제가 발생하므로, [빠른목록] 이 이미 보이고 있을때는 [풍선도움말] 을
        //아예 보여주지 않는 것으로 수정한다.
        //}----------------------------------------------------------------------------

        if(_popupMenu->Visible != true)
        {
          (*_GBalloonBox)->_BalloonBox(Mouse->CursorPos.x, Mouse->CursorPos.y, true, _balloonHelpStr);

        }

      }

    } break;

    case CM_MOUSELEAVE :
    {
      //[2008.02.03.일][추가!!!]
      //[풍선도움말] 을 감춘다.
      if((_balloonHelpUse == true) && ((*_GBalloonBox) != NULL) &&
         //[2018.07.12.목][추가!!!]
         //[폼디자인:모드] 에서는 사용하지 않는다.
         (_GFormDesignMode != true))
      {
        (*_GBalloonBox)->_Hide();

      }  

    } break;

  }

  lbEnd:

  //[기본처리]
  //{
      //기본 메세지는 반드시 처리 되어야 한다.
      TForm::WndProc(Message);

  //}

  return;

}

//------------------------------------------------------------------------------

void __fastcall TGEdit_Form::_BorderColor(int  pBorderColor)
{
  //[2010.02.03.수][생성!!!]

  //[개체존재:확인{****************************
  //[2012.05.01.화][추가!!!]
  //개체가 이미 해제된 뒤라면 처리하지 않는다.
  if(_this_ != true) return;
  //}******************************************

  _borderColor = pBorderColor;

  //[테두리 만들기]
  //{
      _bmp_->Canvas->Pen->Color   = _borderColor;
      _bmp_->Canvas->Pen->Width   = 1;
      _bmp_->Canvas->Brush->Style = bsClear;
      _bmp_->Canvas->Rectangle(_bmp_->Canvas->ClipRect);

  //}

  //[view]{-------------
  //그린 것을 보여준다.
  _Draw();
  //}-------------------

  //[목록처리]
  //{
      //[2010.02.13.토][추가!!!]
      _popupMenu->_BorderColor(_borderColor);
      _popupMenu->_FontSelBackColor(_borderColor);
      _popupMenu->_FontMouseSelBackColor(_borderColor);

  //}

}

void __fastcall TGEdit_Form::_BackColor(int  pBackColor)
{
  //[2010.02.06.토][생성!!!]

  //[개체존재:확인{****************************
  //[2012.05.01.화][추가!!!]
  //개체가 이미 해제된 뒤라면 처리하지 않는다.
  if(_this_ != true) return;
  //}******************************************

  _backColor = pBackColor;

  //[배경에 반영]
  //{
      //바탕색으로 지운다.
      _bmp_back_->Canvas->Brush->Color = _backColor;
      _bmp_back_->Canvas->FillRect(_bmp_back_->Canvas->ClipRect);

  //}

  //[다시그리기]
  //{
      //설정 반영을 위하여 다시 그린다.
      _EditBuf_Text();

  //}

  //[목록처리]
  //{
      //[2010.02.13.토][추가!!!]
      _popupMenu->_BackColor(_backColor);

  //}

}

void __fastcall TGEdit_Form::_FontColor(int  pFontColor)
{
  //[2010.02.06.토][생성!!!]

  //[개체존재:확인{****************************
  //[2012.05.01.화][추가!!!]
  //개체가 이미 해제된 뒤라면 처리하지 않는다.
  if(_this_ != true) return;
  //}******************************************

  _fontColor = pFontColor;

  //[다시그리기]
  //{
      //설정 반영을 위하여 다시 그린다.
      _EditBuf_Text();

  //}

  //[목록처리]
  //{
      //[2010.02.13.토][추가!!!]
      _popupMenu->_FontColor(_fontColor);

  //}

}

void __fastcall TGEdit_Form::_FontStyle(int  pFontStyle)
{
  //[2010.02.06.토][생성!!!]

  //[개체존재:확인{****************************
  //[2012.05.01.화][추가!!!]
  //개체가 이미 해제된 뒤라면 처리하지 않는다.
  if(_this_ != true) return;
  //}******************************************

  _fontStyle = pFontStyle;

  //[다시그리기]
  //{
      //설정 반영을 위하여 다시 그린다.
      _EditBuf_Text();

  //}

}

void __fastcall TGEdit_Form::_Shape(void)
{
  //[2006.11.01.수][생성!!!]{-----------------------------------------
  //이 폼생성시에는 동적 생성되지 않는 내부 요소들이 있으므로 그릴때
  //_EditBuf_Text() 함수를 사용하지 않고 이 함수를 사용해야 한다.
  //이때만 한번 사용된다.
  //
  //[2007.03.25.일][수정!!!]------------------------------------------
  //[전체배경사용] 의 경우 한개의 배경을 사용하는 것처럽 보이게 한다.
  //}-----------------------------------------------------------------

  //[개체존재:확인{****************************
  //[2012.05.01.화][추가!!!]
  //개체가 이미 해제된 뒤라면 처리하지 않는다.
  if(_this_ != true) return;
  //}******************************************

  //[바탕색]
  //{
      //[2007.03.25.일][수정!!!]

      //배경 그림으로 바탕을 지운다.
      BitBlt(_bmp_->Canvas->Handle, d_X_width, 0,
             _bmp_->Width, _bmp_->Height,
             _bmp_back_->Canvas->Handle, 0, 0, SRCCOPY);

  //}

  //[활성/비활성 처리]
  //{
      //[2018.08.26.일][추가!!!]{--------------------------
      //[테두리] 를 제외한 이제까지 그린 것을 비활성 처리.
      //}--------------------------------------------------

      if(this->Enabled == false)
      {
        BMP_Brightness(_bmp_, d_Bright_Disable);

      }

  //}

  //[테두리 만들기]
  //{
      _bmp_->Canvas->Pen->Color   = _borderColor;
      _bmp_->Canvas->Pen->Width   = 1;
      _bmp_->Canvas->Brush->Style = bsClear;
      _bmp_->Canvas->Rectangle(_bmp_->Canvas->ClipRect);

  //}

  //[view]{-------------
  //그린 것을 보여준다.
  _Draw();
  //}-------------------

  return ;

}

void __fastcall TGEdit_Form::_Draw(void)
{
  //[2006.11.27.월][생성!!!]

  //[개체존재:확인{****************************
  //[2012.05.01.화][추가!!!]
  //개체가 이미 해제된 뒤라면 처리하지 않는다.
  if(_this_ != true) return;
  //}******************************************

  BitBlt(this->Canvas->Handle, 0, 0,
         this->Width, this->Height,
         _bmp_->Canvas->Handle, 0, 0, SRCCOPY);

  return;

}

void __fastcall TGEdit_Form::_Width(int  pWidth)
{
  //[2007.09.30.일][생성!!!]{-----------------------------
  //크기가 바뀐경우는 _bmp_ 도 다시 크기를 수정해야 한다.
  //그리고 새롭게 다시 그려준다.
  //}-----------------------------------------------------

  //[개체존재:확인{****************************
  //[2012.05.01.화][추가!!!]
  //개체가 이미 해제된 뒤라면 처리하지 않는다.
  if(_this_ != true) return;
  //}******************************************

  this->Width  = pWidth;

  _bmp_->Width = this->Width;

  //[배경]
  //{
      _bmp_back_->Width  = _bmp_->Width;

      //바탕색으로 지운다.
      _bmp_back_->Canvas->Brush->Color = _backColor;
      _bmp_back_->Canvas->FillRect(_bmp_back_->Canvas->ClipRect);

 //}

  _EditBuf_Text();

}

void __fastcall TGEdit_Form::_Height(int  pHeight)
{
  //[2010.02.06.토][생성!!!]{-----------------------------
  //크기가 바뀐경우는 _bmp_ 도 다시 크기를 수정해야 한다.
  //그리고 새롭게 다시 그려준다.
  //}-----------------------------------------------------

  //[개체존재:확인{****************************
  //[2012.05.01.화][추가!!!]
  //개체가 이미 해제된 뒤라면 처리하지 않는다.
  if(_this_ != true) return;
  //}******************************************

  this->Height  = pHeight;

  _bmp_->Height = this->Height;

  //[배경]
  //{
      _bmp_back_->Height  = _bmp_->Height;

      //바탕색으로 지운다.
      _bmp_back_->Canvas->Brush->Color = _backColor;
      _bmp_back_->Canvas->FillRect(_bmp_back_->Canvas->ClipRect);

 //}

  _EditBuf_Text();

}

//[폼디자인:모드]
void __fastcall TGEdit_Form::_Width_Height(int  pWidth, int  pHeight)
{
  //[2018.06.24.일][생성!!!]{------------------------------------
  //[폼디자이너] 지원을 위하여 둘을 동시 조정 할수있도록 만든다.
  //}------------------------------------------------------------

  //[개체존재:확인{****************************
  //[2012.05.01.화][추가!!!]
  //개체가 이미 해제된 뒤라면 처리하지 않는다.
  if(_this_ != true) return;
  //}******************************************

  this->Width  = pWidth;
  this->Height = pHeight;

  //[bmp 크기 조정]
  //{
      //다른 컨트롤과 달리 크기 조정이 될수 있으므로 이 경우 bmp 도 같이 조정한다.
      _bmp_->Width  = this->Width;
      _bmp_->Height = this->Height;

  //}

  //[배경]
  //{
      _bmp_back_->Width  = _bmp_->Width;
      _bmp_back_->Height = _bmp_->Height;

      //바탕색으로 지운다.
      _bmp_back_->Canvas->Brush->Color = _backColor;
      _bmp_back_->Canvas->FillRect(_bmp_back_->Canvas->ClipRect);

 //}

  _EditBuf_Text();

}

void __fastcall TGEdit_Form::_Left(int  pLeft)
{
  //[2010.02.06.토][생성!!!]

  //[개체존재:확인{****************************
  //[2012.05.01.화][추가!!!]
  //개체가 이미 해제된 뒤라면 처리하지 않는다.
  if(_this_ != true) return;
  //}******************************************

  this->Left  = pLeft;

}

void __fastcall TGEdit_Form::_Top(int  pTop)
{
  //[2010.02.06.토][생성!!!]

  //[개체존재:확인{****************************
  //[2012.05.01.화][추가!!!]
  //개체가 이미 해제된 뒤라면 처리하지 않는다.
  if(_this_ != true) return;
  //}******************************************

  this->Top  = pTop;

}

void __fastcall TGEdit_Form::_Enable(bool  pEnable)
{
  //[2018.08.25.토][생성!!!]{------------------------------
  //[유/무효] 시 [모양] 에 [색] 으로 반영하기 위하여 필요.
  //}------------------------------------------------------

  //[활성/비활성]{*********************************************
  //<_Shape()> 에 <this->Enabled> 를 사용해서 [활성/비활성] 을
  //판별하므로 반드시 먼저 처리.
  this->Enabled = pEnable;
  //}**********************************************************

  //모양에 반영하기 위하여 반드시 다시 그려준다.
  _EditBuf_Text();

}

void __fastcall TGEdit_Form::_EditBuf_Text(void)
{
  //[2006.11.28.화][생성!!!]{-----------------------------------
  //_editbuf[] 의 내용을 일괄 출력한다.
  //[문서원점] 은 현재 설정되 있는 값을 그대로 사용하면 된다.
  //[문서원점] 은 [스크롤] 되는 경우 자동으로 다시 계산된다.

  //[2007.03.25.일][수정!!!]------------------------------------
  //[전체그림사용] 의 경우는 이 폼의 위치에 해당하는 그림영역이
  //표시 되도록 수정한다.
  //}-----------------------------------------------------------

  //[개체존재:확인{****************************
  //[2012.05.01.화][추가!!!]
  //개체가 이미 해제된 뒤라면 처리하지 않는다.
  if(_this_ != true) return;
  //}******************************************

  //[중복출력방지]
  //{
      //[2007.01.06.토][추가!!!]------------------------------------------
      //이 경우는 아무것도 처리하지 않아야 한다.
      //이는 문서를 전환하는 경우 동시에 [수평/수직 스크롤] 이 설정되면서
      //문서가 중복 출력되어 깜박거리는 것을 방지 하기 위한 것이다.
      if(_editbuf_text_on == false) return;
      //------------------------------------------------------------------
  //}

  //[중복 방지]
  //{
      //[2007.03.08.목][추가!!!]
      //[붙이기] 동작을 수행할때 [입력지정자] 를 항상 눈에 보이는 위치에 있게
      //만들어야 하는데 이때 사용되는 _CARET_ViewAlways() 함수안에서 이 함수가
      //한번 처리되므로 [붙이기] 부분에서 한번만 처리 되도록 여기서는 처리하지
      //않아야 한다.
      if(_edit_paste_ing == true) return;

  //}

  //[중복 방지]
  //{
      //[2007.03.09.금][추가!!!]
      if(_editbuf_text_no == true) return;

  //}

  //[바탕색]
  //{
      //[2007.03.25.일][수정!!!]

      //배경 그림으로 바탕을 지운다.
      BitBlt(_bmp_->Canvas->Handle, d_X_width, 0,
             _bmp_->Width, _bmp_->Height,
             _bmp_back_->Canvas->Handle, 0, 0, SRCCOPY);

  //}

  //[문자열]
  //{
      //[수평스크롤 적용]
      //{
          //x 출력 위치는 기본적으로 margin_size 을 기준으로 출력된다.
          //실제 출력시 사용되는 함수상의 x 좌표는 margin_size 에 대한 offset 위치가 된다.
          _font.margin_size = _text_org_x;

      //}

      //[현재행 시작 위치 구하기]
      //{
          //[2007.02.20.화][추가!!!]
          //[선택영역]이 해제되는 경우 [입력지정자] 위치가 바뀌에 되어 _caretindex 값도
          //수정되므로 [입력지정자] 가 위치한 행의 시작 index 도 다시 계산해 주어야 한다.
          STR_Get_ThisLineStartIndex(_editbuf, _caretindex, &_linestartindex);

      //}

      //새로 적용하는 함수.
      _EDITBUF_STRING_Out(0, _text_org_y, _linetop_startindex);

  //}

  //[활성/비활성 처리]
  //{
      //[2018.08.26.일][추가!!!]{--------------------------
      //[테두리] 를 제외한 이제까지 그린 것을 비활성 처리.
      //}--------------------------------------------------

      if(this->Enabled == false)
      {
        BMP_Brightness(_bmp_, d_Bright_Disable);

      }

  //}

  //[테두리 만들기]
  //{
      _bmp_->Canvas->Pen->Color   = _borderColor;
      _bmp_->Canvas->Pen->Width   = 1;
      _bmp_->Canvas->Brush->Style = bsClear;
      _bmp_->Canvas->Rectangle(_bmp_->Canvas->ClipRect);

  //}

  //[표시하기]
  //{
      _Draw();

  //}

}

void __fastcall TGEdit_Form::_EditBuf_Clear(void)
{
  //[2006.12.17.일][생성!!!]{-------------------------------------
  //[저장소] 를 완전히 비운다.
  //표시할 내용이 없으므로 [편집기] 내용도 다시 그려 주어야 한다.
  //}-------------------------------------------------------------

  //[개체존재:확인{****************************
  //[2012.05.01.화][추가!!!]
  //개체가 이미 해제된 뒤라면 처리하지 않는다.
  if(_this_ != true) return;
  //}******************************************

  //[저장소 비우기]
  //{
      _editbuf[0] = '\0';

  //}

  //[문서 출력]
  //{
      //[저장소] 가 비워졌으므로 [편집기] 창을 다시 그려서 비워 주어야 한다.
      _EditBuf_Text();

  //}

  return;

}

void __fastcall TGEdit_Form::_EDIT_Get_TextOrigin(void)
{
  //[2006.12.23.토][생성!!!]{-------------------------------------------------------
  //문서 기준 원점을 얻는다.
  //[스크롤] 되는 경우는 [문서원점] 이 수정되므로 항상 이 함수로 다시 [문서원점] 을
  //설정해 주어야 한다.
  //}-------------------------------------------------------------------------------

  //[개체존재:확인{****************************
  //[2012.05.01.화][추가!!!]
  //개체가 이미 해제된 뒤라면 처리하지 않는다.
  if(_this_ != true) return;
  //}******************************************

  //[좌표수정]
  //{
      //[2022.01.06.목][수정]
      //문자가 너무 왼쪽 테두리에 붙이 않도록 <+2> 정도 간격을 추가한다.
      //--------------------------------------------------------------------------v
      _text_org_x = (-1) * _scroll_h_position * _word_width + d_X_width + 2 - 1 + 2;

  //}

  return;

}

void __fastcall TGEdit_Form::_EDIT_H_Scroll(void)
{
  //[2006.12.31.일][생성!!!]{---------------------------------------------
  //이 문서가 [수평 스크롤] 되는 경우 처리를 수행한다.

  //[2007.02.11.일][수정!!!]
  //[줄이동] 중인 경우는 _scroll_ing == true 로 설정하여 _EDIT_CaretPos()
  //함수안에서 [선택영역] 이 해제되는 것을 막아야 한다.
  //}---------------------------------------------------------------------

  //[개체존재:확인{****************************
  //[2012.05.01.화][추가!!!]
  //개체가 이미 해제된 뒤라면 처리하지 않는다.
  if(_this_ != true) return;
  //}******************************************

  //[문서 출력 원점 얻기]
  //{
      //[문서] 를 출력할 기준 원점을 구한다.
      //이미 [스크롤] 된 상태이므로 이 시점에서 [문서원점] 을 구해야 한다.
      _EDIT_Get_TextOrigin();

  //}

  //[문서 출력]
  //{
      //[편집기] 내용 동기화.
      _EditBuf_Text();

  //}

  //[입력지정자]
  //{
      //현재 [줄이동] 중이므로 [선택영역] 이 해제 되지 않게 만든다.
      _scroll_ing = true;
      //{
          //[문서원점] 이 수정되었으므로 [입력지정자] 위치도 다시 설정한다.
          _EDIT_CaretPos(_caret_x, _caret_y);

      //}
      _scroll_ing = false;
      
  //}

  return;

}

//[편집관련]--------------------------------------------------------------------

void __fastcall TGEdit_Form::_EDIT_Init(void)
{
  //[2007.01.18.목][생성!!!]{-------------------------------------------------------------
  //열려 있던 문서가 다시 전부 닫히는 경우 이 함수를 사용하여 다시 전부 초기화 해야 한다.
  //}-------------------------------------------------------------------------------------

  //[개체존재:확인{****************************
  //[2012.05.01.화][추가!!!]
  //개체가 이미 해제된 뒤라면 처리하지 않는다.
  if(_this_ != true) return;
  //}******************************************

  //[edit 변수]
  //{
      //내용이 아무것도 없는 것이다.
      _editbuf[0] = '\0';
      _tempbuf[0] = '\0';

      //[문서 기준 원점]
      //{
          _font.margin_size = d_X_width + 2 - 1;
          //[2022.01.06.목][수정]
          //문자가 너무 왼쪽 테두리에 붙이 않도록 <+2> 정도 간격을 추가한다.
          //--------------------------------v
          _text_org_x = _font.margin_size + 2;
          _text_org_y = 2 +
                        //[2022.06.13.월][추가]
                        //<_text_org_y> 에 추가 보정을 제공.
                        _text_org_y_ofs;

      //}

      _moonja       = 0;
      _ime_TkorFeng = false;

      _caret_x = 0;
      _caret_y = 0;

      //[관리변수]
      //{
          //[입력지정자] 가 위치해야 하므로 행은 무조건 한개는 있어야 한다.
          _linecount = 1;

          //처음에는 문서가 없다. (단, '\0' 이 있으므로 1 로 설정한다.)
          _filesize = 1;

          _linestartindex = 0;
          _lineendindex   = 0;

          _linetop_linenumber = 0;
          _linetop_startindex = 0;

          _caretindex      = 0;
          _caretindexwidth = 0;

          _row = 0;
          _col = 0;

          _scroll_h_position = 0;
          
      //}

      //기본적으로 문서는 출력되어야 한다.
      _editbuf_text_on  = true;
      _edit_caretpos_on = true;
      _edit_paste_ing   = false;
      _editbuf_text_no  = false;

      //[마우스] 는 기본적으로 눌린 상태는 아니다.
      _mouse_down  = false;
      _shift_down  = false;
      _scroll_ing  = false;

  //}

  //[선택영역]
  //{
      //[2007.03.17.토][추가!!!]
      //[선택영역] 을 초기화 한다.
      //둘중 하나라도 -1 인 경우는 [선택영역] 이 존재하지 않는것이다.
      _select_start.caret_index = -1;
      _select_start.caret_x     = 0;
      _select_start.caret_y     = 0;

      _select_end.caret_index   = -1;
      _select_end.caret_x       = 0;
      _select_end.caret_y       = 0;

      //기본적으로 [선택영역] 은 형성되지 않은 상태이다.
      _select_ing        = false;

  //}

  //[캐럿]
  //{
      //[입력지정자] 의 기본 위치를 지정한다.
      _EDIT_CaretPos(_caret_x, _caret_y);

  //}

  //[문서 출력]
  //{
      //불러들인 문서를 [편집기] 에 출력하여 보여준다.
      _EditBuf_Text();

  //}

  return;

}

void __fastcall TGEdit_Form::_EDIT_TextOut(char  pText[])
{
  //[2007.09.30.일][생성!!!]{---
  //전달받은 문자열을 출력한다.
  //}---------------------------

  //[VAR]
  //{
      int  size;

  //}

  //[개체존재:확인{****************************
  //[2012.05.01.화][추가!!!]
  //개체가 이미 해제된 뒤라면 처리하지 않는다.
  if(_this_ != true) return;
  //}******************************************

  _EDIT_Init();

  size = strlen(pText);

  //[저장소 크기를 넘는가?]
  //{
      if(size < d_BUF_size)
      {
        //문자열 할당.
        STR_Assign(_editbuf, pText);

      }
      else
      {
        //[!]{--------------------------------------------------------------------
        //이 경우는 저장소 크기에 넣을수 있는 만큼만 저장하되, 한글의 경우 문자가
        //잘리지 않도록 자동으로 구분처리하는 STR_SetLength() 함수를 사용한다.
        //}-----------------------------------------------------------------------

        //'\0' 공간은 제외한다-------------------v
        size = STR_SetLength(pText, d_BUF_size - 1, ' ', _editbuf);

      }

  //}

  //추가한 문자의 길이만큼 [문서양] 을 증가시킨다.
  _filesize += size;

  //[문서 출력]
  //{
      //불러들인 문서를 [편집기] 에 출력하여 보여준다.
      _EditBuf_Text();

  //}

  //항상 첫문자를 보이게 만든다.
  _KEY_Home(false);

  return;

}

bool __fastcall TGEdit_Form::_EDIT_IS_ImeHangul(void)
{
  //[2006.12.19.화][생성!!!]{------------
  //현재 IME 가 [한글/영문] 인지 구한다.
  //한글 : true.
  //영문 : false.
  //}------------------------------------

  //[VAR]
  //{
      HIMC   mode;
      DWORD  conversion, sentence;

  //}

  //[개체존재:확인{****************************
  //[2012.05.01.화][추가!!!]
  //개체가 이미 해제된 뒤라면 처리하지 않는다.
  if(_this_ != true) return(false);
  //}******************************************

  mode = ImmGetContext(this->Handle);

  ImmGetConversionStatus(mode, &conversion, &sentence);

  //한글(true)/영문(false) 를 반환한다.
  return (conversion == IME_CMODE_HANGEUL);

}

void __fastcall TGEdit_Form::_EDIT_Change_ImeMode(void)
{
  //[2006.12.19.화][생성!!!]{----------------------------------------------------------
  //현재 Iime-mode 를 전환한다.
  //실제 ime-mode 는 OS 자체에서 자동으로 전환되므로 여기서 추가로 처리할 필요는 없다.
  //}----------------------------------------------------------------------------------

  //[VAR]
  //{
      HIMC   mode;

  //}

  //[개체존재:확인{****************************
  //[2012.05.01.화][추가!!!]
  //개체가 이미 해제된 뒤라면 처리하지 않는다.
  if(_this_ != true) return;
  //}******************************************

  mode = ImmGetContext(this->Handle);

  if (_EDIT_IS_ImeHangul() == true)
  {
    //[한글] => [영문]
    ImmSetConversionStatus(mode, IME_CMODE_ALPHANUMERIC, IME_CMODE_ALPHANUMERIC);

  }
  else
  {
    //[영문] => [한글]
    ImmSetConversionStatus(mode, IME_CMODE_HANGEUL, IME_CMODE_HANGEUL);

  }

  return;

}

void __fastcall TGEdit_Form::_EDIT_CaretPos(int  pX,
                                            int  pY)
{
  //[2006.12.23.토][생성!!!]{------------------------------------------------
  //[입력지정자] 의 위치를 지정한다.
  //이 위치는 [문서원점] 을 원점으로한 위치가 된다.

  //[2007.02.11.일][수정!!!]
  //[입력지정자] 가 이동하는 경우 [선택영역] 이 해제 될수 있다.
  //그러나, 이미 [선택영역] 이 존재하는 경우 [줄이동] 시에는 선택영역을
  //해제하지 않아야 한다.

  //[2007.03.16.금][참고!!!]
  //이 함수는 [입력지정자] 위치만 수정할 뿐 자체적으로 _EditBuf_Text()
  //함수로 다시 그려지지 않는다. 단, [선택영역] 이 해제되는 경우는
  //_EditBuf_Text() 로 다시 그려서 해제된것을 반영해 주어야 한다.

  //[2007.04.08.일][수정!!!]
  //[왼쪽무효영역] 에 [입력지정자] 가 위치하는 경우는 보이지 않게 해야 한다.
  //}------------------------------------------------------------------------

  //[개체존재:확인{****************************
  //[2012.05.01.화][추가!!!]
  //개체가 이미 해제된 뒤라면 처리하지 않는다.
  if(_this_ != true) return;
  //}******************************************

  //처리를 요하는 경우만 처리한다.
  if(_edit_caretpos_on != true) return;

  //[위치지정]
  //{
      //[2007.04.08.일][수정!!!]
      //[왼쪽무효영역] 에 [입력지정자] 가 위치하는 경우는 보이지 않게 해야 한다.
      if(((_text_org_x - d_X_width) + _caret_x) < 0)
      {
        //[왼쪽무효영역(검증판)] 을 벗어나는 경우는 [입력지정자] 를 보이지 않게 만들어야 한다.
        //이 영역은 일반적으로 "gutter" 영역으로 불린다.
        _caret->_Hide();

      }
      else
      {
        //실제 위치를 [문서원점] 에 대한 좌표로 환산하여 지정한다.
        //자동으로 다시 [입력지정자] 가 보여지므로 굳이 _Show() 를 호출하지 않아도 된다.
        _caret->_Position(_text_org_x + pX, _text_org_y + pY);

      }

  //}

  //[행/열 계산]
  //{
      //[입력지정자] 가 위치한 [행/열] 값을 구하여 정보 표시 위치에 표시한다.

      //[행 구하기]
      //{
          //행은 바로 구할수 있으므로 여기서 전역 변수에 저장까지 수행한다.
          _row = _caret_y / _line_height;
          
      //}

  //}

  //[선택영역해제][2007.02.11.일]
  //{
      //[줄이동] 시에는 [선택영역] 은 그대로 유지시킨다.
      if(_scroll_ing == false)
      {
        if((_shift_down == false) && (_mouse_down == false) &&
           ((_select_start.caret_index >= 0) || (_select_end.caret_index >= 0)))
        {
          //[선택영역] 정보 초기화.

          _select_start.caret_index = -1;
          _select_start.caret_x     = 0;
          _select_start.caret_y     = 0;

          _select_end.caret_index   = -1;
          _select_end.caret_x       = 0;
          _select_end.caret_y       = 0;

          _select_ing               = false;

          //[선택영역] 해제 반영. 
          _EditBuf_Text();

        }

      }

  //}

  return;

}

void __fastcall TGEdit_Form::_EDIT_Get_Kor_Eng_Moonja_Width(void)
{
  //[2006.12.27.수][생성!!!]{------------------------------------------------------------
  //현재 [편집기] 는 [고정폭 글꼴] 을 사용하고 있으므로 [한글/영문] 각각 대표하는 문자의
  //[문자폭] 을 한개씩만 대표값으로 구해 놓는다.
  //이는 [입력지정자] 위치 산출등 여러 곳에 사용되는 중요한 값이다.
  //이 함수는 [글꼴] 을 생성한 다음에 한번만 호출하면 된다.
  //덤으로 _line_height 값도 같이 얻는다.
  //
  //직접 문자를 출력하나 [편집기] 배경이 지워지기 전에 이 함수를 사용하면 출력된 문자는
  //배경을 그릴때 다 지워 지므로 상관없다.
  //}------------------------------------------------------------------------------------

  //[개체존재:확인{****************************
  //[2012.05.01.화][추가!!!]
  //개체가 이미 해제된 뒤라면 처리하지 않는다.
  if(_this_ != true) return;
  //}******************************************

  //[한글] 대표문자 출력.
  FONT_Out_Moonja(_bmp_, &_font, 0, 0, _fontColor, _backColor, false, '김', &_font_kor_width, &_line_height);

  //[영문] 대표문자 출력.
  FONT_Out_Moonja(_bmp_, &_font, 0, 0, _fontColor, _backColor, false, 'A', &_font_eng_width, &_line_height);

  return;

}

void __fastcall TGEdit_Form::_CARETINDEX_CaretIndexWidth(int   pLINEstartINDEX,
                                                            int   pCARETINDEX,
                                                            int  *rCARETINDEXwidth,
                                                            int  *rMOONJAcount)
{
  //[2006.12.27.수][생성!!!]{-----------------------------------------------
  //현재 _caretindex 가 위치한 행의 시작 부분에서 _caretindex 이전 문자의
  //모든 [폭] 을 더하여 실제 표시되는 [편집기] 상에서 _caretindex 가 위치한
  //문자까지의 [폭] 을 얻는다.
  //내부 처리시 자동으로 문자수까지 알게 되므로 [문자수] 도 같이 반환한다.
  //
  //[2018.02.24.토][수정!!!]------------------------------------------------
  //}-----------------------------------------------------------------------

  //[방법]
  //{
      //-----------------------------------------------------------------------
      //[영문] 을 제외 하고는 전부 [폭] 이 같으므로 [한글/영문] 의 [문자폭] 의
      //이미 구해 놓은 [대표값] 으로 간단하게 [폭] 을 구할수 있다.
      //즉, [영문] 인지 아닌지만 알면 각각의 문자 폭을 간단하게 알수 있다.
      //-----------------------------------------------------------------------
  //}

  //[주의!!!][2007.01.14.일]
  //{
      //-------------------------------------------------------------------------------------
      //한글은 첫 byte 만 [음수] 가 아니라 2byte 각각 모두 음수이다.
      //이점을 주의해야 한다.
      //단, '똠' 과 같은 문자가 입력되는 경우는 자동으로 OS 가 [조합형코드] 로
      //바꾸어서 전달하는데 이 [조함형코드] 는 무조건 첫 byte 가 [음수] 이다.
      //따라서 [한글/영문] 을 구분하기 위해서는 반드시 첫 byte 가 [음수] 인지 확인해야 한다.
      //이는 순서대로 처리할때는 문제가 없으나, 역방향으로 버퍼를 거꾸로 검사하는 경우는
      //반드시 첫 byte 가 [음수] 인지 확인해 보아야 한다.
      //-------------------------------------------------------------------------------------
  //}

  //[VAR]
  //{
      int  i            = 0;
      int  width        = 0;
      int  moonja_count = 0;

  //}

  //[개체존재:확인{****************************
  //[2012.05.01.화][추가!!!]
  //개체가 이미 해제된 뒤라면 처리하지 않는다.
  if(_this_ != true) return;
  //}******************************************

  for(i = pLINEstartINDEX; i < pCARETINDEX; i++)
  {
    //문자가 [한글/영문] 인지 구별하여 각각 문자의 [폭] 을 전부 합친다.
    if(_editbuf[i] > 0)
    {
      //이 경우는 [영문] 이다.
      width += _font_eng_width;

    }
    else
    {
      //[한글] 등 2 byte-code 문자의 경우.
      width += _font_kor_width;

      //2 byte-code 이므로 다음 위치로 이동해 놓는다.
      i++;

    }

    //문자수 증가.
    moonja_count++;

  }

  //[반환]
  //{
      //[2018.02.24.토][수정!!!]{-------
      //NULL 인 경우는 반환하지 않는다.
      //}-------------------------------

      if(rCARETINDEXwidth != NULL) { (*rCARETINDEXwidth) = width;        }
      if(rMOONJAcount     != NULL) { (*rMOONJAcount)     = moonja_count; }

  //}

  return;

}

void __fastcall TGEdit_Form::_CARETINDEXWIDTH_CaretIndex(int   pLINEstartINDEX,
                                                            int   pLINEendINDEX,
                                                            int  *prCARETINDEXwidth,
                                                            int  *rCARETINDEX,
                                                            int  *rMOONJAcount)
{
  //[2006.12.27.수][생성!!!]{-------------------------------------------------------------
  //_CARETINDEX_CaretIndexWidth() 함수와 정 반대로 형재 행의 _caretindexwidth 값으로
  //역으로 _caretindex 값을 구한다.
  //[한글/영문] 등이 섞여 있는 경우 항상 _caretindexwidth 에 정확하게 맞는 값을
  //얻을수 없으므로 가장 가까운 값을 구한다.(문자 위치에 정확하게 맞아야 한다.)
  //이는 행을 이동하는 경우등에 이전 행의 [입력지정자] 위치를 최대한 비슷하게 유지하기
  //위하여 사용될수 있다. 또한, 마우스로 찍은 경우 해당 위치에 [입력지정자] 를 위치시키는
  //데도 사용된다.
  //}-------------------------------------------------------------------------------------

  //[주의!!!]
  //{
      //처리할 행에 문자열이 없거나 이전 행보다 문자열이 작은 경우는
      //비슷한 [폭] 도 일치하는것이 없으므로 이 경우는 행의 가장 마지막 문자위치를 반환하면 된다.
      //추가로 수정된 cartindexwidth 값을 다시 반환한다.
  //}

  //[주의!!!][2007.01.14.일]
  //{
      //-------------------------------------------------------------------------------------
      //한글은 첫 byte 만 [음수] 가 아니라 2byte 각각 모두 음수이다.
      //이점을 주의해야 한다.
      //단, '똠' 과 같은 문자가 입력되는 경우는 자동으로 OS 가 [조합형코드] 로
      //바꾸어서 전달하는데 이 [조함형코드] 는 무조건 찻 byte 가 [음수] 이다.
      //따라서 [한글/영문] 을 구분하기 위해서는 반드시 첫 byte 가 [음수] 인지 확인해야 한다.
      //이는 순서대로 처리할때는 문제가 없으나, 역방향으로 버퍼를 거꾸로 검사하는 경우는
      //반드시 첫 byte 가 [음수] 인지 확인해 보아야 한다.
      //-------------------------------------------------------------------------------------
  //}

  //[VAR]
  //{
      int  i                = 0;
      int  caretindexwidth  = (*prCARETINDEXwidth);

      int  width_min        = 0;
      int  index_min        = pLINEstartINDEX;
      int  moonja_count_min = 0;

      int  width_max        = 0;
      int  index_max        = pLINEstartINDEX;
      int  moonja_count_max = 0;

  //}

  //[개체존재:확인{****************************
  //[2012.05.01.화][추가!!!]
  //개체가 이미 해제된 뒤라면 처리하지 않는다.
  if(_this_ != true) return;
  //}******************************************

  for(i = pLINEstartINDEX; i < pLINEendINDEX; )
  {
    //문자가 [한글/영문] 인지 구별하여 각각 문자의 [폭] 을 전부 합친다.

    if(_editbuf[i] > 0)
    {
      //이 경우는 [영문] 이다.
      width_max += _font_eng_width;

      i++;

    }
    else
    {
      //[한글] 등 2 byte-code 문자의 경우.
      width_max += _font_kor_width;

      //2 byte-code 이므로 다음 문자 위치로 이동해 놓는다.
      i += 2;

    }

    //문자수 증가.
    moonja_count_max++;

    //[폭비교]
    //{
        //전달받은 [폭] 에 근사한 경우는 이 값을 기록해 놓아야 한다.
        if(width_max >= caretindexwidth)
        {
          //원하는 값을 얻었으므로 더이상 처리하지 않고 종료한다.
          index_max = i;

          //[비교]
          //{
              //비교 대상이 되는 값과 보다 가까운 값을 찾는다.
              if((caretindexwidth - width_min) < (width_max - caretindexwidth))
              {
                //windth_min 이 현재 prCARETINDEXwidth 에 더 가깝다는 것이므로 이 값을 전달한다.

                (*prCARETINDEXwidth) = width_min;
                (*rCARETINDEX)       = index_min;
                (*rMOONJAcount)      = moonja_count_min;

                return;

              }
              else
              {
                //windth_max 이 현재 prCARETINDEXwidth 에 더 가깝다는 것이므로 이 값을 전달한다.

                (*prCARETINDEXwidth) = width_max;
                (*rCARETINDEX)       = index_max;
                (*rMOONJAcount)      = moonja_count_max;

                 return;

              }

          //}

        }
        else
        {
          //현재까지 구한 값을 저장해 놓는다.
          //이는 작은값 과 큰값중 어느 것이 더 가까운 값인지 구하기 위한 것이다.
          width_min        = width_max;
          index_min        = i;
          moonja_count_min = moonja_count_max;

        }

    //}

  }

  //[범위초과]
  //{
      //----------------------------------------------------------------------------
      //여기까지 온 경우는 이전 행의 [입력지정자] 위치가 지금 행의 총폭보다
      //큰 위치에 있는 경우이므로 무조건 지금행의 마지막 위치를 기록해 놓아야 한다.
      (*prCARETINDEXwidth) = width_max;
      (*rCARETINDEX)       = pLINEendINDEX;
      (*rMOONJAcount)      = moonja_count_max;
      //----------------------------------------------------------------------------
  //}

  return;

}

void __fastcall TGEdit_Form::_CARETINDEX_MoveLeft(int   pLINEstartINDEX,
                                                     int  *prCARETINDEX)
{
  //[2006.12.27.수][생성!!!]{------------------------------------------------------
  //_caretindex 를 왼쪽으로 수정하는 경우 [한글/영문] 인지 가려서 위치를 수정한다.
  //}------------------------------------------------------------------------------

  //[주의!!!][2007.01.14.일]
  //{
      //-------------------------------------------------------------------------------------
      //한글은 첫 byte 만 [음수] 가 아니라 2byte 각각 모두 음수이다.
      //이점을 주의해야 한다.
      //단, '똠' 과 같은 문자가 입력되는 경우는 자동으로 OS 가 [조합형코드] 로
      //바꾸어서 전달하는데 이 [조함형코드] 는 무조건 찻 byte 가 [음수] 이다.
      //따라서 [한글/영문] 을 구분하기 위해서는 반드시 첫 byte 가 [음수] 인지 확인해야 한다.
      //이는 순서대로 처리할때는 문제가 없으나, 역방향으로 버퍼를 거꾸로 검사하는 경우는
      //반드시 첫 byte 가 [음수] 인지 확인해 보아야 한다.
      //-------------------------------------------------------------------------------------
  //}

  //[수정!!!][2007.01.15.월]
  //{
      //---------------------------------------------------------------------------------
      //[한글 완성형] 은 2 byte 모두 [음수] 이나, [한글 조합형] 은 [음수:(음수/양수)] 로
      //첫 byte 는 반드시 [음수] 지만 두번째 byte 는 [음수/양수] 도 모두 될수 있어서
      //이전처럼 현 [입력지정자] 위치에서 거꾸로 검사하는 방식으로는 [입력지정자] 왼쪽의
      //문자가 [한글/영문] 인지 확실하게 구분할수 없다.
      //따라서, 이 행의 처음부터 현재 [입력지정자] 위치까지 검사해가는 방법을 사용해야
      //정확한 구분을 할수 있게 된다.
      //이렇게 수정한다.
      //---------------------------------------------------------------------------------
  //}

  //[VAR]
  //{
      bool  TkorFeng = false;
      int   i;

  //}

  //[개체존재:확인{****************************
  //[2012.05.01.화][추가!!!]
  //개체가 이미 해제된 뒤라면 처리하지 않는다.
  if(_this_ != true) return;
  //}******************************************

  //[확인]
  //{
      if((*prCARETINDEX) <= pLINEstartINDEX)
      {
        //현재 행 처음 위치보다 더이상 왼쪽으로 이동할수 없다.
        (*prCARETINDEX) = pLINEstartINDEX;

        return;

      }

  //}

  //[이 행 처음부터 추적한다]
  //{
      //이 행의 처음부터 현재 _caretindex 위치까지 순서대로 검사한다.
      //단, 다음 처리 전에 이미 이 행의 정확한 _linestartindex 값을 알고 있어야 한다.
      for(i = pLINEstartINDEX; i < _caretindex; )
      {
        if(_editbuf[i] < 0)
        {
          //[한글] 인 경우.
          i += 2;
          TkorFeng = true;

        }
        else
        {
          //[영문] 인 경우.
          i += 1;
          TkorFeng = false;

        }

      }

      //여기까지 현재 [입력지정자] 왼쪽 문자가 [한글/영문] 인지 이미 알아냈다.

  //}

  //[한글/영문 구분 처리]
  //{
      if(TkorFeng == true)
      {
        //지금 [입력지정자] 왼쪽의 문자는 [한글] 이다.
        (*prCARETINDEX) -= 2;

      }
      else
      {
        //지금 [입력지정자] 왼쪽의 문자는 [영문] 이다.
        (*prCARETINDEX) -= 1;

      }

  //}

  return;

}

void __fastcall TGEdit_Form::_CARETINDEX_MoveRight(int   pLINEendINDEX,
                                                      int  *prCARETINDEX)
{
  //[2006.12.27.수][생성!!!]{--------------------------------------------------------
  //_caretindex 를 오른쪽으로 수정하는 경우 [한글/영문] 인지 가려서 위치를 수정한다.
  //}--------------------------------------------------------------------------------

  //[주의!!!][2007.01.14.일]
  //{
      //-------------------------------------------------------------------------------------
      //한글은 첫 byte 만 [음수] 가 아니라 2byte 각각 모두 음수이다.
      //이점을 주의해야 한다.
      //단, '똠' 과 같은 문자가 입력되는 경우는 자동으로 OS 가 [조합형코드] 로
      //바꾸어서 전달하는데 이 [조함형코드] 는 무조건 찻 byte 가 [음수] 이다.
      //따라서 [한글/영문] 을 구분하기 위해서는 반드시 첫 byte 가 [음수] 인지 확인해야 한다.
      //이는 순서대로 처리할때는 문제가 없으나, 역방향으로 버퍼를 거꾸로 검사하는 경우는
      //반드시 첫 byte 가 [음수] 인지 확인해 보아야 한다.
      //-------------------------------------------------------------------------------------
  //}

  //[개체존재:확인{****************************
  //[2012.05.01.화][추가!!!]
  //개체가 이미 해제된 뒤라면 처리하지 않는다.
  if(_this_ != true) return;
  //}******************************************

  //[확인]
  //{
      if((*prCARETINDEX) >= pLINEendINDEX)
      {
        //현재 행 끝 위치보다 더이상 오른쪽으로 이동할수 없다.
        (*prCARETINDEX) = pLINEendINDEX;

        return;

      }

  //}

  //[문자] 로 판별한다.
  if(_editbuf[(*prCARETINDEX)] > 0)
  {
    //현재 위치 오른쪽 문자는 [영문] 이다.
    (*prCARETINDEX) += 1;

  }
  else
  {
    //현재 위치 오른쪽 문자는 [한글] 이다.
    (*prCARETINDEX) += 2;

  }

  return;

}

void __fastcall TGEdit_Form::_MOUSEDOWN_Get_LineStartIndex(int   pLINEnumber,
                                                              int  *pLINEstartindex)
{
  //[2007.02.28.수][생성!!!]{------------------------------------------------------------
  //STR_Get_LineStartIndex() 함수를 대체하기 위한 함수로써,
  //결과를 _editbuf[] 처음부터 검사하여 얻지 않고 이미 구해진 _linetop_xxx 관련 변수를
  //참고하여 빠르게 구한다.
  //이는 양이 큰 문서의 경우(10만줄 3M) 마우스로 [선택영역] 을 형성할때
  //STR_Get_LineStartIndex() 함수로 인한 시간 손실을 막기 위한 것이다.

  //[2007.03.01.목][수정!!!]
  //_linetop_number 이후 (보이는 영역) 만 처리하지 않고 그 이전영역(보이지 않는 영역)도
  //같이 처리한다. 이는 [마우스] 를 사용하여 [선택영역] 을 형성하는 과정에서 보이지 않는
  //영역으로 들어 갈수 있기 때문이다.
  //이는 linestartindex 는 어떤 경우라도 항상 정확한 값을 추적하고 있어야 하기 때문이다.
  //}------------------------------------------------------------------------------------

  //[VAR]
  //{
      int  i;
      int  count;
      int  linestartindex;

  //}

  //[개체존재:확인{****************************
  //[2012.05.01.화][추가!!!]
  //개체가 이미 해제된 뒤라면 처리하지 않는다.
  if(_this_ != true) return;
  //}******************************************

  //[행 시작 위치 구하기]
  //{
      //현재 linetop 위치에서 지정 행까지의 행수를 구한다.(linetop 행은 제외)
      count = pLINEnumber - _linetop_linenumber;

      //검사를 시작할 index 위치를 기록한다.
      linestartindex = _linetop_startindex;

      if(count >= 0)
      {
        //가장 윗 행 다음의 행을 처리 하는 경우.(눈에 보이는 영역)

        //계속 지정 행 까지 현재 행의 startindex 값을 구한다.
        for(i = 0; i < count; i++)
        {
          //계속 다음행을 처리하는 것으로 값을 구한다.
          STR_Get_NextLineStartIndex(_editbuf, linestartindex, &linestartindex);

        }

      }
      else
      {
        //가장 윗 행 이전의 행을 처리 하는 경우.(눈에 안보이는 영역)

        //일단 양수로 바꿔준다.
        count = (-1) * count;

        for(i = 0; i < count; i++)
        {
          //계속 이전행을 처리하는 것으로 값을 구한다.
          STR_Get_BeforeLineStartIndex(_editbuf, linestartindex, &linestartindex);

        }

      }

  //}

  //[지정행 시작 위치 반환]
  //{
      (*pLINEstartindex) = linestartindex;

  //}

  return;

}

bool __fastcall TGEdit_Form::_CARETPOS_MouseDown(int  pMOUSEdownX,
                                                    int  pMOUSEdownY)
{
  //[2006.12.28.목][생성!!!]{--------------------------------------------------------------
  //[편집기] 상에 [마우스누르기] 하는 경우 이 위치에 자동으로 [입력지정자] 를
  //위치시켜준다.
  //이미 기록되 있는 각각의 정보를 사용하여 위치를 산출한다.
  //단, 현재 문서의 행을 넘은 영역을 찍은 경우는 마지막 행을 찍은 것으로 본다.

  //[2007.02.08.목][수정!!!]
  //행은 [음수] 가 될수 없으므로 [음수] 행이 나오는 경우는 0 으로 초기화 한다.

  //[2007.02.26.월][추가!!!]
  //새로 구한 위치가 이전 _caret_x, _caret_y 위치와 차이가 없는 경우는 false 를 반환하고,
  //수정된 경우는 true 를 반환한다.

  //[2007.03.19.월][수정!!!]
  //마우스로 [선택영역] 을 만드는 경우 [입력지정자] 의 위치가 보이지 않는 영역에 있게 되는
  //경우가 발생하지 않도록 수정하였다.
  //}--------------------------------------------------------------------------------------

  //[주의!!!]
  //{
      //-------------------------------------------------------------------
      //[행 수] 는 1부터 유효한 값이고, [행 번호] 는 0 부터 유효한 값이다.
      //-------------------------------------------------------------------
  //}

  //[VAR]
  //{
      int  mouse_x;
      int  linenum = 0; //(BASE : 0)

      //[2007.02.26.월][추가!!!]
      int  caret_x, caret_y;

  //}

  //[개체존재:확인{****************************
  //[2012.05.01.화][추가!!!]
  //개체가 이미 해제된 뒤라면 처리하지 않는다.
  if(_this_ != true) return(false);
  //}******************************************

  //[영역 검사]
  //{
      //[2007.03.19.월]{---------------------------------------------------------
      //[입력지정자] 가 보이지 않는 영역에 있어서는 안되므로, 항상 보이는 영역에
      //위치하도록 값을 자동으로 수정해 준다.
      //}------------------------------------------------------------------------

      //[수평 검사]
      //{
          if     (pMOUSEdownX < 0)
          {
            //[왼쪽]
            pMOUSEdownX = 0;

          }
          else if(pMOUSEdownX > this->Width)
          {
            //[오른쪽]
            pMOUSEdownX = this->Width;

          }

      //}

  //}

  //[문서상 위치 환산]
  //{
      //[문서원점] 을 기준으로한 문서상의 위치를 구한다.
      mouse_x = (pMOUSEdownX - _text_org_x);

  //}

  //[행관련 정보 구하기]
  //{
      //현재 행의 시작위치를 구한다.
      _MOUSEDOWN_Get_LineStartIndex(linenum, &_linestartindex);
      //STR_Get_LineStartIndex(_editbuf, linenum, &_linestartindex); //이전사용함수.
      //[참고][2007.03.01.목]
      //{
          //STR_Get_LineStartIndex() 함수는 문서 전체를 처음부터 추적하여 행을 구하므로
          //문서 량이 10만줄 정도 되면 처리 시간이 길어 질수 있다.
          //그러나, 10만줄(3M 용량)의 경우라도 <Release-Mode> 로 빌드한 경우는
          //마우스로 [선택영역] 을 형성해 가는 경우도 처리 속도의 저하를 느낄수 없었다.
          //그러나, 더 빠르게 처리되는 _MOUSEDOWN_Get_LineStartIndex() 함수로 대체한다.
      //}

      //현재 행의 끝 위치를 구한다.
      STR_Get_ThisLineEndIndex(_editbuf, _linestartindex, &_lineendindex);

  //}

  //[_caretindex 구하기]
  //{
      //일단 mouse_x 위치를 _caretindexwidth 로 보고 _caretindex 값을 구한다.
      _caretindexwidth = mouse_x;

      //중요 정보를 얻는다.
      _CARETINDEXWIDTH_CaretIndex(_linestartindex, _lineendindex,
                                  &_caretindexwidth, &_caretindex, &_col);

  //}

  //[입력지정자 위치 설정]
  //{
      //[문서] 상의 위치를 구한다.
      caret_x = _caretindexwidth;
      caret_y = linenum * _line_height;

      if((caret_x == _caret_x) && (caret_y == _caret_y))
      {
        //[2007.02.26.월][추가!!!]
        //위치가 그대로 인경우이므로 false 를 반환한다.
        return(false);

      }

      _caret_x = caret_x;
      _caret_y = caret_y;

      //위치시킨다.
      _EDIT_CaretPos(_caret_x, _caret_y);

  //}

  return(true);

}

void __fastcall TGEdit_Form::_ROOT_TEXTINFO_Init(void)
{
  //[2007.01.04.목][생성!!!]{----------------------------------------------
  //실제 사용되는 문서 정보를 초기화 한다.
  //이는 [문서] 를 처음 여는 경우 표시 위치등을 완전히 초기화 하는 것이다.
  //}----------------------------------------------------------------------

  //[개체존재:확인{****************************
  //[2012.05.01.화][추가!!!]
  //개체가 이미 해제된 뒤라면 처리하지 않는다.
  if(_this_ != true) return;
  //}******************************************

  _font.margin_size = d_X_width + 2 - 1;

  //[2022.01.06.목][수정]
  //문자가 너무 왼쪽 테두리에 붙이 않도록 <+2> 정도 간격을 추가한다.
  //--------------------------------v
  _text_org_x = _font.margin_size + 2;
  _text_org_y = 2 +
                //[2022.06.13.월][추가]
                //<_text_org_y> 에 추가 보정을 제공.
                _text_org_y_ofs;

  //행은 기본적으로 무조건 한개는 있는 것으로 해야 [줄이동막대] 가 제대로 동작한다.
  _linecount = 1;

  _linestartindex = 0;
  _lineendindex   = 0;

  _linetop_startindex = 0;
  _linetop_linenumber = 0;

  _caretindex      = 0;
  _caretindexwidth = 0;

  _row = 0;
  _col = 0;

  _caret_x = 0;
  _caret_y = 0;

  _editbuf_text_on = true;

  //[선택영역]
  //{
      //[2007.03.17.토][추가!!!]
      //[선택영역] 을 초기화 한다.
      //둘중 하나라도 -1 인 경우는 [선택영역] 이 존재하지 않는것이다.
      _select_start.caret_index = -1;
      _select_start.caret_x     = 0;
      _select_start.caret_y     = 0;

      _select_end.caret_index   = -1;
      _select_end.caret_x       = 0;
      _select_end.caret_y       = 0;

  //}

  return;

}

void __fastcall TGEdit_Form::_KEY_Left(bool  pETCcontrol)
{
  //[2007.01.07.일][생성!!!]{------------------------------------------------------------------------------------
  //[방향키-왼] 로 [입력지정자] 를 이동중 [편집기] 왼쪽 부분을 벗어나는 경우는
  //항상 [입력지정자] 가 가장 왼쪽에 위치할수 있도록 자동으로 1열 왼쪽으로 [스크롤] 해 주어야 한다.
  //[한글/영문] 에따라 폭이 달라지나, 기본적으로 [수평스크롤] 은 [영문] 자의 폭을 기준으로 처리
  //되므로 한글의 경우는 [영문]*2 의 폭으로 처리되어야 한다.

  //[2007.02.10.토][추가!!!]
  //[키] 를 사용하여 [선택영역] 을 만드는 경우의 처리를 추가한다.

  //[2007.03.14.수][추가!!!]
  //순수하게 [입력지정자] 를 이동하는 경우 먼저 _CARET_ViewAlways() 함수를 별도로 먼저
  //실행하게 되는데, 이 때 _CARET_ViewAlways() 함수가 처리 된 경우는 _CARET_ViewAlways() 함수
  //내부적으로 _EditBuf_Text() 함수가 실행되지 않도록 하였다.(이는 중복 실행으로인한 깜박임 방지를 위한 것이다.)
  //따라서, pETCcontrol 이 true 인 경우는 _EditBuf_Text() 를 끝에서 한번은 반드시 처리해 주어야 한다.
  //}------------------------------------------------------------------------------------------------------------

  //[주의!!!][2007.01.14.일]
  //{
      //-------------------------------------------------------------------------------------
      //한글은 첫 byte 만 [음수] 가 아니라 2byte 각각 모두 음수이다.
      //이점을 주의해야 한다.
      //단, '똠' 과 같은 문자가 입력되는 경우는 자동으로 OS 가 [조합형코드] 로
      //바꾸어서 전달하는데 이 [조함형코드] 는 무조건 찻 byte 가 [음수] 이다.
      //따라서 [한글/영문] 을 구분하기 위해서는 반드시 첫 byte 가 [음수] 인지 확인해야 한다.
      //이는 순서대로 처리할때는 문제가 없으나, 역방향으로 버퍼를 거꾸로 검사하는 경우는
      //반드시 첫 byte 가 [음수] 인지 확인해 보아야 한다.
      //-------------------------------------------------------------------------------------
  //}

  //[VAR]
  //{
      //현재 position 값.
      int  position       = _scroll_h_position;

      //현재 값.
      int  position_save  = position;

      //실제 변화된 행수.
      int  real_count     = 0;

      //변화하기 전의 현재 _caretindex 값을 저장해 놓는다.
      int  caretindex     = _caretindex;
      int  caret_x        = _caret_x;
      int  caret_y        = _caret_y;

  //}

  //[개체존재:확인{****************************
  //[2012.05.01.화][추가!!!]
  //개체가 이미 해제된 뒤라면 처리하지 않는다.
  if(_this_ != true) return;
  //}******************************************

  //[키동작]
  //{
      //[행 시작/끝]
      //{
          //-------------------------------------------------------------------
          STR_Get_ThisLineStartIndex(_editbuf, _caretindex, &_linestartindex);
          STR_Get_ThisLineEndIndex(_editbuf, _linestartindex, &_lineendindex);
          //-------------------------------------------------------------------
      //}

      //[입력지정자 위치 조정]
      //{
          _CARETINDEX_MoveLeft(_linestartindex, &_caretindex);
          _CARETINDEX_CaretIndexWidth(_linestartindex, _caretindex, &_caretindexwidth, &_col);

          _caret_x = _caretindexwidth;

          //여기서는 스크롤 하지 않아야 한다.
          _edit_caretpos_on = false;
          //{
              _EDIT_CaretPos(_caret_x, _caret_y);

          //}
          _edit_caretpos_on = true;

      //}

  //}

  //[선택영역처리][2007.02.10.토]
  //{
      //<shift> 키가 눌린 경우만 [선택영역] 구성을 처리해야 한다.
      if(_shift_down == true)
      {
        //둘다 -1 인 경우는 [선택영역] 을 처음 시작하는 것으로 본다.
        if((_select_start.caret_index == -1) && (_select_end.caret_index == -1))
        {
          //[선택영역] 시작 위치를 기록한다.(이동하기전 현재 위치이어야 한다.)
          //이미 변하기전 기록해 놓은 값을 사용한다.
          _select_start.caret_index = caretindex;
          _select_start.caret_x     = caret_x;
          _select_start.caret_y     = caret_y;

          //[선택영역] 끝 위치를 초기화 한다.
          _select_end.caret_index = -1;
          _select_end.caret_x     = 0;
          _select_end.caret_y     = 0;

        }

        //[선택영역] 이 생성중이다.
        _select_ing = true;

        //[선택영역 형성]
        //{
            //현재 위치를 [선택영역] 끝 위치로 기록한다.
            //이미 [키] 조작으로 변경된 [입력지정자] 위치가 끝위치가 된다.
            _select_end.caret_index = _caretindex;
            _select_end.caret_x     = _caret_x;
            _select_end.caret_y     = _caret_y;

            //[반영]
            //{
                //문서를 다시 출력하는 것으로 [선택영역] 이 자동반영 되도록 한다.
                _EditBuf_Text();

            //}

        //}

      }

  //}

  //[스크롤해야 하나?]
  //{
      //[입력지정자] 가 보이는 영역을 벗어 난 경우라면 왼쪽으로 스크롤한다.
      if((_text_org_x - d_X_width + _caret_x) < 0)
      {
        //[한글/영문?]
        //{
            //---------------------------------------------------------------------------------------------
            //현재 _caretindex 위치의 문자가 [한글/영문] 에 따라 스크롤 값이 달라야 한다.
            //위에서 이미 _caretindex 에 해당하는 문자를 건너서 이동한 것이므로 이 폭을가지고 따져야 한다.

            if     (_editbuf[_caretindex] > 0)
            {
              //[영문] 인 경우는 [영문폭] 로 이동해야 하므로 -1 만큼 스크롤 한다.
              position -= 1;

            }
            else if(_editbuf[_caretindex] < 0)
            {
              //[한글] 인 경우는 [영문폭*2] 로 이동해야 하므로 -2 만큼 스크롤 한다.
              position -= 2;

            }
            //---------------------------------------------------------------------------------------------
        //}

        //지정 양만큼 수평으로 스크롤 한다.
        _scroll_h_position = position;
        if(_scroll_h_position < 0) { _scroll_h_position = 0; }
        _EDIT_H_Scroll();


        //[실제 _position 변화값 구하기]
        //{
            //-----------------------------------------------------------------------
            //_position 값은 _max 보다 클수 없으로 실제 지정한 값이 전부 그대로
            //적용되지 않을수 있으므로 여기서 정확하게 변화된 _position 값을 구한다.
            real_count = _scroll_h_position - position_save;

            //변화된 행이 없는 경우는 처리하지 앟는다.
            if(real_count <= 0) return;
            //-----------------------------------------------------------------------
        //}

      }
      else
      {
        //[스크롤] 하지 않는 경우라면 _EDIT_CaretPos() 함수로 [입력지정자] 를 현재 위치 시켜야 한다.
        //이는 위에서 [스크롤] 되는 경우 _EDIT_CaretPos() 함수의 중복 사용을 막기 위하여 처리하지 않았기 때문이다.
        _EDIT_CaretPos(_caret_x, _caret_y);

        //[2007.03.14.수][추가!!!]
        if(pETCcontrol == true) _EditBuf_Text();

      }

  //}

  return;

}

void __fastcall TGEdit_Form::_KEY_Right(bool  pETCcontrol,
                                        bool  pSELECTtrue)
{
  //[2007.01.07.일][생성!!!]{------------------------------------------------------------------------------------
  //[방향키-오른] 로 [입력지정자] 를 이동중 [편집기] 오른쪽 부분을 벗어나는 경우는
  //항상 [입력지정자] 가 가장 오른쪽에 위치할수 있도록 자동으로 1열 오른쪽으로 [스크롤] 해 주어야 한다.
  //[한글/영문] 에따라 폭이 달라지나, 기본적으로 [수평스크롤] 은 [영문] 자의 폭을 기준으로 처리
  //되므로 한글의 경우는 [영문]*2 의 폭으로 처리되어야 한다.

  //[2007.02.10.토][추가!!!]
  //[키] 를 사용하여 [선택영역] 을 만드는 경우의 처리를 추가한다.

  //[2007.02.21.수][수정!!!]
  //_CARET_MOONJA_Insert() 함수에서 일반 문자를 출력하는 경우 문자 출력후 [입력지정자] 의 이동을 위하여
  //이 함수를 사용하는데 <Shift-Key> 를 눌러서 [대문자] 를 입력하는 경우 [선택영역] 을 생성하는것으로
  //취급될수 있다. 따라서, 이 경우는 [선택영역] 처리를 하지 않아야 한다.
  //pSELECTtrue == true  : [선택영역] 생성도 처리한다.
  //            == false : [선택영역] 을 처리하지 않는다. (순수 키 처리만 수행한다)

  //[2007.03.14.수][추가!!!]
  //순수하게 [입력지정자] 를 이동하는 경우 먼저 _CARET_ViewAlways() 함수를 별도로 먼저
  //실행하게 되는데, 이 때 _CARET_ViewAlways() 함수가 처리 된 경우는 _CARET_ViewAlways() 함수
  //내부적으로 _EditBuf_Text() 함수가 실행되지 않도록 하였다.(이는 중복 실행으로인한 깜박임 방지를 위한 것이다.)
  //따라서, pETCcontrol 이 true 인 경우는 _EditBuf_Text() 를 끝에서 한번은 반드시 처리해 주어야 한다.
  //}------------------------------------------------------------------------------------------------------------

  //[주의!!!][2007.01.14.일]
  //{
      //-------------------------------------------------------------------------------------
      //한글은 첫 byte 만 [음수] 가 아니라 2byte 각각 모두 음수이다.
      //이점을 주의해야 한다.
      //단, '똠' 과 같은 문자가 입력되는 경우는 자동으로 OS 가 [조합형코드] 로
      //바꾸어서 전달하는데 이 [조함형코드] 는 무조건 찻 byte 가 [음수] 이다.
      //따라서 [한글/영문] 을 구분하기 위해서는 반드시 첫 byte 가 [음수] 인지 확인해야 한다.
      //이는 순서대로 처리할때는 문제가 없으나, 역방향으로 버퍼를 거꾸로 검사하는 경우는
      //반드시 첫 byte 가 [음수] 인지 확인해 보아야 한다.
      //-------------------------------------------------------------------------------------
  //}

  //[VAR]
  //{
      //현재 position 값.
      int  position        = _scroll_h_position;

      //현재 값.
      int  position_save   = position;

      //실제 변화된 행수.
      int  real_count      = 0;

      int  caretindex_save = _caretindex;

      //변화하기 전의 현재 _caretindex 값을 저장해 놓는다.
      int  caretindex      = _caretindex;
      int  caret_x         = _caret_x;
      int  caret_y         = _caret_y;

  //}

  //[개체존재:확인{****************************
  //[2012.05.01.화][추가!!!]
  //개체가 이미 해제된 뒤라면 처리하지 않는다.
  if(_this_ != true) return;
  //}******************************************

  //[키동작]
  //{
      //[행 시작/끝]
      //{
          //-------------------------------------------------------------------
          STR_Get_ThisLineStartIndex(_editbuf, _caretindex, &_linestartindex);
          STR_Get_ThisLineEndIndex(_editbuf, _linestartindex, &_lineendindex);
          //-------------------------------------------------------------------
      //}

      //[입력지정자 위치 조정]
      //{
          _CARETINDEX_MoveRight(_lineendindex, &_caretindex);
          _CARETINDEX_CaretIndexWidth(_linestartindex, _caretindex, &_caretindexwidth, &_col);

          _caret_x = _caretindexwidth;

          //여기서는 스크롤 하지 않아야 한다.
          _edit_caretpos_on = false;
          //{
              _EDIT_CaretPos(_caret_x, _caret_y);

          //}
          _edit_caretpos_on = true;

      //}

  //}

  //[선택영역처리][2007.02.10.토]
  //{
      //<shift> 키가 눌린 경우만 [선택영역] 구성을 처리해야 한다.
      if((pSELECTtrue == true) && (_shift_down == true))
      {
        //둘다 -1 인 경우는 [선택영역] 을 처음 시작하는 것으로 본다.
        if((_select_start.caret_index == -1) && (_select_end.caret_index == -1))
        {
          //[선택영역] 시작 위치를 기록한다.(이동하기전 현재 위치이어야 한다.)
          //이미 변하기전 기록해 놓은 값을 사용한다.
          _select_start.caret_index = caretindex;
          _select_start.caret_x     = caret_x;
          _select_start.caret_y     = caret_y;

          //[선택영역] 끝 위치를 초기화 한다.
          _select_end.caret_index = -1;
          _select_end.caret_x     = 0;
          _select_end.caret_y     = 0;

        }

        //[선택영역] 이 생성중이다.
        _select_ing = true;

        //[선택영역 형성]
        //{
            //현재 위치를 [선택영역] 끝 위치로 기록한다.
            //이미 [키] 조작으로 변경된 [입력지정자] 위치가 끝위치가 된다.
            _select_end.caret_index = _caretindex;
            _select_end.caret_x     = _caret_x;
            _select_end.caret_y     = _caret_y;

            //[반영]
            //{
                //문서를 다시 출력하는 것으로 [선택영역] 이 자동반영 되도록 한다.
                _EditBuf_Text();

            //}

        //}

      }

  //}

  //[스크롤해야 하나?]
  //{
      //[입력지정자] 가 보이는 영역을 벗어 난 경우라면 왼쪽으로 스크롤한다.
      //[한문자] 폭의 여유를 둔다.--------v
      if((_text_org_x + _caret_x + _font_eng_width) > this->Width)
      {
        //[한글/영문?]
        //{
            //--------------------------------------------------------------------------------------------------
            //현재 _caretindex 위치의 문자가 [한글/영문] 에 따라 스크롤 값이 달라야 한다.
            //위에서 이미 _caretindex 에 해당하는 문자를 건너서 이동한 것이므로 이동하기 전의  위치로 검사한다.
            if     (_editbuf[caretindex_save] > 0)
            {
              //[영문] 인 경우는 [영문폭] 로 이동해야 하므로 +1 만큼 스크롤 한다.
              position += 1;

            }
            else if(_editbuf[caretindex_save] < 0)
            {
              //[한글] 인 경우는 [영문폭*2] 로 이동해야 하므로 +2 만큼 스크롤 한다.
              position += 2;

            }
            //--------------------------------------------------------------------------------------------------
        //}

        //지정 양만큼 수평으로 스크롤 한다.
        _scroll_h_position = position;
        if(_scroll_h_position < 0) { _scroll_h_position = 0; }
        _EDIT_H_Scroll();

        //[실제 _position 변화값 구하기]
        //{
            //-----------------------------------------------------------------------
            //_position 값은 _max 보다 클수 없으로 실제 지정한 값이 전부 그대로
            //적용되지 않을수 있으므로 여기서 정확하게 변화된 _position 값을 구한다.
            real_count = position_save - _scroll_h_position;

            //변화된 행이 없는 경우는 처리하지 앟는다.
            if(real_count <= 0) return;
            //-----------------------------------------------------------------------
        //}

      }
      else
      {
        //[스크롤] 하지 않는 경우라면 _EDIT_CaretPos() 함수로 [입력지정자] 를 현재 위치 시켜야 한다.
        //이는 위에서 [스크롤] 되는 경우 _EDIT_CaretPos() 함수의 중복 사용을 막기 위하여 처리하지 않았기 때문이다.
        _EDIT_CaretPos(_caret_x, _caret_y);

        //[2007.03.14.수][추가!!!]
        if(pETCcontrol == true) _EditBuf_Text();

      }

  //}

  return;

}

void __fastcall TGEdit_Form::_KEY_Home(bool  pETCcontrol)
{
  //[2007.01.07.일][생성!!!]{------------------------------------------------------------
  //이미 [수평스크롤] 상태인 경우는 [편집기] 가장 왼쪽에 [입력지정자] 가 위치할수 있도록
  //자동으로 가장 왼쪽으로 [스크롤] 한다. (무조선 _position = 0 인 위치로 이동)

  //[2007.02.10.토][추가!!!]
  //[키] 를 사용하여 [선택영역] 을 만드는 경우의 처리를 추가한다.

  //[2007.03.14.수][추가!!!]
  //순수하게 [입력지정자] 를 이동하는 경우는 먼저 _CARET_ViewAlways() 함수를 별도로 먼저
  //실행하게 되는데, 이 때 이 함수의 실행 결과에 따라서 _EditBuf_Text() 함수가 중복 처리되지
  //않도록 해 주어야 한다.
  //pETCcontrol 이 true 인 경우는 _EditBuf_Text() 를 처리하지 않게 만든다.
  //}------------------------------------------------------------------------------------

  //[VAR]
  //{
      //변화하기 전의 현재 _caretindex 값을 저장해 놓는다.
      int  caretindex = _caretindex;
      int  caret_x    = _caret_x;
      int  caret_y    = _caret_y;

  //}

  //[개체존재:확인{****************************
  //[2012.05.01.화][추가!!!]
  //개체가 이미 해제된 뒤라면 처리하지 않는다.
  if(_this_ != true) return;
  //}******************************************

  //[스크롤]
  //{
      //문조건 첫 열이 보이도록 스크롤 한다.
      //단, 시간낭비를 막기 위하여 이미 _position == 0 인 경우는 처리하지 않아야 한다.
      if(_scroll_h_position > 0)
      {
        //[2007.03.14.수][추가!!!]
        //[선택영역] 이 있는 경우는 바로 아래의 _EDIT_CaretPos() 함수 안에서
        //해제 되기 전의 모습이 보이게 되어 깜박거리는 문제가 발생하므로 여기서는
        //이곳의 _Position() 함수안에서 _EditBuf_Text() 가 중복 처리되는 것을 막아 준다.
        if(pETCcontrol == true) _editbuf_text_no = true;
        //{
            //여기서는 스크롤 하지 않아야 한다.
            _edit_caretpos_on = false;
            //{
                _scroll_h_position = 0;
                _EDIT_H_Scroll();

            //}
            _edit_caretpos_on = true;

        //}
        if(pETCcontrol == true) _editbuf_text_no = false;

      }

  //}

  //[행 시작 위치]
  //{
      //-------------------------------------------------------------------
      STR_Get_ThisLineStartIndex(_editbuf, _caretindex, &_linestartindex);
      //-------------------------------------------------------------------
  //}

  //[키동작]
  //{
      //[입력지정자 위치 조정]
      //{
          _caretindex = _linestartindex;
          _CARETINDEX_CaretIndexWidth(_linestartindex, _caretindex, &_caretindexwidth, &_col);

          _caret_x = _caretindexwidth;
          _EDIT_CaretPos(_caret_x, _caret_y);

      //}

      //[2007.03.14.수][추가!!!]
      //이 경우는 반드시 _EditBuf_Text() 를 처리해 주어야 한다.
      //단, [선택영역] 이 있는 경우는 위에서 완전히 해제 된 다음에 그려져야 하므로
      //반드시 이 위치에서 _EditBuf_Text() 함수로 다시 그려야 한다.
      if(pETCcontrol == true) _EditBuf_Text();

  //}

  //[선택영역처리][2007.02.10.토]
  //{
      //<shift> 키가 눌린 경우만 [선택영역] 구성을 처리해야 한다.
      if(_shift_down == true)
      {
        //둘다 -1 인 경우는 [선택영역] 을 처음 시작하는 것으로 본다.
        if((_select_start.caret_index == -1) && (_select_end.caret_index == -1))
        {
          //[선택영역] 시작 위치를 기록한다.(이동하기전 현재 위치이어야 한다.)
          //이미 변하기전 기록해 놓은 값을 사용한다.
          _select_start.caret_index = caretindex;
          _select_start.caret_x     = caret_x;
          _select_start.caret_y     = caret_y;

          //[선택영역] 끝 위치를 초기화 한다.
          _select_end.caret_index = -1;
          _select_end.caret_x     = 0;
          _select_end.caret_y     = 0;

        }

        //[선택영역] 이 생성중이다.
        _select_ing = true;

        //[선택영역 형성]
        //{
            //현재 위치를 [선택영역] 끝 위치로 기록한다.
            //이미 [키] 조작으로 변경된 [입력지정자] 위치가 끝위치가 된다.
            _select_end.caret_index = _caretindex;
            _select_end.caret_x     = _caret_x;
            _select_end.caret_y     = _caret_y;

            //[반영]
            //{
                //문서를 다시 출력하는 것으로 [선택영역] 이 자동반영 되도록 한다.
                _EditBuf_Text();

            //}

        //}

      }

  //}

  return;

}

void __fastcall TGEdit_Form::_KEY_End(bool  pETCcontrol)
{
  //[2007.01.07.일][생성!!!]{-------------------------------------------------------------------
  //현재 행 끝에 있는 [입력지정자] 가 [편집기] 가장 오른쪽에 위치하도록 [수평스크롤] 을 해준다.

  //[2007.02.10.토][추가!!!]
  //[키] 를 사용하여 [선택영역] 을 만드는 경우의 처리를 추가한다.
  //}-------------------------------------------------------------------------------------------

  //[VAR]
  //{
      //현재 position 값.
      int  position      = _scroll_h_position;

      //현재 값.
      int  position_save = position;

      //실제 변화된 행수.
      int  real_count    = 0;

      //변화하기 전의 현재 _caretindex 값을 저장해 놓는다.
      int  caretindex    = _caretindex;
      int  caret_x       = _caret_x;
      int  caret_y       = _caret_y;

  //}

  //[개체존재:확인{****************************
  //[2012.05.01.화][추가!!!]
  //개체가 이미 해제된 뒤라면 처리하지 않는다.
  if(_this_ != true) return;
  //}******************************************

  //[키동작]
  //{
      //[입력지정자 위치 조정]
      //{
          //이행의 끝 위치를 구한다.(여기서 직접 구하는 것이 정확하다.)
          STR_Get_ThisLineEndIndex(_editbuf, _caretindex, &_lineendindex);

          _caretindex = _lineendindex;
          _CARETINDEX_CaretIndexWidth(_linestartindex, _caretindex, &_caretindexwidth, &_col);

          _caret_x = _caretindexwidth;

          //여기서는 스크롤 하지 않아야 한다.
          _edit_caretpos_on = false;
          //{
              _EDIT_CaretPos(_caret_x, _caret_y);

          //}
          _edit_caretpos_on = true;

      //}

  //}

  //[선택영역처리][2007.02.10.토]
  //{
      //<shift> 키가 눌린 경우만 [선택영역] 구성을 처리해야 한다.
      if(_shift_down == true)
      {
        //둘다 -1 인 경우는 [선택영역] 을 처음 시작하는 것으로 본다.
        if((_select_start.caret_index == -1) && (_select_end.caret_index == -1))
        {
          //[선택영역] 시작 위치를 기록한다.(이동하기전 현재 위치이어야 한다.)
          //이미 변하기전 기록해 놓은 값을 사용한다.
          _select_start.caret_index = caretindex;
          _select_start.caret_x     = caret_x;
          _select_start.caret_y     = caret_y;

          //[선택영역] 끝 위치를 초기화 한다.
          _select_end.caret_index = -1;
          _select_end.caret_x     = 0;
          _select_end.caret_y     = 0;

        }

        //[선택영역] 이 생성중이다.
        _select_ing = true;

        //[선택영역 형성]
        //{
            //현재 위치를 [선택영역] 끝 위치로 기록한다.
            //이미 [키] 조작으로 변경된 [입력지정자] 위치가 끝위치가 된다.
            _select_end.caret_index = _caretindex;
            _select_end.caret_x     = _caret_x;
            _select_end.caret_y     = _caret_y;

            //[반영]
            //{
                //문서를 다시 출력하는 것으로 [선택영역] 이 자동반영 되도록 한다.
                _EditBuf_Text();

            //}

        //}

      }

  //}

  //[스크롤]
  //{
      //[입력지정자] 가 보이는 영역을 벗어 난 경우라면 왼쪽으로 스크롤한다.
      //[한문자] 폭의 여유를 둔다.--------v
      if((_text_org_x + _caret_x + _font_eng_width) > this->Width)
      {
        //[2007.03.14.수][추가!!!]
        //[선택영역] 이 있는 경우는 바로 아래의 _EDIT_CaretPos() 함수 안에서
        //해제 되기 전의 모습이 보이게 되어 깜박거리는 문제가 발생하므로 여기서는
        //이곳의 _Position() 함수안에서 _EditBuf_Text() 가 중복 처리되는 것을 막아 준다.
        if(pETCcontrol == true) _editbuf_text_no = true;
        //{
            //[스크롤 양 계산]
            //{
                position += (((_text_org_x + _caret_x + _font_eng_width) -
                             //약간의 여유 값을 준다.---------v
                             this->Width) / _font_eng_width + 1);

            //}

            //지정 양만큼 수평으로 스크롤 한다.
            _scroll_h_position = position;
            if(_scroll_h_position < 0) { _scroll_h_position = 0; }
            _EDIT_H_Scroll();

        //}
        if(pETCcontrol == true) _editbuf_text_no = false;

        //[실제 _position 변화값 구하기]
        //{
            //-----------------------------------------------------------------------
            //_position 값은 _max 보다 클수 없으로 실제 지정한 값이 전부 그대로
            //적용되지 않을수 있으므로 여기서 정확하게 변화된 _position 값을 구한다.
            real_count = position_save - _scroll_h_position;

            //변화된 행이 없는 경우는 처리하지 앟는다.
            if(real_count <= 0)
            {
              _EDIT_CaretPos(_caret_x, _caret_y);

              //[2007.03.14.수][추가!!!]
              if(pETCcontrol == true) _EditBuf_Text();

              return;
              
            }
            //-----------------------------------------------------------------------
        //}

      }
      else
      {
        //[스크롤] 하지 않는 경우라면 _EDIT_CaretPos() 함수로 [입력지정자] 를 현재 위치 시켜야 한다.
        //이는 위에서 [스크롤] 되는 경우 _EDIT_CaretPos() 함수의 중복 사용을 막기 위하여 처리하지 않았기 때문이다.
        _EDIT_CaretPos(_caret_x, _caret_y);

        //[2007.03.14.수][추가!!!]
        if(pETCcontrol == true) _EditBuf_Text();

      }

  //}

  return;

}

void __fastcall TGEdit_Form::_KEY_Delete(void)
{
  //[2007.01.18.목][생성!!!]{-------------------------------------------------------------
  //[Delete-Key] 를 누른 경우를 처리한다.
  //[일반문자] 의 경우와 [CRLF] 의 경우를 구분하여 처리해야 한다.

  //[2007.02.01.목][수정!!!]
  //이전에는 [입력지정자] 의 위치에서부터 행 끝가지 다시 출력했으나 [문법강조] 를 위하여
  //행의 처음부터 완전히 다시 출력하는 것으로 수정한다.

  //[2007.02.13.화][추가!!!]
  //[선택영역] 이 이미 존재하는 경우 별도 처리를 추가한다.

  //[2007.02.20.화][수정!!!]
  //[선택영역] 을 _SELECT_Delete() 함수로 삭제할때 [선택영역] 안에 CRLF 가 삭제되면
  //전체 행수가 줄어 들게 되므로 관리변수인 _linecount 를 여기서 직접 수정해 주어야 한다.
  //삭제된 CRLF 개수는 _SELECT_Delete() 함수에서 반환된다.

  //[2007.02.27.화][수정!!!]
  //먼저 삭제하기전에 [입력지정자] 를 [선택영역] 시작 위치로 이동하게 되는데,
  //이때 _EDIT_CaretPos() 함수안에서 [선택영역] 이 해제 되므로 정보를 상실하게 된다.
  //따라서, _SELECT_Get_StartEndIndex() 에서 얻어진 정보를 _EDIT_CaretPos() 함수 처리 후
  //다시 설정해 줌으로써 [선택영역] 을 _SELECT_Delete() 함수로 삭제할수 있게 만든다.

  //[2007.03.11.일][수정!!!]
  //caret_viewalways 를 사용하여 중복 처리를 막는다.

  //[2007.03.26.월][수정!!!]
  //[전체배경사용] 의 경우는 이 창에 해당하는 부분이 표시 되도록 한다.


  //}-------------------------------------------------------------------------------------

  //[주의!!!]
  //{
      //[2007.01.19.금][수정!!!]
      //이전에는 [편집기] 상의 보이는 영역 끝까지 지웠으나 이럴 경우 다음에 더 크게 크기를 조정하는경우
      //보이지 않던 영역이 나타나는 경우 이전 출력된 내용이 반영되지 않는 문제가 있다.
      //따라서, _bmp_ 의 끝까지 항상 지우는 것으로 수정한다.
  //}

  //[VAR]
  //{
      TRect             rect;

      //삭제된 문자가 [CRLF] 인지 구분 여부가 들어있다.
      bool              is_crlf = false;

      int               r_lineendindex;

      struct GEdit_CARETINFO  select_start;
      struct GEdit_CARETINFO  select_end;

      int               crlf_count = 0;

      //_CARET_ViewAlways() 함수가 내부적으로 동작한 경우 true 로 된다.
      bool              caret_viewalways = false;

  //}

  //[개체존재:확인{****************************
  //[2012.05.01.화][추가!!!]
  //개체가 이미 해제된 뒤라면 처리하지 않는다.
  if(_this_ != true) return;
  //}******************************************

  //[선택영역 처리]
  //{
      //[설명]{-----------------------------------------------
      //[2007.02.20.화]
      //1. 일단 [선택영역] 구간을 _editbuf[] 상에서 삭제한다.
      //2. <Key> 동작은 처리하지 않는다. (바로 종료한다.)
      //}-----------------------------------------------------

      //먼저 [입력지정자] 의 위치를 [선택영역] 시작 지점으로 이동시킨다.
      if(_SELECT_Get_StartEndIndex(&select_start, &select_end) != true) goto KEY_DEFAULT;

      //[입력지정자 위치 수정]
      //{
          //[선택영역] 이 있는 경우만 처리한다.
          //[입력지정자] 위치는 select_startindex 위치가 된다.(작은 index)
          _caretindex = select_start.caret_index;
          _caret_x    = select_start.caret_x;
          _caret_y    = select_start.caret_y;

          //[입력지정자 위치 조정]
          _EDIT_CaretPos(_caret_x, _caret_y);

      //}

      //[입력지정자 항상 보이기]
      //{
          //_EditBuf_Text() 함수가 바로 다음에 실행되므로 아래 함수 처리시
          //동작하지 않게 만든다.
          _editbuf_text_no = true;
          //{
              //키는 항상 눈에 보이는 위치에 있어야 한다.
              _CARET_ViewAlways();
              //<키> 를 처리하지 않고 여기서 종료 되므로 이곳에서 한번 실행해 주어야 한다.

          //}
          _editbuf_text_no = false;

      //}

      //[선택영역 정보 복구]
      //{
          //위에서 처리한 _EDIT_CaretPos() 에서 해제된 [선택영역] 의 정보를 다음 처리부에서
          //삭제할수 있도록 이미 얻어 놓은 정보로 복구한다.
          _select_start = select_start;
          _select_end   = select_end;

      //}

      //[선택영역] 삭제.
      if(_SELECT_Delete(&select_start, &crlf_count) == true)
      {
        //[행수 수정]
        //{
            //CRLF 가 삭제된 것이 있는 경우만 수정한다.
            if(crlf_count > 0) { _linecount -= crlf_count; }

        //}

        //[수정 반영]
        //{
            //[선택영역] 이 삭제된 경우는 <키> 를 처리하지 않고 여기서 종료하게
            //되므로 이곳에서 직접 수정된 내용을 반영해 주어야 한다.
            _EditBuf_Text();

        //}

        //<키> 처리를 하지 않고 여기서 종료한다.
        return;

      }

  //}

  //[순수한 KEY 처리]-----------------------------------------------------------

  KEY_DEFAULT:

  //[입력지정자 항상 보이기]
  //{
      _editbuf_text_no = true;
      //{
          //키는 항상 눈에 보이는 위치에 있어야 한다.
          caret_viewalways = _CARET_ViewAlways();

      //}
      _editbuf_text_no = false;

  //}

  //[문자 삭제]
  //{
      //_editbuf[] 상의 _caretindex 위치에 있는 문자를 한개 삭제한다.
      //문자가 삭제 되지 않은 경우는 처리하지 않는다.
      if(_EDITBUF_MOONJA_Delete(_caretindex, &is_crlf) != true) return;

  //}

  //[행변환유무검사]
  //{
      if(is_crlf == true)
      {
        //[설명]{--------------------------------------------------------------------
        //이 경우는 CRLF 가 삭제되어 행수가 수정된 것이다.
        //따라서, 현재 행부터 보이는 [편집기] 끝까지 전부 새로 문서를 출력해야 한다.
        //단, [수직스크롤] 은 되지 않으나 문서행수가 줄어 들게 되므로 문서양에 맞게
        //[수직스크롤] 을 다시 설정해 주어야 한다.
        //}--------------------------------------------------------------------------

        //[행수수정]
        //{
            //CRLF 가 삭제되었으므로 행이 하나 줄었다.
            _linecount--;

        //}

        if(caret_viewalways == false)
        {
          //[출력 위치 지우기]
          //{
              //현재 [입력지정자] 가 위치한 행의 처음부터 _bmp_ 의 끝까지 [사각형] 영역을 완전히 지운다.

              //[영역구하기]
              //{
                  rect     = _bmp_->Canvas->ClipRect;
                  rect.Top = _text_org_y + _caret_y;

              //}

              //[출력영역지우기]
              //{
                  //그냥 지우면 배경 그림이 지워지므로 반드시 배경 그림을 사용하여 지워야 한다.
                  BitBlt(_bmp_->Canvas->Handle, rect.Left + d_X_width, rect.Top, _bmp_->Width,
                         //적용 영역의 높이는 현재 행부터 _bmp_ 의 끝까지 해당한다.
                         _bmp_->Height - (_text_org_y + _caret_y),
                         _bmp_back_->Canvas->Handle, rect.Left, rect.Top, SRCCOPY);

              //}

          //}

          //[문자열]
          //{
              //[수평스크롤 적용]
              //{
                  //x 출력 위치는 기본적으로 margin_size 을 기준으로 출력된다.
                  //실제 출력시 사용되는 함수상의 x 좌표는 margin_size 에 대한 offset 위치가 된다.
                  _font.margin_size = _text_org_x;

              //}

              //새로 적용하는 함수.
              _EDITBUF_STRING_Out(0, _text_org_y + _caret_y, _linestartindex);

          //}

        }

        //[입력지정자 이동]
        //{
            //현재 [입력지정자] 위치의 문자만 지우므로 위치는 그대로 있으면 된다.
        //}

        if(caret_viewalways == false)
        {
          //[수정된 영역만 다시 그리기]
          //{
              //_Draw() 함수를 사용하여 전체를 다시 그리지 않고 수정된 행만 다시 그려준다.
              //이는 키가 계속눌려서 문자가 계속 출력되는 경우 처리속도를 최대한 높이기 위한 것이다.
              BitBlt(this->Canvas->Handle, rect.Left, rect.Top, this->Width,
                     //적용 영역의 높이는 현재 행부터 _bmp_ 의 끝까지 해당한다.
                     _bmp_->Height - (_text_org_y + _caret_y),
                     _bmp_->Canvas->Handle, rect.Left, rect.Top, SRCCOPY);

          //}
        }
        else
        {
          _EditBuf_Text();

        }

      }
      else
      {
        //[설명]{-------------------------------------------------
        //이 경우는 일반 문자가 삭제된 것이므로 행의 변화는 없다.
        //따라서, 현재 행만 다시 출력하여 수정하면 된다.
        //}-------------------------------------------------------

        if(caret_viewalways == false)
        {
          //[출력 위치 지우기]
          //{
              //현재 [입력지정자] 가 위치한 행의 처음부터 보이는 행의 마지막 행까지
              //[사각형] 영역을 완전히 지운다.

              //[영역구하기]
              //{
                  rect      = _bmp_->Canvas->ClipRect;
                  rect.Left = _text_org_x;
                  rect.Top  = _text_org_y + _caret_y;

              //}

              //[출력영역지우기]
              //{
                  //현재 [입력지정자] 가 위치한 행만 지운다.
                  //그냥 지우면 배경 그림이 지워지므로 반드시 배경 그림을 사용하여 지워야 한다.
                  BitBlt(_bmp_->Canvas->Handle, rect.Left + d_X_width, rect.Top, rect.Right - rect.Left, _line_height,
                         _bmp_back_->Canvas->Handle, rect.Left, rect.Top, SRCCOPY);

              //}

          //}

          //[문자열]
          //{
              //[현재행 시작 위치 구하기]
              //{
                  STR_Get_ThisLineStartIndex(_editbuf, _caretindex, &_linestartindex);

              //}

              //[수평스크롤 적용]
              //{
                  //x 출력 위치는 기본적으로 margin_size 을 기준으로 출력된다.
                  //실제 출력시 사용되는 함수상의 x 좌표는 margin_size 에 대한 offset 위치가 된다.
                  _font.margin_size = _text_org_x;

              //}

              //현재 [입력지정자] 가 위치한 행 시작부터 끝까지 해당 문자열을 다시 츨력한다.
              //행 처음부터 다시 출력해야 [문법강조] 를 수행할수 있다.
              //입력위치 x 의 _text_org_x 는 이미 _font.margin_size 에 적용된 상태이므로 따로 더할필요는 없다.
              _EDITBUF_LINE_Out(0, _text_org_y + _caret_y, _linestartindex, &r_lineendindex);

          //}

        }

        //[입력지정자 이동]
        //{
            //현재 [입력지정자] 위치의 문자만 지우므로 위치는 그대로 있으면 된다.
        //}

        if(caret_viewalways == false)
        {
          //[수정된 영역만 다시 그리기]
          //{
              //_Draw() 함수를 사용하여 전체를 다시 그리지 않고 수정된 행만 다시 그려준다.
              //이는 키가 계속눌려서 문자가 계속 출력되는 경우 처리속도를 최대한 높이기 위한 것이다.
              BitBlt(this->Canvas->Handle, rect.Left, rect.Top, rect.Right - rect.Left, _line_height + 1,
                     _bmp_->Canvas->Handle, rect.Left, rect.Top, SRCCOPY);

          //}

        }
        else
        {
          _EditBuf_Text();

        }

      }

  //}

  return;

}

void __fastcall TGEdit_Form::_KEY_Backspace(void)
{
  //[2007.01.19.금][생성!!!]{-------------------------------------------------------------
  //[Backspace-Key] 를 누른 경우를 처리한다.
  //[일반문자] 의 경우와 [CRLF] 의 경우를 구분하여 처리해야 한다.
  //단, 모든 것은 이미 만들어진 KEY_XXX() 함수의 조합으로 간단하게 처리할수 있다.
  //이 경우 [스크롤] 의 경우도 완전히 자동으로 처리된다.
  //예상외로 처리 속도가 빠르다.([입력지정자] 의 이동 모습이 눈에 띄지 않는다.)

  //[2007.02.13.화][추가!!!]
  //[선택영역] 이 이미 존재하는 경우 별도 처리를 추가한다.

  //[2007.02.20.화][수정!!!]
  //[선택영역] 을 _SELECT_Delete() 함수로 삭제할때 [선택영역] 안에 CRLF 가 삭제되면
  //전체 행수가 줄어 들게 되므로 관리변수인 _linecount 를 여기서 직접 수정해 주어야 한다.
  //삭제된 CRLF 개수는 _SELECT_Delete() 함수에서 반환된다.

  //[2007.02.27.화][수정!!!]
  //먼저 삭제하기전에 [입력지정자] 를 [선택영역] 시작 위치로 이동하게 되는데,
  //이때 _EDIT_CaretPos() 함수안에서 [선택영역] 이 해제 되므로 정보를 상실하게 된다.
  //따라서, _SELECT_Get_StartEndIndex() 에서 얻어진 정보를 _EDIT_CaretPos() 함수 처리 후
  //다시 설정해 줌으로써 [선택영역] 을 _SELECT_Delete() 함수로 삭제할수 있게 만든다.

  //[2007.03.11.일][수정!!!]
  //caret_viewalways 를 사용하여 중복 처리를 막는다.
  //}-------------------------------------------------------------------------------------

  //[VAR]
  //{
      struct GEdit_CARETINFO  select_start;
      struct GEdit_CARETINFO  select_end;

      int               crlf_count = 0;

      //_CARET_ViewAlways() 함수가 내부적으로 동작한 경우 true 로 된다.
      bool              caret_viewalways = false;

  //}

  //[개체존재:확인{****************************
  //[2012.05.01.화][추가!!!]
  //개체가 이미 해제된 뒤라면 처리하지 않는다.
  if(_this_ != true) return;
  //}******************************************

  //[선택영역 처리]
  //{
      //[선택영역] 을 삭제하는 경우는 _caretindex == 0 인 경우도 동작을
      //해야 하므로 여기서 처리한다.

      //[설명]{-----------------------------------------------
      //[2007.02.13.화]
      //1. 일단 [선택영역] 구간을 _editbuf[] 상에서 삭제한다.
      //2. <Key> 동작은 처리하지 않는다. (바로 종료한다.)
      //}-----------------------------------------------------

      //먼저 [입력지정자] 의 위치를 [선택영역] 시작 지점으로 이동시킨다.
      if(_SELECT_Get_StartEndIndex(&select_start, &select_end) != true) goto KEY_DEFAULT;

      //[입력지정자 위치 수정]
      //{
          //[선택영역] 이 있는 경우만 처리한다.
          //[입력지정자] 위치는 select_startindex 위치가 된다.(작은 index)
          _caretindex = select_start.caret_index;
          _caret_x    = select_start.caret_x;
          _caret_y    = select_start.caret_y;

          //[입력지정자 위치 조정]
          _EDIT_CaretPos(_caret_x, _caret_y);

      //}

      //[입력지정자 항상 보이기]
      //{
          _editbuf_text_no = true;
          //{
              //키는 항상 눈에 보이는 위치에 있어야 한다.
              _CARET_ViewAlways();
              //<키> 를 처리하지 않고 여기서 종료 되므로 이곳에서 한번 실행해 주어야 한다.

          //}
          _editbuf_text_no = false;

      //}

      //[선택영역 정보 복구]
      //{
          //위에서 처리한 _EDIT_CaretPos() 에서 해제된 [선택영역] 의 정보를 다음 처리부에서
          //삭제할수 있도록 이미 얻어 놓은 정보로 복구한다.
          _select_start = select_start;
          _select_end   = select_end;

      //}

      //[선택영역] 삭제.
      if(_SELECT_Delete(&select_start, &crlf_count) == true)
      {
        //[행수 수정]
        //{
            //CRLF 가 삭제된 것이 있는 경우만 수정한다.
            if(crlf_count > 0) { _linecount -= crlf_count; }

        //}

        //[수정 반영]
        //{
            //[선택영역] 이 삭제된 경우는 <키> 를 처리하지 않고 여기서 종료하게
            //되므로 이곳에서 직접 수정된 내용을 반영해 주어야 한다.
            _EditBuf_Text();

        //}

        //<키> 처리를 하지 않고 여기서 종료한다.
        return;

      }

  //}

  //[순수한 KEY 처리]-----------------------------------------------------------

  KEY_DEFAULT:

  //[입력지정자 항상 보이기]
  //{
      _editbuf_text_no = true;
      //{
          //키는 항상 눈에 보이는 위치에 있어야 한다.
          caret_viewalways = _CARET_ViewAlways();

      //}
      _editbuf_text_no = false;

  //}

  if(_caretindex <= 0)
  {
    //문서의 가장 처음 위치에 있는 경우는 더이상 처리할수 없다.
    //바로 종료한다.
    goto END;

  }

  if((_editbuf[_caretindex - 2] == 0x0d) && (_editbuf[_caretindex - 1] == 0x0a))
  {
    //현재 [입력지정자] 왼쪽의 문자가 CRLF 인경우.
    //CRLF 가 삭제되었으므로 윗행 마지막으로 이동해야 한다.
    _KEY_End(false);
    _KEY_Delete();

  }
  else
  {
    //현재 [입력지정자] 왼쪽의 문자가 [한글/영문] 인경우.
    //왼쪽의 문자를 삭제하면서 왼쪽으로 이동한다.
    _KEY_Left(false);
    _KEY_Delete();

  }

  END:

  if(caret_viewalways == true)
  {
    _EditBuf_Text();

  }

  return;

}

bool __fastcall TGEdit_Form::_EDITBUF_MOONJA_Delete(int   pDELETEindex,
                                                          bool *rIScrlf)
{
  //[2007.01.18.목][생성!!!]{----------------------------------------------------
  //_editbuf[] 상의 지정 위치의 문자를 삭제한다.
  //memmove() 함수를 사용하여 고속으로 다음 문자열을 해당 문자위치에 당겨서 덮는
  //방식을 사용한다.
  //}----------------------------------------------------------------------------

  //[주의!!!]
  //{
      //--------------------------------------------------------------------------------------
      //_filesize 는 FILE_Open_inBUF() 함수로 구해지므로 '\0' 을 포함한 크기다.
      //단, 문서가 수정된 것이므로 해당 정보도 다 수정해 주어야한다.

      //한글은 첫 byte 만 [음수] 가 아니라 2byte 각각 모두 음수이다.
      //이점을 주의해야 한다.
      //단, '똠' 과 같은 문자가 입력되는 경우는 자동으로 OS 가 [조합형코드] 로
      //바꾸어서 전달하는데 이 [조함형코드] 는 무조건 찻 byte 가 [음수] 이다.
      //따라서 [한글/영문] 을 구분하기 위해서는 반드시 첫 byte 가 [음수] 인지 확인해야 한다.
      //이는 순서대로 처리할때는 문제가 없으나, 역방향으로 버퍼를 거꾸로 검사하는 경우는
      //반드시 첫 byte 가 [음수] 인지 확인해 보아야 한다.

      //CRLF 가 처리되는 경우도 있으므로 이를 처리해 주어야 하는데,
      //0x0d0a 로 첫 byte 의 [음수] 인지 여부 만으로는 [영문] 으로 인식될수 있으므로
      //CRLF 는 별도로 검사해주어야 한다.

      //CRLF 를 삭제한 경우는 이를 알려 주어야 한다.(행이 바뀐 경우이므로 특별한 처리를 요함)
      //--------------------------------------------------------------------------------------
  //}

  //[VAR]
  //{
      int  length;

  //}

  //[개체존재:확인{****************************
  //[2012.05.01.화][추가!!!]
  //개체가 이미 해제된 뒤라면 처리하지 않는다.
  if(_this_ != true) return(false);
  //}******************************************

  //[한글/영문 인가?]
  //{
      if     (_editbuf[pDELETEindex] == '\0')
      {
        //현재 위치가 문서 끝이라면 더이상 처리하지 않는다.
        return(false);

      }
      else if((_editbuf[pDELETEindex] == 0x0d) && (_editbuf[pDELETEindex + 1] == 0x0a))
      {
        //CRLF.
        length     = 2;
        (*rIScrlf) = true;

      }
      else if(_editbuf[pDELETEindex] < 0)
      {
        //한글.
        length     = 2;
        (*rIScrlf) = false;

      }
      else
      {
        //영문.
        length     = 1;
        (*rIScrlf) = false;

      }

  //}

  //[정보수정]
  //{
      if(_filesize >= length)
      {
        //[공간 만들기]
        //{
            //당겨 붙이는 방법은 memmove() 함수를 사용하여 고속으로 간단하게 처리할수 있다.
            memmove(&(_editbuf[pDELETEindex]), &(_editbuf[pDELETEindex + length]),
                    //pDELETEindex 위치부터 파일끝까지만 당겨 붙이면 된다.
                    _filesize - pDELETEindex);

        //}

        //삭제한 문자의 길이만큼 [문서양] 이 준다.
        _filesize -= length;

      }
      else
      {
        //더이상 처리할수 없으므로 종료.
        return(false);

      }

  //}

  return(true);

}

bool __fastcall TGEdit_Form::_SELECT_Get_StartEndIndex(struct GEdit_CARETINFO  *rSELECTstart,
                                                       struct GEdit_CARETINFO  *rSELECTend)
{
  //[2007.02.14.수][생성!!!]{------------------------------------------------------------------------------
  //현재 [선택영역] 졍보 _select_start.caret_index, _select_end.caret_index 의 값중 작은 값을
  //startindex 로, 큰 값을 endindex 로 구하여 반환한다.
  //이는 [선택영역] 을 아래에서 위로 설정하는 경우는 "_select_start.caret_index > _select_end.caret_index"
  //가 될수 있기 때문인데 무조건 "startindex < endindex" 를 만족시키는 값을 얻어야 한다.
  //[선택영역] 이 있어서 처리된 경우는 true 를 반환한다.

  //[2007.02.17.토][수정!!!]-------------------------------------------------------------------------------
  //struct GEdit_CARETINFO 를 사용하여 정보 구조체를 전부 반환하는 것으로 수정한다.
  //}------------------------------------------------------------------------------------------------------

  //[개체존재:확인{****************************
  //[2012.05.01.화][추가!!!]
  //개체가 이미 해제된 뒤라면 처리하지 않는다.
  if(_this_ != true) return(false);
  //}******************************************

  //[선택영역 정리]
  //{
      if((_select_start.caret_index >= 0) && (_select_end.caret_index >= 0))
      {
        //[선택영역] 의 2 개 정보를 크기 순서로 다시 정리한다.
        //이는 [선택영역] 이 아래에서 위로 만들어 질수도 있기 때문이다.

        if(_select_start.caret_index <= _select_end.caret_index)
        {
          //[위=>아래] 로 기본적인 상태.
          (*rSELECTstart) = _select_start;
          (*rSELECTend)   = _select_end;

          return(true);

        }
        else
        {
          //[아래 => 위] 로 거꾸로 형성된 상태.
          (*rSELECTend)   = _select_start;
          (*rSELECTstart) = _select_end;

          return(true);

        }

      }
      else
      {
        //해당 없는 경우는 완전히 초기화 한다.
        rSELECTstart->caret_index = -1;
        rSELECTstart->caret_x     = 0;
        rSELECTstart->caret_y     = 0;

        rSELECTend->caret_index   = -1;
        rSELECTend->caret_x       = 0;
        rSELECTend->caret_y       = 0;

        return(false);

      }

  //}

  return(true) ;

}

bool __fastcall TGEdit_Form::_SELECT_Delete(struct GEdit_CARETINFO  *rSELECTstart,
                                               int               *rCRLFcount)
{
  //[2007.02.13.화][생성!!!]{---------------------------------------------------------------------
  //[선택영역] 의 문자열을 _editbuf[] 에서 삭제한다.
  //[선택영역] 정보는 관리변수 _select_start.caret_index, _select_endindex 를 참고한다.
  //[삭제] 하는 것은 memmove() 함수를 사용하여 고속으로 처리할수 있다.

  //_EDITBUF_MOONJA_Delete() 함수를 참고하여 만들었다.
  //_filesize 는 FILE_Open_inBUF() 함수로 구해지므로 '\0' 을 포함한 크기다.
  //단, 문서가 수정된 것이므로 해당 정보도 다 수정해 주어야한다.

  //[주의!!!][2007.02.14.수][수정!!!]
  //[선택영역] 안에 CRLF 가 들어 있는 개수만큼 행이 줄어 들게 되므로 이 함수를
  //사용하는 함수에서 관리정보인 _linecount 를 수정할수 있게 삭제된 구간안의
  //CRLF 의 개수를 반환한다.(전문 처리 함수를 사용한다.)

  //[2007.02.20.화][추가!!!]
  //여기서 [선택영역] 이 삭제 됬으므로 _select_start, _select_end 정보를 완전히 초기화 해야 한다.
  //}---------------------------------------------------------------------------------------------

  //[VAR]
  //{
      int               del_length;
      int               move_length;

      struct GEdit_CARETINFO  select_start;
      struct GEdit_CARETINFO  select_end;

  //}

  //[개체존재:확인{****************************
  //[2012.05.01.화][추가!!!]
  //개체가 이미 해제된 뒤라면 처리하지 않는다.
  if(_this_ != true) return(false);
  //}******************************************

  //[선택영역 정보 얻기]
  //{
      //처리할 [선택영역] 이 없는 경우는 여기서 바로 종료한다.
      if(_SELECT_Get_StartEndIndex(&select_start, &select_end) != true) return(false);

      //현재 [선택영역] 의 index 중 작은 값을 반환한다.
      (*rSELECTstart) = select_start;

      //삭제된 CRLF 개수 구하기.
      (*rCRLFcount) = STR_Get_CRLF_Count_withStartEndIndex(_editbuf, select_start.caret_index, select_end.caret_index);

  //}

  //[길이 정보 구하기]
  //{
      //삭제되는 [선택영역] 길이.
      del_length  = select_end.caret_index - select_start.caret_index;

      //복사 이동할 byte 단위 길이를 구한다.
      move_length = _filesize - select_end.caret_index;

  //}

  //[삭제]
  //{
      if(_filesize >= del_length)
      {
        //[공간 만들기]
        //{
            //memmove() 함수를 사용하여 _select_endindex => _select_start.caret_index 로
            //전부다 끌어당겨 붙이면 된다.
            memmove(&(_editbuf[select_start.caret_index]), &(_editbuf[select_end.caret_index]), move_length);

        //}

        //삭제한 문자의 길이만큼 [문서양] 이 준다.
        _filesize -= del_length;

      }
      else
      {
        //더이상 처리할수 없으므로 종료.
        return(false);

      }

  //}

  //[선택영역 초기화]
  //{
      //[2007.02.20.화][추가!!!]
      //[선택영역] 이 삭제 되었으므로 해당 정보를 완전히 초기화 한다.
      _select_start.caret_index = -1;
      _select_start.caret_x     = 0;
      _select_start.caret_y     = 0;

      _select_end.caret_index   = -1;
      _select_end.caret_x       = 0;
      _select_end.caret_y       = 0;

  //}

  return(true);

}

void __fastcall TGEdit_Form::_EDITBUF_MOONJA_Insert(int    pINSERTindex,
                                                          short  pCODE)
{
  //[2007.01.10.수][생성!!!]{--------------------------------------------------
  //_editbuf[] 상의 지정 위치에 전달받은 [문자코드] 의 문자를 끼워넣는다.
  //끼워 넣을 장소를 만드는데 처리의 특성상 memmove() 함수를 사용할수 없으므로
  //for() 문으로 직접 처리한다.
  //일단, 끼워넣을 문자 만큼의 공간을 만들고 해당 위치에 끼워넣으면 된다.
  //문자 한개만 처리하므로 [한글/영문] 인지만 구분하여 처리하면 된다.
  //}--------------------------------------------------------------------------

  //[주의!!!]
  //{
      //------------------------------------------------------------------------
      //_filesize 는 FILE_Open_inBUF() 함수로 구해지므로 '\0' 을 포함한 크기다.
      //단, 문서가 수정된 것이므로 해당 정보도 다 수정해 주어야한다.
      //------------------------------------------------------------------------
  //}

  //[주의!!!][2007.01.14.일]
  //{
      //-------------------------------------------------------------------------------------
      //한글은 첫 byte 만 [음수] 가 아니라 2byte 각각 모두 음수이다.
      //이점을 주의해야 한다.
      //단, '똠' 과 같은 문자가 입력되는 경우는 자동으로 OS 가 [조합형코드] 로
      //바꾸어서 전달하는데 이 [조함형코드] 는 무조건 찻 byte 가 [음수] 이다.
      //따라서 [한글/영문] 을 구분하기 위해서는 반드시 첫 byte 가 [음수] 인지 확인해야 한다.
      //이는 순서대로 처리할때는 문제가 없으나, 역방향으로 버퍼를 거꾸로 검사하는 경우는
      //반드시 첫 byte 가 [음수] 인지 확인해 보아야 한다.
      //-------------------------------------------------------------------------------------
  //}

  //[수정!!!][2007.01.16.화]
  //{
      //CRLF 가 처리되는 경우도 있으므로 이를 처리해 주어야 하는데,
      //어차피 2 byte 라서 [한글] 처럼 검사되므로 추가 처리는 하지 않아도 자동으로 처리된다.
  //}

  //[참고!!!][2007.01.17.수]
  //{
      //계속 [Enter-Key] 를 누르고 있는 경우 _KEY_Enter() 가 호출되는데,
      //_EDITBUF_MOONJA_Insert() 함수는 for() 문을 사용하여 일일히 _editbuf[] 의
      //내용을 밀어내기를 하므로 문서량이 많은 경우는 처리 속도가 느려질수 있다.
      //그러나, 10만줄의 겨우도 그렇게 느낄 정도는 아니다.
  //}

  //[VAR]
  //{
      //전달받은 문자열의 길이.
      int  length;

  //}

  //[개체존재:확인{****************************
  //[2012.05.01.화][추가!!!]
  //개체가 이미 해제된 뒤라면 처리하지 않는다.
  if(_this_ != true) return;
  //}******************************************

  //[한글/영문 인가?]
  //{
      if((pCODE & 0xff00) == 0)
      {
        //영문.
        length = 1;

      }
      else
      {
        //한글 또는 [CRLF] 가 된다.
        length = 2;

      }

  //}

  //[공간 만들기]
  //{
      //[2007.04.09.월][수정!!!]
      //for() 문을 사용하여 공간을 만드는 것은 문서량이 많은 경우 시간이 많이 걸리므로
      //_tempbuf[] 와 memmove() 함수를 사용하여 복사 방식으로 처리한다.

      //끼워 넣을 시작위치부터 문서끝까지의 문자열을 _tempbuf[] 에 대피시킨다.
      memmove(_tempbuf, &(_editbuf[pINSERTindex]), _filesize - pINSERTindex);

      //끼워넣을 공간 다음에 다시 복사해서 붙여준다.
      memmove(&(_editbuf[pINSERTindex + length]), _tempbuf, _filesize - pINSERTindex);

  //}

  //[끼워넣기]
  //{
      //지정 위치에 문자열을 끼워넣는다.
      if(length == 1)
      {
        //영문.
        _editbuf[pINSERTindex] = (pCODE & 0xff);

      }
      else
      {
        //한글.
        _editbuf[pINSERTindex + 0] = ((pCODE >> 8) & 0xff); //상위 바이트 출력.
        _editbuf[pINSERTindex + 1] = (pCODE & 0xff);        //하위 바이트 출력.

      }

  //}

  //[정보수정]
  //{
      //추가한 문자의 길이만큼 [문서양] 이 는다.
      _filesize += length;

  //}

  return;

}

void __fastcall TGEdit_Form::_CARET_MOONJA_Insert(short  pCODE)
{
  //[2007.01.12.금][생성!!!]{----------------------------------------------------------------
  //현재 _caretindex 위치에 전달받은 문자를 끼워넣는다.
  //
  //단, _EditBuf_Text() 처럼 전체를 다시 출력하는 것이 아니라 지정 일부를 출력하여
  //연속하여 문자를 계속 출력하는 경우, 처리 시간을 최소한으로 줄이기 위한 것이다.
  //또한, 이 함수는 한 행에서만 유효하다.

  //[2007.02.01.목][수정!!!]
  //이전에는 [입력지정자] 의 위치에서부터 행 끝가지 다시 출력했으나 [문법강조] 를 위하여
  //행의 처음부터 완전히 다시 출력하는 것으로 수정한다.

  //[2007.02.13.화][추가!!!]
  //[선택영역] 이 이미 존재하는 경우 별도 처리를 추가한다.

  //[2007.02.20.화][수정!!!]
  //[선택영역] 을 _SELECT_Delete() 함수로 삭제할때 [선택영역] 안에 CRLF 가 삭제되면
  //전체 행수가 줄어 들게 되므로 관리변수인 _linecount 를 여기서 직접 수정해 주어야 한다.
  //삭제된 CRLF 개수는 _SELECT_Delete() 함수에서 반환된다.

  //[2007.02.27.화][수정!!!]
  //먼저 삭제하기전에 [입력지정자] 를 [선택영역] 시작 위치로 이동하게 되는데,
  //이때 _EDIT_CaretPos() 함수안에서 [선택영역] 이 해제 되므로 정보를 상실하게 된다.
  //따라서, _SELECT_Get_StartEndIndex() 에서 얻어진 정보를 _EDIT_CaretPos() 함수 처리 후
  //다시 설정해 줌으로써 [선택영역] 을 _SELECT_Delete() 함수로 삭제할수 있게 만든다.

  //<Shift> 를 눌러 [대문자] 등을 입력하는 경우는 _shift_down == true 가 되어
  //_EDIT_CaretPos() 함수에서 [선택영역] 이 해제 되지 않으므로 깜박거리는 문제가 발생한다.
  //따라서, 이 함수처럼 [문자] 를 입력하는 경우는 자동적으로 [선택영역] 이 해제되어야 하므로
  //여기서 _shift_down = false 해주어 _EDIT_CaretPos() 함수 처리시 자동으로 [선택영역] 이
  //해제 되도록 만든다.

  //[2007.03.10.토][수정!!!]
  //_EditBuf_Text() 등 다시 그리는 함수가 중복 처리 되어 깜박이는 문제를 수정하였다.

  //[2007.03.26.월][수정!!!]
  //[전체배경사용] 의 경우는 이 창에 해당하는 부분이 보여지도록 한다.
  //
  //[2018.02.14.수][중요수정!!!]*************************************************************
  //UBUNTU 등 리눅스에서 "Wine" 으로 구동하는 경우 [한글입력문제] 가 발생하여 이를 수정.
  //[문제] : [한/영키:한글] 인 경우는 한글뿐 아니라 Ascii 문자도 전부 2 바이트로 넘어 온다.
  //         즉, WM_CHAR 에서 처리되지 않고 WM_IME_COMPOSITION 에서 한글과 같이 처리되어
  //         전체 2 바이트가 된다.
  //         '6' 문자의 경우 0x36 의 값이나 실제는 0x3600 으로 넘어 오게 된다.
  //         이경우 [상위 바이트] 만으로 1 바이트 코드가 되도록 내부 적으로 선행처리를 수행
  //         하도록 수정한다.
  //}----------------------------------------------------------------------------------------

  //[방법]
  //{
      //------------------------------------------------------------------------------
      //(1) : _editbuf[] 상에 문자를 집어 넣는다.
      //(2) : 문자열이 출력될 부분을 전부 지운다.(해당 행에 국한된다.)
      //      이는 처리 시간을 최소한으로 줄이기 위하여 출력을 시작할 문자의 위치부터
      //      이 행의 끝까지 전부 지운다.
      //(3) : _editbuf[] 상의 지정 범위의 문자열을 출력한다.
      //(4) : [입력지정자] 를 현재 입력한 문자열 다음위치로 이동시킨다.
      //      이는 이미 모든 처리를 할수 있는 _KEY_Right() 함수로 간단하게 처리한다.
      //------------------------------------------------------------------------------
  //}

  //[VAR]
  //{
      TRect             rect;

      int               r_lineendindex;

      struct GEdit_CARETINFO  select_start;
      struct GEdit_CARETINFO  select_end;

      int               crlf_count = 0;

      //_CARET_ViewAlways() 함수가 내부적으로 동작한 경우 true 로 된다.
      bool              caret_viewalways = false;
      bool              select_use       = false;

  //}

  //[개체존재:확인{****************************
  //[2012.05.01.화][추가!!!]
  //개체가 이미 해제된 뒤라면 처리하지 않는다.
  if(_this_ != true) return;
  //}******************************************

  //[<Shift> 해제]
  //{
      //[2007.02.27.화][추가!!!]
      //<Shift> 는 문자 입력시 무효하므로 [선택영역] 의 해제를 위하여 기록을 지운다.
      //이렇게 하는 이유는 _EDIT_CaretPos() 함수안의 [선택영역] 처리부분을 참고한다.
      _shift_down = false;

  //}

  //[선택영역 처리]
  //{
      //[설명]{-----------------------------------------------
      //[2007.02.13.화]
      //1. 일단 [선택영역] 구간을 _editbuf[] 상에서 삭제한다.
      //2. <Key> 동작을 처리한다.
      //}-----------------------------------------------------

      //먼저 [입력지정자] 의 위치를 [선택영역] 시작 지점으로 이동시킨다.
      if(_SELECT_Get_StartEndIndex(&select_start, &select_end) != true) goto KEY_DEFAULT;

      //[입력지정자 위치 수정]
      //{
          //[선택영역] 이 있는 경우만 처리한다.
          //[입력지정자] 위치는 select_startindex 위치가 된다.(작은 index)
          _caretindex = select_start.caret_index;
          _caret_x    = select_start.caret_x;
          _caret_y    = select_start.caret_y;

          //[입력지정자 위치 조정]
          _EDIT_CaretPos(_caret_x, _caret_y);

      //}

      //[입력지정자 항상 보이기]
      //{
          _editbuf_text_no = true;
          //{
              //키는 항상 눈에 보이는 위치에 있어야 한다.
              caret_viewalways = _CARET_ViewAlways();
              //<키> 를 처리하지 않고 여기서 종료 되므로 이곳에서 한번 실행해 주어야 한다.

          //}
          _editbuf_text_no = false;

      //}

      //[선택영역 정보 복구]
      //{
          //위에서 처리한 _EDIT_CaretPos() 에서 해제된 [선택영역] 의 정보를 다음 처리부에서
          //삭제할수 있도록 이미 얻어 놓은 정보로 복구한다.
          _select_start = select_start;
          _select_end   = select_end;

      //}

      //[선택영역] 삭제.
      if(_SELECT_Delete(&select_start, &crlf_count) == true)
      {
        //[선택영역] 을 처리했다는 것을 알린다.
        select_use = true;

        //[행수 수정]
        //{
            //CRLF 가 삭제된 것이 있는 경우만 수정한다.
            if(crlf_count > 0) { _linecount -= crlf_count; }

        //}

        //[삭제 반영]
        //{
            //이 문자키의 경우는 한줄만 다시 그려지므로 여기서 처리하지 않으면
            //삭제된 내용이 반영되지 않는다.(반드시 여기서 처리한다)
            //_EditBuf_Text();
            //[2007.03.10.토][수정!!!]
            //여기서 처리 하지 않고 중복 처리를 방지하기 위하여 이 함수 끝에서 한번만 처리한다.
        //}

      }

      goto CONTINUE;

  //}

  //[순수한 KEY 처리]-----------------------------------------------------------

  KEY_DEFAULT:

  //[입력지정자 항상 보이기]
  //{
      _editbuf_text_no = true;
      //{
          //키는 항상 눈에 보이는 위치에 있어야 한다.
          caret_viewalways = _CARET_ViewAlways();

      //}
      _editbuf_text_no = false;

  //}

  CONTINUE:

  //[wine 에서 구동하는 경우를 위한 수정]
  //{
      //[2018.02.14.수][중요수정!!!]-------------------------------------------------------------
      //UBUNTU 등 리눅스에서 "Wine" 으로 구동하는 경우 [한글입력문제] 가 발생하여 이를 수정.
      //[문제] : [한/영키:한글] 인 경우는 한글뿐 아니라 Ascii 문자도 전부 2 바이트로 넘어 온다.
      //         즉, WM_CHAR 에서 처리되지 않고 WM_IME_COMPOSITION 에서 한글과 같이 처리되어
      //         전체 2 바이트가 된다.
      //         '6' 문자의 경우 0x36 의 값이나 실제는 0x3600 으로 넘어 오게 된다.
      //         이경우 [상위 바이트] 만으로 1 바이트 코드가 되도록 내부 적으로 선행처리를 수행
      //         하도록 수정한다.
      //
      //[한글] 은 항상 2바이트 값이다.
      //}----------------------------------------------------------------------------------------

      if((pCODE & 0x00ff) == 0)
      {
        //[하위 바이트] 가 0 이면 Ascii 로 보고 1바이트 코드로 변환.
        pCODE = (pCODE >> 8) & 0x00ff;

      }

  //}

  //[버퍼 수정]
  //{
      //_editbuf[] 상의 _caretindex 위치에 일단 문자를 집어 넣는다.
      _EDITBUF_MOONJA_Insert(_caretindex, pCODE);

  //}

  //[출력 위치 지우기]
  //{
      if((caret_viewalways == false) && (select_use == false))
      {
        //[영역구하기]
        //{
            rect      = _bmp_->Canvas->ClipRect;
            rect.Left = _text_org_x;
            rect.Top  = _text_org_y + _caret_y;

        //}

        //[출력영역지우기]
        //{
            //현재 [입력지정자] 가 위치한 행만 지운다.
            //그냥 지우면 배경 그림이 지워지므로 반드시 배경 그림을 사용하여 지워야 한다.
            BitBlt(_bmp_->Canvas->Handle, rect.Left + d_X_width, rect.Top, rect.Right - rect.Left, _line_height,
                   _bmp_back_->Canvas->Handle, rect.Left, rect.Top, SRCCOPY);
            
        //}

      }

  //}

  if((caret_viewalways == false) && (select_use == false))
  {
    //[현재행 시작 위치 구하기]
    //{
        STR_Get_ThisLineStartIndex(_editbuf, _caretindex, &_linestartindex);

    //}

    //[문자열 출력]
    //{
        //x 가 적용될 절대 기준 위치를 설정해 놓는다.
        _font.margin_size = _text_org_x;

        //현재 [입력지정자] 가 위치한 행 시작부터 끝까지 해당 문자열을 다시 츨력한다.
        //행 처음부터 다시 출력해야 [문법강조] 를 수행할수 있다.
        //입력위치 x 의 _text_org_x 는 이미 _font.margin_size 에 적용된 상태이므로 따로 더할필요는 없다.
        _EDITBUF_LINE_Out(0, _text_org_y + _caret_y, _linestartindex, &r_lineendindex);

    //}

  }

  //[입력지정자 이동]
  //{
      //현재 [입력지정자] 위치에 한개의 문자를 입력했으므로 이 문자 다음위치로 이동시킨다.
      //단, 문자를 입력하는 과정이므로 false 로 설정하여 <Shift-Key> 로 문자를 입력하는 경우
      //[선택영역] 을 생성하지 않고 [문자] 를 입력할수 있게 한다.
      _KEY_Right(false, false);

  //}

  //[수정된 영역만 다시 그리기]
  //{
      if((caret_viewalways == false) && (select_use == false))
      {
        //_Draw() 함수를 사용하여 전체를 다시 그리지 않고 수정된 행만 다시 그려준다.
        //이는 키가 계속눌려서 문자가 계속 출력되는 경우 처리속도를 최대한 높이기 위한 것이다.
        BitBlt(this->Canvas->Handle, rect.Left, rect.Top, rect.Right - rect.Left, _line_height + 1,
               _bmp_->Canvas->Handle, rect.Left, rect.Top, SRCCOPY);

      }
      else
      {
        _EditBuf_Text();

      }

  //}

  return;

}

//[문자열관련]------------------------------------------------------------------

void __fastcall TGEdit_Form::_WORD_LineGet(char  *pSTR,
                                              int    pINDEX,
                                              char   rWORD[],
                                              int   *rWORDindex,
                                              int   *rWORDlength)
{
  //[2007.01.24.수][생성!!!]{-------------------------------------------------------
  //pSTR[] 상의 지정 위치부터 처음 발견되는 단어를 추출하여 반환한다.
  //이 함수는 [문법강조] 를 처리 하기 위하여 사용되며, [한국형-C] 언어에서는
  //[한글] 이름도 지원한다.
  //[CRLF], ['\0'], [이름], [//], ["], ['], [\"], [\'], [#], [기타기호등] 이 단어로
  //반환되는데 이는 각각 문자색을 적용하는데 중요한 정보를 갖기 때문이다.
  //처리 속도를 빠르게 하기 위하여 다른 자세한 것은 여기서 구분하지 않는다.

  //[2007.01.27.토][추가!!!]
  //[공백] 이 계속 이어지는 경우나 [공백] 도 전부 단어로 취급한다.

  //[2007.01.31.수][수정!!!]
  //[//], ["], ['], [\"], [\'], [#] 은 각각이 단어로 완전히 분리 되어야 한다.
  //abc//efg 는 "abc" "//" "efg" 의 3 개 단어로 구분되어야한다.
  //}-------------------------------------------------------------------------------

  //[VAR]
  //{
      int   i = pINDEX;

      //문자가 저장되는 시작 위치로 이 위치에는 항상 '\0' 이 있다.
      //즉, w 값이 결국 반환하는 단어의 길이가 된다.
      int   w = 0;

      char  wkind = 0;
      //'n' : 이름
      //'s' : 기호
      //'_' : 공백

  //}

  //[개체존재:확인{****************************
  //[2012.05.01.화][추가!!!]
  //개체가 이미 해제된 뒤라면 처리하지 않는다.
  if(_this_ != true) return;
  //}******************************************

  //[초기화]
  //{
      //------------------------------
      //현재는 아무 문자도 없는 상태.
      rWORD[0]       = '\0';
      (*rWORDindex)  = i;
      (*rWORDlength) = w;
      //------------------------------
  //}

  START:

  //문자 code 를 추출한다.
  if     ((pSTR[i] == ' ') || (pSTR[i] == '\t'))
  {
    if(rWORD[0] == '\0')
    {
      //지금 문자가 첫문자인 경우는 [공백] 조합이 시작됬음을 알린다.
      wkind = '_';

      //현재 문자의 시작 위치를 기록한다.
      (*rWORDindex) = i;

      //초기화.
      w        = 0;
      rWORD[w] = '\0';

    }
    else
    {
      if(wkind != '_')
      {
        //이 경우는 [name/symbol] 조함중 다른 문자가 발견된 것이므로 여기서 조합된 단어를 반환한다.

        //[단어반환]
        //{
            //이미 단어가 완성된 상태이므로 여기서 바로 종료하면 된다.
            return;

        //}

      }

    }

    //[단어합성]
    //{
        //1 byte 만 처리한다.

        rWORD[w++] = pSTR[i++];
        rWORD[w]   = '\0';

        (*rWORDlength) = w;

    //}

    //계속 문자를 검사한다.
    goto START;

  }
  else if(pSTR[i] == '\0')
  {
    //현재 문서의 끝이라면 행의 끝이므로 더이상 처리하지 않고
    //현재 단어를 반환한다.

    if(rWORD[0] == '\0')
    {
      //첫문자가 '\0' 인 경우도 이것을 문자로 반환해야 한다.
      (*rWORDindex) = i;

      rWORD[w]   = '\0';

      (*rWORDlength) = w;

    }

    //[단어반환]
    //{
        //이미 단어가 완성된 상태이므로 여기서 바로 종료하면 된다.
        return;

    //}

  }
  else if((pSTR[i] == 0x0d) || (pSTR[i + 1] == 0x0a))
  {
    //현재 행의 끝이라면 더이상 처리 하지 않고 조합된 단어를 즉시 반환한다.

    if(rWORD[0] == '\0')
    {
      //첫문자가 CRLF 인 경우는 이 code 를 그대로 단어로 반환해 준다.
      (*rWORDindex) = i;

      rWORD[w++] = 0x0d;
      rWORD[w++] = 0x0a;
      rWORD[w]   = '\0';

      (*rWORDlength) = w;

    }

    //[단어반환]
    //{
        //이미 단어가 완성된 상태이므로 여기서 바로 종료하면 된다.
        return;

    //}

  }
  else if(pSTR[i] < 0)
  {
    //[한글] 은 2 byte 를 같이 처리한다.

    if(rWORD[0] == '\0')
    {
      //지금 문자가 첫문자인 경우는 [name] 조합이 시작됬음을 알린다.
      wkind = 'n';

      //현재 문자의 시작 위치를 기록한다.
      (*rWORDindex) = i;

      //초기화.
      w        = 0;
      rWORD[w] = '\0';

    }
    else
    {
      if(wkind != 'n')
      {
        //이 경우는 [space/symbol] 조함중 다른 문자가 발견된 것이므로 여기서 조합된 단어를 반환한다.

        //[단어반환]
        //{
            //이미 단어가 완성된 상태이므로 여기서 바로 종료하면 된다.
            return;

        //}

      }

    }

    //[단어합성]
    //{
        //한글은 2 byte 를 처리해야 한다.

        rWORD[w++] = pSTR[i++];
        rWORD[w++] = pSTR[i++];
        rWORD[w]   = '\0';

        (*rWORDlength) = w;

    //}

    //계속 문자를 검사한다.
    goto START;

  }
  else if((pSTR[i] == '_') || (('0' <= pSTR[i]) && (pSTR[i] <= '9')) ||
          (('a' <= pSTR[i]) && (pSTR[i] <= 'z')) || (('A' <= pSTR[i]) && (pSTR[i] <= 'Z')))
  {
    //[한글] 을 제외하고 순순하게 [이름] 을 구성할수 있는 문자인경우.

    if(rWORD[0] == '\0')
    {
      //지금 문자가 첫문자인 경우는 [name] 조합이 시작됬음을 알린다.
      wkind = 'n';

      //현재 문자의 시작 위치를 기록한다.
      (*rWORDindex) = i;

      //초기화.
      w        = 0;
      rWORD[w] = '\0';

    }
    else
    {
      if(wkind != 'n')
      {
        //이 경우는 [space/symbol] 조함중 다른 문자가 발견된 것이므로 여기서 조합된 단어를 반환한다.

        //[단어반환]
        //{
            //이미 단어가 완성된 상태이므로 여기서 바로 종료하면 된다.
            return;

        //}

      }

    }

    //[단어합성]
    //{
        //1 byte 만 처리한다.

        rWORD[w++] = pSTR[i++];
        rWORD[w]   = '\0';

        (*rWORDlength) = w;

    //}

    //계속 문자를 검사한다.
    goto START;

  }

  //분리 기호의 경우는 문자를 직접 검사하여 처리한다.
  else if((pSTR[i] == '/') && (pSTR[i + 1] == '/'))
  {
    // [//] 단어처리.
    if(wkind == 0)
    {
      //이 단어가 처음 시작하는 경우.
      (*rWORDindex) = i;

      rWORD[w++] = '/';
      rWORD[w++] = '/';
      rWORD[w]   = '\0';

      (*rWORDlength) = w;

    }

    //[단어반환]
    //{
        //이미 단어가 완성된 상태이므로 여기서 바로 종료하면 된다.
        return;

    //}

  }
  else if((pSTR[i] == '\\') && (pSTR[i + 1] == '\"'))
  {
    // [\"] 단어처리.
    if(wkind == 0)
    {
      //이 단어가 처음 시작하는 경우.
      (*rWORDindex) = i;

      rWORD[w++] = '\\';
      rWORD[w++] = '\"';
      rWORD[w]   = '\0';

      (*rWORDlength) = w;

    }

    //[단어반환]
    //{
        //이미 단어가 완성된 상태이므로 여기서 바로 종료하면 된다.
        return;

    //}

  }
  else if((pSTR[i] == '\\') && (pSTR[i + 1] == '\''))
  {
    // [\'] 단어처리.
    if(wkind == 0)
    {
      //이 단어가 처음 시작하는 경우.
      (*rWORDindex) = i;

      rWORD[w++] = '\\';
      rWORD[w++] = '\'';
      rWORD[w]   = '\0';

      (*rWORDlength) = w;

    }

    //[단어반환]
    //{
        //이미 단어가 완성된 상태이므로 여기서 바로 종료하면 된다.
        return;

    //}

  }
  else if(pSTR[i] == '\"')
  {
    // ["] 단어처리.
    if(wkind == 0)
    {
      //이 단어가 처음 시작하는 경우.
      (*rWORDindex) = i;

      rWORD[w++] = '\"';
      rWORD[w]   = '\0';

      (*rWORDlength) = w;

    }

    //[단어반환]
    //{
        //이미 단어가 완성된 상태이므로 여기서 바로 종료하면 된다.
        return;

    //}

  }
  else if(pSTR[i] == '\'')
  {
    // ['] 단어처리.
    if(wkind == 0)
    {
      //이 단어가 처음 시작하는 경우.
      (*rWORDindex) = i;

      rWORD[w++] = '\'';
      rWORD[w]   = '\0';

      (*rWORDlength) = w;

    }

    //[단어반환]
    //{
        //이미 단어가 완성된 상태이므로 여기서 바로 종료하면 된다.
        return;

    //}

  }
  else if(pSTR[i] == '#')
  {
    // [#] 단어처리.
    if(wkind == 0)
    {
      //이 단어가 처음 시작하는 경우.
      (*rWORDindex) = i;

      rWORD[w++] = '#';
      rWORD[w]   = '\0';

      (*rWORDlength) = w;

    }

    //[단어반환]
    //{
        //이미 단어가 완성된 상태이므로 여기서 바로 종료하면 된다.
        return;

    //}

  }

  else
  {
    //그밖의 기호, 영문등을 처리한다.
    //특별하게 단어로 규정되지 않는 이상 전부 단어로 이어 붙인다.

    if(rWORD[0] == '\0')
    {
      //지금 문자가 첫문자인 경우는 [symbol] 조합이 시작됬음을 알린다.
      wkind = 's';

      //현재 문자의 시작 위치를 기록한다.
      (*rWORDindex) = i;

      //초기화.
      w        = 0;
      rWORD[w] = '\0';

    }
    else
    {
      if(wkind != 's')
      {
        //이 경우는 [space/name] 조함중 다른 문자가 발견된 것이므로 여기서 조합된 단어를 반환한다.

        //[단어반환]
        //{
            //이미 단어가 완성된 상태이므로 여기서 바로 종료하면 된다.
            return;

        //}

      }

    }

    //[단어합성]
    //{
        //1 byte 만 처리한다.

        rWORD[w++] = pSTR[i++];
        rWORD[w]   = '\0';

        (*rWORDlength) = w;

    //}

    //[특별 단어 검사]
    //{
        //해당되는 단어를 찾은 경우는 즉시 종료한다.
        if(STR_Match(rWORD, "//")   == true) return;
        if(STR_Match(rWORD, "\"")   == true) return;
        if(STR_Match(rWORD, "\'")   == true) return;
        if(STR_Match(rWORD, "\\\"") == true) return;
        if(STR_Match(rWORD, "\\\'") == true) return;
        if(STR_Match(rWORD, "#")    == true) return;

    //}

    //계속 문자를 검사한다.
    goto START;

  }

  return;

}

void __fastcall TGEdit_Form::_MOONJACONFIG_LineGet(int  pLINEstartindex)
{
  //[2007.01.23.화][생성!!!]{-------------------------------------------------------------------
  //현재 행의 문자열을 분석하여 [문법강조] 등의 특성을 적용하여 해당 정보 구조체에 기록해 준다.
  //대상은 _editbuf[] 이고 처리 결과는 _wordconfig[] 전역 구조체에 저장된다.
  //행을 구분하는 것은 이 함수안에서 자체적으로 처리 되어야한다.
  //즉, CRLF, '\0' 인 경우는 현재 행이 끝난 것으로 보고 종료한다.
  //단, [선택영역] 은 여기서 처리하지 않는다.

  //[2007.02.03.토][추가!!!]
  //[2진수], [16진수] 의 경우도 지정된 색으로 입힌다.
  //}-------------------------------------------------------------------------------------------

  //[주의!!!]
  //{
      //_wordconfig[] 의 정보를 사용하여 문자열을 출력하게 되므로,
      //행의 끝을 알수 있는 CRLF, '\0' 도 같이 처리해 주어야 한다.

      //[2007.02.03.토][참고!!!]
      //STR_Match() 로 2 문자 이하의 단어를 검사하는 경우는 문자를 각각 비교하는것에
      //비하여 시간 낭비일수 있다.
  //}

  //[VAR]
  //{
      char  word[1024];
      int   wordindex;
      int   wordlength;

      int   i;

      //단어구분을 위한것.
      char  wkind = 0;

      bool  first_word = true;

  //}

  //[개체존재:확인{****************************
  //[2012.05.01.화][추가!!!]
  //개체가 이미 해제된 뒤라면 처리하지 않는다.
  if(_this_ != true) return;
  //}******************************************

  //[초기화]
  //{
      _wordconfig_count = 0;

  //}

  //[문법강조처리]
  //{
      for(i = pLINEstartindex; ; i = (wordindex + wordlength))
      {
        _WORD_LineGet(_editbuf, i, word, &wordindex, &wordlength);

        if(((word[0] == 0x0d) && (word[1] == 0x0a)) || (word[0] == '\0'))
        {
          //이 경우는 완전히 현재 검사하는 행이 끝난 것이므로 처리를 종료한다.
          break;

        }


        //[전부 기본 문자 설정]
        //{
            //[2007.11.15.목][수정!!!]{-----------------------------------------------------------
            //원래 [색동문자] 기능을 가지고 있었으나 일반 Edit 기능만 있으면 되므로 이를 제거하고
            //전부 기본 설정으로 설정한다.
            //[색동문자] 기능을 사용할 경우는 아래의 주석문을 해제하고 지금 이 구간을 전부 주석문
            //처리하면 된다.
            //}-----------------------------------------------------------------------------------

            _wordconfig[_wordconfig_count].index       = wordindex;
            _wordconfig[_wordconfig_count].length      = wordlength;
            _wordconfig[_wordconfig_count].front_color = _fontColor;
            _wordconfig[_wordconfig_count].back_color  = _backColor;
            _wordconfig[_wordconfig_count].transparent = true;
            _wordconfig[_wordconfig_count].style       = _fontStyle;

            _wordconfig_count++;

            first_word = false;

        //}

      }

      
/*

        //[종류판별]
        //{
            //이미 설정 정보가 있는 경우는 바로 해당 처리부로 이동해야 한다.

            switch(wkind)
            {
              case '#' :
              {
                //이미 [지시어] 안의 단어라면, 이 행의 끝까지 [지시어] 색으로 처리한다.
                goto DIRECTIVE;

              } break;

              case 's' :
              {
                //현재 [문자열] 안이다.
                goto STRING;

              } break;

              case 'c' :
              {
                //현재 [문자상수] 안이다.
                goto CHAR;

              } break;

              case 'j' :
              {
                //현재 [주석문] 안이므로, 이 행의 끝까지 [주석문] 색으로 처리한다.
                goto COMMENT;

              } break;

            }
        //}

        SPACE:
        {
          if(word[0] == ' ')
          {
            //[공백] 이 계속되는 경우도 단어로 취급되므로 이 경우는 기본 설정으로 처리한다.
            //이 경우는 첫문자만 검사하면 된다.
            //[공백] 도 단어로 추출은 되므로 단어검사부분에서 쓸데없는 시간 낭비를 막기 위하여
            //여기서 따로 처리한다.
            _wordconfig[_wordconfig_count].index       = wordindex;
            _wordconfig[_wordconfig_count].length      = wordlength;
            _wordconfig[_wordconfig_count].front_color = IDE_COLOR_1;
            _wordconfig[_wordconfig_count].back_color  = IDE_COLOR_2;
            _wordconfig[_wordconfig_count].transparent = true;
            _wordconfig[_wordconfig_count].style       = d_FONTSTYLE_default;

            _wordconfig_count++;

            continue;

          }

        }

        KEYWORD:
        {
          if(STR_Match(word, "break")  || STR_Match(word, "else")    || STR_Match(word, "switch")   ||
             STR_Match(word, "case")   || STR_Match(word, "return")  || STR_Match(word, "continue") ||
             STR_Match(word, "for")    || STR_Match(word, "default") || STR_Match(word, "goto")     ||
             STR_Match(word, "do")     || STR_Match(word, "if")      || STR_Match(word, "while")    ||
             STR_Match(word, "struct") || STR_Match(word, "union")   || STR_Match(word, "double")   ||
             STR_Match(word, "int")    || STR_Match(word, "long")    || STR_Match(word, "char")     ||
             STR_Match(word, "float")  || STR_Match(word, "short")   || STR_Match(word, "unsigned") ||
             STR_Match(word, "signed") || STR_Match(word, "void")    || STR_Match(word, "include")  ||
             STR_Match(word, "define") || STR_Match(word, "undef"))
          {
            //[예약어] 는 지정색으로 칠한다.
            _wordconfig[_wordconfig_count].index       = wordindex;
            _wordconfig[_wordconfig_count].length      = wordlength;
            _wordconfig[_wordconfig_count].front_color = IDE_COLOR_keyword;
            _wordconfig[_wordconfig_count].back_color  = IDE_COLOR_2;
            _wordconfig[_wordconfig_count].transparent = true;
            _wordconfig[_wordconfig_count].style       = d_FONTSTYLE_bold;

            _wordconfig_count++;

            first_word = false;

            continue;

          }

        }

        NUMBER:
        {
          if     ((word[0] == '0') && ((word[1] == 'b')||(word[1] == 'B')))
          {
            //[2진수] 는 지정색으로 칠한다.
            _wordconfig[_wordconfig_count].index       = wordindex;
            _wordconfig[_wordconfig_count].length      = wordlength;
            _wordconfig[_wordconfig_count].front_color = IDE_COLOR_num_bin;
            _wordconfig[_wordconfig_count].back_color  = IDE_COLOR_2;
            _wordconfig[_wordconfig_count].transparent = true;
            _wordconfig[_wordconfig_count].style       = d_FONTSTYLE_default;

            _wordconfig_count++;

            first_word = false;

            continue;

          }
          else if((word[0] == '0') && ((word[1] == 'x')||(word[1] == 'X')))
          {
            //[16진수] 는 지정색으로 칠한다.
            _wordconfig[_wordconfig_count].index       = wordindex;
            _wordconfig[_wordconfig_count].length      = wordlength;
            _wordconfig[_wordconfig_count].front_color = IDE_COLOR_num_hex;
            _wordconfig[_wordconfig_count].back_color  = IDE_COLOR_2;
            _wordconfig[_wordconfig_count].transparent = true;
            _wordconfig[_wordconfig_count].style       = d_FONTSTYLE_default;

            _wordconfig_count++;

            first_word = false;

            continue;

          }

        }

        DEFAULT:
        {
          //[구분처리]
          //{
              if(STR_Match(word, "#"))
              {
                //이 단어가 첫 단어라면 이 단어부터 이 행의 끝까지는 전부 [지시문] 이므로
                //[지시어] 색으로 적용한다.
                if((wkind == 0) && (first_word == true))
                {
                  //현재 단어부터 행 끝가지 [지시어] 가 된다.
                  wkind = '#';
                  goto DIRECTIVE;

                }

              }
              else if(STR_Match(word, "\""))
              {
                goto STRING;

              }
              else if(STR_Match(word, "\'"))
              {
                goto CHAR;

              }
              else if(STR_Match(word, "//"))
              {
                //이 단어부터 행의 끝까지 전부 [주석문] 이 된다.
                //행 끝가지 무조건 적용 되므로 해제할 필요는 없다.
                wkind = 'j';
                goto COMMENT;

              }
          //}

          //여기까지 온것은 [일반문자] 이다.

          //[일반문자] 는 기본설정.
          _wordconfig[_wordconfig_count].index       = wordindex;
          _wordconfig[_wordconfig_count].length      = wordlength;
          _wordconfig[_wordconfig_count].front_color = IDE_COLOR_1;
          _wordconfig[_wordconfig_count].back_color  = IDE_COLOR_2;
          _wordconfig[_wordconfig_count].transparent = true;
          _wordconfig[_wordconfig_count].style       = d_FONTSTYLE_default;

          _wordconfig_count++;

          first_word = false;

          continue;

        }

        DIRECTIVE:
        {
          //[주석문 시작 검사]
          //{
              //만일 [지시문] 안이라도 [주석문] 이 시작되면 이때부터는 [주석문] 이 된다.
              if(STR_Match(word, "//"))
              {
                //이 단어부터 행의 끝까지 전부 [주석문] 이 된다.
                //행 끝가지 무조건 적용 되므로 해제할 필요는 없다.
                wkind = 'j';
                goto COMMENT;

              }
          //}

          _wordconfig[_wordconfig_count].index       = wordindex;
          _wordconfig[_wordconfig_count].length      = wordlength;
          _wordconfig[_wordconfig_count].front_color = IDE_COLOR_directive;
          _wordconfig[_wordconfig_count].back_color  = IDE_COLOR_2;
          _wordconfig[_wordconfig_count].transparent = true;
          _wordconfig[_wordconfig_count].style       = d_FONTSTYLE_default;

          _wordconfig_count++;

          first_word = false;

          continue;

        }

        STRING:
        {
          _wordconfig[_wordconfig_count].index       = wordindex;
          _wordconfig[_wordconfig_count].length      = wordlength;
          _wordconfig[_wordconfig_count].front_color = IDE_COLOR_string;
          _wordconfig[_wordconfig_count].back_color  = IDE_COLOR_2;
          _wordconfig[_wordconfig_count].transparent = true;
          _wordconfig[_wordconfig_count].style       = d_FONTSTYLE_default;

          _wordconfig_count++;

          first_word = false;

          if(STR_Match(word, "\""))
          {
            //이 단어부터 다시 이 단어가 나올때 까지 [문자열] 이 된다.
            if     (wkind == 0)
            {
              //지금 [단어] 를 포함하여 [문자열] 이 시작되었다.
              wkind = 's';

            }
            else if(wkind == 's')
            {
              //현재 단어까지는 [문자열] 이나, 다음 단어를 위하여 해제한다.
              wkind = 0;

            }

          }

          continue;

        }

        CHAR:
        {
          _wordconfig[_wordconfig_count].index       = wordindex;
          _wordconfig[_wordconfig_count].length      = wordlength;
          _wordconfig[_wordconfig_count].front_color = IDE_COLOR_string;
          _wordconfig[_wordconfig_count].back_color  = IDE_COLOR_2;
          _wordconfig[_wordconfig_count].transparent = true;
          _wordconfig[_wordconfig_count].style       = d_FONTSTYLE_default;

          _wordconfig_count++;

          first_word = false;

          if(STR_Match(word, "\'"))
          {
            //이 단어부터 다시 이 단어가 나올때 까지 [문자상수] 가 된다.
            if     (wkind == 0)
            {
              //지금 [단어] 를 포함하여 [문자상수] 가 시작되었다.
              wkind = 'c';

            }
            else if(wkind == 'c')
            {
              //현재 단어까지는 [문자상수] 이나, 다음 단어를 위하여 해제한다.
              wkind = 0;

            }

          }

          continue;

        }

        COMMENT:
        {
          _wordconfig[_wordconfig_count].index       = wordindex;
          _wordconfig[_wordconfig_count].length      = wordlength;
          _wordconfig[_wordconfig_count].front_color = IDE_COLOR_comment;
          _wordconfig[_wordconfig_count].back_color  = IDE_COLOR_2;
          _wordconfig[_wordconfig_count].transparent = true;
          _wordconfig[_wordconfig_count].style       = d_FONTSTYLE_default;

          _wordconfig_count++;

          first_word = false;

          continue;

        }

      }

  //}

*/

  //여기까지 모든 정보가 처리되어 _wordconfig[] 안에 저장되었다.

  return;

}

bool __fastcall TGEdit_Form::_EDITBUF_LINE_Out(int   pX,
                                                     int   pY,
                                                     int   pLINEstartindex,
                                                     int  *rLINEendindex)
{
  //[2007.01.22.월][생성!!!]{-----------------------------------------------------------------------------------------
  //FONT_Out_LineStr_withSTARTENDindex() 함수를 대체한다.
  //한행에서만 유효하고 _editbuf[] 에서 지정된 행의 시작 위치부터 이 행의 끝까지(CRLF 또는 '\0' 까지)
  //문자열을 여러 설정을 적용하여 출력한다.(선택, 문법강조 등)
  //한 행만을 대상으로 하므로 CRLF 는 처리하지 않고 단지 '\t' 만 구분하여 처리한다.
  //이 함수는 한개의 행을 출력하는 기본함수이고 반드시 행을 출력하는 경우는 이 함수를 사용해야 한다.

  //_block_startindex, _block_endindex 사이에 처리 문자가 위치한 경우는 [반전출력 영역] 에 있는 것이므로
  //지정 색을 반전 시켜준다.

  //[2007.02.01.목][수정!!!]
  //_EDITBUF_STRING_Out() 함수에서 이 함수를 연속으로 사용하여 여러줄을 출력할때 다음행의 시작위치를
  //다시 구하지 않고 바로 알수 있도록, 이 함수가 처리한 행의 끝 위치를 반환한다.
  //이는 일반적으로 _lineednindex 와 같은 의미이다. 즉, 마지막 문자 + 1 의 위치가 된다.

  //[2007.02.05.월][수정!!!]
  //[굵은글꼴] 을 사용할수 있게 수정되었다.
  //이에 따라 FONT_Out_Moonja2() => FONT_Out_StyleMoonja() 함수로 교체 되었다.

  //[2007.02.06.화][수정!!!]
  //(_select_start.caret_index < _select_end.caret_index) 의 관계가 꼭 성립되는 것은 아니므로 서로의 크기를 비교하여
  //처리하는 것으로 다시 수정한다.
  //이는 단지 시작과 끝의 위치를 가지고 있는 것일 뿐이며, [선택영역] 은 아래에서 위로 형성되 갈수 있기
  //때문이다.

  //[2007.02.06.화][수정!!!]
  //_select_start.caret_index, _select_end.caret_index 둘 중 하나라도 -1 인 경우는 [선택영역] 이 없는 것이므로
  //아예 따질 필요는 없다.

  //[2007.02.08.목][수정!!!]
  //j  를 직접 증가 하는 경우 [선택영역처리] 부분에서 이미 증가되어 변경된 j 값을 사용하게 되므로
  //정확한 단어의 위치를 알수 없게 된다. 따라서, j 는 j_add 에 증가할 값을 기록해 놓은 다음에
  //이 값으로 [선택영역처리] 후에 증가시킨다.

  //[2007.04.15.일][추가!!!]
  //현재 출력 행이 [정지점] 이 지정된 행이라면 출력 문자열 속정이 전부 적용된 상태에서
  //다시 [정지점] 지정색 을 사용하여 처리해야 한다.
  //C++Builder 와 비슷한 모양으로 처리한다.
  //}-----------------------------------------------------------------------------------------------------------------

  //[VAR]
  //{
      //출력한 [문자] 의 영역 크기.
      int               moonja_width;
      int               moonja_height;

      //2byte-code 문자의 code.
      unsigned short    code;

      bool              select_enable = false;
      struct GEdit_CARETINFO  select_start,
                        select_end;
      int               i, j, j_add;

      int               front_color;
      int               back_color;
      bool              transparent;
      int               style = d_FONTSTYLE_default;

  //}

  //[개체존재:확인{****************************
  //[2012.05.01.화][추가!!!]
  //개체가 이미 해제된 뒤라면 처리하지 않는다.
  if(_this_ != true) return(false);
  //}******************************************

  //[초기화]
  //{
      //일단 시작 위치값으로 초기화 한다.
      (*rLINEendindex) = pLINEstartindex;

  //}

  //[선택영역 정보 정리][2007.02.06.화][추가!!!]
  //{
      //전용 함수로 크기순으로 정리된 start/index index 값을 얻는다.
      select_enable = _SELECT_Get_StartEndIndex(&select_start, &select_end);

  //}

  //[현재행 문자열 정보얻기]
  //{
      //행 문자열의 문법 강조등의 정보를 얻는다. 결과는 해당 전역변수에 저장된다.
      _MOONJACONFIG_LineGet(pLINEstartindex);

  //}

  //[문자열 출력]
  //{
      //[설명]
      //{
          //[반전출력 영역] 인지 구분하고, 그외 [문법강조] 등의 처리도 이곳에서 처리한다.
          //이미 문자 정보가 정리된 상태이므로 이 정보를 사용한다.

          //CRLF : 발견 즉시 종료한다.(이 함수는 한개의 행만 처리하므로...)
          //       또는 '\0' 즉, 문서의 끝에 도달한 경우도 종료해야 한다.
          //'\t' : [공백] 으로 변환 처리한다.

      //}

      //_wordconfig[] 를 하나씩 읽어서 처리한다.
      for(i = 0, j = pLINEstartindex; i < _wordconfig_count; i++)
      {
        //해당 단어의 문자를 하나씩 읽어서 처리한다.
        for(j = _wordconfig[i].index; j < (_wordconfig[i].index + _wordconfig[i].length) ; j += j_add)
        {
          if     (_editbuf[j] == '\0')
          {
            //[문서끝] 이라면 더이상 처리하지 않는다.
            goto END;

          }
          else if((_editbuf[j    ] == 0x0d) &&
                  (_editbuf[j + 1] == 0x0a))
          {
            //CRLF 인 경우는 행 끝이므로 더이상 처리하지 않는다.
            goto END;

          }
          else if(_editbuf[j] < 0)
          {
            //첫 byte 가 [음수] 라면 [한글/기호/한자] 이다.

            //2byte-code 합성.
            code = ((((unsigned short)_editbuf[j    ]) << 8) & 0xff00) |
                   ((((unsigned short)_editbuf[j + 1]) << 0) & 0x00ff);

            j_add = 2;

          }
          else if(_editbuf[j] == '\t')
          {
            //<tab> 의 경우는 [공백] 으로 대체한다.
            code = ' ';

            j_add = 1;

          }
          else
          {
            //[영문] 이 해당한다.
            code = _editbuf[j];

            j_add = 1;

          }

          //[설정적용]
          //{
              front_color = _wordconfig[i].front_color;
              back_color  = _wordconfig[i].back_color;
              transparent = _wordconfig[i].transparent;
              style       = _wordconfig[i].style;

          //}

          //[선택영역적용]
          //{
              //[선택영역] 이 유효한 경우만 처리한다.
              if(select_enable == true)
              {
                if((select_start.caret_index <= j) && (j < select_end.caret_index))
                {
                  //현재 문자가 [선택영역] 에 있으므로 현재 이 문자의 속성에 지정된 모든 색을 반전시키고,
                  //[배경투명속성] 을 해제한다.

                  //24bit 색을 사용하므로 하위3 byte 만 반전시켜야 한다.(결과는 0x00xxxxxx 이 되야 한다.)
                  front_color = (~front_color) & 0x00ffffff;
                  back_color  = (~back_color)  & 0x00ffffff;
                  transparent = false;

                }

              }

          //}

          //[문자출력]
          //{
              if(FONT_Out_StyleMoonja(_bmp_, &_font, pX, pY, front_color, back_color, transparent, style, code,
                                      d_FONT_underline_no, true, 0, front_color,
                                      &moonja_width, &moonja_height) != true)
              {
                return(false);

              }

              //[문자] 출력 좌표 수정.
              pX += moonja_width;

          //}

        }

      }

  //}

  END:

  //[rLINEendindex 반환]
  //{
      //j 는 가장 마지막 문자위치 + 1 의 위치를 가지고 있으므로 이 값을 반환한다.
      (*rLINEendindex) = j;

  //}

  //[테두리 만들기]
  //{
      //[2009.09.28.금][추가!!!]
      //GEdit 는 한줄만 편집하므로 한줄을 출력시 테두리가 지워지게 된다.
      //따라서 테두리도 항상 마지막에 같이 그려주어야 한다.
      _bmp_->Canvas->Pen->Color   = _borderColor;
      _bmp_->Canvas->Pen->Width   = 1;
      _bmp_->Canvas->Brush->Style = bsClear;
      _bmp_->Canvas->Rectangle(_bmp_->Canvas->ClipRect);

  //}

  return(true);

}

bool __fastcall TGEdit_Form::_EDITBUF_STRING_Out(int  pX,
                                                 int  pY,
                                                 int  pLINEstartindex)
{
  //[2007.02.01.목][생성!!!]{------------------------------------------
  //FONT_Out_String2_withLINEstartINDEX() 함수를 대체하는 함수.
  //이는 [문법강조] 를 하기 위하여 _EDITBUF_LINE_Out() 함수를 사용하여
  //여러 행을 출력한다.
  //}------------------------------------------------------------------

  //[VAR]
  //{
      int             i;
      int             r_lineendindex;

      //[출력행계산관련]
      //{
          //현재 출력하는 문서의 행번호.
          int         line_number = 0;
          //출력 시작 행 번호(BASE:0)
          int         doc_line_start;
          //출력 끝 행 번호(BASE:0)
          int         doc_line_end;

      //}

  //}

  //[개체존재:확인{****************************
  //[2012.05.01.화][추가!!!]
  //개체가 이미 해제된 뒤라면 처리하지 않는다.
  if(_this_ != true) return(false);
  //}******************************************

  //[출력행정보계산]
  //{
      //[설명!!!]{------------------------------------------------------
      //출력 행의 범위를 계산한다.
      //여기서 "행" 은 "문서" 의 행을 말한다.(출력 영역의 행이 아니다.)
      //여기서 구한 두행 사이의 문서행만 실제로 출력하게 된다.
      //}---------------------------------------------------------------

      //[보정]
      //{
          //[문서원점] 은 +2 의 offset 이 존재하므로 -2 해서 원래 값으로 돌려놓고
          //행을 계산해야 바른 값을 얻을수 있다.
      //}

      if(pY < 0)
      {
        //출력 시작 위치가 [음수] 인 경우, [음수 영역] 은 출력대상이 되지 않는다.

        //[출력 시작 행]
        doc_line_start = ((-1) * (pY - 2)) / _line_height;

        //[출력 끝 행]
        doc_line_end   = (_bmp_->Height - (pY - 2)) / _line_height;

      }
      else
      {
        //표시영역안은 항상 문서의 0 행부터 출력하면 된다.

        //[출력 시작 행]
        doc_line_start = 0;

        //[출력 끝 행]
        doc_line_end   = (_bmp_->Height - (pY - 2)) / _line_height;

      }

  //}

  //[문자 출력]
  //{
      //[설명]{----------------------
      //전달받은 문자열을 출력한다.
      //CRLF : 행을 바꾸어 준다.
      //'\t' : [공백] 으로 처리한다.
      //}----------------------------

      //[출력위치계산]
      //{
          //------------------------------------
          //pX = 0 이되므로 pY 만 계산한다.
          pY += (doc_line_start * _line_height);
          line_number = doc_line_start;
          //------------------------------------
      //}

      for(i = pLINEstartindex; ; )
      {
        //[행 출력]
        //{
            //현재 행을 출력한다.
            _EDITBUF_LINE_Out(pX, pY, i, &r_lineendindex);

        //}

        //[행 바꾸기]
        //{
            if((_editbuf[r_lineendindex] == 0x0d) && (_editbuf[r_lineendindex+1] == 0x0a))
            {
              //CRLF 의 경우 문자는 출력하지 않고 CRLF 도 2byte-code 로 전달하여 현재 글꼴의
              //출력 행의 높이 정보를 얻어 pY 위치만 수정한다.

              //다음 행의 좌표로 수정한다.
              pX  = 0;
              pY += _line_height;

              i = (r_lineendindex + 2);

              //다음 행번호를 지정한다.
              line_number++;

              if(!(line_number <= doc_line_end))
              {
                //처리행을 넘었으므로 더이상 처리하지 않는다.
                return(true);

              }

            }
            else if(_editbuf[r_lineendindex] == '\0')
            {
              //더이상 처리하지 않는다.
              return(true);

            }

        //}

      }

  //}

  return(true);

}

void __fastcall TGEdit_Form::_SELECTSCROLL_Left(int  pMOUSEx)
{
  //[2007.02.08.목][생성!!!]{----------------------------------------------------------------
  //[선택영역] 을 형성할때 [편집기] 왼쪽을 넘는 경우는 자동으로 [줄이동] 을 해주어야 한다.

  //[2007.03.19.월][수정!!!]
  //[마우스] 로 [선택영역] 을 만드는 과정에서 [줄이동] 을 하는 경우를 위하여 직접 pMOUSEx 로
  //위치를 입력받아 처리하는 것으로 수정.
  //}----------------------------------------------------------------------------------------

  //[VAR]
  //{
      //현재 position 값.
      int  position      = _scroll_h_position;

  //}

  //[개체존재:확인{****************************
  //[2012.05.01.화][추가!!!]
  //개체가 이미 해제된 뒤라면 처리하지 않는다.
  if(_this_ != true) return;
  //}******************************************

  //[스크롤해야 하나?]
  //{
      //[입력지정자] 가 보이는 영역을 벗어 난 경우라면 왼쪽으로 스크롤한다.

      //[2007.04.10.화][수정!!!]
      //단, 이미 왼쪽끝에 와 있는 상태라면 처리하지 않아야 한다.
      //이는 아래 _Position() 함수로 줄이동 하는 경우 해당 처리부에서
      //_EditBuf_Text() 함수가 중복 처리 되어 깜박임이 발생하기 때문이다.

      if((pMOUSEx < d_X_width) && (position > 0))
      {
        //[한글/영문?]
        //{
            //현재 _caretindex 위치의 문자가 [한글/영문] 에 따라 스크롤 값이 달라야 한다.
            //위에서 이미 _caretindex 에 해당하는 문자를 건너서 이동한 것이므로 이 폭을가지고 따져야 한다.

            if     (_editbuf[_caretindex] > 0)
            {
              //[영문] 인 경우는 [영문폭] 로 이동해야 하므로 -1 만큼 스크롤 한다.
              position -= 1;

            }
            else if(_editbuf[_caretindex] < 0)
            {
              //[한글] 인 경우는 [영문폭*2] 로 이동해야 하므로 -2 만큼 스크롤 한다.
              position -= 2;

            }

        //}

        //지정 양만큼 수평으로 스크롤 한다.
        _scroll_h_position = position;
        if(_scroll_h_position < 0) { _scroll_h_position = 0; }
        _EDIT_H_Scroll();

      }

  //}

  return;

}

void __fastcall TGEdit_Form::_SELECTSCROLL_Right(int  pMOUSEx)
{
  //[2007.02.08.목][생성!!!]{----------------------------------------------------------------
  //[선택영역] 을 형성할때 [편집기] 오른쪽을 넘는 경우는 자동으로 [줄이동] 을 해주어야 한다.

  //[2007.03.19.월][수정!!!]
  //[마우스] 로 [선택영역] 을 만드는 과정에서 [줄이동] 을 하는 경우를 위하여 직접 pMOUSEx 로
  //위치를 입력받아 처리하는 것으로 수정.
  //}----------------------------------------------------------------------------------------

  //[VAR]
  //{
      //현재 position 값.
      int  position        = _scroll_h_position;

      int  caretindex_save = _caretindex;

  //}

  //[개체존재:확인{****************************
  //[2012.05.01.화][추가!!!]
  //개체가 이미 해제된 뒤라면 처리하지 않는다.
  if(_this_ != true) return;
  //}******************************************

  //[스크롤해야 하나?]
  //{
      //[입력지정자] 가 보이는 영역을 벗어 난 경우라면 왼쪽으로 스크롤한다.
      if(pMOUSEx > this->Width)
      {
        //[한글/영문?]
        //{
            //현재 _caretindex 위치의 문자가 [한글/영문] 에 따라 스크롤 값이 달라야 한다.
            //위에서 이미 _caretindex 에 해당하는 문자를 건너서 이동한 것이므로 이동하기 전의  위치로 검사한다.
            if     (_editbuf[caretindex_save] > 0)
            {
              //[영문] 인 경우는 [영문폭] 로 이동해야 하므로 +1 만큼 스크롤 한다.
              position += 1;

            }
            else if(_editbuf[caretindex_save] < 0)
            {
              //[한글] 인 경우는 [영문폭*2] 로 이동해야 하므로 +2 만큼 스크롤 한다.
              position += 2;

            }

        //}

        //지정 양만큼 수평으로 스크롤 한다.
        _scroll_h_position = position;
        if(_scroll_h_position < 0) { _scroll_h_position = 0; }
        _EDIT_H_Scroll();

      }

  //}

  return;

}

bool __fastcall TGEdit_Form::_CARET_ViewAlways(void)
{
  //[2007.02.24.토][생성!!!]{-----------------------------------------------------------------
  //어떤 상황에서도 [입력지정자] 가 이동하는 경우는 항상 [편집기] 상에서 눈에
  //보이는위치에 있도록 자동으로 [수평/수직 줄이동] 을 해준다.
  //현재 [입력지정자] 의 위치가 눈에 보이지 않는 위치에 있다면 [줄이동] 만으로
  //간단하게 눈에 보이는 위치에 있도록 만들수 있다.

  //[2007.02.28.수][수정!!!]
  //두개의 [줄이동] 이 동시에 처리되는 경우는 _EditBuf_Text() 함수가 한번만
  //실행될수 있도록 _editbuf_text_on 상태를 조정하여 준다.
  //이를 위하여 _Position() 함수를 바로 사용하지 않고 먼저 값을 구한뒤 일괄 처리한다.

  //[2007.03.10.토][수정!!!]
  //경우에 해당되어 이 함수를 처리한 경우는 true 를 반환하여 처리 했다는 것을 알린다.
  //이는 중복으로 새로 그려지는 것을 막아서 깜박거림을 방지하기 위한 것이다.

  //[2007.03.11.일][수정!!!]
  //_KEY_Left() 등 함수를 수행할때 이 함수가 먼저 호출되는데, 이때는 정확하게 [입력지정자]
  //가 화면의 가장 왼쪽에 있게 된다. 이 후 _KEY_Left() 가 호출되면 다시 한번 한칸 왼쪽으로
  //이동하게 되므로 결과적으로 두번 왼쪽으로 [줄이동] 하는 셈이 되어 동작이 구분되어 보이므로
  //보기 좋지 않다.
  //이것을 막기 위하여 아예 _CARET_ViewAlways() 함수에서 1줄, 1칸씩 여유를 더 준다.
  //이 경우, 이미 1칸 여유가 있으므로 _KEY_Left() 에서는 이미 이동을 해도 [입력지정자] 가
  //보이는 위치에 있으므로 [줄이동] 을 하지 않아서 문제가 말끔하게 해결된다.
  //}-----------------------------------------------------------------------------------------

  //[방법]
  //{
      //[줄이동자] 의 _position 값이 [수직] 인 경우는] 가장 윗줄의 행번호가 되고,
      //[수평] 인 경우는 [영문자 폭] 을 기준으로 한 열번호가 된다.
      //따라서, _position 값을 구해진 값으로 직접 수정하여 [입력지정자] 가 보이도록
      //[줄이동] 한다.

      //수직/수평을 별도로 분리 처리한다.(순서대로 처리)
  //}

  //[주의!!!]
  //{
      //수평의 경우는 정확하게 [줄이동] 을 할수 없으므로 [영문자폭] 1 개 단위정도
      //여유를 두어서 처리하는 것이 좋다.
  //}

  //[VAR]
  //{
      //현재 [줄이동자] 의 _position 값을 기록한다.
      int   scroll_H_position;

      //해당 [줄이동] 을 해야 하는 경우는 true 로 설정.
      bool  scroll_h_use = false;

      //[수평 줄이동] 관련 처리정보.
      int   gap_x     = 0,
            gap_x_col = 0;

  //}

  //[개체존재:확인{****************************
  //[2012.05.01.화][추가!!!]
  //개체가 이미 해제된 뒤라면 처리하지 않는다.
  if(_this_ != true) return(false);
  //}******************************************

  //[스크롤정보얻기]
  //{
      scroll_H_position = _scroll_h_position;

  //}

  //[수평검사]
  //{
      //[2022.01.06.목][수정]
      //문자가 너무 왼쪽 테두리에 붙이 않도록 <+2> 정도 간격을 추가한다.
      //-----------------------------------------v
      if     ((_caret_x + (_text_org_x + 2 - 1 + 2)) < d_X_width)
      {
        //[입력지정자] 가 [편집기] 왼쪽으로 사라져 있는 상태.
        //이 경우는 [입력지정자] 가 [편집기] 가장 왼쪽에 위치하도록 [수평줄이동] 해주어야 한다.

        //[행수 구하기]
        //{
            //벗어난 길이를 구한다.
            //[2022.01.06.목][수정]
            //문자가 너무 왼쪽 테두리에 붙이 않도록 <+2> 정도 간격을 추가한다.
            //---------------------------------------------------------v
            gap_x     = d_X_width - (_caret_x + (_text_org_x + 2 - 1 + 2));

            //기본적으로 열 1 개는 추가한다.(이미 보이지 않는 영역에 있으므로)
            //1 개더 여유를 준다.---------------------v
            gap_x_col = gap_x / _font_eng_width + 1 + 1;

        //}

        //[줄이동]
        //{
            //[입력지정자] 가 가장 왼쪽열에 위치하도록 [수평줄이동] 한다.
            scroll_h_use       = true;
            //_position 값을 구한다.
            scroll_H_position -= gap_x_col;

        //}

      }
      //[2022.01.06.목][수정]
      //문자가 너무 왼쪽 테두리에 붙이 않도록 <+2> 정도 간격을 추가한다.
      //-----------------------------------------v
      else if((_caret_x + (_text_org_x + 2 - 1/* + 2*/)) > this->Width)
      {
        //[입력지정자] 가 [편집기] 오른쪽으로 사라져 있는 상태.
        //이 경우는 [입력지정자] 가 [편집기] 가장 오른쪽에 위치하도록 [수평줄이동] 해주어야 한다.

        //[행수 구구하기]
        //{
            //벗어난 길이를 구한다.
            //[2022.01.06.목][수정]
            //문자가 너무 왼쪽 테두리에 붙이 않도록 <+2> 정도 간격을 추가한다.
            //----------------------------------------------v
            gap_x     = ((_caret_x + (_text_org_x + 2 - 1/* + 2*/)) - this->Width);

            //기본적으로 열 1 개는 추가한다.(이미 보이지 않는 영역에 있으므로)
            //1 개더 여유를 준다.---------------------v
            gap_x_col = gap_x / _font_eng_width + 1 + 1;

        //}

        //[줄이동]
        //{
            //[입력지정자] 가 가장 아랫줄에 위치하도록 [수직줄이동] 한다.
            scroll_h_use       = true;
            //_position 값을 구한다.
            scroll_H_position += gap_x_col;

        //}

      }

  //}

  //[실제 줄이동]
  //{
      //[2007.02.28.수][수정!!!]
      //구해 놓은 값으로 실제 [줄이동] 한다.
      //단, [수평/수직] 둘다 [줄이동] 하는 경우는 _EditBuf_Text() 함수가 한번만 실행하게 만든다.

      if(scroll_h_use == true)
      {
        //[수평줄이동] 만 하는 경우.
        _scroll_h_position = scroll_H_position;
        if(_scroll_h_position < 0) { _scroll_h_position = 0; }
        _EDIT_H_Scroll();

        return(true);

      }

  //}

  //현재 [입력지정자] 가 눈에 보이는 위치에 있으므로 이 함수를 처리하지 않은 것이다.
  return(false);

}

void __fastcall TGEdit_Form::_EDIT_SelectDelete(void)
{
  //[2007.03.07.수][생성!!!]{----------------------------------------
  //[삭제] 는 <Del-Key> 로 처리한다.
  //단, [선택영역] 이 없는 경우는 처리하지 않아야 한다.
  //이 함수는 [간편기능목록] 에서 [삭제] 항목을 처리하는데 사용된다.
  //}----------------------------------------------------------------

  //[VAR]
  //{
      struct GEdit_CARETINFO   select_start;
      struct GEdit_CARETINFO   select_end;

  //}

  //[개체존재:확인{****************************
  //[2012.05.01.화][추가!!!]
  //개체가 이미 해제된 뒤라면 처리하지 않는다.
  if(_this_ != true) return;
  //}******************************************

  //[선택영역 존재 확인]
  //{
      //[선택영역] 이 없는 경우는 바로 종료한다.
      if(_SELECT_Get_StartEndIndex(&select_start, &select_end) != true) return;

  //}

  //[선택영역 삭제]
  //{
      _KEY_Delete();

  //}

  return;

}

void __fastcall TGEdit_Form::_EDIT_Paste(char *pPASTEstr)
{
  //[2007.03.07.수][생성!!!]{-------------------------------------------------------------
  //전달받은 문자열을 _editbuf[] 의 현재 [입력지정자] 위치에 붙여넣고,
  //수정된 내용의 반영을 위하여 _EditBuf_Text() 함수로 다시 한번 출력한다.
  //
  //[붙여넣기] 는 [문서] 를 관리하는 정보의 전체적인 수정이 가해진다.
  //이 함수는 [간편기능목록] 에서 [붙이기] 항목을 처리하는데 사용된다.
  //
  //CRLF 를 얻어서 행수를 STR_Get_CRLF_Count_withStartEndIndex() 함수를 사용하여
  //구한다음 해당 관리변수(_line_count)도 수정해 주어야 한다.

  //[2007.03.20.화][수정!!!]
  //이미 [선택영역] 이 있는 경우는 이 [선택영역] 을 삭제하고 내용을 [붙여넣기] 해야 한다.
  //1) [선택영역] 을 삭제한다.(이때 [입력지정자] 는 [선택영역] 시작위치에 있는 상태)
  //2) 현 [입력지정자] 위치에 지정 문자열을 끼워넣는다.
  //
  //[2010.06.28.월][중요수정!!!]----------------------------------------------------------
  //아래 두개 사항을 수정/보완한다.
  //
  //1) [붙여넣기] 하는 문자열은 첫 행에 한정한다.
  //
  //2) [문자열] 추가시 [저장소] 를 넘지 않아야 한다.
  //
  //[2018.02.24.토][추가!!!]--------------------------------------------------------------
  //[입력지정자] 위치를 붙여넣기한 문자열 끝에 위치 하도록 수정.
  //}-------------------------------------------------------------------------------------

  //[VAR]
  //{
      //전달받은 문자열의 길이.('\0' 은 제외한 길이)
      int  size;

      int  crlf_count = 0;

      //[2007.03.20.화][추가!!!]
      struct GEdit_CARETINFO  select_start;
      struct GEdit_CARETINFO  select_end;

      //[2010.06.28.월][추가!!!]
      char   str[d_BUF_size];

      //[입력지정자 위치 지정 관련]
      //{
          //[2018.02.24.토][추가!!!]
          int   caretIndexWidth;
          int   caretX;

      //}

  //}

  //[개체존재:확인{****************************
  //[2012.05.01.화][추가!!!]
  //개체가 이미 해제된 뒤라면 처리하지 않는다.
  if(_this_ != true) return;
  //}******************************************

  //[<Shift> 해제]
  //{
      //[2007.02.27.화][추가!!!]
      //<Shift> 는 문자 입력시 무효하므로 [선택영역] 의 해제를 위하여 기록을 지운다.
      //이렇게 하는 이유는 _EDIT_CaretPos() 함수안의 [선택영역] 처리부분을 참고한다.
      _shift_down = false;

  //}

  //[선택영역 처리]
  //{
      //[설명]{-----------------------------------------------
      //[2007.02.13.화]
      //1. 일단 [선택영역] 구간을 _editbuf[] 상에서 삭제한다.
      //2. [붙이기] 동작을 처리한다.
      //}-----------------------------------------------------

      //먼저 [입력지정자] 의 위치를 [선택영역] 시작 지점으로 이동시킨다.
      if(_SELECT_Get_StartEndIndex(&select_start, &select_end) != true) goto PASTE;

      //[입력지정자 위치 수정]
      //{
          //[선택영역] 이 있는 경우만 처리한다.
          //[입력지정자] 위치는 select_startindex 위치가 된다.(작은 index)
          _caretindex = select_start.caret_index;
          _caret_x    = select_start.caret_x;
          _caret_y    = select_start.caret_y;

          //[입력지정자 위치 조정]
          _EDIT_CaretPos(_caret_x, _caret_y);

      //}

      //[선택영역 정보 복구]
      //{
          //위에서 처리한 _EDIT_CaretPos() 에서 해제된 [선택영역] 의 정보를 다음 처리부에서
          //삭제할수 있도록 이미 얻어 놓은 정보로 복구한다.
          _select_start = select_start;
          _select_end   = select_end;

      //}

      //[선택영역] 삭제.
      if(_SELECT_Delete(&select_start, &crlf_count) == true)
      {
        //[행수 수정]
        //{
            //CRLF 가 삭제된 것이 있는 경우만 수정한다.
            if(crlf_count > 0) { _linecount -= crlf_count; }

        //}

        //[삭제 반영]
        //{
            //이 문자키의 경우는 한줄만 다시 그려지므로 여기서 처리하지 않으면
            //삭제된 내용이 반영되지 않는다.(반드시 여기서 처리한다)
            //_EditBuf_Text();
            //[2007.03.10.토][수정!!!]
            //여기서 처리 하지 않고 중복 처리를 방지하기 위하여 이 함수 끝에서 한번만 처리한다.

        //}
        
      }

      goto CONTINUE;

  //}

  //[순수한 "붙이기" 처리]------------------------------------------------------

  PASTE:

  //[입력지정자 항상 보이기]
  //{
      //_EditBuf_Text() 함수가 중복 처리 되는 것을 막기 위한것이다.
      //이 함수 끝에서 한번만 직접 처리하게 만든다.
      _edit_paste_ing = true;
      //{
          //키는 항상 눈에 보이는 위치에 있어야 한다.
          //[주의!!!]
          //문서가 수정되기 전에 미리 해 주어야 정확하게 동작한다.
          _CARET_ViewAlways();

      //}
      _edit_paste_ing = false;

  //}

  CONTINUE:

  //[저장소를 넘나?]
  //{
      //[!]{---------------------------------------------------------------------
      //문자열을 추가하는 경우 [저장소] 를 넘는 경우는 [저장소] 안에 넣을수 있는
      //크기로 다시 길이를 조정한다.
      //}------------------------------------------------------------------------

      if(STR_Length(pPASTEstr) >= (d_BUF_size - _filesize - 1))
      {
        //'\0' 공간은 제외한다-----------------------------------v
        size = STR_SetLength(pPASTEstr, d_BUF_size - _filesize - 1, ' ', str);

      }
      else
      {
        STR_Assign(str, pPASTEstr);

      }

  //}

  //[붙일 문자열 정리/저장]
  //{
      //[2010.06.28.월][추가!!!]{----
      //첫번째 행의 문자열만 취한다.
      //}----------------------------

      //직접 원본을 수정한다-----------v
      size = STR_Cut_CRLF(str, str, 0, true);

  //}

  //[포함된 행수 구하기]
  //{
      crlf_count = STR_Get_CRLF_Count_withStartEndIndex(str, 0, size - 1);

  //}

  //[공간 만들기]
  //{
      //[2007.04.09.월][수정!!!]
      //for() 문을 사용하여 공간을 만드는 것은 문서량이 많은 경우 시간이 많이 걸리므로
      //_tempbuf[] 와 memmove() 함수를 사용하여 복사 방식으로 처리한다.

      //끼워 넣을 시작위치부터 문서끝까지의 문자열을 _tempbuf[] 에 대피시킨다.
      memmove(_tempbuf, &(_editbuf[_caretindex]), _filesize - _caretindex);

      //끼워넣을 공간 다음에 다시 복사해서 붙여준다.
      memmove(&(_editbuf[_caretindex + size]), _tempbuf, _filesize - _caretindex);

  //}

  //[끼워넣기]
  //{
      //비워진 공간에 전달받은 문자열을 끼워 넣는다.
      //memmove() 를 사용하여 고속으로 [복사] 한다.
      memmove(&(_editbuf[_caretindex]), str, size);

  //}

  //[후처리]
  //{
      //[문서량수정]
      //{
          //추가한 문자의 길이만큼 [문서양] 이 는다.
          _filesize += size;

      //}

      //[행수 수정]
      //{
          //CRLF 가 삭제된 것이 있는 경우만 수정한다.
          if(crlf_count > 0) { _linecount += crlf_count; }

      //}

      //[수정 반영]
      //{
          //[선택영역] 이 삭제된 경우는 <키> 를 처리하지 않고 여기서 종료하게
          //되므로 이곳에서 직접 수정된 내용을 반영해 주어야 한다.
          _EditBuf_Text();

      //[입력 지정자 위치 이동]
      //{
          //[2018.02.24.토][추가!!!]{------------------------
          //[입력지정자] 를 붙여넣은 문자열 끝에 위치시킨다.
          //}------------------------------------------------

          //[입력지정자:위치 수정]
          //{
              //붙여넣기한 문자열 끝으로 이동.
              _caretindex += size;

          //}    

          //[<caretindexwidth> 얻기]
          //{
              //[!]{-----------------------------------------------------------
              //<caretLineStartIndex> 를 사용하지 않고 <caretIndexWidth> 사용.
              //큰 상관은 없으나 정확하게 구별 하기 위한것.
              //}--------------------------------------------------------------
              
              //[한줄편집기] 이므로 항상 행 시작 위치는 0.
              //--------------------------v
              _CARETINDEX_CaretIndexWidth(0, _caretindex, &caretIndexWidth, NULL);

          //}

          //[입력지정자 위치 얻기]
          //{
              //[!]{-----------------------------------------------------------
              //[문서] 상의 위치 계산.
              //<caretLineStartIndex> 를 사용하지 않고 <caretIndexWidth> 사용.
              //큰 상관은 없으나 정확하게 구별 하기 위한것.
              //}--------------------------------------------------------------

              //[한줄편집기] 이므로 X 위치만 계산.
              caretX = caretIndexWidth;

          //}

          //[입력지정자 위치 설정]
          //{
              //[!]{--------------------------------------------------------------
              //[입력지정자] 는 지정 위치에 있어야 한다(실제 선택영역 시작 위치).
              //}-----------------------------------------------------------------

              //[한줄편집기] 이므로 X 위치만 수정.
              _caret_x = caretX;

              _EDIT_CaretPos(_caret_x, _caret_y);

          //}

      //}

  //}

  //[입력지정자 항상 보이기]
  //{
      //[2018.02.24.토][추가!!!]{-------------------------------------------
      //_EditBuf_Text() 함수가 중복 처리 되는 것을 막기 위한것.
      //이 함수 끝에서 한번만 직접 처리하게 만든다.
      //
      //[주의!!!]
      //[반드시] 처리해야 하므로 <_edit_paste_ing> 관련 처리는 하지 않는다.
      //}-------------------------------------------------------------------

      _CARET_ViewAlways();

  //}

  return;

}

void __fastcall TGEdit_Form::_EDIT_SelectAll(void)
{
  //[2007.03.20.화][생성!!!]{-----------------------------------------------
  //[전체선택] 을 처리한다.
  //현재 보이는 영역이 그대로 보이도록 해야 하고 [줄이동] 은 하지 않는다.
  //단, [선택영역] 을 관리 하는 변수는 모두 정확한 값을 가지고 있어야 하고,
  //[입력지정자] 는 문서의 가장 끝에 위치해야 한다.
  //단, 이때 [입력지정자] 가 보이도록 다시 [줄이동] 하지 않아야 한다.
  //}-----------------------------------------------------------------------

  //[VAR]
  //{
      int  start_linestartindex;

      int  end_linestartindex,
           end_lineendindex;

      int  start_caretindexwidth,
           end_caretindexwidth;

      int  start_caret_x, start_caret_y;
      int  end_caret_x, end_caret_y;

      int  moonjacount, col;

  //}

  //[개체존재:확인{****************************
  //[2012.05.01.화][추가!!!]
  //개체가 이미 해제된 뒤라면 처리하지 않는다.
  if(_this_ != true) return;
  //}******************************************

  //[시작/끝 위치 얻기]
  //{
      //[시작위치]
      //{
          //가장 처음행의 시작위치를 구한다.
          _MOUSEDOWN_Get_LineStartIndex(0, &start_linestartindex);

      //}

      //[끝위치]
      //{
          //가장 마지막 행의 시작 위치를 구한다.
          //_linecount 값이 최소 1 을 갖고 행수(index 개념)과는 달리 1 부터 시작하므로
          // -1 을 해서 행번호로 수정하여 사용해야 한다.
          _MOUSEDOWN_Get_LineStartIndex(_linecount - 1, &end_linestartindex);

          //이 행의 끝 index 값 얻기.([선택영역] 끝 위치를 얻기 위한 것)
          STR_Get_ThisLineEndIndex(_editbuf, end_linestartindex, &end_lineendindex);

      //}

  //}

  //[입력지정자 위치 얻기]
  //{
      //[설명]{--------------------------------------------------------------
      //[선택영역] 을 [문서] 의 [처음 ~ 끝] 까지 가상적으로 만드는 것이므로,
      //실제로 [입력지정자] 의 위치를 정확하게 계산하여 해당 정보 관리변수에
      //할당해 주어야 한다.
      //}--------------------------------------------------------------------

      //[caretindexwidth 구하기]
      //{
          //[시작위치]
          //{
              _CARETINDEX_CaretIndexWidth(start_linestartindex, start_linestartindex,
                                          &start_caretindexwidth, &moonjacount);

          //}

          //[끝위치]
          //{
              _CARETINDEX_CaretIndexWidth(end_linestartindex, end_lineendindex,
                                          &end_caretindexwidth, &col);

          //}

      //}

      //[입력지정자 위치 구하기]
      //{
          //[시작위치]
          //{
              //[문서] 상의 위치를 구한다.
              start_caret_x = start_caretindexwidth;
              start_caret_y = 0 * _line_height;

          //}

          //[끝위치]
          //{
              //[문서] 상의 위치를 구한다.
              end_caret_x = end_caretindexwidth;
              end_caret_y = (_linecount - 1) * _line_height;

          //}

      //}

  //}

  //[선택영역 정보 설정]
  //{
      //[시작위치]
      //{
          _select_start.caret_index = start_linestartindex;
          _select_start.caret_x     = start_caret_x;
          _select_start.caret_y     = start_caret_y;

      //}

      //[끝위치]
      //{
          _select_end.caret_index   = end_lineendindex;
          _select_end.caret_x       = end_caret_x;
          _select_end.caret_y       = end_caret_y;

      //}

  //}

  //[입력지정자 위치 설정]
  //{
      //입력지정자는 문서의 가장 마지막에 위치시켜준다.(단, [줄이동] 은 해서는 안된다.)
      //_EDIT_CaretPos() 함수에서 [선택영역] 이 해제 되지 않도록 처리한다.
      _scroll_ing = true;
      //{
          //위에서 얻은 [열] 위치 값을 기록해 주어야 한다.
          //아래 _EDIT_CaretPos() 안에서는 _raw 만 계산이 된다.
          _col = col;

          //반드시 _caretindex 값을 설정해 주어야 한다.
          _caretindex = _select_end.caret_index;
          _caret_x    = _select_end.caret_x;
          _caret_y    = _select_end.caret_y;

          _EDIT_CaretPos(_caret_x, _caret_y);

      //}
      _scroll_ing = false;

  //}

  //[선택영역 반영]
  //{
      //[선택영역] 을 반영하기 위하여 다시 그려준다.
      _EditBuf_Text();

  //}

  return;

}

void __fastcall TGEdit_Form::_BalloonHelpUse(bool    pUse)
{
  //[2008.02.03.일][생성!!!]{-----------------
  //[풍선도움말] 기능의 사용 유무를 결정한다.
  //}-----------------------------------------

  //[개체존재:확인{****************************
  //[2012.05.01.화][추가!!!]
  //개체가 이미 해제된 뒤라면 처리하지 않는다.
  if(_this_ != true) return;
  //}******************************************

  _balloonHelpUse = pUse;

  return;

}

void __fastcall TGEdit_Form::_BalloonHelpStr(char   *pStr)
{
  //[2008.02.03.일][생성!!!]{-----------------
  //[풍선도움말] 에 표시할 문자열을 기록한다.
  //지정된 문자열의 길이만큼 동적 생성한다.
  //}-----------------------------------------

  //[개체존재:확인{****************************
  //[2012.05.01.화][추가!!!]
  //개체가 이미 해제된 뒤라면 처리하지 않는다.
  if(_this_ != true) return;
  //}******************************************

  //지금 버퍼에 내용이 있다면 삭제한다.
  if(_balloonHelpStr != NULL) { delete [] _balloonHelpStr; _balloonHelpStr = NULL; }

  //동적으로 공간을 생성한다.
  //_balloonHelpStr = new char [strlen(pStr) + 1];
  //[동적생성단위적용][2021.01.24.일][수정]
  _balloonHelpStr = new char [UnitX(1024, strlen(pStr) + 1)];

  //문자열을 복사한다.
  STR_Assign(_balloonHelpStr, pStr);

  return;

}

//[동작함수]--------------------------------------------------------------------

void __fastcall TGEdit_Form::_EVENT_ItemSelect_popupMenu(void)
{
  //[2010.02.13.토][생성!!!]{-----------------------------------
  //[빠른목록] 에서 선택된 항목을 구하고 이에 맞는 처리를 한다.
  //}-----------------------------------------------------------

  //[개체존재:확인{****************************
  //[2012.05.01.화][추가!!!]
  //개체가 이미 해제된 뒤라면 처리하지 않는다.
  if(_this_ != true) return;
  //}******************************************

  //[풍선도움말 감추기][2008.04.05.토][추가!!!]{---------------
  //다른 기능이 동작한다면 표시하지 않아야 한다.
  if((_balloonHelpUse == true) && ((*_GBalloonBox) != NULL) &&
     //[2018.07.12.목][추가!!!]
     //[폼디자인:모드] 에서는 사용하지 않는다.
     (_GFormDesignMode != true)) { (*_GBalloonBox)->_Hide(); }
  //}----------------------------------------------------------

  switch(_popupMenu->_itemSelIndex)
  {
    case 0 : //복사.
    {
      _CLIPBOARD_Copy();

    } break;

    case 1 : //잘라내기.
    {
      _CLIPBOARD_Cut();

      //[동작함수]
      //{
          //[2022.06.0213.월][추가]
          if(_EVENT_Change != NULL) { _EVENT_Change(); }

      //}

    } break;

    case 2 : //붙이기.
    {
      _CLIPBOARD_Paste();

      //[동작함수]
      //{
          //[2022.06.0213.월][추가]
          if(_EVENT_Change != NULL) { _EVENT_Change(); }

      //}

    } break;

    case 3 : //삭제.
    {
      _EDIT_SelectDelete();

      //[동작함수]
      //{
          //[2022.06.0213.월][추가]
          if(_EVENT_Change != NULL) { _EVENT_Change(); }

      //}

    } break;

    case 4 : //모두선택.
    {
      _EDIT_SelectAll();

      //[동작함수]
      //{
          //[2022.06.13.월][추가]{-----------------
          //[전체선택] 의 경우는 [키:End] 로 취급.
          //}--------------------------------------

          if(_EVENT_KeyDown_End != NULL) { _EVENT_KeyDown_End(); }

      //}

    } break;

  }

  return;

}

//[클립보드관련]----------------------------------------------------------------

bool __fastcall TGEdit_Form::_CLIPBOARD_IsText(void)
{
  //[2007.03.06.화][생성!!!]{-------------------------
  //현재 [클립보드] 안의 내용이 [문서] 인지 확인한다.
  //}-------------------------------------------------

  //[개체존재:확인{****************************
  //[2012.05.01.화][추가!!!]
  //개체가 이미 해제된 뒤라면 처리하지 않는다.
  if(_this_ != true) return(false);
  //}******************************************

  if(Clipboard()->HasFormat(CF_TEXT) == true) { return(true); }

  return(false);

}

void __fastcall TGEdit_Form::_CLIPBOARD_Copy(void)
{
  //[2007.03.07.수][생성!!!]{---------------------------------------------
  //이미 _select_start, _select_end 구조체 정보에 들어 있는 것을 참고하여
  //현재 [선택영역] 의 문자열을 [클립보드] 에 넣어준다.
  //메모리를 절약하기 위하여 동적 생성으로 처리한다.
  //}---------------------------------------------------------------------

  //[VAR]
  //{
      struct GEdit_CARETINFO   select_start;
      struct GEdit_CARETINFO   select_end;

      //선택된 문자열을 저장하기 위한 저장소.
      char              *select_text = NULL;
      //'\0' 까자 포함된 크기를 가져야 한다.
      int                size;

  //}

  //[개체존재:확인{****************************
  //[2012.05.01.화][추가!!!]
  //개체가 이미 해제된 뒤라면 처리하지 않는다.
  if(_this_ != true) return;
  //}******************************************

  //[선택영역 정보 구하기]
  //{
      //[선택영역] 이 없는 경우는 바로 종료한다.
      if(_SELECT_Get_StartEndIndex(&select_start, &select_end) != true) return;

      //문자열 길이 얻기. '\0' 을 포함한 크기여야 한다.----------v
      size = select_end.caret_index - select_start.caret_index + 1;

  //}

  //[저장소 처리]
  //{
      //[저장소] 를 동적 생성한다.
      //select_text = new char [size];
      //[동적생성단위적용][2021.01.24.일][수정]
      select_text = new char [UnitX(1024, size)];

      //[선택영역] 문자열을 추출하여 저장한다.(자동으로 뒤에 '\0' 이 붙어서 반환된다.)
      STR_SubString(_editbuf,
                    select_start.caret_index, select_end.caret_index - select_start.caret_index,
                    select_text);

  //}

  //[클립보드에 할당]
  //{
      Clipboard()->AsText = (AnsiString)select_text;

  //}

  //[후처리]
  //{
      //동적 생성한 [저장소] 를 삭제한다.
      if(select_text != NULL) { delete [] select_text; select_text = NULL; }

  //}

}

void __fastcall TGEdit_Form::_CLIPBOARD_Cut(void)
{
  //[2007.03.07.수][생성!!!]{--------------------------------
  //[복사] 수행후 [선택영역] 을 삭제하면 된다.

  //[2007.03.22.목][수정!!!]
  //[선택영역] 이 존재하지 않는 경우는 처리하지 않아야 한다.
  //}--------------------------------------------------------

  //[VAR]
  //{
      struct GEdit_CARETINFO   select_start;
      struct GEdit_CARETINFO   select_end;

  //}

  //[개체존재:확인{****************************
  //[2012.05.01.화][추가!!!]
  //개체가 이미 해제된 뒤라면 처리하지 않는다.
  if(_this_ != true) return;
  //}******************************************

  //[선택영역 존재 확인]
  //{
      //[선택영역] 이 없는 경우는 바로 종료한다.
      if(_SELECT_Get_StartEndIndex(&select_start, &select_end) != true) return;

  //}

  //일단 [복사] 를 수행한 다음에 [선택영역] 을 삭제하면 된다.
  _CLIPBOARD_Copy();

  //[삭제] 는 <Del-Key> 를 누른 것으로 간단하게 처리할수 있다.
  _KEY_Delete();

  return;

}

void __fastcall TGEdit_Form::_CLIPBOARD_Paste(void)
{
  //[2007.03.07.수][생성!!!]{---------
  //내용이 있는 경우만 처리해야 한다.
  //}---------------------------------

  //[개체존재:확인{****************************
  //[2012.05.01.화][추가!!!]
  //개체가 이미 해제된 뒤라면 처리하지 않는다.
  if(_this_ != true) return;
  //}******************************************

  //[내용이 문서인가?]
  //{
      //[문서] 가 아니라면 처리하지 않는다.
      if(_CLIPBOARD_IsText() != true) return;

  //}

  //[내용이 있는가?]
  //{
      //처리할 내용이 없다면 처리하지 않는다.
      if(((AnsiString)(Clipboard()->AsText)).Length() == 0) return;

  //}

  //[붙여넣기]
  //{
      //[클립보드] 안의 [문서] 내용을 현재 [입력지정자] 위치에 끼워넣는다.
      _EDIT_Paste(((AnsiString)(Clipboard()->AsText)).c_str());

  //}

  return;

}

//[EVENT]-----------------------------------------------------------------------

void __fastcall TGEdit_Form::FormMouseDown(TObject *Sender,
      TMouseButton Button, TShiftState Shift, int X, int Y)
{
  //[2006.11.28.화][생성!!!]{----------------------------------------------
  //[휠마우스] 를 사용하게 하기 위하여 반드시 포커스를 주어야 한다.

  //[2007.02.06.화][추가!!!]
  //[선택영역] 처리를 추가한다.

  //[2007.02.11.일][추가!!!]
  //이미 [선택영역] 이 있고, <shift-key> 가 눌린 상태에서 마우스로
  //[선택영역] 을 만드는 경우는 새로 만들지 않고 이전에 만든 [선택영역] 을
  //계속 이어서 형성한다.

  //[선택영역] 의 경우는 마우스 [왼쪽버튼] 인 경우로 한정한다.
  //}----------------------------------------------------------------------

  //[개체존재:확인{****************************
  //[2012.05.01.화][추가!!!]
  //개체가 이미 해제된 뒤라면 처리하지 않는다.
  if(_this_ != true) return;
  //}******************************************

  if(Button == mbLeft)
  {
    //[왼쪽 버튼] 만으로 한정한다.

    //[상태기록]
    //{
        //[마우스] 가 눌린 것이다.
        _mouse_down = true;

    //}

    //[촛점갖기]
    //{
        //이미 [촛점] 이 있는 경우는 WM_SETFOCUS 의 중복처리 방지를 위하여 처리하지 않는다.
        if(this->Focused() != true)
        {
          this->SetFocus();

        }

    //}

    //[입력지정자 위치 지정]
    //{
        //[입력지정자] 위치를 현재 찍은 위치로 수정한다.
        _CARETPOS_MouseDown(X, Y);

    //}

    //[선택영역 처리]
    //{
        //[설명]{-----------------------------------------------------------------------------
        //[마우스] 가 처음 down 된 지점부터 일단은 [선택영역] 이 시작된 것으로 간주해야 한다.
        //단, <shift-key> 가 눌린 상태라면, 이전에 형성된 [선택영역] 을 계속 이어간다.
        //}-----------------------------------------------------------------------------------

        //[선택영역] 형성이 시작됬음을 알린다.
        _select_ing = true;

        //[예외처리]
        //{
            //[2007.02.11.일][추가!!!]
            //이미  [선택영역] 이 있고 <shift-key> 가 눌린 경우는 초기화 하지 않고 계속 이어간다.
            if((_select_start.caret_index >= 0) && (_select_end.caret_index >= 0) && (_shift_down == true)) return;

        //}

        //[선택영역반영]
        //{
            //새로 시작하는 것이므로 현재 [선택영역] 이 있는 경우는 해제한다.
            if((_select_start.caret_index >= 0) && (_select_end.caret_index >= 0))
            {
              //[선택영역] 은 caret_index 가 둘중 하나라도 -1 인 경우는 처리되지 않으므로
              //일단 둘다 -1 로 해 놓는다.
              _select_start.caret_index = -1;
              _select_end.caret_index   = -1;

              //[선택영역] 이 해제된 것을 반영하기 위하여 다시 그린다.
              _EditBuf_Text();

            }

        //}

        //이 시작위치는 현재 [입력지정자] 의 위치가 된다.
        _select_start.caret_index = _caretindex;
        _select_start.caret_x     = _caret_x;
        _select_start.caret_y     = _caret_y;

        //[영역초기화]
        //{
            //여기서 _select_endindex 는 -1 로 초기화해 주어야 이전에 설정된 것을 완전히
            //무효화 하고 새로 시작할수 있다.(지금 새로운 [선택영역] 이 시작된 것이므로...)
            _select_end.caret_index = -1;
            _select_end.caret_x     = 0;
            _select_end.caret_y     = 0;

        //}

    //}

  }

  return;

}

void __fastcall TGEdit_Form::FormMouseMove(TObject *Sender,
      TShiftState Shift, int X, int Y)
{
  //[2007.02.06.화][생성!!!]{---------------------------------------------------------------
  //마우스를 집어끌어서 [선택영역] 을 지정하기 위하여 사용된다.
  //(_block_ing == true) 인 경우만 처리가 유효하다.

  //[2007.02.08.목][추가!!!]
  //마우스로 [선택영역] 을 만들어 가는 도중 [편집기] 안의 영역을 벗어나는 경우는
  //자동으로 [줄이동] 을 하여 항상 [입력지정자] 가 눈에 보이는 위치에 있도록 만들어야 한다.

  //[선택영역] 의 경우는 마우스 [왼쪽버튼] 인 경우로 한정한다.

  //[2007.02.26.월][추가!!!]
  //마우스로 한번 찍는 경우 Down 으로 그치지 않고 이곳 Move 가지 들어 오게 된다.
  //이때 (움직이지는 않은 경우) 이 함수에서 [선택영역] 끝 위치가 지정되는 것을 막기 위하여
  //[입력지정자] 위치에 변화가 없는 경우는 Move 는 아닌것으로 보고 [선택영역] 끝 위치를
  //처리하지 않는다.

  //[2007.04.10.화][추가!!!]
  //[표시지정자] 의 모양을 해당 영역에 따라 다르게 지정해야 한다.
  //이는 [편집기] 안에 [검증창] 이 있고 이 영역은 편집영역이 아니기 때문이다.
  //}---------------------------------------------------------------------------------------

  //[VAR]
  //{
      //[2007.02.26.월][추가!!!]
      bool  caret_pos_changed = false;

  //}

  //[개체존재:확인{****************************
  //[2012.05.01.화][추가!!!]
  //개체가 이미 해제된 뒤라면 처리하지 않는다.
  if(_this_ != true) return;
  //}******************************************

  //[표시지정자 모양 전환]
  //{
      //[2007.04.10.화][추가!!!]{-------------------------------------
      //[표시지정자] 의 모양을 [검증창/편집기] 에 따라 바꾸어야 한다.
      //}-------------------------------------------------------------

      if((0 <= X) && (X <= d_X_width))
      {
        //일반 화살표 모양으로 바꾼다.
        if(this->Cursor != crDefault) this->Cursor = crDefault;

      }
      else
      {
        //모양을 편집용 모양으로 만든다.
        if(this->Cursor != crIBeam) this->Cursor = crIBeam;

      }

  //}

  if(Shift.Contains(ssLeft))
  {
    //[선택영역]
    //{
        //반드시 (_select_ing == true) 로 [선택영역] 이 형성되는 과정에서만 처리해야한다.
        //또한, [마우스] 가 눌린 상태인 경우만 처리한다.
        //이는 [키] 로 영역을 만드는 과정에서 아래를 처리하지 않기 위함이다.
        if((_mouse_down == true) && (_select_ing == true))
        {
          //[설명]{----------------------------------------------------------------
          //_select_endindex 를 계속 설정하고 이 값이 수정되는 경우는 계속 다시
          //[선택영역] 을 반영해 주어야 한다.
          //이것은 현재 보이는 [편집기] 의 내용을 다시 출력하면 자동으로 적용된다.
          //}----------------------------------------------------------------------

          //[편집기 영역 확인]
          //{
              //[편집영역] 을 벗어난 경우는 자동으로 [줄이동] 을 수행하여 [입력지정자] 가
              //항상 보이는 위치에 있도록 만든다.
              //아래 각각의 함수내부에서 자동으로 자신이 처리할 부분인지 확인하여 처리하므로
              //굳이 여기서 벗어났는지 확인하여 처리할 필요는 없다.
              _SELECTSCROLL_Left(X);
              _SELECTSCROLL_Right(X);

              //전부다 수정한다.(마우스가 움직인 만큼 한꺼번에 수정될수 있어야 한다.)

          //}

          //[입력지정자 위치 지정]
          //{
              //[입력지정자] 위치를 현재 찍은 위치로 수정한다.
              //위치가 바뀌었는지 결과를 같이 얻는다.
              caret_pos_changed = _CARETPOS_MouseDown(X, Y);

          //}

          //[2007.02.26.월][수정!!!]
          //[입력지정자] 의 위치변화가 있는 경우만 처리한다.(쓸데없는 시간 낭비도 줄일수 있다.)
          if(caret_pos_changed == true)
          {
            //[선택영역 끝 위치 구하기]
            //{
                //마지막 위치는 현재 설정된 [입력지정자] 의 위치가 된다.
                _select_end.caret_index = _caretindex;
                _select_end.caret_x     = _caret_x;
                _select_end.caret_y     = _caret_y;

            //}

            //[반영]
            //{
                //문서를 다시 출력하는 것으로 자동반영 되도록 한다.
                _EditBuf_Text();

            //}

          }

        }

    //}

  }

  //[2008.02.03.일][추가!!!]{-----------------------------------------------------
  //[풍선도움말] 위치를 설정한다.
  if((_balloonHelpUse == true) && ((*_GBalloonBox) != NULL) &&
     //[2010.03.09.화][추가!!!]
     //[편집기] 에 촛점이 있는 경우는 작업중이므로 [풍선도움말] 을 보이지 않는다.
     (this->Focused() != true) &&
     //[2018.07.12.목][추가!!!]
     //[폼디자인:모드] 에서는 사용하지 않는다.
     (_GFormDesignMode != true))
  {
    (*_GBalloonBox)->_BalloonBoxPos(Mouse->CursorPos.x, Mouse->CursorPos.y);

  }
  //}-----------------------------------------------------------------------------

  return;

}

void __fastcall TGEdit_Form::FormMouseUp(TObject *Sender,
      TMouseButton Button, TShiftState Shift, int X, int Y)
{
  //[2007.02.06.화][생성!!!]{----------------------------------
  //[선택영역] 이 형성되고 있는 상태라면 여기서 종료해야 한다.
  //
  //[선택영역] 의 경우는 마우스 [왼쪽버튼] 인 경우로 한정한다.
  //}----------------------------------------------------------

  //[VAR]
  //{
      //[2009.05.08.금][추가!!!]
      TPoint mousePos;

      //[2010.02.18.목][추가!!!]
      int    absTop, absLeft;

      //[2010.03.09.화][중요추가!!!]
      //<_balloonHelpUse> 설정값을 일시 저장한다.
      bool   balloonHelpUse;

  //}

  //[개체존재:확인{****************************
  //[2012.05.01.화][추가!!!]
  //개체가 이미 해제된 뒤라면 처리하지 않는다.
  if(_this_ != true) return;
  //}******************************************

  switch(Button)
  {
    case mbLeft :
    {
      //[선택영역종료]
      //{
          //[선택영역] 형성의 종료를 알린다.
          _select_ing = false;
          
      //}

      //[상태기록]
      //{
          //[마우스] 가 더이상 눌린 상태가 아니다.
          _mouse_down = false;

      //}

    } break;

    case mbRight :
    {
      //[<_balloonHelpUse> 설정 대피/재설정]
      //{
          //[2010.03.09.화][추가!!!]
          balloonHelpUse = _balloonHelpUse;

          //[중요!!!]{-------------------------------------------------------------------
          //무조건 여기서는 사용하지 않는 것으로 해야 한다.
          //그렇지 않은 경우는 [빠른목록] 과 촛점이 중복되어 오류가 발생한다.
          //[풍선도움말] 이 타이머로 일정 시간 지연뒤 보일때, 이 중간에 [빠른목록] 이
          //보여지는 경우 [풍선도움말] 이 보여지면 [빠른목록] 이 촛점을 잃게 되는데
          //이때 [촛점중복처리] 가 발생하여 오류가 된다.
          //따라서, 아예 이미 [풍선도움말 타이머] 가 동작중이라도 무조건 [풍선도움말] 을
          //사용하지 않는 것으로 여기서 설정을 해야 안전하다.
          _balloonHelpUse = false;

          //현재 타이머가 구동중일수 있으므로 무조건 정지시킨다.
          if((*_GBalloonBox) != NULL) { (*_GBalloonBox)->_Stop(); }
          //}----------------------------------------------------------------------------

      //}

      //[촛점 설정 부품 기록]
      //{
          //[목록] 이 촛점을 잃을 경우 촛점을 가질 [부품] 을 기록해 준다.
          _popupMenu->_FocusedPart(this);

      //}

      //[동작함수 등록]
      //{
          _popupMenu->_EVENT_ItemSelect = _EVENT_ItemSelect_popupMenu;

      //}

      //[보이기]
      //{
          //[2010.02.18.목][수정!!!]{----------------------------------------
          //어떤 경우라도 정확한 위치에 [빠른목록] 이 보일수 있도록 새로만든
          //<GPART_Get_AbsTopLeft()> 함수를 사용하는 것으로 수정한다.
          //}----------------------------------------------------------------

          //[절대위치 얻기]
          //{
              //빠른 목록이 표시되는 그림부품의 [절대위치] 를 구한다.
              GPART_Get_AbsTopLeft(_popupMenu->Parent, this, &absTop, &absLeft);

          //}

          //[보이기]
          //{
              //[절대위치] 에 [빠른목록] 을 표시한다.

              //[목록] 이 [화면지정자] 를 가리지 않고 계속 [한줄 편집기] 에 있도록
              //약간 오른쪽에 위치시킨다.---------v
              _popupMenu->_MenuShow(absLeft + X + 8, absTop + Y);

              //_popupMenu->_MenuShowInPart(this, X + 8, Y);

          //}

      //}

      //[<_balloonHelpUse> 설정 북구]
      //{
          //[2010.03.09.화][추가!!!]
          _balloonHelpUse = balloonHelpUse;

      //}


    } break;

  }

  return;

}

//[폼디자인:모드]
void __fastcall TGEdit_Form::FormResize(TObject *Sender)
{
  //[2018.06.24.일][생성!!!]

  //[개체존재:확인{****************************
  //개체가 이미 해제된 뒤라면 처리하지 않는다.
  if(_this_ != true) return;
  //}******************************************

  //[폼디자인:모드]
  //{
      if(_GFormDesignMode == true)
      {
        //[크기조정/다시 그리기]
        //{
            //크기 조정후 내부적으로 다시 그려준다.
            _Width_Height(this->Width, this->Height);

        //}

        if((*_GCover) != NULL)
        {
          //[Gcover] 의 크기도 동일한 크기로 조정한다.
          (*_GCover)->_Width_Height(this->Width, this->Height);

        }

      }

  //}

}

//------------------------------------------------------------------------------
//[폼디자인:모드]---------------------------------------------------------------
//------------------------------------------------------------------------------

//[폼디자인:모드]
void __fastcall TGEdit_Form::_FormDesignMode(bool  pUse)
{
  //[2018.06.24.일][생성!!!]{-------------------------
  //[폼디자인:모드] 로 설정/해제 하고 관련 처리 수행.
  //}-------------------------------------------------

  _GFormDesignMode = pUse;

  //[기본 이벤트 해제]
  //{
      this->OnClick = NULL;
      this->OnMouseDown = NULL;
      this->OnMouseMove = NULL;
      this->OnMouseUp = NULL;

  //}

  //[MouseDown:이벤트 일원화]
  //{
      this->OnMouseDown = FormDesignMode_FormMouseDown;

  //}

  //[<*_GCover> 위치 얻기]
  //{
      GPART_GCover_GetPtr2(&_GCover);

  //}

}

//[폼디자인:모드]
void __fastcall TGEdit_Form::FormDesignMode_FormMouseDown(TObject *Sender, TMouseButton Button,
                                                          TShiftState Shift, int X, int Y)
{
  //[2018.06.24.일][추가!!!]{----------------------------------------------------
  //해당 [그림부품] 의 [선택] 을 처리.
  //
  //1) <GCover> 를 생성하고 이 [그림부품] 전체를 덮는다.
  //2) 관련 처리.
  //
  //[2019.02.10.일][중요수정]----------------------------------------------------
  //[폼디자이너] 의 [모체:변경] 시 [마우스 지정 위치] 에 [구성원 이동/배치] 기능
  //지원을 위한 [LMR/X/Y] 정보 추가.
  //}----------------------------------------------------------------------------

  //[VAR]
  //{
      //[2019.02.10.일][추가]
      int  lmr;

  //}

  //[버튼 종류 구분]
  //{
      //[2019.02.10.일][추가]
      switch(Button)
      {
        case  mbLeft   : { lmr = d_mouse_left;   } break;
        case  mbRight  : { lmr = d_mouse_right;  } break;
        case  mbMiddle : { lmr = d_mouse_middle; } break;

      }

  //}

  //생성.
  //[2019.02.10.일][추가]
  //[LMR/X/Y] 정보 추가-------------v
  GPART_GCover_Create(this, _GPart, X, Y, lmr);

}

//------------------------------------------------------------------------------
//[API GUI 지원함수]------------------------------------------------------------
//------------------------------------------------------------------------------

void __fastcall GEdit_Create(//개체 생성자.
                             void            *pPart,
                             //모체 생성자.
                             void            *pMother,
                             //사용자가 직접 지정한 [식별자].
                             unsigned short   pID,
                             int              pLeft,
                             int              pTop,
                             int              pWidth,
                             int              pHeight,
                             //생성후 보일것인지 아닌지.
                             bool             pVisible)
{
  //[2010.03.09.화][생성!!!]{-----------------------------------------------
  //VMOS 안에서 API 요청으로 <GEdit> 개체를 하나 생성한다.
  //실제 개체 포인터는 VMOS 상에 존재하며 해당 API 처리 분에서 실제 PC 상의
  //물리번지로 변환된 실제 [포인터] 에 직접 생성해 준다.
  //즉, VMOS 상에서 실제 <물리번지:PC> 의 주소를 관리하는 것이다.
  //
  //[2012.05.05.토][중요추가!!!]--------------------------------------------
  //<_GUse> 속성을 반드시 <맷돌C:true> 로 설정한다.
  //
  //[2012.07.09.월][중요수정!!!]********************************************
  //[생성자] 안에 <GID> 통합관련 수정.
  //}-----------------------------------------------------------------------

  //[VAR]
  //{
      //생성할 개체.
      TGEdit_Form  *GEdit = NULL;

      void           *mOwner  = NULL;
      void           *mParent = NULL;

  //}

  //[모체 정보 얻기]
  //{
      //생성할 모체에 대한 정보를 얻는다.
      GPART_GetMotherInfo(pMother, &mOwner, &mParent);

  //}

  //[생성]
  //{
      //[그림 부품 독립 사용 유/무]
      //{
          //[2018.10.13.토][추가!!!]{---------------------------------------------------------
          //모체가 없이 독립적으로 생성하는 경우는
          //void __fastcall TGForm_Form::CreateParams(TCreateParams &Params) 를 사용하여
          //기본 준비를 해 놓았으므로 추가 속성을 설정하여 완전히 독립적인 창으로 만들어준다.
          //반드시 생성되기 전에 해당 변수를 확정 지어야 한다.
          //}---------------------------------------------------------------------------------

          if(pMother == NULL)
          {
            //창을 완전히 독립적인 속성으로 만든다.
            _indpt_GEdit = true;

          }
          else
          {
            _indpt_GEdit = false;

          }

      //}

      //[생성]
      //{
          //위에서 얻은 [모체정보] 를 바탕으로 바로 설정하면 된다.
          GEdit         = new TGEdit_Form((TComponent *)mOwner);
          GEdit->Parent = (TWinControl *)mParent;

      //}

      //[초기화]
      //{
          //[중요!!!][2018.10.13.토][추가!!!]{*******************************
          //전역변수이므로 반드시 [기본값:false] 로 초기화 한다.
          //[맷돌-C] 에서는 반드시 이 <Gxxx_Create()> 를 사용해서 생성하지만
          //내부적으로 다른 그림부품에 사용시에는 이 함수를 거치지 않으므로
          //사용후 반드시 기본값인 <false> 로 초기화 해야 한다.
          //}****************************************************************

          _indpt_GEdit = false;

      //}

      //[식별자]
      //{
          //[2012.07.09.월][중요수정!!!]{---------------------------------
          //[생성자] 안에 <GID> 가 통합되었다.
          //[생성자(12)] = { [절대주소(8)] + [부품고유ID(2)] + [GID(2)] }
          //}-------------------------------------------------------------

          //사용자 지정 ID 를 설정한다.
          //GEdit->_GID = pID;
          ((DTU2 *)pPart)[5] = pID;

      //}

      //[위치/크기]
      //{
          //위치를 설정한다.
          GEdit->_Left(pLeft);
          GEdit->_Top(pTop);
          GEdit->_Width(pWidth);
          GEdit->_Height(pHeight);

      //}

      //[보이기/감추기]
      //{
          //기본적으로는 보이지 않는 속성으로 처리되므로 보이기 속성을 인수대로 설정한다.
          GEdit->Visible = pVisible;

      //}

  //}

  //[생성자 정보 정리]
  //{
      //[생성자] 에 해당 정보를 정리해 준다.

      //현재 생성된 개체 위치 저장(PC 상의 물리절대번지).
      (*((DTU8 *)pPart)) = (DTU8)GEdit;

      //개체의 [고유 부품 ID] 를 기록.
      ((DTU2 *)pPart)[4] = d_GID_Edit;

      //[생성자 위치 저장]
      //{
          //[!]{----------------------------------------------------------------------
          //생성된 개체의 [생성자] 위치를 관리변수에 저장한다.
          //이는, 이 개체가 스스로 API 로 삭제되지 않고, 다른 그림부품에 속해 있다가
          //이 그림부품이 삭제될때 같이 삭제되는 경우에도 [생성자] 정보를 초기화 할수
          //있게 하기 위한 것이다.
          //}-------------------------------------------------------------------------

          GEdit->_GPart = pPart;

      //}

  //}

  //[사용구분 설정]
  //{
      //[2012.05.05.토][중요추가!!!]{--------
      //반드시 [맷돌C:그림부품] 용임을 기록.
      //}------------------------------------

      GEdit->_GUse = true;

  //}

}

void __fastcall GEdit_Delete(//개체 생성자.
                             void  *pPart)
{
  //[2010.03.09.화][생성!!!]{----------
  //지정된 <GEdit> 을 삭제해 준다.
  //
  //[2012.07.09.월][중요수정!!!]*******
  //[생성자] 안에 <GID> 통합관련 수정.
  //}----------------------------------

  //[VAR]
  //{
      TGEdit_Form  *GEdit;

  //}

  //[GEdit 위치 얻기]
  //{
      //삭제할 대상을 얻는다.
      GEdit = (TGEdit_Form *)(*((DTU8 *)pPart));

  //}

  //[삭제]
  //{
      //이미 생성돼 있는 경우만 처리한다.
      if(GEdit != NULL) { delete GEdit; GEdit = NULL; }

  //}

  //[생성자 정보 정리]
  //{
      //[!]{----------------------------------------------------------
      //[생성자] 에 해당 정보를 정리해 준다.
      //
      //[2012.07.09.월][중요수정!!!]{*********************************
      //[생성자] 안에 <GID> 가 통합되었다.
      //[생성자(12)] = { [절대주소(8)] + [부품고유ID(2)] + [GID(2)] }
      //}-------------------------------------------------------------

      //현재 생성된 개체 위치 저장(PC 상의 물리절대번지).
      (*((DTU8 *)pPart)) = 0;

      //개체의 [고유 부품 ID] 를 기록.
      ((DTU2 *)pPart)[4] = 0;

      //[2012.07.09.월][추가!!!]
      //개체의 <GID> 를 기록.
      ((DTU2 *)pPart)[5] = 0;

  //}

}

void __fastcall GEdit_CreatorInit(//개체 생성자.
                                  void  *pPart)
{
  //[2010.03.09.화][생성!!!]{--------------------------
  //[생성자] 를 받아서 안의 내용을 완전히 초기화 한다.
  //
  //[2012.07.09.월][중요수정!!!]***********************
  //[생성자] 안에 <GID> 통합관련 수정.
  //}--------------------------------------------------

  //[생성자가 존재하나?]
  //{
      //[생성자] 가 존재하지 않는다면 처리하지 않는다.
      if(pPart == NULL) return;

  //}

  //[생성자 정보 정리]
  //{
      //[!]{----------------------------------------------------------
      //[생성자] 에 해당 정보를 정리해 준다.
      //
      //[2012.07.09.월][중요수정!!!]{*********************************
      //[생성자] 안에 <GID> 가 통합되었다.
      //[생성자(12)] = { [절대주소(8)] + [부품고유ID(2)] + [GID(2)] }
      //}-------------------------------------------------------------

      //현재 생성된 개체 위치 저장(PC 상의 물리절대번지).
      (*((DTU8 *)pPart)) = 0;

      //개체의 [고유 부품 ID] 를 기록.
      ((DTU2 *)pPart)[4] = 0;

      //[2012.07.09.월][추가!!!]
      //개체의 <GID> 를 기록.
      ((DTU2 *)pPart)[5] = 0;

  //}

}

//---------------------------------------------------------------------------




