Exemplo n.º 1
0
bool CmpPattern::Matches(const wxString& line, BuildLineInfo& lineInfo)
{
    long fidx, lidx;
    if ( !m_fileIndex.ToLong ( &fidx ) || !m_lineIndex.ToLong ( &lidx ) )
        return false;

    if ( !m_regex || !m_regex->IsValid() )
        return false;

    if ( !m_regex->Matches( line ) )
        return false;

    lineInfo.SetSeverity(m_severity);
    if ( m_regex->GetMatchCount() > (size_t)fidx ) {
        lineInfo.SetFilename( m_regex->GetMatch(line, fidx) );
    }
    
    // keep the match length
    lineInfo.SetRegexLineMatch( m_regex->GetMatch(line, 0).length() );
    
    if ( m_regex->GetMatchCount() > (size_t)lidx ) {
        long lineNumber;
        wxString strLine = m_regex->GetMatch(line, lidx);
        strLine.ToLong(&lineNumber);
        lineInfo.SetLineNumber( --lineNumber );
    }
    return true;
}
Exemplo n.º 2
0
void NewBuildTab::DoToggleWindow()
{
    bool success = m_errorCount == 0 && (m_skipWarnings || m_warnCount == 0);
    bool viewing = ManagerST::Get()->IsPaneVisible(wxT("Output View")) &&
                   (clMainFrame::Get()->GetOutputPane()->GetNotebook()->GetCurrentPage() == this);
    bool skipwarnings(false);

    if(!success) {
        if(viewing) {
            if(m_buildpaneScrollTo != ScrollToEnd) {
                // The user may have opted to go to the first error, the first item, or /dev/null
                skipwarnings = (m_errorCount > 0) && (m_buildpaneScrollTo == ScrollToFirstError);
                BuildLineInfo* bli = NULL;
                if(skipwarnings && !m_errorsList.empty()) {
                    bli = m_errorsList.front();

                } else if(!m_errorsAndWarningsList.empty()) {
                    bli = m_errorsAndWarningsList.front();
                }

                // Sanity
                if(bli) { CenterLineInView(bli->GetLineInBuildTab()); }
            }
        }

    } else if(m_autoHide && viewing && !m_buildInterrupted) {
        ManagerST::Get()->HidePane(clMainFrame::Get()->GetOutputPane()->GetCaption());

    } else if(m_showMe == BuildTabSettingsData::ShowOnEnd && !m_autoHide) {
        ManagerST::Get()->ShowOutputPane(BUILD_WIN);
    }
}
Exemplo n.º 3
0
bool CmpPattern::Matches(const wxString& line, BuildLineInfo& lineInfo)
{
    long fidx, lidx;
    if(!m_fileIndex.ToLong(&fidx) || !m_lineIndex.ToLong(&lidx)) return false;

    if(!m_regex || !m_regex->IsValid()) return false;
    if(!m_regex->Matches(line)) return false;

    long colIndex;
    if(!m_colIndex.ToLong(&colIndex)) return false;

    lineInfo.SetSeverity(m_severity);
    if(m_regex->GetMatchCount() > (size_t)fidx) { lineInfo.SetFilename(m_regex->GetMatch(line, fidx)); }

    // keep the match length
    lineInfo.SetRegexLineMatch(m_regex->GetMatch(line, 0).length());

    if(m_regex->GetMatchCount() > (size_t)lidx) {
        long lineNumber;
        wxString strLine = m_regex->GetMatch(line, lidx);
        strLine.ToLong(&lineNumber);
        lineInfo.SetLineNumber(--lineNumber);
    }

    if(m_regex->GetMatchCount() > (size_t)colIndex) {
        long column;
        wxString strCol = m_regex->GetMatch(line, colIndex);
        if(strCol.StartsWith(":")) { strCol.Remove(0, 1); }

        if(!strCol.IsEmpty() && strCol.ToLong(&column)) { lineInfo.SetColumn(column); }
    }
    return true;
}
Exemplo n.º 4
0
void NewBuildTab::OnStyleNeeded(wxStyledTextEvent& event)
{
    int startPos = m_view->GetEndStyled();
    int endPos = event.GetPosition();
    wxString text = m_view->GetTextRange(startPos, endPos);
#if wxCHECK_VERSION(3, 1, 1) && !defined(__WXOSX__)
    // The scintilla syntax in e.g. wx3.1.1 changed
    m_view->StartStyling(startPos);
#else
    m_view->StartStyling(startPos, 0x1f);
#endif

    int curline = m_view->GetLineCount();
    curline -= 1; // The view always ends with a "\n", we don't count it as a line
    wxArrayString lines = ::wxStringTokenize(text, wxT("\n"), wxTOKEN_RET_DELIMS);

    // the last line that we coloured
    curline -= lines.size();

    for(size_t i = 0; i < lines.size(); ++i) {
        const wxString& strLine = lines.Item(i);
        if(m_viewData.count(curline)) {
            BuildLineInfo* b = m_viewData.find(curline)->second;
            switch(b->GetSeverity()) {
            case SV_WARNING:
                m_view->SetStyling(strLine.length(), LEX_GCC_WARNING);
                break;
            case SV_ERROR:
                m_view->SetStyling(strLine.length(), LEX_GCC_ERROR);
                break;
            case SV_SUCCESS:
                m_view->SetStyling(strLine.length(), LEX_GCC_DEFAULT);
                break;
            case SV_DIR_CHANGE:
                m_view->SetStyling(strLine.length(), LEX_GCC_INFO);
                break;
            case SV_NONE:
            default:
                m_view->SetStyling(strLine.length(), LEX_GCC_DEFAULT);
                break;
            }
        } else {
            m_view->SetStyling(strLine.length(), LEX_GCC_DEFAULT);
        }
        ++curline;
    }
}
Exemplo n.º 5
0
bool NewBuildTab::DoSelectAndOpen(const wxDataViewItem& item)
{
    if( item.IsOk() == false )
        return false;

    m_listctrl->UnselectAll(); // Clear any selection
    m_listctrl->EnsureVisible(item);
    m_listctrl->Select(item);

    BuildLineInfo* bli = (BuildLineInfo*)m_listctrl->GetItemData(item);
    if( bli ) {
        wxFileName fn(bli->GetFilename());

        if ( !fn.IsAbsolute() ) {
            std::vector<wxFileName> files;
            std::vector<wxFileName> candidates;
            ManagerST::Get()->GetWorkspaceFiles(files, true);

            for(size_t i=0; i<files.size(); ++i) {
                if( files.at(i).GetFullName() == fn.GetFullName() ) {
                    candidates.push_back( files.at(i) );
                }
            }

            if ( candidates.empty() )
                return false;

            if ( candidates.size() == 1 )
                fn = candidates.at(0);

            else {
                // prompt the user
                wxArrayString fileArr;
                for(size_t i=0; i<candidates.size(); ++i) {
                    fileArr.Add( candidates.at(i).GetFullPath() );
                }

                wxString selection = wxGetSingleChoice(_("Select a file to open:"), _("Choose a file"), fileArr);
                if(selection.IsEmpty())
                    return false;

                fn = wxFileName(selection);
                // if we resolved it now, open the file there is no point in searching this file
                // in m_buildInfoPerFile since the key on this map is kept as full name
                LEditor* editor = clMainFrame::Get()->GetMainBook()->FindEditor(fn.GetFullPath());
                if ( !editor ) {
                    editor = clMainFrame::Get()->GetMainBook()->OpenFile(fn.GetFullPath(), wxT(""), bli->GetLineNumber(), wxNOT_FOUND, OF_AddJump);
                }

                if ( editor ) {
                    // We already got compiler markers set here, just goto the line
                    clMainFrame::Get()->GetMainBook()->SelectPage( editor );
                    editor->GotoLine(bli->GetLineNumber());
                    SetActive(editor);
                    return true;
                }
            }
        }

        if ( fn.IsAbsolute() ) {
            
            // try to locate the editor first
            LEditor* editor = clMainFrame::Get()->GetMainBook()->FindEditor(fn.GetFullPath());
            if ( !editor ) {
                // Open it
                editor = clMainFrame::Get()->GetMainBook()->OpenFile(bli->GetFilename(), wxT(""), bli->GetLineNumber(), wxNOT_FOUND, OF_AddJump);
            }
            
            if ( editor ) {
                if ( !editor->HasCompilerMarkers())
                    MarkEditor( editor );
                
                int lineNumber = bli->GetLineNumber();
                if ( lineNumber > 0 ) {
                    lineNumber --;
                }
            
                // We already got compiler markers set here, just goto the line
                clMainFrame::Get()->GetMainBook()->SelectPage( editor );
                editor->GotoLine( bli->GetLineNumber() );
                editor->ScrollToLine( bli->GetLineNumber() );
                editor->EnsureVisible( lineNumber );
                editor->EnsureCaretVisible();
                SetActive(editor);
                return true;
            }
        }
    }
    return false;
}
Exemplo n.º 6
0
void NewBuildTab::DoProcessOutput(bool compilationEnded, bool isSummaryLine)
{
    if ( !compilationEnded && m_output.Find(wxT("\n")) == wxNOT_FOUND ) {
        // still dont have a complete line
        return;
    }

    wxArrayString lines = ::wxStringTokenize(m_output, wxT("\n"), wxTOKEN_RET_DELIMS);
    m_output.Clear();

    // Process only completed lines (i.e. a line that ends with '\n')
    for(size_t i=0; i<lines.GetCount(); i++) {
        if( !compilationEnded && !lines.Item(i).EndsWith(wxT("\n")) ) {
            m_output << lines.Item(i);
            return;
        }

        wxString buildLine = lines.Item(i);//.Trim().Trim(false);
        // If this is a line similar to 'Entering directory `'
        // add the path in the directories array
        DoSearchForDirectory(buildLine);
        BuildLineInfo *buildLineInfo = DoProcessLine(buildLine, isSummaryLine);

        //keep the line info
        if(buildLineInfo->GetFilename().IsEmpty() == false) {
            m_buildInfoPerFile.insert(std::make_pair(buildLineInfo->GetFilename(), buildLineInfo));
        }

        // Append the line content

        if( buildLineInfo->GetSeverity() == SV_ERROR ) {
            if ( !isSummaryLine ) {
                buildLine.Prepend(ERROR_MARKER);
            }

        } else if( buildLineInfo->GetSeverity() == SV_WARNING ) {
            if ( !isSummaryLine ) {
                buildLine.Prepend(WARNING_MARKER);
            }
        }

        if ( isSummaryLine ) {

            // Add a marker for drawing the bitmap
            if ( m_errorCount ) {
                buildLine.Prepend(SUMMARY_MARKER_ERROR);

            } else if ( m_warnCount ) {
                buildLine.Prepend(SUMMARY_MARKER_WARNING);

            } else {
                buildLine.Prepend(SUMMARY_MARKER_SUCCESS);
            }
            buildLine.Prepend(SUMMARY_MARKER);
        }

        wxVector<wxVariant> data;
        data.push_back( wxVariant(buildLine) );
        
        // Keep the line number in the build tab
        buildLineInfo->SetLineInBuildTab( m_listctrl->GetItemCount() );
        m_listctrl->AppendItem(data, (wxUIntPtr)buildLineInfo);
        
        
        if ( clConfig::Get().Read("build-auto-scroll", true) ) {
            unsigned int count = m_listctrl->GetStore()->GetItemCount();
            wxDataViewItem lastItem = m_listctrl->GetStore()->GetItem(count-1);
            m_listctrl->EnsureVisible( lastItem );
        }

    }

}
Exemplo n.º 7
0
void NewBuildTab::MarkEditor(LEditor* editor)
{
    if ( !editor )
        return;

    editor->DelAllCompilerMarkers();
    editor->AnnotationClearAll();
    editor->AnnotationSetVisible(2); // Visible with box around it

    BuildTabSettingsData options;
    EditorConfigST::Get()->ReadObject(wxT("build_tab_settings"), &options);

    // Are markers or annotations enabled?
    if( options.GetErrorWarningStyle() == BuildTabSettingsData::EWS_NoMarkers ) {
        return;
    }

    std::pair<MultimapBuildInfo_t::iterator, MultimapBuildInfo_t::iterator> iter = m_buildInfoPerFile.equal_range(editor->GetFileName().GetFullPath());
    if ( iter.first == iter.second ) {
        // could not find any, try the fullname
        iter = m_buildInfoPerFile.equal_range(editor->GetFileName().GetFullName());
#if defined(__WXGTK__)
        if ( iter.first == iter.second ) {
            // Nope. Perhaps it's a symlink
            iter = m_buildInfoPerFile.equal_range(CLRealPath(editor->GetFileName().GetFullPath()));
        }
#endif
    }
    
    editor->InitializeAnnotations();
    
    // Merge all the errors from the same line into a single error
    AnnotationInfoByLineMap_t annotations;
    
    for(; iter.first != iter.second; ++iter.first) {
        BuildLineInfo *bli = iter.first->second;
        wxString text = m_listctrl->GetTextValue(bli->GetLineInBuildTab(), 0).Trim().Trim(false);
        
        // strip any build markers
        StripBuildMarkders(text);
        
        // remove the line part from the text
        text = text.Mid(bli->GetRegexLineMatch());
        
        // if the line starts with ':' remove it as well
        text.StartsWith(":", &text);
        text.Trim().Trim(false);
        
        if ( !text.IsEmpty() ) {
            if( bli && (bli->GetSeverity() == SV_ERROR || bli->GetSeverity() == SV_WARNING) ) {
                if( annotations.count(bli->GetLineNumber()) ) {
                    // we already have an error on this line, concatenate the message
                    AnnotationInfo &info = annotations[bli->GetLineNumber()];
                    info.text << "\n" << text;
                    
                    if ( bli->GetSeverity() == SV_ERROR ) {
                        // override the severity to ERROR
                        info.severity = SV_ERROR; 
                    }
                    
                } else {
                    // insert new one
                    AnnotationInfo info;
                    info.line = bli->GetLineNumber();
                    info.severity = bli->GetSeverity();
                    info.text = text;
                    annotations.insert( std::make_pair(bli->GetLineNumber(), info) );
                }
            }
            
        }
    }
    
    AnnotationInfoByLineMap_t::iterator annIter = annotations.begin();
    for(; annIter != annotations.end(); ++annIter) {
        if ( annIter->second.severity == SV_ERROR ) {
            editor->SetErrorMarker(annIter->first, annIter->second.text);
        } else {
            editor->SetWarningMarker(annIter->first, annIter->second.text);
        }
    }
    // now place the errors
    editor->Refresh();
}
Exemplo n.º 8
0
BuildLineInfo* NewBuildTab::DoProcessLine(const wxString& line, bool isSummaryLine)
{
    BuildLineInfo *buildLineInfo = new BuildLineInfo();

    if ( isSummaryLine ) {
        // Set the severity
        if( m_errorCount == 0 && m_warnCount == 0 ) {
            buildLineInfo->SetSeverity(SV_SUCCESS);

        } else if ( m_errorCount ) {
            buildLineInfo->SetSeverity(SV_ERROR);

        } else {

            buildLineInfo->SetSeverity(SV_WARNING);
        }

    } else {

        DoUpdateCurrentCompiler(line); // Usering the current line, update the active compiler based on the current project being compiled
        // Find *warnings* first
        bool isWarning = false;

        CmpPatterns cmpPatterns;
        if(!DoGetCompilerPatterns(m_cmp->GetName(), cmpPatterns)) {
            return buildLineInfo;
        }

        // If it is not an error, maybe it's a warning
        for(size_t i=0; i<cmpPatterns.warningPatterns.size(); i++) {
            CmpPatternPtr cmpPatterPtr = cmpPatterns.warningPatterns.at(i);
            BuildLineInfo bli;
            if ( cmpPatterPtr->Matches(line, bli) ) {
                buildLineInfo->SetFilename(bli.GetFilename());
                buildLineInfo->SetSeverity(bli.GetSeverity());
                buildLineInfo->SetLineNumber(bli.GetLineNumber());
                buildLineInfo->NormalizeFilename(m_directories, m_cygwinRoot);
                buildLineInfo->SetRegexLineMatch(bli.GetRegexLineMatch());
                
                // keep this info in the errors+warnings list only
                m_errorsAndWarningsList.push_back(buildLineInfo);

                m_warnCount++;
                isWarning = true;
                break;
            }
        }
        if ( !isWarning ) {
            for(size_t i=0; i<cmpPatterns.errorsPatterns.size(); i++) {
                BuildLineInfo bli;
                CmpPatternPtr cmpPatterPtr = cmpPatterns.errorsPatterns.at(i);
                if ( cmpPatterPtr->Matches(line, bli) ) {
                    buildLineInfo->SetFilename(bli.GetFilename());
                    buildLineInfo->SetSeverity(bli.GetSeverity());
                    buildLineInfo->SetLineNumber(bli.GetLineNumber());
                    buildLineInfo->NormalizeFilename(m_directories, m_cygwinRoot);
                    buildLineInfo->SetRegexLineMatch(bli.GetRegexLineMatch());
                    
                    // keep this info in both lists (errors+warnings AND errors)
                    m_errorsAndWarningsList.push_back(buildLineInfo);
                    m_errorsList.push_back(buildLineInfo);
                    m_errorCount++;
                    break;
                }
            }
        }
    }
    return buildLineInfo;
}
Exemplo n.º 9
0
bool NewBuildTab::DoSelectAndOpen(int buildViewLine, bool centerLine)
{
    if(!m_viewData.count(buildViewLine)) { return false; }

    BuildLineInfo* bli = m_viewData.find(buildViewLine)->second;
    if(bli) {
        wxFileName fn(bli->GetFilename());

        // Highlight the clicked line on the view
        m_view->MarkerDeleteAll(LEX_GCC_MARKER);
        m_view->MarkerAdd(bli->GetLineInBuildTab(), LEX_GCC_MARKER);

        if(!fn.IsAbsolute()) {
            std::set<wxString> files;
            std::vector<wxFileName> candidates;
            ManagerST::Get()->GetWorkspaceFiles(files);

            std::for_each(files.begin(), files.end(), [&](const wxString& filepath) {
                wxFileName fnFilePath(filepath);
                if(fnFilePath.GetFullName() == fn.GetFullName()) { candidates.push_back(fnFilePath); }
            });

            if(candidates.empty()) { return false; }

            if(candidates.size() == 1) {
                fn = candidates.at(0);

            } else {
                // prompt the user
                wxArrayString fileArr;
                for(size_t i = 0; i < candidates.size(); ++i) {
                    fileArr.Add(candidates.at(i).GetFullPath());
                }

                clSingleChoiceDialog dlg(EventNotifier::Get()->TopFrame(), fileArr);
                dlg.SetLabel(_("Select a file to open"));
                if(dlg.ShowModal() != wxID_OK) return false;

                wxString selection = dlg.GetSelection();
                if(selection.IsEmpty()) return false;

                fn = wxFileName(selection);
                // if we resolved it now, open the file there is no point in searching this file
                // in m_buildInfoPerFile since the key on this map is kept as full name
                clEditor* editor = clMainFrame::Get()->GetMainBook()->FindEditor(fn.GetFullPath());
                if(!editor) {
                    editor = clMainFrame::Get()->GetMainBook()->OpenFile(fn.GetFullPath(), wxT(""),
                                                                         bli->GetLineNumber(), wxNOT_FOUND, OF_AddJump);
                }

                if(editor) {
                    DoCentreErrorLine(bli, editor, centerLine);
                    return true;
                }
            }
        }

        if(fn.IsAbsolute()) {

            // try to locate the editor first
            clEditor* editor = clMainFrame::Get()->GetMainBook()->FindEditor(fn.GetFullPath());
            if(!editor) {
                // Open it
                editor = clMainFrame::Get()->GetMainBook()->OpenFile(bli->GetFilename(), wxT(""), bli->GetLineNumber(),
                                                                     wxNOT_FOUND, OF_AddJump);
            }

            if(editor) {
                if(!editor->HasCompilerMarkers()) MarkEditor(editor);

                int lineNumber = bli->GetLineNumber();
                if(lineNumber > 0) { lineNumber--; }

                DoCentreErrorLine(bli, editor, centerLine);
                return true;
            }
        }
    }
    return false;
}
Exemplo n.º 10
0
void NewBuildTab::DoProcessOutput(bool compilationEnded, bool isSummaryLine)
{
    wxUnusedVar(isSummaryLine);
    if(!compilationEnded && m_output.Find(wxT("\n")) == wxNOT_FOUND) {
        // still dont have a complete line
        return;
    }

    wxArrayString lines = ::wxStringTokenize(m_output, wxT("\n"), wxTOKEN_RET_DELIMS);
    m_output.Clear();

    // Process only completed lines (i.e. a line that ends with '\n')
    for(size_t i = 0; i < lines.GetCount(); ++i) {
        if(!compilationEnded && !lines.Item(i).EndsWith(wxT("\n"))) {
            m_output << lines.Item(i);
            return;
        }

        wxString buildLine = lines.Item(i); //.Trim().Trim(false);
        // If this is a line similar to 'Entering directory `'
        // add the path in the directories array
        DoSearchForDirectory(buildLine);
        BuildLineInfo* buildLineInfo = DoProcessLine(buildLine);

        // keep the line info
        if(buildLineInfo->GetFilename().IsEmpty() == false) {
            m_buildInfoPerFile.insert(std::make_pair(buildLineInfo->GetFilename(), buildLineInfo));
        }

        if(isSummaryLine) {
            buildLine.Trim();
            buildLine.Prepend("====");
            buildLine.Append("====");
            buildLineInfo->SetSeverity(SV_NONE);
        }

        // Keep the line number in the build tab
        buildLineInfo->SetLineInBuildTab(m_view->GetLineCount() - 1); // -1 because the view always has 1 extra "\n"
        // Store the line info *before* we add the text
        // it is needed in the OnStyle function
        m_viewData.insert(std::make_pair(buildLineInfo->GetLineInBuildTab(), buildLineInfo));

        m_view->SetEditable(true);
        buildLine.Trim();
        wxString modText;
        ::clStripTerminalColouring(buildLine, modText);

        int curline = m_view->GetLineCount() - 1;
        m_view->AppendText(modText + "\n");

        // get the newly added line width
        int endPosition = m_view->GetLineEndPosition(curline); // get character position from begin
        int beginPosition = m_view->PositionFromLine(curline); // and end of line

        wxPoint beginPos = m_view->PointFromPosition(beginPosition);
        wxPoint endPos = m_view->PointFromPosition(endPosition);

        int curLen = (endPos.x - beginPos.x) + 10;
        m_maxlineWidth = wxMax(m_maxlineWidth, curLen);
        if(m_maxlineWidth > 0) { m_view->SetScrollWidth(m_maxlineWidth); }
        m_view->SetEditable(false);

        if(clConfig::Get().Read(kConfigBuildAutoScroll, true)) { m_view->ScrollToEnd(); }
    }
}
Exemplo n.º 11
0
BuildLineInfo* NewBuildTab::DoProcessLine(const wxString& line)
{
    BuildLineInfo* buildLineInfo = new BuildLineInfo();
    LINE_SEVERITY severity;
    // Get the matching regex for this line
    CmpPatternPtr cmpPatterPtr = GetMatchingRegex(line, severity);
    buildLineInfo->SetSeverity(severity);
    BuildLineInfo bli;
    if(cmpPatterPtr && cmpPatterPtr->Matches(line, bli)) {
        buildLineInfo->SetFilename(bli.GetFilename());
        buildLineInfo->SetSeverity(bli.GetSeverity());
        buildLineInfo->SetLineNumber(bli.GetLineNumber());
        buildLineInfo->NormalizeFilename(m_directories, m_cygwinRoot);
        buildLineInfo->SetRegexLineMatch(bli.GetRegexLineMatch());
        buildLineInfo->SetColumn(bli.GetColumn());
        if(severity == SV_WARNING) {
            // Warning
            m_errorsAndWarningsList.push_back(buildLineInfo);
            m_warnCount++;
        } else {
            // Error
            m_errorsAndWarningsList.push_back(buildLineInfo);
            m_errorsList.push_back(buildLineInfo);
            m_errorCount++;
        }
    }
    return buildLineInfo;
}
Exemplo n.º 12
0
void NewBuildTab::OnBuildEnded(clCommandEvent& e)
{
    e.Skip();
    CL_DEBUG("Build Ended!");
    m_buildInProgress = false;

    DoProcessOutput(true, false);

    std::vector<clEditor*> editors;
    clMainFrame::Get()->GetMainBook()->GetAllEditors(editors, MainBook::kGetAll_Default);
    for(size_t i = 0; i < editors.size(); i++) {
        MarkEditor(editors.at(i));
    }

    // Add a summary line
    wxString problemcount =
        wxString::Format(wxT("%d %s, %d %s"), m_errorCount, _("errors"), m_warnCount, _("warnings"));
    wxString term = problemcount;
    long elapsed = m_sw.Time() / 1000;
    if(elapsed > 10) {
        long sec = elapsed % 60;
        long hours = elapsed / 3600;
        long minutes = (elapsed % 3600) / 60;
        term << wxString::Format(wxT(", %s: %02ld:%02ld:%02ld %s"), _("total time"), hours, minutes, sec, _("seconds"));
    }

    m_output = term;
    DoProcessOutput(true, true);

    if(m_buildInterrupted) {
        wxString InterruptedMsg;
        InterruptedMsg << _("(Build Cancelled)") << wxT("\n\n");
        m_output = InterruptedMsg;
        DoProcessOutput(true, false);
    }

    // Hide / Show the build tab according to the settings
    DoToggleWindow();

    // make it invalid
    m_curError = m_errorsAndWarningsList.begin();
    CL_DEBUG("Posting wxEVT_BUILD_ENDED event");

    // 0 = first error
    // 1 = first error or warning
    // 2 = to the end
    if(m_buildTabSettings.GetBuildPaneScrollDestination() == ScrollToFirstError && !m_errorsList.empty()) {
        BuildLineInfo* bli = m_errorsList.front();
        DoSelectAndOpen(bli->GetLineInBuildTab(), true);
    }

    if(m_buildTabSettings.GetBuildPaneScrollDestination() == ScrollToFirstItem && !m_errorsAndWarningsList.empty()) {
        BuildLineInfo* bli = m_errorsAndWarningsList.front();
        DoSelectAndOpen(bli->GetLineInBuildTab(), true);
    }

    if(m_buildTabSettings.GetBuildPaneScrollDestination() == ScrollToEnd) { m_view->ScrollToEnd(); }

    // notify the plugins that the build has ended
    clBuildEvent buildEvent(wxEVT_BUILD_ENDED);
    buildEvent.SetErrorCount(m_errorCount);
    buildEvent.SetWarningCount(m_warnCount);
    EventNotifier::Get()->AddPendingEvent(buildEvent);
}