/////////////////////////////////////////////////////////////////////////////
// Name:        samples/docview/view.cpp
// Purpose:     View classes implementation
// Author:      Julian Smart
// Modified by: Vadim Zeitlin: merge with the MDI version and general cleanup
// Created:     04/01/98
// RCS-ID:      $Id: view.cpp 56053 2008-10-03 15:16:01Z VZ $
// Copyright:   (c) 1998 Julian Smart
//              (c) 2008 Vadim Zeitlin
// Licence:     wxWindows license
/////////////////////////////////////////////////////////////////////////////

// For compilers that support precompilation, includes "wx/wx.h".
#include "wx/wxprec.h"

#ifdef __BORLANDC__
    #pragma hdrstop
#endif

#ifndef WX_PRECOMP
    #include "wx/wx.h"
#endif

#if !wxUSE_DOC_VIEW_ARCHITECTURE
    #error You must set wxUSE_DOC_VIEW_ARCHITECTURE to 1 in setup.h!
#endif

#include "docview.h"
#include "doc.h"
#include "view.h"

// ----------------------------------------------------------------------------
// DrawingView implementation
// ----------------------------------------------------------------------------

IMPLEMENT_DYNAMIC_CLASS(DrawingView, wxView)

BEGIN_EVENT_TABLE(DrawingView, wxView)
    EVT_MENU(wxID_CUT, DrawingView::OnCut)
END_EVENT_TABLE()

// What to do when a view is created. Creates actual
// windows for displaying the view.
bool DrawingView::OnCreate(wxDocument *doc, long WXUNUSED(flags) )
{
    MyApp& app = wxGetApp();
    if ( app.GetMode() != MyApp::Mode_Single )
    {
        // create a new window and canvas inside it
        m_frame = app.CreateChildFrame(doc, this, true);
        m_frame->SetTitle("Drawing View");

        m_canvas = new MyCanvas(this, m_frame);
        m_frame->Show(true);
    }
    else // single document mode
    {
        // reuse the existing window and canvas
        m_frame = wxStaticCast(app.GetTopWindow(), wxFrame);
        m_canvas = app.GetMainWindowCanvas();
        m_canvas->SetView(this);

        // Associate the appropriate frame with this view.
        SetFrame(m_frame);

        // Make sure the document manager knows that this is the
        // current view.
        Activate(true);

        // Initialize the edit menu Undo and Redo items
        doc->GetCommandProcessor()->SetEditMenu(app.GetMainWindowEditMenu());
        doc->GetCommandProcessor()->Initialize();
    }

    return true;
}

// Sneakily gets used for default print/preview as well as drawing on the
// screen.
void DrawingView::OnDraw(wxDC *dc)
{
    dc->SetPen(*wxBLACK_PEN);

    // simply draw all lines of all segments
    const DoodleSegments& segments = GetDocument()->GetSegments();
    for ( DoodleSegments::const_iterator i = segments.begin();
          i != segments.end();
          ++i )
    {
        const DoodleLines& lines = i->GetLines();
        for ( DoodleLines::const_iterator j = lines.begin(); 
              j != lines.end();
              ++j )
        {
            const DoodleLine& line = *j;

            dc->DrawLine(line.x1, line.y1, line.x2, line.y2);
        }
    }
}

DrawingDocument* DrawingView::GetDocument()
{
    return wxStaticCast(wxView::GetDocument(), DrawingDocument);
}

void DrawingView::OnUpdate(wxView *WXUNUSED(sender), wxObject *WXUNUSED(hint))
{
    if ( m_canvas )
        m_canvas->Refresh();
}

// Clean up windows used for displaying the view.
bool DrawingView::OnClose(bool deleteWindow)
{
    if ( !GetDocument()->Close() )
        return false;

    Activate(false);

    // Clear the canvas in single-window mode in which it stays alive
    if ( wxGetApp().GetMode() == MyApp::Mode_Single )
    {
        m_canvas->ClearBackground();
        m_canvas->ResetView();
        m_canvas = NULL;

        if ( m_frame )
            m_frame->SetTitle(wxTheApp->GetAppDisplayName());
    }
    else // not single window mode
    {
        if ( deleteWindow )
            wxDELETE(m_frame);
    }

    SetFrame(NULL);

    return true;
}

