//htmTools.cpp

#include "htmTools.h"

IMPLEMENT_APP(MainApp)

//---------------- main app -------------------------
bool MainApp::OnInit()
{
    MainFrame *frame = new MainFrame(_T("htmTools"));
    frame->Show(TRUE);
    SetTopWindow(frame);
    return TRUE;
}

int MainApp::OnExit()
{
	return 0;
}


//---------------- main frame -------------------------
BEGIN_EVENT_TABLE(MainFrame, wxFrame)
    EVT_MENU(ID_QUIT, MainFrame::OnQuit)
    EVT_MENU(ID_SET, MainFrame::OnToolSet)
    EVT_MENU(ID_INFO, MainFrame::OnInfo)
    EVT_TREE_SEL_CHANGED(ID_TREE, MainFrame::OnTreeChanged)
    EVT_NOTEBOOK_PAGE_CHANGED(ID_BOOK, MainFrame::OnNotebookPage)
END_EVENT_TABLE()


MainFrame::MainFrame(const wxString& title) : wxFrame(NULL, wxID_ANY, title)
{
    //set defaults
    wxString setDir = wxGetCwd();

#ifdef __WXGTK__
    //change to user home dir in linux
    setDir = wxFileName::GetHomeDir()+wxFileName::GetPathSeparator()+_T(".htmTools");
    if (!wxFileName::DirExists(setDir)) wxMkdir(setDir);
#endif

    //read values stored in settings.xml
    wxString setFile = setDir+wxFileName::GetPathSeparator()+_T("settings.xml");
    toolDlg *dialog = new toolDlg(this, -1, _T(""), setFile);
    items = dialog->GetItems();
    
    CreateGUIControls();
}

MainFrame::~MainFrame() 
{ 
}


void MainFrame::CreateGUIControls()
{
    //main window
    SetBackgroundColour(wxColour(*wxWHITE));
    SetSize(800,600);
    
    //icon
    SetIcon(wxIcon(tools_xpm));
    
    //menu
    wxMenu *menuFile = new wxMenu;
    menuFile->Append(ID_QUIT, _T("E&xit") );
    
    wxMenu *menuSet = new wxMenu;
    menuSet->Append(ID_SET, _T("&Tools") );
    
    wxMenu *menuAbout = new wxMenu;
    menuAbout->Append(ID_INFO, _T("A&bout") );

    wxMenuBar *menuBar = new wxMenuBar;
    menuBar->Append( menuFile, _T("&File") );
    menuBar->Append( menuSet, _T("&Settings") );
    menuBar->Append( menuAbout, _T("&Info") );

    SetMenuBar( menuBar );

    //widgets
    MainSplitter = new wxSplitterWindow(this, -1, wxPoint(0,0), wxSize(800,600));
    MainTree = new wxTreeCtrl(MainSplitter, ID_TREE, wxPoint(5,5), wxSize(120,600));
	rootId = MainTree->AddRoot(_T("htmTools"));
	MainBook = new wxNotebook(MainSplitter, ID_BOOK, wxPoint(5,5), wxSize(600,600));
  
    //attach tools
    pages.Empty();
    comments.Empty();
    
    for (size_t i=0; i<items.GetCount(); i++)
    {
        //set pages
        wxString line=items[i];
        wxString page=line.BeforeFirst(_T('\t'));
        line=line.AfterFirst(_T('\t'));
        
        MainTree->AppendItem(rootId, page);
        pages.Add(page);
        
        //pattern and check for subdir search
        wxString patt=line.BeforeFirst(_T('\t'));
        line=line.AfterFirst(_T('\t'));
        wxString subchk=line.BeforeFirst(_T('\t'));
        line=line.AfterFirst(_T('\t'));
        bool chk; if (subchk==_T("1")) chk=true; else chk=false;
       
        if (i==0)   //first tool is internal
        {
            chTxt *PanelChTxt = new chTxt();
            PanelChTxt->Create(MainBook, -1, wxPoint(5,5), wxSize(600,600));
            PanelChTxt->SetPattern(patt, chk);
            PanelChTxt->CreateControls();
            MainBook->AddPage(PanelChTxt, page, true);
            comments.Add(_T("changes text in multiple files"));
        }
        else
        {
            xTool *Tool = new xTool();
            Tool->Create(MainBook, -1, wxPoint(5,5), wxSize(600,600));
            Tool->SetPattern(patt, chk);
           
            //set tool
            wxString tool=line.BeforeFirst(_T('\t'));
            line=line.AfterFirst(_T('\t'));
            wxString i_opt=line.BeforeFirst(_T('\t'));
            line=line.AfterFirst(_T('\t'));
            wxString o_opt=line.BeforeFirst(_T('\t'));
            line=line.AfterFirst(_T('\t'));
            Tool->SetTool(tool, i_opt, o_opt);
            
            //set output file pattern and subdir
            wxString o_patt=line.BeforeFirst(_T('\t'));
            line=line.AfterFirst(_T('\t'));
            wxString o_sub=line.BeforeFirst(_T('\t'));
            line=line.AfterFirst(_T('\t'));
            Tool->SetOutFile(o_patt, o_sub);
            
            //set output options
            wxString p_size=line.BeforeFirst(_T('\t'));
            line=line.AfterFirst(_T('\t'));
            wxString p_ext=line.BeforeFirst(_T('\t'));
            line=line.AfterFirst(_T('\t'));
            bool chk_s; if (p_size==_T("1")) chk_s=true; else chk_s=false;
            bool chk_x; if (p_ext==_T("1")) chk_x=true; else chk_x=false;
            Tool->SetOutOpts(chk_s, chk_x);
            
            //set comments
            wxString cmt=line.BeforeFirst(_T('\t'));
            comments.Add(cmt);
 
            Tool->CreateControls();
            MainBook->AddPage(Tool, page, false);
        }
    }
	MainTree->Expand(rootId);
    MainSplitter->SplitVertically(MainTree, MainBook, 160);
	
    //statusbar
    CreateStatusBar();
    SetStatusText(comments.Item(0));
}


