/*
* This file is part of wxSmith plugin for Code::Blocks Studio
* Copyright (C) 2006-2007  Bartlomiej Swiecki
*
* wxSmith 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 3 of the License, or
* (at your option) any later version.
*
* wxSmith 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 wxSmith. If not, see <http://www.gnu.org/licenses/>.
*
*/

#include    <iostream>
#include    "wxsTreeListCtrl.h"

using namespace std;


//-----------------------------------------------------------------------------
// max levels we can reasonably expect a tree to reach
// this is not the max number of items, but max depth of the hierarchy

#define     kLMAX           24

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

namespace
{
#include "images/TreeList16.xpm"
#include "images/TreeList32.xpm"

wxsRegisterItem<wxsTreeListCtrl> Reg (
    _T("wxTreeListCtrl"),           // Class name
    wxsTWidget,                     // Item type
    _T("wxWindows"),                // License
    _T("Ron Collins"),              // Author
    _T("rcoll@theriver.com"),       // Author's email
    _T(""),                         // Item's homepage
    _T("Contrib"),                  // Category in palette
    30,                             // Priority in palette
    _T("TreeListCtrl"),             // Base part of names for new items
    wxsCPP,                         // List of coding languages supported by this item
    1, 0,                           // Version
    wxBitmap (TreeList32_xpm),      // 32x32 bitmap
    wxBitmap (TreeList16_xpm),      // 16x16 bitmap
    false);                         // We do not allow this item inside XRC files



WXS_ST_BEGIN (wxsTreeListCtrlStyles,_T ("wxTR_DEFAULT_STYLE") )
WXS_ST_CATEGORY ("wxTreeListCtrl")
WXS_ST (wxTR_EDIT_LABELS)
WXS_ST (wxTR_NO_BUTTONS)
WXS_ST (wxTR_HAS_BUTTONS)
WXS_ST (wxTR_TWIST_BUTTONS)
WXS_ST (wxTR_NO_LINES)
WXS_ST (wxTR_FULL_ROW_HIGHLIGHT)
WXS_ST (wxTR_LINES_AT_ROOT)
WXS_ST (wxTR_HIDE_ROOT)
WXS_ST (wxTR_ROW_LINES)
WXS_ST (wxTR_HAS_VARIABLE_ROW_HEIGHT)
WXS_ST (wxTR_SINGLE)
WXS_ST (wxTR_MULTIPLE)
#if !wxCHECK_VERSION(3, 0, 0)
WXS_ST (wxTR_EXTENDED)
#endif
WXS_ST (wxTR_DEFAULT_STYLE)
WXS_ST (wxTR_VIRTUAL)
WXS_ST_DEFAULTS()
WXS_ST_END()

WXS_EV_BEGIN (wxsTreeListCtrlEvents)
WXS_EVI (EVT_TREE_BEGIN_DRAG,wxEVT_COMMAND_TREE_BEGIN_DRAG,wxTreeEvent,BeginDrag)
WXS_EVI (EVT_TREE_BEGIN_RDRAG,wxEVT_COMMAND_TREE_BEGIN_RDRAG,wxTreeEvent,BeginRDrag)
WXS_EVI (EVT_TREE_END_DRAG,wxEVT_COMMAND_TREE_END_DRAG,wxTreeEvent,EndDrag)
WXS_EVI (EVT_TREE_BEGIN_LABEL_EDIT,wxEVT_COMMAND_TREE_BEGIN_LABEL_EDIT,wxTreeEvent,BeginLabelEdit)
WXS_EVI (EVT_TREE_END_LABEL_EDIT,wxEVT_COMMAND_TREE_END_LABEL_EDIT,wxTreeEvent,EndLabelEdit)
WXS_EVI (EVT_TREE_DELETE_ITEM,wxEVT_COMMAND_TREE_DELETE_ITEM,wxTreeEvent,DeleteItem)
WXS_EVI (EVT_TREE_GET_INFO,wxEVT_COMMAND_TREE_GET_INFO,wxTreeEvent,GetInfo)
WXS_EVI (EVT_TREE_SET_INFO,wxEVT_COMMAND_TREE_SET_INFO,wxTreeEvent,SetInfo)
WXS_EVI (EVT_TREE_ITEM_ACTIVATED,wxEVT_COMMAND_TREE_ITEM_ACTIVATED,wxTreeEvent,ItemActivated)
WXS_EVI (EVT_TREE_ITEM_COLLAPSED,wxEVT_COMMAND_TREE_ITEM_COLLAPSED,wxTreeEvent,ItemCollapsed)
WXS_EVI (EVT_TREE_ITEM_COLLAPSING,wxEVT_COMMAND_TREE_ITEM_COLLAPSING,wxTreeEvent,ItemCollapsing)
WXS_EVI (EVT_TREE_ITEM_EXPANDED,wxEVT_COMMAND_TREE_ITEM_EXPANDED,wxTreeEvent,ItemExpanded)
WXS_EVI (EVT_TREE_ITEM_EXPANDING,wxEVT_COMMAND_TREE_ITEM_EXPANDING,wxTreeEvent,ItemExpanding)
WXS_EVI (EVT_TREE_ITEM_RIGHT_CLICK,wxEVT_COMMAND_TREE_ITEM_RIGHT_CLICK,wxTreeEvent,ItemRightClick)
WXS_EVI (EVT_TREE_ITEM_MIDDLE_CLICK,wxEVT_COMMAND_TREE_ITEM_MIDDLE_CLICK,wxTreeEvent,ItemMiddleClick)
WXS_EVI (EVT_TREE_SEL_CHANGED,wxEVT_COMMAND_TREE_SEL_CHANGED,wxTreeEvent,SelectionChanged)
WXS_EVI (EVT_TREE_SEL_CHANGING,wxEVT_COMMAND_TREE_SEL_CHANGING,wxTreeEvent,SelectionChanging)
WXS_EVI (EVT_TREE_KEY_DOWN,wxEVT_COMMAND_TREE_KEY_DOWN,wxTreeEvent,KeyDown)
WXS_EVI (EVT_TREE_ITEM_GETTOOLTIP,wxEVT_COMMAND_TREE_ITEM_GETTOOLTIP,wxTreeEvent,ItemGetToolTip)
WXS_EVI (EVT_TREE_ITEM_MENU,wxEVT_COMMAND_TREE_ITEM_MENU,wxTreeEvent,ItemMenu)
WXS_EV_END()
}

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

