/*
* This file is part of Code::Blocks Studio, an open-source cross-platform IDE
* Copyright (C) 2003  Yiannis An. Mandravellos
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*
* Contact e-mail: Yiannis An. Mandravellos <mandrav@codeblocks.org>
* Program URL   : http://www.codeblocks.org
*
* $Id: classbrowser.cpp,v 1.13.2.1 2005/10/25 07:59:01 mandrav Exp $
* $Date: 2005/10/25 07:59:01 $
*/

#include <sdk.h>
#include "classbrowser.h" // class's header file
#include "nativeparser.h"
#include <wx/intl.h>
#include <wx/notebook.h>
#include <wx/sizer.h>
#include <wx/menu.h>
#include <configmanager.h>
#include <pluginmanager.h>
#include <editormanager.h>
#include <projectmanager.h>
#include <globals.h>

int ID_ClassBrowser = wxNewId();
int idMenuJumpToDeclaration = wxNewId();
int idMenuJumpToImplementation = wxNewId();
int idMenuRefreshTree = wxNewId();
int idCBViewInheritance = wxNewId();
int idCBViewModeFlat = wxNewId();
int idCBViewModeStructured = wxNewId();
int idMenuForceReparse = wxNewId();

BEGIN_EVENT_TABLE(ClassBrowser, wxPanel)
	EVT_TREE_ITEM_ACTIVATED(ID_ClassBrowser, ClassBrowser::OnTreeItemDoubleClick)
    EVT_TREE_ITEM_RIGHT_CLICK(ID_ClassBrowser, ClassBrowser::OnTreeItemRightClick)
    EVT_MENU(idMenuJumpToDeclaration, ClassBrowser::OnJumpTo)
    EVT_MENU(idMenuJumpToImplementation, ClassBrowser::OnJumpTo)
    EVT_MENU(idMenuRefreshTree, ClassBrowser::OnRefreshTree)
    EVT_MENU(idMenuForceReparse, ClassBrowser::OnForceReparse)
    EVT_MENU(idCBViewInheritance, ClassBrowser::OnCBViewMode)
    EVT_MENU(idCBViewModeFlat, ClassBrowser::OnCBViewMode)
    EVT_MENU(idCBViewModeStructured, ClassBrowser::OnCBViewMode)
END_EVENT_TABLE()

// class constructor
ClassBrowser::ClassBrowser(wxNotebook* parent, NativeParser* np)
    : wxPanel(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL | wxCLIP_CHILDREN),
    m_Parent(parent),
    m_NativeParser(np),
	m_pParser(0L)
{
    wxBoxSizer* bs = new wxBoxSizer(wxVERTICAL);
	m_Tree = new wxTreeCtrl(this, ID_ClassBrowser);
    bs->Add(m_Tree, 1, wxEXPAND | wxALL);
    SetAutoLayout(TRUE);
    SetSizer(bs);

    m_Parent->AddPage(this, _("Symbols"));
    m_PageIndex = m_Parent->GetPageCount() - 1;
}

// class destructor
ClassBrowser::~ClassBrowser()
{
//	m_Parent->RemovePage(m_PageIndex);
}

void ClassBrowser::SetParser(Parser* parser)
{
	if (parser != m_pParser)
	{
		m_pParser = parser;
		Update();
	}
}

void ClassBrowser::Update()
{
	if (m_pParser)
	{
		wxArrayString treeState;
		::SaveTreeState(m_Tree, m_pParser->GetRootNode(), treeState);
		m_pParser->BuildTree(*m_Tree);
		::RestoreTreeState(m_Tree, m_pParser->GetRootNode(), treeState);
	}
	else
		m_Tree->DeleteAllItems();
}

void ClassBrowser::ShowMenu(wxTreeItemId id, const wxPoint& pt)
{
// NOTE: local variables are tricky! If you build two local menus
// and attach menu B to menu A, on function exit both menu A and menu B
// will be destroyed. But when destroying menu A, menu B will be destroyed
// again. Its already-freed memory will be accessed, generating a segfault.

// A safer approach is to make all menus heap-based, and delete the topmost
// on exit.

    if ( !id.IsOk() )
        return;

#if wxUSE_MENUS
	wxString caption;
    wxMenu *menu=new wxMenu(wxEmptyString);

	ClassTreeData* ctd = (ClassTreeData*)m_Tree->GetItemData(id);
    if (ctd)
    {
        switch (ctd->GetToken()->m_TokenKind)
        {
            case tkConstructor:
            case tkDestructor:
            case tkFunction:
                if (ctd->GetToken()->m_ImplLine != 0 && !ctd->GetToken()->m_ImplFilename.IsEmpty())
                    menu->Append(idMenuJumpToImplementation, _("Jump to &implementation"));
                // intentionally fall through
            default:
                menu->Append(idMenuJumpToDeclaration, _("Jump to &declaration"));
        }
    }

    // ask any plugins to add items in this menu
    Manager::Get()->GetPluginManager()->AskPluginsForModuleMenu(mtClassBrowser, menu, m_Tree->GetItemText(id));

    if (menu->GetMenuItemCount() != 0)
		menu->AppendSeparator();

	wxMenu *sub = new wxMenu(_T(""));
    sub->AppendCheckItem(idCBViewInheritance, _("Show inherited members"));
    sub->AppendSeparator();
    sub->AppendRadioItem(idCBViewModeFlat, _("Flat"));
    sub->AppendRadioItem(idCBViewModeStructured, _("Structured"));

	menu->Append(wxNewId(), _("&View options"), sub);
    menu->Append(idMenuRefreshTree, _("&Refresh tree"));

    if (id == m_Tree->GetRootItem())
    {
        menu->AppendSeparator();
        menu->Append(idMenuForceReparse, _("Re-parse now"));
    }

	menu->Check(idCBViewInheritance, m_pParser ? m_pParser->ClassBrowserOptions().showInheritance : false);
	sub->Check(idCBViewModeFlat, m_pParser ? m_pParser->ClassBrowserOptions().viewFlat : false);
	sub->Check(idCBViewModeStructured, m_pParser ? !m_pParser->ClassBrowserOptions().viewFlat : false);

    PopupMenu(menu, pt);
    delete menu; // Prevents memory leak
#endif // wxUSE_MENUS
}