void MainFrame::OnQuit(wxCommandEvent& WXUNUSED(event))
{
    Close(TRUE);
}


void MainFrame::OnInfo(wxCommandEvent &event)
{
    wxMessageBox(_T("htmlTools 0.1.1\nhttp://htmtools.sourceforge.net/"), _T("About"), wxOK , this);
}


void MainFrame::OnToolSet(wxCommandEvent& event)
{
    wxString setDir = wxGetCwd();
    
#ifdef __WXGTK__
    //change to user home dir in linux
    setDir = wxFileName::GetHomeDir()+wxFileName::GetPathSeparator()+_T(".htmTools");
#endif

     //read values stored in settings.xml
    wxString setFile = setDir+wxFileName::GetPathSeparator()+_T("settings.xml");
    toolDlg *dialog = new toolDlg(this, -1, _T("Tool Settings"), setFile);
    
    if (dialog->ShowModal()) 
    {
        wxArrayString get_items = dialog->GetItems();
        if (items!=get_items)
        {
            items=get_items;
            size_t sel = MainBook->GetSelection();     //remember selection           
            this->DestroyChildren();
            this->CreateGUIControls();
            if (MainBook->GetPageCount()>sel) MainBook->SetSelection(sel);
        }
    }
}


void MainFrame::OnTreeChanged(wxTreeEvent& event)
{
    wxTreeItemId itemId = event.GetItem();

    if (itemId!=rootId)
    {
        wxString label = MainTree->GetItemText(itemId);
        wxString page;

        int nr=0;
        while (page!=label)
        {
            page=pages[nr];
            nr++;
        }

        MainBook->SetSelection(nr-1);
        SetStatusText(comments.Item(nr-1));
    }
    else SetStatusText(_T("htmTools"));
}


void MainFrame::OnNotebookPage(wxNotebookEvent& event)
{
    int newPage = event.GetSelection();
    wxString page = pages[newPage];
    wxString label;

    wxTreeItemId itemId;
    wxTreeItemIdValue dummy;

    itemId=MainTree->GetFirstChild(rootId, dummy);
    label=MainTree->GetItemText(itemId);

    while (page!=label)
    {
        itemId=MainTree->GetNextChild(rootId, dummy);
        label=MainTree->GetItemText(itemId);
    }

    MainTree->SelectItem(itemId);
}


//---------------- tool settings dialog ----------------
BEGIN_EVENT_TABLE(toolDlg, wxDialog)
    EVT_BUTTON(ID_BT_OK, toolDlg::OnClose)
    EVT_BUTTON(ID_BT_ESC, toolDlg::OnClose)
    EVT_BUTTON(ID_BT_OPEN, toolDlg::OnBtOpen)
    EVT_BUTTON(ID_BT_INS, toolDlg::OnBtIns)
    EVT_BUTTON(ID_BT_DEL, toolDlg::OnBtDel)
    EVT_GRID_SELECT_CELL(toolDlg::OnGrid)
