#include #include "todolistview.h" #include #include #include #include #include #include #include #include #include #include #include #include int idSource = wxNewId(); int idUser = wxNewId(); int idRefresh = wxNewId(); BEGIN_EVENT_TABLE(ToDoListView, SimpleListLog) EVT_COMBOBOX(idSource, ToDoListView::OnComboChange) EVT_COMBOBOX(idUser, ToDoListView::OnComboChange) EVT_BUTTON(idRefresh, ToDoListView::OnRefresh) END_EVENT_TABLE() ToDoListView::ToDoListView(wxNotebook* parent, const wxString& title, int numCols, int widths[], const wxArrayString& titles, const wxArrayString& m_Types) : SimpleListLog(parent, title, numCols, widths, titles), m_pSource(0L), m_pUser(0L), m_Types(m_Types) { //ctor int id = m_pList->GetId(); Connect(id, -1, wxEVT_COMMAND_LIST_ITEM_SELECTED, (wxObjectEventFunction) (wxEventFunction) (wxCommandEventFunction) &ToDoListView::OnListItemSelected); wxSizer* bs = m_pList->GetContainingSizer(); if (bs) { wxArrayString choices; choices.Add(_("Current file")); choices.Add(_("Open files")); choices.Add(_("All project files")); wxBoxSizer* hbs = new wxBoxSizer(wxHORIZONTAL); hbs->Add(new wxStaticText(this, wxID_ANY, _("Scope:")), 0, wxTOP, 4); m_pSource = new wxComboBox(this, idSource, wxEmptyString, wxDefaultPosition, wxDefaultSize, 3, &choices[0], wxCB_READONLY); m_pSource->SetSelection(0); hbs->Add(m_pSource, 0, wxLEFT | wxRIGHT, 8); hbs->Add(new wxStaticText(this, wxID_ANY, _("User:")), 0, wxTOP, 4); m_pUser = new wxComboBox(this, idUser, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, 0L, wxCB_READONLY); m_pUser->Append(_("")); m_pUser->SetSelection(0); hbs->Add(m_pUser, 0, wxLEFT, 8); m_pRefresh = new wxButton(this, idRefresh, _("Refresh list")); hbs->Add(m_pRefresh, 0, wxLEFT, 8); bs->Add(hbs, 0, wxGROW | wxALL, 8); } } ToDoListView::~ToDoListView() { //dtor } void ToDoListView::LoadUsers() { wxString oldStr = m_pUser->GetStringSelection(); m_pUser->Clear(); m_pUser->Append(_("")); // loop through all todos and add distinct users for (unsigned int i = 0; i < m_Items.GetCount(); ++i) { wxString user = m_Items[i].user; if (!user.IsEmpty()) { if (m_pUser->FindString(user) == wxNOT_FOUND) m_pUser->Append(user); } } int old = m_pUser->FindString(oldStr); if (old != wxNOT_FOUND) m_pUser->SetSelection(old); else m_pUser->SetSelection(0); // all users } void ToDoListView::FillList() { LoadUsers(); GetListControl()->Freeze(); Clear(); for (unsigned int i = 0; i < m_Items.GetCount(); ++i) { const ToDoItem& item = m_Items[i]; if (m_pUser->GetSelection() == 0 || // all users m_pUser->GetStringSelection().Matches(item.user)) // or matches user { int idx = GetListControl()->InsertItem(GetListControl()->GetItemCount(), item.type); GetListControl()->SetItem(idx, 1, item.text); GetListControl()->SetItem(idx, 2, item.user); GetListControl()->SetItem(idx, 3, item.priorityStr); GetListControl()->SetItem(idx, 4, item.lineStr); GetListControl()->SetItem(idx, 5, item.filename); } } GetListControl()->Thaw(); } void ToDoListView::Parse() { // wxBusyCursor busy; // based on user prefs, parse files for todo items m_Items.Clear(); switch (m_pSource->GetSelection()) { case 0: // current file only { // this is the easiest selection ;) cbEditor* ed = Manager::Get()->GetEditorManager()->GetBuiltinEditor(Manager::Get()->GetEditorManager()->GetActiveEditor()); ParseEditor(ed); break; } case 1: // open files { // easy too; parse all open editor files... for (int i = 0; i < Manager::Get()->GetEditorManager()->GetEditorsCount(); ++i) { cbEditor* ed = Manager::Get()->GetEditorManager()->GetBuiltinEditor(Manager::Get()->GetEditorManager()->GetEditor(i)); ParseEditor(ed); } break; } case 2: // all project files { // loop all project files // but be aware: if a file is opened, use the open file because // it might not be the same on the disk... cbProject* prj = Manager::Get()->GetProjectManager()->GetActiveProject(); if (!prj) return; for (int i = 0; i < prj->GetFilesCount(); ++i) { ProjectFile* pf = prj->GetFile(i); wxString filename = pf->file.GetFullPath(); cbEditor* ed = Manager::Get()->GetEditorManager()->IsBuiltinOpen(filename); if (ed) ParseEditor(ed); else ParseFile(filename); } break; } } FillList(); } int ToDoListView::CalculateLineNumber(const wxString& buffer, int upTo) { int line = 0; for (int i = 0; i < upTo; ++i) { if (buffer.GetChar(i) == _T('\r') && buffer.GetChar(i + 1) == _T('\n')) // CR+LF continue; // we 'll count on \n (next loop) else if (buffer.GetChar(i) == _T('\r') || // CR only buffer.GetChar(i) == _T('\n')) // lf only ++line; } return line; } void ToDoListView::ParseEditor(cbEditor* pEditor) { if (pEditor) ParseBuffer(pEditor->GetControl()->GetText(), pEditor->GetFilename()); } void ToDoListView::ParseFile(const wxString& filename) { wxLogNull ln; if (!wxFileExists(filename)) return; // open file wxString st; wxFile file(filename); if(!cbRead(file,st)) return; ParseBuffer(st, filename); } void ToDoListView::ParseBuffer(const wxString& buffer, const wxString& filename) { // this is the actual workhorse... // ok, we look for two basic kinds of todo entries in the text // our version... // TODO (mandrav#0#): Implement code to do this and the other... // and a generic version... // TODO: Implement code to do this and the other... for (unsigned int i = 0; i < m_Types.GetCount(); ++i) { //Manager::Get()->GetMessageManager()->DebugLog("Looking for %s", m_Types[i].c_str()); int pos = buffer.find(m_Types[i], 0); while (pos > 0) { // ok, start parsing now... // keep a temp copy of pos to work with int idx = pos; bool isValid = false; // found it in a comment? bool isC = false; // C or C++ style comment? //#warning TODO (mandrav#1#): Make viewtododlg understand and display todo notes that are compiler warnings/errors... // first check what type of comment we have wxString allowedChars = _T(" \t/*"); wxChar lastChar = _T('\0'); while (idx >= 0) { wxChar c = buffer.GetChar(--idx); if ((int)allowedChars.Index(c) != wxNOT_FOUND) { if (c == _T('/') && (lastChar == _T('/') || lastChar == _T('*'))) { isValid = true; isC = lastChar == _T('*'); break; } } else break; lastChar = c; } //Manager::Get()->GetMessageManager()->DebugLog("Found %s %s style %s at %d", isValid ? "valid" : "invalid", isC ? "C" : "C++", m_Types[i].c_str(), pos); if (isValid) { ToDoItem item; item.type = m_Types[i]; item.filename = filename; idx = pos + m_Types[i].Length(); wxChar c = _T('\0'); //Manager::Get()->GetMessageManager()->DebugLog("1"); // skip to next non-blank char while (idx < (int)buffer.Length()) { c = buffer.GetChar(idx); if (c != _T(' ') && c != _T('\t')) break; ++idx; } //Manager::Get()->GetMessageManager()->DebugLog("2"); // is it ours or generic todo? if (c == _T('(')) { // it's ours, find user and/or priority ++idx; // skip ( while (idx < (int)buffer.Length()) { wxChar c1 = buffer.GetChar(idx); if (c1 != _T('#') && c1 != _T(')')) { // a little logic doesn't hurt ;) if (c1 == _T(' ') || c1 == _T('\t') || c1 == _T('\r') || c1 == _T('\n')) { // allow one consecutive space if (item.user.Last() != _T(' ')) item.user << _T(' '); } else item.user << c1; } else if (c1 == _T('#')) { // look for priority c1 = buffer.GetChar(++idx); allowedChars = _T("0123456789"); if ((int)allowedChars.Index(c1) != wxNOT_FOUND) item.priorityStr << c1; // skip to start of text while (idx < (int)buffer.Length()) { wxChar c2 = buffer.GetChar(idx++); if (c2 == _T(')') || c2 == _T('\r') || c2 == _T('\n')) break; } break; } else break; ++idx; } } //Manager::Get()->GetMessageManager()->DebugLog("3"); // ok, we 've reached the actual todo text :) // take everything up to the end of line or end of comment (if isC) wxChar lastChar = _T('\0'); if (buffer.GetChar(idx) == _T(':')) ++idx; while (idx < (int)buffer.Length()) { wxChar c1 = buffer.GetChar(idx++); if (c1 == _T('\r') || c1 == _T('\n')) break; if (isC && c1 == _T('/') && lastChar == _T('*')) { // remove last char '*' item.text.RemoveLast(); break; } if (c1 == _T(' ') || c1 == _T('\t')) { // allow one consecutive space if (item.text.Last() != _T(' ')) item.text << _T(' '); } else item.text << c1; lastChar = c1; } //Manager::Get()->GetMessageManager()->DebugLog("4"); // do some clean-up item.text.Trim(); item.text.Trim(false); item.user.Trim(); item.user.Trim(false); item.line = CalculateLineNumber(buffer, pos); item.lineStr << item.line + 1; // 1-based line number for list m_Items.Add(item); } else break; // invalid style... pos = buffer.find(m_Types[i], idx); } // Manager::Get()->GetMessageManager()->DebugLog("Found it at %d", pos); } } void ToDoListView::OnComboChange(wxCommandEvent& event) { Parse(); } void ToDoListView::OnListItemSelected(wxListEvent& event) { if (event.GetIndex() == -1) return; wxString file = m_Items[event.GetIndex()].filename; long int line = m_Items[event.GetIndex()].line; if (file.IsEmpty() || line <= 0) return; // jump to file/line selected cbEditor* ed = Manager::Get()->GetEditorManager()->Open(file); if (ed) { ed->GetControl()->GotoLine(line); ed->Activate(); } } void ToDoListView::OnRefresh(wxCommandEvent& event) { Parse(); }