wxsTreeListCtrl::wxsTreeListCtrl (wxsItemResData* Data) :
    wxsWidget (
        Data,
        &Reg.Info,
        wxsTreeListCtrlEvents,
        wxsTreeListCtrlStyles)
{
    m_ColCount = 5;
    m_ColWidth = 64;
    m_ReadOnly = false;

    const wxString cols(_("!Define Column Titles\n"
                          "!Text lines that begin with \"!\" are ignored, but blank\n"
                          "!lines are significant (a blank line is a blank title for\n"
                          "!a column).\n"
                          "First\n"
                          "Second\n"
                          "Third\n"
                          "Fourth\n"
                          "Fifth"));

    m_ColTitles = wxSplit(cols, '\n');

    const wxString tree(_("!Define node data for a wxTreeCtrl.\n"
                          "!Note that all lines that start with a \"!\" are comment lines and ignored.\n"
                          "!Also, all empty lines are ignored (a line of spaces is not an empty line).\n"
                          "!\n"
                          "!The node data is organized similar to a Tabbed Hierarchical Values (THV) file,\n"
                          "!such that each line of text represents one label for a single tree node.\n"
                          "!The number of leading TAB characters indicate the indentation level of the\n"
                          "!tree node.\n"
                          "!\n"
                          "!Space (or blank) characters are not used the same as TAB characters; if your\n"
                          "!editor substitutes spaces for TABs, use the \"TAB Char\" attribute to change\n"
                          "!the character used to indicate the tree node level.\n"
                          "!\n"
                          "!Following is a sample tree data definition:\n"
                          "\n"
                          "First level, no tabs\tSecond Column\tThird Column\tFourth Column\tFifth Column\n"
                          "\tSecond level, 1 tab\tSecond Column\tThird Column\tFourth Column\tFifth Column\n"
                          "\t\tThird level, 2 tabs\tSecond Column\tThird Column\tFourth Column\tFifth Column\n"
                          "First level again, no tabs\tSecond Column\tThird Column\tFourth Column\tFifth Column\n"
                          "\tSecond level again, 1 tab\tSecond Column\tThird Column\tFourth Column\tFifth Column\n"
                          "\tAnother second level, 1 tab\tSecond Column\tThird Column\tFourth Column\tFifth Column\n"
                          "\t\tThird level again, 2 tabs\tSecond Column\tThird Column\tFourth Column\tFifth Column\n"
                          ""));

    m_TreeData = wxSplit(tree, '\n');
}

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

