Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Manager] Add button in event log to display only alerts (errors) #5313

Merged
merged 10 commits into from
Jul 22, 2023
231 changes: 209 additions & 22 deletions clientgui/DlgEventLog.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// This file is part of BOINC.
// http://boinc.berkeley.edu
// Copyright (C) 2022 University of California
// Copyright (C) 2023 University of California
//
// BOINC is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License
Expand Down Expand Up @@ -60,6 +60,8 @@ const int dlgEventlogMinHeight = 250;

static bool s_bIsFiltered = false;
static bool s_bFilteringChanged = false;
static bool s_bErrorIsFiltered = false;
static bool s_bErrorFilteringChanged = false;
static std::string s_strFilteredProjectName;

/*!
Expand All @@ -79,6 +81,7 @@ BEGIN_EVENT_TABLE( CDlgEventLog, DlgEventLogBase )
EVT_BUTTON(wxID_OK, CDlgEventLog::OnOK)
EVT_BUTTON(ID_COPYAll, CDlgEventLog::OnMessagesCopyAll)
EVT_BUTTON(ID_COPYSELECTED, CDlgEventLog::OnMessagesCopySelected)
EVT_BUTTON(ID_TASK_MESSAGES_FILTERBYERROR, CDlgEventLog::OnErrorFilter)
EVT_BUTTON(ID_TASK_MESSAGES_FILTERBYPROJECT, CDlgEventLog::OnMessagesFilter)
EVT_BUTTON(ID_SIMPLE_HELP, CDlgEventLog::OnButtonHelp)
EVT_MENU(ID_SGDIAGNOSTICLOGFLAGS, CDlgEventLog::OnDiagnosticLogFlags)
Expand Down Expand Up @@ -327,6 +330,9 @@ void CDlgEventLog::CreateControls()

itemFlexGridSizer2->Add(itemBoxSizer4, 0, wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL|wxALL, 12);

m_pErrorFilterButton = new wxButton(this, ID_TASK_MESSAGES_FILTERBYERROR, _("Show only aler&ts"), wxDefaultPosition, wxDefaultSize);
itemBoxSizer4->Add(m_pErrorFilterButton, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5);

m_pFilterButton = new wxButton(this, ID_TASK_MESSAGES_FILTERBYPROJECT, _("&Show only this project"), wxDefaultPosition, wxDefaultSize);
itemBoxSizer4->Add(m_pFilterButton, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5);

Expand Down Expand Up @@ -381,6 +387,20 @@ void CDlgEventLog::SetFilterButtonText() {
m_pFilterButton->SetHelpText( _("Show only the messages for the selected project") );
#ifdef wxUSE_TOOLTIPS
m_pFilterButton->SetToolTip(_("Show only the messages for the selected project"));
#endif
}
if (s_bErrorIsFiltered) {
m_pErrorFilterButton->SetLabel(_("Show al&l"));
m_pErrorFilterButton->SetHelpText(_("Shows messages of all types (information, alerts, etc.)"));
#ifdef wxUSE_TOOLTIPS
m_pErrorFilterButton->SetToolTip(_("Shows messages of all types (information, alerts, etc.)"));
#endif
}
else {
m_pErrorFilterButton->SetLabel(_("Show only aler&ts"));
m_pErrorFilterButton->SetHelpText(_("Shows only the messages that are alerts"));
#ifdef wxUSE_TOOLTIPS
m_pErrorFilterButton->SetToolTip(_("Show only the messages that are alerts"));
#endif
}
// Adjust button size for new text
Expand Down Expand Up @@ -470,38 +490,100 @@ void CDlgEventLog::OnClose(wxCloseEvent& WXUNUSED(event)) {
}


// Function used to filter errors displayed in the Event Log.
// This Function will first check if errors are currently filtered. Then, regardless if true or false,
// will evaluate if a project filter is active or not. Depending on the combination of error and
// project filters, the filtered list will be updated. One of three possibilities could happen:
// 1. Restarting from the original event log list (if error filter was active and it is being deactivated)
// 2. Restarting the list and re-filtering (if both filters were active, but then one is being deactivated)
// 3. Filtering the currently filtered list (if one filter was active and the other filter is being activated).
// After filtering is completed, then the buttons in the event log window are updated and the event log list
// is updated to reflect the changes made to the list.
//
void CDlgEventLog::OnErrorFilter(wxCommandEvent& WXUNUSED(event)) {
wxLogTrace(wxT("Function Start/End"), wxT("CDlgEventLog::OnErrorFilter - Function Begin"));
wxASSERT(m_pList);
if (s_bErrorIsFiltered) {
// Errors are currently filtered. Whether a project filter is enabled or not,
// the list will need to be reset to the original list first.
//
s_bErrorIsFiltered = false;
m_iFilteredDocCount = m_iTotalDocCount;
m_iFilteredIndexes.Clear();
m_iTotalDeletedFilterRows = 0;
// Now that the settings are changed, need to determine if project filtering is currently
// enabled or not. If it is not enabled, do nothing. If it is enabled, need to re-filter
// by the project.
//
if (s_bIsFiltered) { // Errors are currently filtered and a project is filtered
FindProjectMessages(false);
}
}
else {
// Errors are not currently filtered. Take the list as it is and filter out errors.
s_bErrorIsFiltered = true;
if (!s_bIsFiltered) { // Errors are not currently filtered but are filtered by a project. Filter from the current list.
m_iFilteredDocCount = m_iTotalDocCount;
m_iFilteredIndexes.Clear();
m_iTotalDeletedFilterRows = 0;
}
FindErrorMessages(s_bIsFiltered);
}

s_bErrorFilteringChanged = true;
SetFilterButtonText();
// Force a complete update
m_iPreviousRowCount = 0;
m_pList->DeleteAllItems();
m_pList->SetItemCount(m_iFilteredDocCount);
OnRefresh();
wxLogTrace(wxT("Function Start/End"), wxT("CDlgEventLog::OnErrorFilter - Function End"));
}


void CDlgEventLog::OnMessagesFilter( wxCommandEvent& WXUNUSED(event) ) {
wxLogTrace(wxT("Function Start/End"), wxT("CDlgEventLog::OnMessagesFilter - Function Begin"));

wxInt32 iIndex = -1;
MESSAGE* message;

wxASSERT(m_pList);

m_iFilteredIndexes.Clear();
s_strFilteredProjectName.clear();
m_iTotalDeletedFilterRows = 0;

if (s_bIsFiltered) {
// Event log is filtering by a project. Whether the error filter is enabled or not,
// the list will need to be reset to the original list first.
//
s_bIsFiltered = false;
m_iFilteredDocCount = m_iTotalDocCount;
} else {
m_iFilteredIndexes.Clear();
s_strFilteredProjectName.clear();
m_iTotalDeletedFilterRows = 0;
// Now that the settings are changed, need to determine if error filtering is currently
// enabled or not. If it is not enabled, do nothing. If it is enabled, need to re-filter
// by errors.
//
if (s_bErrorIsFiltered) { // List is currently filtered by project and by error.
FindErrorMessages(false);
}
} else { // List will now be filtered by a project.
// Get project name to be filtered.
s_strFilteredProjectName.clear();
MESSAGE* message;
iIndex = m_pList->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
if (iIndex >= 0) {
message = wxGetApp().GetDocument()->message(iIndex);
message = wxGetApp().GetDocument()->message(GetFilteredMessageIndex(iIndex));
if (message) {
if ((message->project).size() > 0) {
s_strFilteredProjectName = message->project;
s_bIsFiltered = true;
for (iIndex = 0; iIndex < m_iTotalDocCount; iIndex++) {
message = wxGetApp().GetDocument()->message(iIndex);
if (message->project.empty() || (message->project == s_strFilteredProjectName)) {
m_iFilteredIndexes.Add(iIndex);
}

}
m_iFilteredDocCount = (int)(m_iFilteredIndexes.GetCount());
}
if (!s_bErrorIsFiltered) {
// List is not filtered by errors nor by a project, so clear filtered indexes, count, etc.
m_iFilteredIndexes.Clear();
m_iFilteredDocCount = m_iTotalDocCount;
m_iTotalDeletedFilterRows = 0;
}
FindProjectMessages(s_bErrorIsFiltered);
s_bIsFiltered = true;
}

s_bFilteringChanged = true;
Expand All @@ -518,7 +600,7 @@ void CDlgEventLog::OnMessagesFilter( wxCommandEvent& WXUNUSED(event) ) {


wxInt32 CDlgEventLog::GetFilteredMessageIndex( wxInt32 iRow) const {
if (s_bIsFiltered) return m_iFilteredIndexes[iRow];
if (s_bIsFiltered || s_bErrorIsFiltered) return m_iFilteredIndexes[iRow];
return iRow;
}

Expand All @@ -529,6 +611,7 @@ wxInt32 CDlgEventLog::GetFilteredMessageIndex( wxInt32 iRow) const {
//
// Get the (possibly filtered) item count (i.e., the Row count) and
// make any needed adjustments if oldest items have been deleted.
//
wxInt32 CDlgEventLog::GetDocCount() {
int i, j, numDeletedRows;
CMainDocument* pDoc = wxGetApp().GetDocument();
Expand All @@ -543,7 +626,7 @@ wxInt32 CDlgEventLog::GetDocCount() {
}
m_iNumDeletedFilteredRows = 0;

if (s_bIsFiltered) {
if (s_bIsFiltered || s_bErrorIsFiltered) {
if (numDeletedRows > 0) {
// Remove any deleted messages from our filtered list
while (m_iFilteredIndexes.GetCount() > 0) {
Expand All @@ -564,10 +647,20 @@ wxInt32 CDlgEventLog::GetDocCount() {
if (i < 0) i = 0;
for (; i < m_iTotalDocCount; i++) {
MESSAGE* message = pDoc->message(i);
if (message->project.empty() || (message->project == s_strFilteredProjectName)) {
if (message) {
if (s_bIsFiltered) {
if (s_bErrorIsFiltered) {
if (message->priority == MSG_USER_ALERT && (message->project.empty() || message->project == s_strFilteredProjectName)) {
m_iFilteredIndexes.Add(i);
}
} else if (message->project.empty() || message->project == s_strFilteredProjectName) {
m_iFilteredIndexes.Add(i);
}
} else if (s_bErrorIsFiltered && message->priority == MSG_USER_ALERT) {
m_iFilteredIndexes.Add(i);
}
}
}
m_iFilteredDocCount = (int)(m_iFilteredIndexes.GetCount());
} else {
m_iFilteredDocCount = m_iTotalDocCount;
Expand Down Expand Up @@ -596,7 +689,7 @@ wxInt32 CDlgEventLog::GetDocCount() {
}
}

return s_bIsFiltered ? m_iFilteredDocCount : m_iTotalDocCount;
return (s_bIsFiltered || s_bErrorIsFiltered) ? m_iFilteredDocCount : m_iTotalDocCount;
}


Expand All @@ -620,7 +713,13 @@ void CDlgEventLog::OnRefresh() {
wxInt32 iRowCount = GetDocCount();
long topItem = m_pList->GetTopItem();

if (0 >= iRowCount) {
// If the total rows is negative then it is presumed that something went wrong.
// This conditional resets message filtering, clears the list of event logs that were to
// be displayed. This also happens if the row count is zero, since there should always be
// a message. One exception to this is when error filtering is enabled since it is possible
// for no errors to occur.
//
if ((0 >= iRowCount) && !s_bErrorIsFiltered) {
m_pList->DeleteAllItems();
ResetMessageFiltering();
m_iPreviousFirstMsgSeqNum = 0;
Expand Down Expand Up @@ -666,6 +765,9 @@ void CDlgEventLog::OnRefresh() {
if (s_bFilteringChanged) {
m_pList->EnsureVisible(iRowCount - 1);
s_bFilteringChanged = false;
} else if (s_bErrorFilteringChanged) {
m_pList->EnsureVisible(iRowCount - 1);
s_bErrorFilteringChanged = false;
} else {
if (m_iPreviousLastMsgSeqNum != pDoc->GetLastMsgSeqNum()) {
if (EnsureLastItemVisible()) {
Expand Down Expand Up @@ -959,14 +1061,99 @@ void CDlgEventLog::OnColResize( wxListEvent& ) {

void CDlgEventLog::ResetMessageFiltering() {
s_bFilteringChanged = false;
s_bErrorFilteringChanged = false;
s_bIsFiltered = false;
s_bErrorIsFiltered = false;
s_strFilteredProjectName.clear();
m_iFilteredIndexes.Clear();
SetFilterButtonText();
m_iTotalDeletedFilterRows = 0;
}


// Function to search through messages and find messages that contain an error. This function first reads the input
// to see if it should search all indexes or a pre-filtered set of indexes. Then, it will search through the indexes.
// For each index that is an error (priority == MSG_USER_ALERT), it will add that message's index to a temporary
// array of indexes. After the for loop is complete, the recently filtered indexes will be written to m_iFilteredIndexes
// and the recently filtered indexes will be stored in m_iFilteredDocCount.
//
// The following input variable is required:
// isfiltered: If the wxArrayInt that we want to search is already filtered (m_iFilteredIndexes), this will be true.
// If we want to search all indexes (m_iTotalIndexes), this will be false).
//
void CDlgEventLog::FindErrorMessages(bool isFiltered) {
wxArrayInt filteredindexes;
MESSAGE* message;
wxInt32 i = 0;
if (isFiltered) {
for (i; i < m_iFilteredDocCount; i++) {
message = wxGetApp().GetDocument()->message(GetFilteredMessageIndex(i));
if (message) {
if (message->priority == MSG_USER_ALERT) {
filteredindexes.Add(GetFilteredMessageIndex(i));
}
}
}
}
else {
for (i; i < m_iTotalDocCount; i++) {
message = wxGetApp().GetDocument()->message(i);
if (message) {
if (message->priority == MSG_USER_ALERT) {
filteredindexes.Add(i);
}
}
}
}
m_iFilteredIndexes = filteredindexes;
m_iFilteredDocCount = static_cast<wxInt32>(filteredindexes.GetCount());
}


// Function to search through messages and find all messages that are associated with a specific project. Messages that do
// not have a project are included in the filtered indexes.
//
// This function first reads the input to see if it should search all indexes or a pre-filtered set of indexes.
// Then, it will search through those indexes. For each message that matches the associated
// project (s_strFilteredProjectName) or has no associated project, it will add that message's index to a temporary
// array of indexes. After the for loop is complete, the recently filtered indexes will be written to
// m_iFilteredIndexes and the recently filtered indexes will be stored in m_iFilteredDocCount.
//
// It is assumed s_strFilteredProjectName is correct prior to this function being called.
//
// The following input variable is required:
// isfiltered: If the wxArrayInt that we want to search is already filtered (m_iFilteredIndexes), this will be true.
// If we want to search all indexes (m_iTotalIndexes), this will be false).
//
void CDlgEventLog::FindProjectMessages(bool isFiltered) {
wxArrayInt filteredindexes;
MESSAGE* message;
wxInt32 i = 0;
if (isFiltered) {
for (i; i < m_iFilteredDocCount; i++) {
message = wxGetApp().GetDocument()->message(GetFilteredMessageIndex(i));
if (message) {
if (message->project.empty() || message->project == s_strFilteredProjectName) {
filteredindexes.Add(GetFilteredMessageIndex(i));
}
}
}
}
else {
for (i; i < m_iTotalDocCount; i++) {
message = wxGetApp().GetDocument()->message(i);
if (message) {
if (message->project.empty() || message->project == s_strFilteredProjectName) {
filteredindexes.Add(i);
}
}
}
}
m_iFilteredIndexes = filteredindexes;
m_iFilteredDocCount = static_cast<wxInt32>(m_iFilteredIndexes.GetCount());
}


void CDlgEventLog::UpdateButtons() {
bool enableFilterButton = s_bIsFiltered;
bool enableCopySelectedButton = false;
Expand All @@ -978,7 +1165,7 @@ void CDlgEventLog::UpdateButtons() {

if ((n == 1) && (! s_bIsFiltered)) {
n = m_pList->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
MESSAGE* message = wxGetApp().GetDocument()->message(n);
MESSAGE* message = wxGetApp().GetDocument()->message(GetFilteredMessageIndex(n));
if ((message->project).size() > 0) {
enableFilterButton = true;
}
Expand Down
9 changes: 8 additions & 1 deletion clientgui/DlgEventLog.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// This file is part of BOINC.
// http://boinc.berkeley.edu
// Copyright (C) 2008 University of California
// Copyright (C) 2023 University of California
//
// BOINC is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License
Expand Down Expand Up @@ -124,6 +124,9 @@ class CDlgEventLog : public DlgEventLogBase
/// wxEVT_COMMAND_BUTTON_CLICKED event handler for ID_COPYSELECTED
void OnMessagesCopySelected( wxCommandEvent& event );

// wxEVT_COMMAND_BUTTON_CLICKED event handler for ID_TASK_MESSAGES_FILTERBYERROR
void OnErrorFilter(wxCommandEvent& event);

/// wxEVT_COMMAND_BUTTON_CLICKED event handler for ID_TASK_MESSAGES_FILTERBYPROJECT
void OnMessagesFilter( wxCommandEvent& event );

Expand Down Expand Up @@ -173,6 +176,7 @@ class CDlgEventLog : public DlgEventLogBase
wxInt32 m_iPreviousRowCount;
wxButton* m_pFilterButton;
wxButton* m_pCopySelectedButton;
wxButton* m_pErrorFilterButton;

wxListItemAttr* m_pMessageInfoAttr;
wxListItemAttr* m_pMessageErrorAttr;
Expand All @@ -198,6 +202,9 @@ class CDlgEventLog : public DlgEventLogBase

void ResetMessageFiltering();

void FindErrorMessages(bool isFiltered);
void FindProjectMessages(bool isFiltered);

bool EnsureLastItemVisible();
wxInt32 FormatProjectName( wxInt32 item, wxString& strBuffer ) const;
wxInt32 FormatTime( wxInt32 item, wxString& strBuffer ) const;
Expand Down
Loading
Loading