// events

void ClassBrowser::OnTreeItemRightClick(wxTreeEvent& event)
{
	m_Tree->SelectItem(event.GetItem());
    ShowMenu(event.GetItem(), event.GetPoint());
}

void ClassBrowser::OnJumpTo(wxCommandEvent& event)
{
	wxTreeItemId id = m_Tree->GetSelection();
	ClassTreeData* ctd = (ClassTreeData*)m_Tree->GetItemData(id);
    if (ctd)
    {
        cbProject* prj = Manager::Get()->GetProjectManager()->GetActiveProject();
        if (prj)
        {
            wxString base = prj->GetBasePath();
            wxFileName fname;
            if (event.GetId() == idMenuJumpToImplementation)
                fname.Assign(ctd->GetToken()->m_ImplFilename);
            else
                fname.Assign(ctd->GetToken()->m_Filename);
            fname.Normalize(wxPATH_NORM_ALL & ~wxPATH_NORM_CASE, base);
        	cbEditor* ed = Manager::Get()->GetEditorManager()->Open(fname.GetFullPath());
			if (ed)
			{
                int line;
                if (event.GetId() == idMenuJumpToImplementation)
                    line = ctd->GetToken()->m_ImplLine - 1;
                else
                    line = ctd->GetToken()->m_Line - 1;
				int pos = ed->GetControl()->PositionFromLine(line);
				ed->GetControl()->GotoPos(pos);
			}
        }
    }
}

void ClassBrowser::OnTreeItemDoubleClick(wxTreeEvent& event)
{
	wxTreeItemId id = m_Tree->GetSelection();
	ClassTreeData* ctd = (ClassTreeData*)m_Tree->GetItemData(id);
    if (ctd)
    {
        cbProject* prj = Manager::Get()->GetProjectManager()->GetActiveProject();
        if (prj)
        {
			bool toImp = false;
			switch (ctd->GetToken()->m_TokenKind)
			{
			case tkConstructor:
            case tkDestructor:
            case tkFunction:
                if (ctd->GetToken()->m_ImplLine != 0 && !ctd->GetToken()->m_ImplFilename.IsEmpty())
                    toImp = true;
				break;
			default:
				break;
			}

            wxString base = prj->GetBasePath();
            wxFileName fname;
            if (toImp)
                fname.Assign(ctd->GetToken()->m_ImplFilename);
            else
                fname.Assign(ctd->GetToken()->m_Filename);
            fname.Normalize(wxPATH_NORM_ALL & ~wxPATH_NORM_CASE, base);
        	cbEditor* ed = Manager::Get()->GetEditorManager()->Open(fname.GetFullPath());
			if (ed)
			{
				int line;
                if (toImp)
                    line = ctd->GetToken()->m_ImplLine - 1;
                else
                    line = ctd->GetToken()->m_Line - 1;
				int pos = ed->GetControl()->PositionFromLine(line);
				ed->GetControl()->GotoPos(pos);

				wxFocusEvent ev(wxEVT_SET_FOCUS);
				ev.SetWindow(this);
				ed->GetControl()->AddPendingEvent(ev);
			}
        }
    }
}

void ClassBrowser::OnRefreshTree(wxCommandEvent& event)
{
	Update();
}

void ClassBrowser::OnForceReparse(wxCommandEvent& event)
{
    if (m_NativeParser)
        m_NativeParser->ForceReparseActiveProject();
}

void ClassBrowser::OnCBViewMode(wxCommandEvent& event)
{
	if (!m_pParser)
		return;

	if (event.GetId() == idCBViewInheritance)
		m_pParser->ClassBrowserOptions().showInheritance = event.IsChecked();
	else if (event.GetId() == idCBViewModeFlat)
		m_pParser->ClassBrowserOptions().viewFlat = event.IsChecked();
	else if (event.GetId() == idCBViewModeStructured)
		m_pParser->ClassBrowserOptions().viewFlat = !event.IsChecked();
	else
		return;

	m_pParser->WriteOptions();
	Update();
}


syntax highlighted by Code2HTML, v. 0.9.1