CustomPanel.cs 8.16 KB
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace flowchart
{
    // 각종 선택 상황을 열거형으로 정의
    public enum State
    {
        NONE, RECTANGLE, RHOMBUS, PARALLELOGRAM, LINKLINE, ELLIPSE, MOVE
    }


    // flowchart를 그리는 판넬에 필요한 기능을 삽입. (Panel 객체를 상속해서 사용했음)
    // 마우스 클릭을 놓으면 어떤 도형 또는 선을 선택했는지 판단해서, 해당 자료구조를 생성하고, 페인트 함수를 통해 그린다. 
    class CustomPanel : Panel
    {
        public static Control _parentControl = null;  // 텍스트박스에서 부모로 정의하기 위한 변수

        public CustomPanel()  // 생성자
        {
            _parentControl = this;
        }

        private State _selectState = State.NONE;    // 열거형을 사용하기 위한 변수

        private List<FigBase> _shapes = new List<FigBase>();             // 도형을 저장한 리스트 자료구조
        private List<FigLinkline> _linkLines = new List<FigLinkline>();  // 링크 선을 저장한 리스트 자료구조

        private FigBase _linkStartShape = null;   // 처음 선택한 도형을 저장하는 변수
        private FigBase _movingShape = null;      // 도형을 움직이겠다고 선택된 경우 해당 도형을 저장하는 변수                          

        // 외부 클래스에서 호출되는 변수(열거형 값) 정의
        public State SelectState { get { return _selectState; } set { _selectState = value; } }

        // 마우스를 판넬에서 움질일때 호출되는 이벤트 함수
        protected override void OnMouseMove(MouseEventArgs e)
        {
            // System.Diagnostics.Trace.WriteLine("debug >>>" + _state); // 디버깅

            // 마우스 버튼을 누르지 않은 상태일 경우
            if (e.Button == MouseButtons.None)
            {
                // 그려진 도형위에 마우스가 오면 마우스 모양을 변경
                FigBase shape = FindShapeByLocation(e.Location);
                
                if (shape != null)
                    Cursor = Cursors.SizeAll; // 도형 안에 마우스가 있을 경우 마우스 모양 변경
                else 
                    Cursor = Cursors.Default; // 도형 밖에 마우스 포인터가 있을 경우 마우스 모양 변경
                // 도형 안에 마우스가 있고, 링크 라인을 그리겠다고 선택 
                if (shape != null && _selectState == State.LINKLINE)  
                    Cursor = Cursors.Cross;

            }
            // 마우스를 클릭한 상태에서 링크 라인을 그리지 않겠다고 선택된 경우
            else if (e.Button == MouseButtons.Left && _selectState != State.LINKLINE)
            {
                FigBase shape = FindShapeByLocation(e.Location);  // 마우스가 어떤 도형안에 있는지 검색
                if (shape != null)
                {
                    _selectState = State.MOVE;    // 움직이는 상태로 바꿈
                    _movingShape = shape;         // 해당 도형을 레퍼런스
                }
            }
            // 마우스를 클릭한 상태에서 링크 라인을 그리겠다고 선택한 경우
            else if (e.Button == MouseButtons.Left && _selectState == State.LINKLINE)
            {
                // 처음 도형을 선택한것만 저장하기 위함. (다른 영역의 도형으로 넘어가도 실행안함)
                if (_linkStartShape == null)  
                {
                    _linkStartShape = FindShapeByLocation(e.Location); // 마우스가 어떤 도형안에 있는지 검색
                    if (_linkStartShape != null)
                    {
                        _selectState = State.LINKLINE;
                    }
                }
            }

            base.OnMouseMove(e);
        }

        // 실제 그릴 도형정보를 리스트 자료구조에 삽입하고, OnPaint() 함수를 호출
        protected override void OnMouseUp(MouseEventArgs e)
        {
            if (_selectState == State.RECTANGLE)  // 사각형 그리기 선택
            {
                // 사각형에 대한 정보를 자료구조에 삽입한다.
                FigRectangle rectangle = new FigRectangle(e.Location, new System.Drawing.Size(100, 50));
                _shapes.Add(rectangle);
            }
            else if (_selectState == State.RHOMBUS) // 마름모 그리기 선택
            {
                FigRhombus rhombus = new FigRhombus(e.Location, new System.Drawing.Size(100, 50));
                _shapes.Add(rhombus);
            }
            else if (_selectState == State.PARALLELOGRAM) // 평행사변형 그리기 선택
            {
                FigParallelogram parallelogram = new FigParallelogram(e.Location, new System.Drawing.Size(100, 50));
                _shapes.Add(parallelogram);
            }
            else if (_selectState == State.ELLIPSE) // 원형 그리기 선택
            {
                FigEllipse ellipse = new FigEllipse(e.Location, new System.Drawing.Size(100, 50));
                _shapes.Add(ellipse);
            }
            else if (_selectState == State.MOVE)  // 도형을 움직이는 상태로 선택한 경우
            {
                if (_movingShape != null)
                {
                    _movingShape.Location = e.Location; // 자료구조에서 찾은 도형의 위치값을 옮길위치로 수정
                }
            }
            else if (_selectState == State.LINKLINE)  // 선을 그리기로 선택한 경우
            {
                if (_linkStartShape != null)
                {
                    FigBase shape = FindShapeByLocation(e.Location);  // 마우스를 클릭을 놓은 위치에 도형이 있는지 확인
                    if (shape == null)
                    {
                        MessageBox.Show(this, "해당 위치에 연결대상 도형이 없습니다.", "알림",
                            MessageBoxButtons.OK, MessageBoxIcon.Information);
                        _linkStartShape = null;     // mouse move 이벤트 함수에서 한번은 실행하게 처리
                    }
                    else
                    {
                        FigLinkline linkLine = new FigLinkline(_linkStartShape, shape);  // 처음 선택한 도형과 현재 도형에 링크 라인을 연결 함
                        _linkLines.Add(linkLine);
                        _linkStartShape = null;    // mouse move 이벤트 함수에서 한번은 실행하게 처리
                    }
                  
                }
            }

            _selectState = State.NONE;
            this.Refresh(); // 다시 그리기 요청: OnPaint()
            base.OnMouseUp(e);
        }
        
        // 도형 안에서 마우스를 더블클릭하면 텍스트 에디터를 삽입한다.
        protected override void OnMouseDoubleClick(MouseEventArgs e)
        {
            FigBase shape = FindShapeByLocation(e.Location);
            if (shape != null)
            {
                shape.EnterEditMode();  // FigBase에 함수 있음
            }

            base.OnMouseDoubleClick(e);
        }

        // 실제 그리는 OnPaint를 통해 내가 작성한 Draw함수를 호출한다.
        protected override void OnPaint(PaintEventArgs e)
        {
            // 자료구조에 저장된 도형을 다시 그림
            foreach (FigBase s in _shapes)
            {
                s.Draw(e.Graphics);
            }
            // 자료구조에 저장된 링크라인을 다시 그림
            foreach (FigLinkline l in _linkLines)
            {
                l.Draw(e.Graphics);
            }

            base.OnPaint(e);
        }
        // 현재 위치에 해당하는 도형을 자료구조에서 찾아서 레퍼런스 함
        private FigBase FindShapeByLocation(Point location)
        {
            foreach (FigBase s in _shapes)
            {
                if (s.PointInRegion(location))
                {
                    return s;
                }
            }

            return null;
        }
    }
}