void wxsTreeListCtrl::OnBuildCreatingCode()
{

    if (GetLanguage() == wxsCPP)
    {
        AddHeader (_T("<wx/treelistctrl.h>"),GetInfo().ClassName,0);
        Codef(_T("%C(%W, %I, %P, %S, %T, %V, %N);\n"));
        BuildItemCode();
        BuildSetupWindowCode();
    }
    else
    {
        wxsCodeMarks::Unknown(_T("wxsTreeCtrl::OnBuildCreatingCode"),GetLanguage());
    }
}

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

wxObject* wxsTreeListCtrl::OnBuildPreview (wxWindow* Parent,long Flags)
{
    wxTreeListCtrl*  Preview;

    Preview = new wxTreeListCtrl (Parent, GetId(), Pos (Parent), Size (Parent), Style() );
    PreviewItemCode (Preview);
    SetupWindow (Preview, Flags);

    return Preview;
}

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

void wxsTreeListCtrl::OnEnumWidgetProperties (long Flags)
{
    static const long    TabValues[] = {        0,       1,       2,  0};
    static const wxChar* TabNames[]  = {_T("TAB"), _T("+"), _T(">"),  0};


// how many columns?

    WXS_LONG (wxsTreeListCtrl, m_ColCount, _("Column Count"), _T("colcount"), 1);

// default size of each column

    WXS_LONG (wxsTreeListCtrl, m_ColWidth, _("Column Width"), _T("colwidth"), 64);

// select the char to delimit the node levels

    WXS_ENUM (wxsTreeListCtrl, m_TabChar, _("TAB Char"), _T("tabchar"), TabValues, TabNames, 0);

// pre-load column titles

    WXS_ARRAYSTRING (wxsTreeListCtrl, m_ColTitles, _("Column Titles"), _T("coltitles"), _T("item"));

// allow user to pre-load tree node data

    WXS_ARRAYSTRING (wxsTreeListCtrl, m_TreeData, _("Tree Items"), _T("treedata"), _T("node"));

// is this tree read-only?

    WXS_BOOL (wxsTreeListCtrl, m_ReadOnly, _("Read Only"), _T("readonly"), false);

}

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

void wxsTreeListCtrl::BuildItemCode (void)
{
    int             i,n;
    int             j,k;
    wxString        idname;
    wxString        ss, tt, pp;
    int             id[kLMAX];
    wxArrayString   items;


// enuough columns?

    if (m_ColCount < 1)
        m_ColCount = 1;

// add all the col info

    n = 0;
    for (i=0; i < (int)m_ColTitles.GetCount(); i++)
    {
        if (n >= m_ColCount)
            break;

        ss = m_ColTitles.Item (i);
        ss.Trim (true);
        ss.Trim (false);
        if (ss[0] == '!')
            continue;

        Codef (_T("%AAddColumn(%t, %d);\n"), ss.wx_str(), static_cast<int>(m_ColWidth));
        n += 1;
    }

// count number of nodes, add 1 for the root

    n = 0;
    for (i=0; i < (int)m_TreeData.GetCount(); i++)
    {
        ss = m_TreeData.Item (i);
        if (ss.Len() == 0)
            continue;

        if (ss[0] == '!')
            continue;

        n += 1;
    }

    n += 1;

// name of the array of node IDs

    idname = GetVarName() + _("ID");

// add declaration for that many IDs

    ss.Printf (_T("wxTreeItemId    %s[%d];"), idname.wx_str(), n);
    AddDeclaration (ss);

// make sure there is no false readings

    for (i = 0; i < kLMAX; i++)
        id[i] = 0;

// make our own root item

    Codef (_T("%s[0] = %AAddRoot(_(\"(root)\"));\n"), idname.wx_str() );
    id[0] = 0;

// and now each item in the tree data list

    k = 0;
    for (i=0; i < (int)m_TreeData.GetCount(); i++)
    {

// the string

        ss = m_TreeData.Item (i);

// some lines are skipped over

        if (ss.Len() == 0)
            continue;

        if (ss[0] == '!')
            continue;

// get the level and the label texts

        ParseItems (ss, n, items);
        if (n <= 0)
            n = 1;

// ID of the new item

        k += 1;
        id[n] = k;
        ss.Printf (_T("%s[%d]"), idname.c_str(), k);

// the parent item

        j = id[n-1];
        pp.Printf (_T("%s[%d]"), idname.c_str(), id[n-1]);

// base text of the new item

        if (items.GetCount() == 0)
            tt = _T("");
        else
            tt = items.Item (0);

// make the base item

        Codef (_T("%s = %AAppendItem(%s, _(\"%s\"));\n"), ss.wx_str(), pp.wx_str(), tt.wx_str() );

// now each of the sub-items

        for (j=1; j < (int)items.GetCount(); j++)
        {
            if (j >= m_ColCount)
                break;

            tt = items.Item (j);
            Codef (_T("%ASetItemText(%s, %d, %t);\n"), ss.wx_str(), j, tt.wx_str() );
        }
    }

// show everything

    Codef (_T("%AExpandAll(%s[0]);\n"), idname.wx_str() );

// read-only or editable?

    for (i=0; i<m_ColCount; i++)
    {
        Codef (_T("%ASetColumnEditable(%d, %b);\n"), i, m_ReadOnly);
    }


}

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