END_EVENT_TABLE()

toolDlg::toolDlg(wxFrame *parent, wxWindowID id, const wxString &title, wxString file)
        : wxDialog(parent, id, title, wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
{
    SetBackgroundColour(wxColour(*wxWHITE));
    wxPoint pos = wxGetMousePosition();
    SetSize(pos.x, pos.y, 200, 200);

    //buttons
    wxButton *BtOK, *BtEsc;
    wxPanel *PanelBt = new wxPanel(this, -1, wxDefaultPosition, wxSize(140,40));
    BtOK = new wxButton(PanelBt, ID_BT_OK, _T("OK"), wxPoint(0,0), wxSize(60,25));
    BtEsc = new wxButton(PanelBt, ID_BT_ESC, _T("Cancel"), wxPoint(70,0), wxSize(60,25));

    wxButton *BtIns, *BtDel;
    wxPanel *PanelBtChg = new wxPanel(this, -1, wxDefaultPosition, wxSize(160,40));
    BtOpen = new wxBitmapButton(PanelBtChg, ID_BT_OPEN, wxBitmap(open_xpm), wxPoint(0,0), wxSize(40,25));
    BtIns = new wxButton(PanelBtChg, ID_BT_INS, _T("+"), wxPoint(60,0), wxSize(40,25));
    BtDel = new wxButton(PanelBtChg, ID_BT_DEL, _T("-"), wxPoint(110,0), wxSize(40,25));

    //read or create file, set defaults
    xmLoc = file;
    wxArrayString headers, values;
    if (!wxFile::Exists(file)) CreateDefaultFile(file);
    ReadXmlFile(file, headers, values);

    //create grid
    int ht=40+values.GetCount()*20; 
    if (ht<100) ht=100;
    grid = new wxGrid(this, ID_GRID, wxPoint(0,0), wxSize(600,ht));
    grid->CreateGrid(0,0,wxGrid::wxGridSelectCells);
    grid->SetLabelBackgroundColour(*wxWHITE);
    grid->SetRowLabelSize(20);
    grid->SetColLabelSize(20);

    grid->AppendCols(int(headers.GetCount()));
    grid->AppendRows(int(values.GetCount()));

    for (size_t col=0; col<headers.GetCount(); col++) grid->SetColLabelValue(col, headers[col]);
    for (size_t row=0; row<values.GetCount(); row++)
    {
        int col=0;
        wxString line = values[row];
        while (!line.IsEmpty())
        {
            wxString val = line.BeforeFirst(_T('\t'));
            grid->SetCellValue(row, col, val);
            if (col==2 || col==8 ||col==9)
            {
                grid->SetCellAlignment(wxALIGN_CENTRE, row, col);
                grid->SetCellRenderer(row, col, new wxGridCellBoolRenderer);
                grid->SetCellEditor(row, col, new wxGridCellBoolEditor);
            }
            line=line.AfterFirst(_T('\t'));
            col++;
        }
    }
    grid->AutoSizeColumns(false);

    //set internal tool readonly
    for (size_t col=3; col<headers.GetCount(); col++) grid->SetReadOnly(0, col);

    wxBoxSizer *Btsizer = new wxBoxSizer(wxHORIZONTAL);
    Btsizer->Add(PanelBt, 1, wxALIGN_CENTRE | wxALL, 10);
    Btsizer->Add(PanelBtChg, 0, wxALIGN_RIGHT | wxALL, 10);

    wxBoxSizer *Vsizer = new wxBoxSizer(wxVERTICAL);
    Vsizer->Add(grid, 1, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, 10);
    Vsizer->Add(Btsizer, 0, wxEXPAND | wxALL, 10);

    SetSizer(Vsizer);
    Vsizer->SetSizeHints(this);
}


void toolDlg::OnClose(wxCommandEvent &event)
{
    if (event.GetId()==ID_BT_OK) WriteXmlFile();
    Close(TRUE);
}


void toolDlg::OnGrid(wxGridEvent &event)
{
    event.Skip();
    int row=event.GetRow();
    int col=event.GetCol();
    if (col==3 && row!=0) BtOpen->Show(true); else BtOpen->Show(false);
}


void toolDlg::OnBtOpen(wxCommandEvent &event)
{
    int row = grid->GetGridCursorRow();
    int col = grid->GetGridCursorCol();

    wxFileDialog *openFileDialog = new wxFileDialog(this,_T("Choose Tool:"));

	if (openFileDialog->ShowModal() == wxID_OK)
	{
		wxFileName filename(openFileDialog->GetPath());
        filename.MakeRelativeTo(wxGetCwd());
        if (filename.GetPath().Find(_T(".."))!=wxNOT_FOUND) filename.MakeAbsolute();
        grid->SetCellValue(row, col, filename.GetFullPath());
    }
}


void toolDlg::OnBtIns(wxCommandEvent &event)
{
    int row = grid->GetGridCursorRow();
    grid->InsertRows(row+1);

    //clone previous
    for (int col=0; col<grid->GetNumberCols(); col++)
    {
        wxString val = grid->GetCellValue(row, col);
        if (col==0) val+=_T("+");
        grid->SetCellValue(row+1, col, val);
        if (col==2 || col==8 ||col==9)
        {
            grid->SetCellRenderer(row+1, col, new wxGridCellBoolRenderer);
            grid->SetCellEditor(row+1, col, new wxGridCellBoolEditor);
            grid->SetCellAlignment(wxALIGN_CENTRE, row+1, col);
        }
    }

    //internal tool cannot be cloned
    if (row==0)
    {
        grid->SetCellValue(row+1, 0, _T("New Tool"));
        grid->SetCellValue(row+1, 3, _T("tool..."));
        grid->SetCellValue(row+1, 10, _T(""));
    }
}


void toolDlg::OnBtDel(wxCommandEvent &event)
{
    int row = grid->GetGridCursorRow();
    if (row!=0)
    {
        int del = wxMessageBox(_T("Delete ")+grid->GetCellValue(row,0)+_T(" ?"), _T("Confirm"), wxYES_NO , this);
        if (del==wxYES) grid->DeleteRows(row);
    }
    else wxMessageBox(_T("Deletion of ")+grid->GetCellValue(row,0)+_T(" not possible"), _T("Internal Tool"), wxOK , this);
}


void toolDlg::ReadXmlFile(wxString file, wxArrayString &headers, wxArrayString &values)
{
    wxXmlDocument doc;
    if (doc.Load(file))
    {
        headers.Empty();
        values.Empty();
        wxXmlNode *child = doc.GetRoot()->GetChildren();

        //get headers
        wxXmlNode *subchild = child->GetChildren();
        while (subchild)
        {
            headers.Add(subchild->GetName());
            subchild = subchild->GetNext();
        }

        //get values
        while (child)
        {
            wxString val;
            wxXmlNode *subchild = child->GetChildren();
            while (subchild)
            {
                val+=subchild->GetNodeContent()+_T('\t');
                subchild = subchild->GetNext();
            }

            values.Add(val);
            child = child->GetNext();
        }
    }
}


void toolDlg::WriteXmlFile()
{
    wxArrayString labels;
    for (int i=0; i<grid->GetNumberCols(); i++) labels.Add(grid->GetColLabelValue(i));
    wxArrayString values = GetItems();

    wxXmlDocument doc;
	wxXmlNode *root = new wxXmlNode(wxXML_ELEMENT_NODE, _T("tools"));
	doc.SetRoot(root);

	for (size_t i=0; i<values.GetCount(); i++)
	{
	    wxString line = values[i];
        wxXmlNode *child = new wxXmlNode(wxXML_ELEMENT_NODE, _T("item"));
        for (size_t j=0; j<labels.GetCount(); j++)
	    {
            wxXmlNode *subchild = new wxXmlNode(wxXML_ELEMENT_NODE, labels[j]);
            subchild->AddChild(new wxXmlNode(wxXML_TEXT_NODE, _T(""),line.BeforeFirst(_T('\t'))));
            line=line.AfterFirst(_T('\t'));
            child->AddChild(subchild);
        }
	    root->AddChild(child);
    }
	doc.Save(xmLoc);
}


wxString toolDlg::ChkXML(wxString line)
{
    wxString cStr(line);
    cStr.Replace(_T("<"), _T("&lt;"));
    cStr.Replace(_T(">"), _T("&gt;"));
    cStr.Replace(_T("&"), _T("&amp;"));
    cStr.Replace(_T("\""), _T("&quot;"));
    cStr.Replace(_T("'"), _T("&apos;"));
    cStr.Replace(_T("´"), _T("&apos;"));
    cStr.Replace(_T("`"), _T("&apos;"));
    return cStr;
}


void toolDlg::CreateDefaultFile(wxString file)
{
    //create default xml file if settings.xml is missing
    wxTextFile *xmlFile = new wxTextFile;
    xmlFile->Create(file);
    wxArrayString xmlBody;
	xmlBody.Add(_T("<?xml version=\"1.0\" standalone=\"yes\"?>"));
    xmlBody.Add(_T("<tools>"));
    xmlBody.Add(_T("  <item>"));
    xmlBody.Add(_T("	<page>ChangeText</page>"));
    xmlBody.Add(_T("	<pattern>*.htm*</pattern>"));
    xmlBody.Add(_T("	<subdirs>1</subdirs>"));
    xmlBody.Add(_T("	<tool />"));
    xmlBody.Add(_T("	<input_opts />"));
    xmlBody.Add(_T("	<output_opts />"));
    xmlBody.Add(_T("	<output_patt />"));
    xmlBody.Add(_T("	<create_subdir />"));
    xmlBody.Add(_T("	<prt_size />"));
    xmlBody.Add(_T("	<ext_output />"));
    xmlBody.Add(_T("	<comment>internal</comment>"));
    xmlBody.Add(_T("  </item>"));
    xmlBody.Add(_T("</tools>"));
    for (size_t i=0; i<xmlBody.GetCount(); i++) xmlFile->AddLine(xmlBody[i]);
    xmlFile->Write();
    xmlFile->Close();
}


wxArrayString toolDlg::GetItems()
{
    //check for name duplicates
    wxArrayString names;
    for (int row=0; row<grid->GetNumberRows(); row++)
    {
        wxString name = grid->GetCellValue(row,0);
        if (IsInArray(name, names))
        {
            while (IsInArray(name, names)) name+=_T("+");
            grid->SetCellValue(row,0,name);  
        }  
        names.Add(name);        
    }

    //read values from grid
    wxArrayString text;
    for (int row=0; row<grid->GetNumberRows(); row++)
    {
        wxString line;
        for (int col=0; col<grid->GetNumberCols(); col++)
            line+=grid->GetCellValue(row,col)+_T('\t');
        text.Add(line);
    }
    
    return text;
}


bool toolDlg::IsInArray(wxString s, wxArrayString a)
{
    bool fd(false);
    for (size_t i=0; i<a.GetCount(); i++) if (s==a[i]) fd=true;
    return fd;
}
    

//---------------- xTool - run external command line tool ----------------
BEGIN_EVENT_TABLE(xTool, wxPanel)
    EVT_BUTTON(ID_BT_OPEN, xTool::OnOpenDir)
    EVT_TEXT(ID_TC_FILE, xTool::OnTcChanged)
    EVT_TEXT(ID_TC_PATT, xTool::OnTcChanged)
    EVT_CHECKBOX(ID_CHK_SUB, xTool::OnTcChanged) 
    EVT_BUTTON(ID_BT_GO, xTool::OnAction)
END_EVENT_TABLE()


void xTool::CreateControls()
{
    //widgets
	wxStaticText *TxtHeader;
    TxtHeader = new wxStaticText(this, -1, _T("Select Folder:"), wxPoint(0,0), wxDefaultSize);

	TcFile = new wxTextCtrl(this, ID_TC_FILE, _T(""),wxPoint(0,0), wxSize(300,25));
	DragContent = new dndFile(TcFile);
	SetDropTarget(DragContent);
	TcPatt = new wxTextCtrl(this, ID_TC_PATT, Pattern, wxPoint(0,0), wxSize(50,25));
	wxBitmapButton *BtOpen = new wxBitmapButton(this, ID_BT_OPEN, wxBitmap(open_xpm), wxPoint(0,0), wxSize(40,25));
	ChkSub = new wxCheckBox(this, ID_CHK_SUB, _T("subdirs"),  wxPoint(0,0), wxSize(80,25));
    ChkSub->SetValue(chkSub);

    wxBoxSizer *H1sizer = new wxBoxSizer(wxHORIZONTAL);
    H1sizer->Add(TcFile, 1, wxALL, 10);
    H1sizer->Add(TcPatt, 0, wxALL, 10);
    H1sizer->Add(BtOpen, 0, wxALL, 10);
    H1sizer->Add(ChkSub, 0, wxALL, 10);

    wxButton *BtGo;
    wxPanel *PanelS = new wxPanel(this, -1, wxPoint(0,0), wxSize(540,35));
    BtGo = new wxButton(PanelS, ID_BT_GO, _T("Go!"), wxPoint(10,0), wxSize(60,25));
    wxStaticText *ToolHeader;
    ToolHeader = new wxStaticText(PanelS, -1, _T(""), wxPoint(90,5), wxDefaultSize);

    TcRes = new wxTextCtrl(this, -1, _T(""),wxPoint(0,00),wxSize(540,200),wxTE_MULTILINE);
   
    wxBoxSizer *Vsizer = new wxBoxSizer(wxVERTICAL);
    Vsizer->Add(TxtHeader, 0, wxEXPAND | wxTOP | wxLEFT | wxRIGHT, 30);
    Vsizer->Add(H1sizer, 0, wxEXPAND | wxLEFT | wxRIGHT, 20);
    Vsizer->Add(PanelS, 0, wxEXPAND | wxLEFT | wxRIGHT, 20);
    Vsizer->Add(TcRes, 1, wxEXPAND | wxALL, 30);

    SetSizer(Vsizer);
    Vsizer->SetSizeHints(this);

    //check if tool exists
    wxString cmd;
    chkTool = wxFile::Exists(Tool);
    if (!chkTool) cmd=_T("Couldn\'t find: ")+wxFileName(Tool).GetFullName()+_T(" - check path in Settings!\n");
    else 
    {
        cmd = wxFileName(Tool).GetFullName();
        if (!iOpts.IsEmpty()) cmd+=_T(" ")+iOpts;
        cmd+=_T(" *");
        if (!oOpts.IsEmpty()) cmd+=_T(" ")+oOpts;
        if (!oSub.IsEmpty())  cmd+=_T(" ")+oSub + wxFileName::GetPathSeparator();
        if (!oPatt.IsEmpty()) if (!oSub.IsEmpty()) cmd+=oPatt; else cmd+=_T(" ")+oPatt;
    }
    ToolHeader->SetLabel(cmd);
    
}


void xTool::OnOpenDir(wxCommandEvent& event)
{
    wxString dirName = TcFile->GetValue();
    wxDirDialog *openDirDialog = new wxDirDialog(this, _T("Choose Folder:"), dirName);

	if (openDirDialog->ShowModal() == wxID_OK)
	{
        TcFile->Clear();
        TcFile->AppendText(openDirDialog->GetPath());
    }
}


void xTool::OnTcChanged(wxCommandEvent& event)
{
    wxString dirName = TcFile->GetValue();
    if (!dirName.IsEmpty() && wxDir::Exists(dirName)) SelectFiles(dirName);
}


void xTool::SelectFiles(wxString dirName) 
{   
    TcRes->Clear();
    TcRes->AppendText(_T("Searching: ")+dirName+_T(" ...\n"));

    int sub = wxDIR_DIRS;
    if (!ChkSub->IsChecked()) sub=0;

    files.Empty();
    wxDir::GetAllFiles(dirName, &files, TcPatt->GetValue(), wxDIR_FILES | sub);
    wxString nf; nf.Printf(_T("%d"), files.GetCount());
    TcRes->AppendText(_T("\nFound ")+nf+_T(" Files:\n\n"));

    for (size_t i=0; i<files.GetCount(); i++)
    {
        wxFileName file(files[i]);
        wxString path=file.GetFullPath();
        path.Replace(dirName, _T(""), false);
        TcRes->AppendText(path+_T("\n"));
    }
    TcRes->SetInsertionPoint(0);

    wxLogStatus(nf + _T(" files selected"));
}


void xTool::OnAction(wxCommandEvent& event)
{
    if (chkTool)
    {
        int n = files.GetCount();
        if (n>0)
        {
            TcRes->Clear();
            wxLogStatus(_T("processing files, please wait ..."));
            wxString nf, nr;
            nf.Printf(_T("%d"),n);
            
            for (size_t i=0; i<files.GetCount(); i++)
            {
                nr.Printf(_T("%d"),i+1);
                wxFileName fname(files[i]);
                
                TcRes->AppendText(_T("File ")+nr+_T("/")+nf+_T(": ")+fname.GetFullName()+_T("\n------------------------------------------------------------\n"));
                wxString i_size = fname.GetHumanReadableSize();

                //create command
                wxString cmd = CreateCommand(fname);
                TcRes->AppendText(cmd+_T("\n"));

                wxArrayString output, extd;
                wxExecute(cmd, output, extd);

                //extended output if checked
                if (chkExt)
                    for (size_t j=0; j<extd.GetCount(); j++)
                        if (!extd[j].IsEmpty()) output.Add(extd[j]);

                for (size_t j=0; j<output.GetCount(); j++) TcRes->AppendText(output[j]+_T("\n"));

                //show size change if checked
                if (chkSze) TcRes->AppendText(_T("- Size: ") + i_size + _T(" -> ") + fname.GetHumanReadableSize() + _T("\n"));

                TcRes->AppendText(_T("\n"));

            }
        TcRes->AppendText(_T("--- finished ---\n"));
        wxLogStatus(_T("finished"));

        }
        else TcRes->AppendText(_T("No Files selected, try again!\n"));
    }
    else TcRes->AppendText(_T("Couldn\'t find: ")+Tool+_T(" - check path in Settings!"));
}


wxString xTool::CreateCommand(wxFileName &fname)
{
    wxString cmd = Tool + _T(" ") + iOpts +  _T(" \"") + fname.GetFullPath() + _T("\"");

    //if output file is given
    if (!oPatt.IsEmpty())
    {
        wxString ext = fname.GetExt();
        wxString outFile(oPatt);
        outFile.Replace(_T("*"), fname.GetName());
        fname.SetFullName(outFile);
        if (!fname.HasExt()) fname.SetExt(ext);

        //if subfolder is given
        if (!oSub.IsEmpty())
        {
            wxFileName sname(fname);
            sname.AppendDir(oSub);
            if (sname.IsOk())
            {
                if (!wxFileName::DirExists(sname.GetPath())) wxMkdir(sname.GetPath());
                fname=sname;
            }
        }

        outFile=fname.GetFullPath();
        if (!oOpts.IsEmpty()) cmd+= _T(" ") + oOpts;
        cmd+=_T(" \"") + outFile + _T("\"");
    }
    return cmd;
}
  

//---------------- chTxt - change text in multiple files ----------------
void chTxt::CreateControls()
{
    //widgets
    wxStaticText *TxtHeader;
    TxtHeader = new wxStaticText(this, -1, _T("Select Folder:"), wxPoint(0,0), wxDefaultSize);

	TcFile = new wxTextCtrl(this, ID_TC_FILE, _T(""),wxPoint(0,0), wxSize(300,25));
	DragContent = new dndFile(TcFile);
	SetDropTarget(DragContent);
	TcPatt = new wxTextCtrl(this, ID_TC_PATT, Pattern, wxPoint(0,0), wxSize(50,25));
	wxBitmapButton *BtOpen = new wxBitmapButton(this, ID_BT_OPEN, wxBitmap(open_xpm), wxPoint(0,0), wxSize(40,25));
	ChkSub = new wxCheckBox(this, ID_CHK_SUB, _T("subdirs"),  wxPoint(0,0), wxSize(80,25));
    ChkSub->SetValue(chkSub);

    wxBoxSizer *H1sizer = new wxBoxSizer(wxHORIZONTAL);
    H1sizer->Add(TcFile, 1, wxALL, 10);
    H1sizer->Add(TcPatt, 0, wxALL, 10);
    H1sizer->Add(BtOpen, 0, wxALL, 10);
    H1sizer->Add(ChkSub, 0, wxALL, 10);

    TcRes = new wxTextCtrl(this, -1, _T(""),wxPoint(0,0),wxSize(540,200),wxTE_MULTILINE);

    //search/replace panel
    PanelSR = new wxPanel(this, -1, wxPoint(0,0), wxSize(300,100), wxSUNKEN_BORDER);
    wxStaticText *Srch, *Repl;
    Srch = new wxStaticText(PanelSR, -1, _T("Search for:"), wxPoint(0,0), wxSize(75,25));
    Repl = new wxStaticText(PanelSR, -1, _T("Replace with:"), wxPoint(0,0), wxSize(75,25));
    TcSrch = new wxTextCtrl(PanelSR, -1, _T(""),wxPoint(0,0), wxSize(200,25));
    TcRepl = new wxTextCtrl(PanelSR, -1, _T(""),wxPoint(0,0), wxSize(200,25));
    wxButton *BtGo = new wxButton(PanelSR, ID_BT_GO, _T("Go!"), wxPoint(0,0), wxSize(40,25));
   
    ChkTst = new wxCheckBox(PanelSR, -1, _T("Test"),  wxPoint(0,0), wxSize(40,25));
    ChkTst->SetValue(true);

    wxBoxSizer *H2sizer = new wxBoxSizer(wxHORIZONTAL);
    H2sizer->Add(Srch, 0, wxTOP, 13);
    H2sizer->Add(TcSrch, 1, wxALL, 10);
    H2sizer->Add(BtGo, 0, wxALL, 10);

    wxBoxSizer *H3sizer = new wxBoxSizer(wxHORIZONTAL);
    H3sizer->Add(Repl, 0, wxTOP, 13);
    H3sizer->Add(TcRepl, 1, wxALL, 10);
    H3sizer->Add(ChkTst, 0, wxALL, 10);

    wxBoxSizer *V2sizer = new wxBoxSizer(wxVERTICAL);
    V2sizer->Add(H2sizer, 1, wxEXPAND | wxLEFT | wxRIGHT, 10);
    V2sizer->Add(H3sizer, 1, wxEXPAND | wxLEFT | wxRIGHT, 10);
    PanelSR->SetSizer(V2sizer);
    V2sizer->SetSizeHints(PanelSR);

    wxBoxSizer *Vsizer = new wxBoxSizer(wxVERTICAL);
    Vsizer->Add(TxtHeader, 0, wxEXPAND | wxTOP | wxLEFT | wxRIGHT, 30);
    Vsizer->Add(H1sizer, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 20);
    Vsizer->Add(TcRes, 1, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 30);
    Vsizer->Add(PanelSR, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 30);

    SetSizer(Vsizer);
    Vsizer->SetSizeHints(this);

}


void chTxt::OnAction(wxCommandEvent& event)
{
    if (files.GetCount()>0)
    {
        if (!TcSrch->GetValue().IsEmpty())
        {
            TcRes->Clear();
            for (size_t i=0; i<files.GetCount(); i++)
            {
                TcRes->AppendText(_T("File: ")+files[i]+_T("\n------------------------------------------------------------\n"));

                wxTextFile *txtFile = new wxTextFile;
                txtFile->Open(files[i]);

                bool fd(false);
                for (size_t j=0; j<txtFile->GetLineCount(); j++)
                {
                    wxString line = txtFile->GetLine(j);
                    wxString fStr = TcSrch->GetValue();
                    wxString rStr = TcRepl->GetValue();
                    
                    //check for multibyte encoding
                    bool utf(false);
                    wxString a_line(line.wc_str(wxConvUTF8), wxConvLocal);
                    if (a_line!=line)
                    {
                        utf=true;
                        fStr = wxString(fStr.wc_str(wxConvLocal), wxConvUTF8);
                        rStr = wxString(rStr.wc_str(wxConvLocal), wxConvUTF8);
                    }

                    if (line.Find(fStr)>-1)
                    {
                        fd=true;
                        wxString new_line(line);
                        new_line.Replace(fStr,rStr);
                        
                        txtFile->RemoveLine(j);
                        txtFile->InsertLine(new_line,j);
                        
                        if (utf) 
                        {
                            line = a_line;
                            new_line = wxString(new_line.wc_str(wxConvUTF8), wxConvLocal);
                        }
                        TcRes->AppendText(_T("Found: ")+line+_T("\n"));
                        TcRes->AppendText(_T("Replaced: ")+new_line+_T("\n"));
                        if (ChkTst->GetValue()) TcRes->AppendText(_T("--- Test Mode - nothing replaced yet ---\n"));
                        TcRes->AppendText(_T("\n"));
                    }
                }
                if (!ChkTst->GetValue()) txtFile->Write();
                txtFile->Close();
                
                if (!fd) TcRes->AppendText(_T("Text not found\n\n"));
            }
        }
        else wxLogMessage(_T("Enter string in Search Panel!"));
    }
    else TcRes->AppendText(_T("No Files selected, try again!\n"));

    TcRes->SetInsertionPoint(0);
}

//---------------- drag n drop ----------------
bool dndFile::OnDropFiles(wxCoord x, wxCoord y, const wxArrayString& filenames)
{
    wxString dir;
    wxFileName drop(filenames.Item(0)); 
    if (drop.GetExt().IsEmpty()) dir=filenames.Item(0); else dir=drop.GetPath();
    
    TargetWindow->Clear();
    TargetWindow->AppendText(dir);

    return true;
}