void DrawingView::OnCut(wxCommandEvent& WXUNUSED(event) )
{
    DrawingDocument * const doc = GetDocument();

    doc->GetCommandProcessor()->Submit(new DrawingRemoveSegmentCommand(doc));
}

// ----------------------------------------------------------------------------
// TextEditView implementation
// ----------------------------------------------------------------------------

IMPLEMENT_DYNAMIC_CLASS(TextEditView, wxView)

BEGIN_EVENT_TABLE(TextEditView, wxView)
    EVT_MENU(wxID_COPY, TextEditView::OnCopy)
    EVT_MENU(wxID_PASTE, TextEditView::OnPaste)
    EVT_MENU(wxID_SELECTALL, TextEditView::OnSelectAll)
END_EVENT_TABLE()

bool TextEditView::OnCreate(wxDocument *doc, long WXUNUSED(flags))
{
    m_frame = wxGetApp().CreateChildFrame(doc, this, false);
    m_text = new wxTextCtrl(m_frame, wxID_ANY, "",
                            wxPoint(0, 0), m_frame->GetClientSize(),
                            wxTE_MULTILINE);

    m_frame->SetTitle("Text View");
    m_frame->Show(true);

    Activate(true);

    return true;
}

void TextEditView::OnDraw(wxDC *WXUNUSED(dc))
{
    // nothing to do here, wxTextCtrl draws itself
}

bool TextEditView::OnClose(bool deleteWindow)
{
    if ( !GetDocument()->Close() )
        return false;

    Activate(false);

    if ( wxGetApp().GetMode() == MyApp::Mode_Single )
    {
        m_text->Clear();
    }
    else // not single window mode
    {
        if ( deleteWindow )
            wxDELETE(m_frame);
    }

    return true;
}

// ----------------------------------------------------------------------------
// MyCanvas implementation
// ----------------------------------------------------------------------------

BEGIN_EVENT_TABLE(MyCanvas, wxScrolledWindow)
    EVT_MOUSE_EVENTS(MyCanvas::OnMouseEvent)
END_EVENT_TABLE()

// Define a constructor for my canvas
MyCanvas::MyCanvas(wxView *view, wxWindow *parent)
    : wxScrolledWindow(parent, wxID_ANY, wxPoint(0, 0), parent->GetClientSize())
{
    m_view = view;
    m_currentSegment = NULL;
    m_lastMousePos = wxDefaultPosition;

    SetCursor(wxCursor(wxCURSOR_PENCIL));

    // this is completely arbitrary and is done just for illustration purposes
    SetVirtualSize(1000, 1000);
    SetScrollRate(20, 20);

    SetBackgroundColour(*wxWHITE);
}

MyCanvas::~MyCanvas()
{
    delete m_currentSegment;
}

// Define the repainting behaviour
void MyCanvas::OnDraw(wxDC& dc)
{
    if ( m_view )
        m_view->OnDraw(& dc);
}

// This implements a tiny doodling program. Drag the mouse using the left
// button.
void MyCanvas::OnMouseEvent(wxMouseEvent& event)
{
    if ( !m_view )
        return;

    wxClientDC dc(this);
    PrepareDC(dc);

    dc.SetPen(*wxBLACK_PEN);

    const wxPoint pt(event.GetLogicalPosition(dc));

    // is this the end of the current segment?
    if ( m_currentSegment && event.LeftUp() )
    {
        if ( !m_currentSegment->IsEmpty() )
        {
            // We've got a valid segment on mouse left up, so store it.
            DrawingDocument * const
                doc = wxStaticCast(m_view->GetDocument(), DrawingDocument);

            doc->GetCommandProcessor()->Submit(
                new DrawingAddSegmentCommand(doc, *m_currentSegment));

            doc->Modify(true);
        }

        delete m_currentSegment;
        m_currentSegment = NULL;
    }

    // is this the start of a new segment?
    if ( m_lastMousePos != wxDefaultPosition && event.Dragging() )
    {
        if ( !m_currentSegment )
            m_currentSegment = new DoodleSegment;

        m_currentSegment->AddLine(m_lastMousePos, pt);

        dc.DrawLine(m_lastMousePos, pt);
    }

    m_lastMousePos = pt;
}