void wxsTreeListCtrl::PreviewItemCode (wxTreeListCtrl* inPreview)
{
    int             i,n;
    int             j;
    wxString        ss, tt;
    wxTreeItemId    id[kLMAX];
    wxArrayString   items;

// enuough columns?

    if (m_ColCount < 1)
        m_ColCount = 1;

// add all the col info

    n = 0;
    for (i=0; i < (int)m_ColTitles.GetCount(); i++)
    {
        if (n >= m_ColCount)
            break;

        ss = m_ColTitles.Item (i);
        ss.Trim (true);
        ss.Trim (false);
        if (ss[0] == '!')
            continue;

        inPreview->AddColumn (ss, m_ColWidth);
        n += 1;
    }

// make sure there is no false readings

    for (i=0; i < kLMAX; i++)
        id[i].Unset();

// make our own root item

    id[0] = inPreview->AddRoot(_("(root)"));

// and now each item in the tree data list

    for (i=0; i < (int)m_TreeData.GetCount(); i++)
    {

// the string

        ss = m_TreeData.Item (i);

// some lines are skipped over

        if (ss.Len() == 0)
            continue;

        if (ss[0] == '!')
            continue;

// get the level and the label texts

        ParseItems (ss, n, items);
        if (n <= 0)
            n = 1;

// make a new item, checking for valid parent

        if (! id[n-1].IsOk() )
            return;

        if (items.GetCount() == 0)
            tt = _T("");
        else
            tt = items.Item(0);

        id[n] = inPreview->AppendItem (id[n-1], tt);

// then all the sub-items

        for (j=1; j < (int)items.GetCount(); j++)
        {
            if (j >= m_ColCount)
                break;

            tt = items.Item (j);
            inPreview->SetItemText (id[n], j, tt);
        }
    }

// show everything

    inPreview->ExpandAll (id[0]);

// read only?

    for (i=0; i<m_ColCount; i++)
    {
        inPreview->SetColumnEditable (i, m_ReadOnly);
    }

}

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

void wxsTreeListCtrl::ParseItems (wxString inSource, int& outLevel, wxArrayString& outItems)
{
    int         i,n;
    int         tab;
    wxString    ss, tt;

// the correct tab char

    if      (m_TabChar == 0) { tab = '\t'; }
    else if (m_TabChar == 1) { tab = '+'; }
    else if (m_TabChar == 2) { tab = '>'; }
    else                     { tab = '\t'; }

// working copy

    ss = inSource;

// delete old junk

    outItems.Clear();

// count the level depth; level is always 1 higher than tab count

    n = 0;
    while ( (ss.Len() > 0) && (ss[0] == tab) )
    {
        n += 1;
        ss.erase (0, 1);
    }
    
    outLevel = n + 1;

// copy over labels

    i = 0;
    while (i < (int)ss.Len() )
    {
        tt = _T("");
        while ( (i < (int)ss.Len() ) && (ss[i] != tab) )
            tt += ss[i++];

        outItems.Add (tt);
        if (i < (int)ss.Len() )
            i += 1;
    }

// and the last item

    tt = ss;
    tt.Trim (true);
    tt.Trim (false);
    outItems.Add (tt);
}
