Fixed GUI unresponsiveness when acknowledging large number of alarms
[public/netxms.git] / src / console / win32 / AlarmBrowser.cpp
1 // AlarmBrowser.cpp : implementation file
2 //
3
4 #include "stdafx.h"
5 #include "nxcon.h"
6 #include "AlarmBrowser.h"
7
8 #ifdef _DEBUG
9 #define new DEBUG_NEW
10 #undef THIS_FILE
11 static char THIS_FILE[] = __FILE__;
12 #endif
13
14 //
15 // Compare two list view items
16 //
17
18 static int CALLBACK CompareListItems(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
19 {
20 NXC_ALARM *pAlarm1, *pAlarm2;
21 NXC_OBJECT *pObject1, *pObject2;
22 TCHAR szName1[MAX_OBJECT_NAME], szName2[MAX_OBJECT_NAME];
23 int iResult;
24
25 pAlarm1 = ((CAlarmBrowser *)lParamSort)->FindAlarmInList(lParam1);
26 pAlarm2 = ((CAlarmBrowser *)lParamSort)->FindAlarmInList(lParam2);
27 if ((pAlarm1 == NULL) || (pAlarm2 == NULL))
28 return 0;
29
30 switch(((CAlarmBrowser *)lParamSort)->SortMode())
31 {
32 case 0: // Severity
33 iResult = (pAlarm1->wSeverity < pAlarm2->wSeverity) ? -1 :
34 ((pAlarm1->wSeverity > pAlarm2->wSeverity) ? 1 : 0);
35 break;
36 case 1: // Source
37 pObject1 = NXCFindObjectById(g_hSession, pAlarm1->dwSourceObject);
38 pObject2 = NXCFindObjectById(g_hSession, pAlarm2->dwSourceObject);
39
40 if (pObject1 == NULL)
41 _tcscpy(szName1, _T("<unknown>"));
42 else
43 _tcscpy(szName1, pObject1->szName);
44
45 if (pObject2 == NULL)
46 _tcscpy(szName2, _T("<unknown>"));
47 else
48 _tcscpy(szName2, pObject2->szName);
49
50 iResult = _tcsicmp(szName1, szName2);
51 break;
52 case 2: // Message
53 iResult = _tcsicmp(pAlarm1->szMessage, pAlarm2->szMessage);
54 break;
55 case 3: // Timestamp
56 iResult = (pAlarm1->dwTimeStamp < pAlarm2->dwTimeStamp) ? -1 :
57 ((pAlarm1->dwTimeStamp > pAlarm2->dwTimeStamp) ? 1 : 0);
58 break;
59 case 4: // Ack
60 iResult = (pAlarm1->wIsAck < pAlarm2->wIsAck) ? -1 :
61 ((pAlarm1->wIsAck > pAlarm2->wIsAck) ? 1 : 0);
62 break;
63 default:
64 iResult = 0;
65 break;
66 }
67 return (((CAlarmBrowser *)lParamSort)->SortDir() == 0) ? iResult : -iResult;
68 }
69
70
71 /////////////////////////////////////////////////////////////////////////////
72 // CAlarmBrowser
73
74 IMPLEMENT_DYNCREATE(CAlarmBrowser, CMDIChildWnd)
75
76 CAlarmBrowser::CAlarmBrowser()
77 {
78 m_pImageList = NULL;
79 m_bShowAllAlarms = FALSE;
80 m_iSortDir = 1;
81 m_iSortMode = 0;
82 m_bRestoredDesktop = FALSE;
83 m_dwNumAlarms = 0;
84 m_pAlarmList = NULL;
85 }
86
87 CAlarmBrowser::CAlarmBrowser(TCHAR *pszParams)
88 {
89 TCHAR szBuffer[32];
90
91 m_pImageList = NULL;
92 m_bShowAllAlarms = FALSE;
93 m_bRestoredDesktop = TRUE;
94 m_dwNumAlarms = 0;
95 m_pAlarmList = NULL;
96
97 if (ExtractWindowParam(pszParams, _T("SM"), szBuffer, 32))
98 {
99 m_iSortMode = _tcstol(szBuffer, NULL, 0);
100 if ((m_iSortMode < 0) || (m_iSortMode > 4))
101 m_iSortMode = 0;
102 }
103 else
104 {
105 m_iSortMode = 0;
106 }
107
108 if (ExtractWindowParam(pszParams, _T("SD"), szBuffer, 32))
109 {
110 m_iSortDir = _tcstol(szBuffer, NULL, 0);
111 if ((m_iSortDir != 0) && (m_iSortDir != 1))
112 m_iSortDir = 0;
113 }
114 else
115 {
116 m_iSortDir = 1;
117 }
118 }
119
120 CAlarmBrowser::~CAlarmBrowser()
121 {
122 delete m_pImageList;
123 safe_free(m_pAlarmList);
124 }
125
126
127 BEGIN_MESSAGE_MAP(CAlarmBrowser, CMDIChildWnd)
128 //{{AFX_MSG_MAP(CAlarmBrowser)
129 ON_WM_CREATE()
130 ON_WM_DESTROY()
131 ON_WM_SETFOCUS()
132 ON_WM_SIZE()
133 ON_COMMAND(ID_VIEW_REFRESH, OnViewRefresh)
134 ON_WM_CONTEXTMENU()
135 ON_COMMAND(ID_ALARM_ACKNOWLEGE, OnAlarmAcknowlege)
136 ON_UPDATE_COMMAND_UI(ID_ALARM_ACKNOWLEGE, OnUpdateAlarmAcknowlege)
137 ON_WM_CLOSE()
138 //}}AFX_MSG_MAP
139 ON_NOTIFY(LVN_COLUMNCLICK, ID_LIST_VIEW, OnListViewColumnClick)
140 ON_MESSAGE(WM_GET_SAVE_INFO, OnGetSaveInfo)
141 END_MESSAGE_MAP()
142
143 /////////////////////////////////////////////////////////////////////////////
144 // CAlarmBrowser message handlers
145
146 //
147 // Overrided PreCreateWindow()
148 //
149
150 BOOL CAlarmBrowser::PreCreateWindow(CREATESTRUCT& cs)
151 {
152 if (cs.lpszClass == NULL)
153 cs.lpszClass = AfxRegisterWndClass(CS_HREDRAW | CS_VREDRAW,
154 NULL,
155 GetSysColorBrush(COLOR_WINDOW),
156 AfxGetApp()->LoadIcon(IDI_ALARM));
157 return CMDIChildWnd::PreCreateWindow(cs);
158 }
159
160
161 //
162 // WM_CREATE message handler
163 //
164
165 int CAlarmBrowser::OnCreate(LPCREATESTRUCT lpCreateStruct)
166 {
167 RECT rect;
168 static int widths[6] = { 50, 100, 150, 200, 250, -1 };
169 static int icons[5] = { IDI_SEVERITY_NORMAL, IDI_SEVERITY_WARNING, IDI_SEVERITY_MINOR,
170 IDI_SEVERITY_MAJOR, IDI_SEVERITY_CRITICAL };
171 int i;
172 BYTE *pwp;
173 UINT iBytes;
174 LVCOLUMN lvCol;
175
176 if (CMDIChildWnd::OnCreate(lpCreateStruct) == -1)
177 return -1;
178
179 GetClientRect(&rect);
180
181 // Create status bar
182 m_wndStatusBar.Create(WS_CHILD | WS_VISIBLE, rect, this, IDC_STATUS_BAR);
183 m_wndStatusBar.SetParts(6, widths);
184 for(i = 0; i < 5; i++)
185 m_wndStatusBar.SetIcon(i, (HICON)LoadImage(theApp.m_hInstance,
186 MAKEINTRESOURCE(icons[i]),
187 IMAGE_ICON, 16, 16, LR_SHARED));
188 m_iStatusBarHeight = GetWindowSize(&m_wndStatusBar).cy;
189 rect.bottom -= m_iStatusBarHeight;
190
191 // Create font for elements
192 m_fontNormal.CreateFont(-MulDiv(8, GetDeviceCaps(GetDC()->m_hDC, LOGPIXELSY), 72),
193 0, 0, 0, FW_BOLD, FALSE, FALSE, FALSE, ANSI_CHARSET,
194 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, PROOF_QUALITY,
195 VARIABLE_PITCH | FF_DONTCARE, "MS Sans Serif");
196
197 // Create list view control
198 m_wndListCtrl.Create(WS_CHILD | WS_VISIBLE | LVS_REPORT | LVS_SHAREIMAGELISTS,
199 rect, this, ID_LIST_VIEW);
200 m_wndListCtrl.SetExtendedStyle(LVS_EX_TRACKSELECT | LVS_EX_UNDERLINEHOT |
201 LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES);
202 m_wndListCtrl.SetHoverTime(0x7FFFFFFF);
203
204 //m_wndListCtrl.SetFont(&m_fontNormal, FALSE);
205
206 // Create image list
207 m_pImageList = CreateEventImageList();
208 m_iSortImageBase = m_pImageList->GetImageCount();
209 m_pImageList->Add(theApp.LoadIcon(IDI_SORT_UP));
210 m_pImageList->Add(theApp.LoadIcon(IDI_SORT_DOWN));
211 m_wndListCtrl.SetImageList(m_pImageList, LVSIL_SMALL);
212
213 // Setup columns
214 m_wndListCtrl.InsertColumn(0, _T("Severity"), LVCFMT_LEFT, 80);
215 m_wndListCtrl.InsertColumn(1, _T("Source"), LVCFMT_LEFT, 140);
216 m_wndListCtrl.InsertColumn(2, _T("Message"), LVCFMT_LEFT, 400);
217 m_wndListCtrl.InsertColumn(3, _T("Time Stamp"), LVCFMT_LEFT, 135);
218 m_wndListCtrl.InsertColumn(4, _T("Ack"), LVCFMT_CENTER, 30);
219
220 // Mark sorting column in list control
221 lvCol.mask = LVCF_IMAGE | LVCF_FMT;
222 lvCol.fmt = LVCFMT_BITMAP_ON_RIGHT | LVCFMT_IMAGE | LVCFMT_LEFT;
223 lvCol.iImage = m_iSortImageBase + m_iSortDir;
224 m_wndListCtrl.SetColumn(m_iSortMode, &lvCol);
225
226 // Restore window size and position if we have one
227 if (!m_bRestoredDesktop)
228 {
229 if (theApp.GetProfileBinary(_T("AlarmBrowser"), _T("WindowPlacement"),
230 &pwp, &iBytes))
231 {
232 if (iBytes == sizeof(WINDOWPLACEMENT))
233 {
234 RestoreMDIChildPlacement(this, (WINDOWPLACEMENT *)pwp);
235 }
236 delete pwp;
237 }
238 }
239
240 ((CConsoleApp *)AfxGetApp())->OnViewCreate(IDR_ALARMS, this);
241
242 PostMessage(WM_COMMAND, ID_VIEW_REFRESH, 0);
243 return 0;
244 }
245
246
247 //
248 // WM_DESTROY message handler
249 //
250
251 void CAlarmBrowser::OnDestroy()
252 {
253 ((CConsoleApp *)AfxGetApp())->OnViewDestroy(IDR_ALARMS, this);
254 CMDIChildWnd::OnDestroy();
255 }
256
257
258 //
259 // WM_SETFOCUS message handler
260 //
261
262 void CAlarmBrowser::OnSetFocus(CWnd* pOldWnd)
263 {
264 CMDIChildWnd::OnSetFocus(pOldWnd);
265
266 m_wndListCtrl.SetFocus();
267 }
268
269
270 //
271 // WM_SIZE message handler
272 //
273
274 void CAlarmBrowser::OnSize(UINT nType, int cx, int cy)
275 {
276 CMDIChildWnd::OnSize(nType, cx, cy);
277
278 m_wndStatusBar.SetWindowPos(NULL, 0, 0, 0, 0, SWP_NOZORDER);
279 m_wndListCtrl.SetWindowPos(NULL, 0, 0, cx, cy - m_iStatusBarHeight, SWP_NOZORDER);
280 }
281
282
283 //
284 // WM_COMMAND::ID_VIEW_REFRESH message handler
285 //
286
287 void CAlarmBrowser::OnViewRefresh()
288 {
289 DWORD i, dwRetCode;
290
291 m_wndListCtrl.DeleteAllItems();
292 safe_free(m_pAlarmList);
293 m_dwNumAlarms = 0;
294 m_pAlarmList = NULL;
295 dwRetCode = DoRequestArg4(NXCLoadAllAlarms, g_hSession, (void *)m_bShowAllAlarms,
296 &m_dwNumAlarms, &m_pAlarmList, _T("Loading alarms..."));
297 if (dwRetCode == RCC_SUCCESS)
298 {
299 memset(m_iNumAlarms, 0, sizeof(int) * 5);
300 for(i = 0; i < m_dwNumAlarms; i++)
301 AddAlarm(&m_pAlarmList[i]);
302 UpdateStatusBar();
303 m_wndListCtrl.SortItems(CompareListItems, (LPARAM)this);
304 }
305 else
306 {
307 theApp.ErrorBox(dwRetCode, _T("Error loading alarm list: %s"));
308 }
309 }
310
311
312 //
313 // Add new alarm record to list
314 //
315
316 void CAlarmBrowser::AddAlarm(NXC_ALARM *pAlarm)
317 {
318 int iIdx;
319 struct tm *ptm;
320 TCHAR szBuffer[64];
321 NXC_OBJECT *pObject;
322
323 pObject = NXCFindObjectById(g_hSession, pAlarm->dwSourceObject);
324 iIdx = m_wndListCtrl.InsertItem(0x7FFFFFFF, g_szStatusTextSmall[pAlarm->wSeverity],
325 pAlarm->wSeverity);
326 if (iIdx != -1)
327 {
328 m_wndListCtrl.SetItemData(iIdx, pAlarm->dwAlarmId);
329 m_wndListCtrl.SetItemText(iIdx, 1, pObject->szName);
330 m_wndListCtrl.SetItemText(iIdx, 2, pAlarm->szMessage);
331 ptm = localtime((const time_t *)&pAlarm->dwTimeStamp);
332 strftime(szBuffer, 32, "%d-%b-%Y %H:%M:%S", ptm);
333 m_wndListCtrl.SetItemText(iIdx, 3, szBuffer);
334 m_wndListCtrl.SetItemText(iIdx, 4, pAlarm->wIsAck ? _T("X") : _T(""));
335 }
336 m_iNumAlarms[pAlarm->wSeverity]++;
337 }
338
339
340 //
341 // Process alarm updates
342 //
343
344 void CAlarmBrowser::OnAlarmUpdate(DWORD dwCode, NXC_ALARM *pAlarm)
345 {
346 int iItem;
347
348 iItem = FindAlarmRecord(pAlarm->dwAlarmId);
349 switch(dwCode)
350 {
351 case NX_NOTIFY_NEW_ALARM:
352 if ((iItem == -1) && ((m_bShowAllAlarms) || (pAlarm->wIsAck == 0)))
353 {
354 AddAlarm(pAlarm);
355 AddAlarmToList(pAlarm);
356 UpdateStatusBar();
357 m_wndListCtrl.SortItems(CompareListItems, (LPARAM)this);
358 }
359 break;
360 case NX_NOTIFY_ALARM_ACKNOWLEGED:
361 if ((iItem != -1) && (!m_bShowAllAlarms))
362 {
363 DeleteAlarmFromList(pAlarm->dwAlarmId);
364 m_wndListCtrl.DeleteItem(iItem);
365 m_iNumAlarms[pAlarm->wSeverity]--;
366 UpdateStatusBar();
367 }
368 break;
369 case NX_NOTIFY_ALARM_DELETED:
370 if (iItem != -1)
371 {
372 DeleteAlarmFromList(pAlarm->dwAlarmId);
373 m_wndListCtrl.DeleteItem(iItem);
374 m_iNumAlarms[pAlarm->wSeverity]--;
375 UpdateStatusBar();
376 }
377 break;
378 default:
379 break;
380 }
381 }
382
383
384 //
385 // Find alarm record in list control by alarm id
386 // Will return record index or -1 if no records exist
387 //
388
389 int CAlarmBrowser::FindAlarmRecord(DWORD dwAlarmId)
390 {
391 LVFINDINFO lvfi;
392
393 lvfi.flags = LVFI_PARAM;
394 lvfi.lParam = dwAlarmId;
395 return m_wndListCtrl.FindItem(&lvfi);
396 }
397
398
399 //
400 // WM_CONTEXTMENU message handler
401 //
402
403 void CAlarmBrowser::OnContextMenu(CWnd* pWnd, CPoint point)
404 {
405 int iItem;
406 UINT uFlags;
407 CMenu *pMenu;
408 CPoint pt;
409
410 pt = point;
411 pWnd->ScreenToClient(&pt);
412 iItem = m_wndListCtrl.HitTest(pt, &uFlags);
413 if ((iItem != -1) && (uFlags & LVHT_ONITEM))
414 {
415 pMenu = theApp.GetContextMenu(6);
416 pMenu->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y, this, NULL);
417 }
418 }
419
420
421 //
422 // Alarm acknowlegement worker function
423 //
424
425 static DWORD AcknowlegeAlarms(DWORD dwNumAlarms, DWORD *pdwAlarmList)
426 {
427 DWORD i, dwResult = RCC_SUCCESS;
428
429 for(i = 0; (i < dwNumAlarms) && (dwResult == RCC_SUCCESS); i++)
430 dwResult = NXCAcknowlegeAlarm(g_hSession, pdwAlarmList[i]);
431 return dwResult;
432 }
433
434
435 //
436 // WM_COMMAND::ID_ALARM_ACKNOWLEGE message handler
437 //
438
439 void CAlarmBrowser::OnAlarmAcknowlege()
440 {
441 int iItem;
442 DWORD i, dwNumAlarms, *pdwAlarmList, dwResult;
443
444 dwNumAlarms = m_wndListCtrl.GetSelectedCount();
445 pdwAlarmList = (DWORD *)malloc(sizeof(DWORD) * dwNumAlarms);
446
447 iItem = m_wndListCtrl.GetNextItem(-1, LVNI_SELECTED);
448 for(i = 0; (iItem != -1) && (i < dwNumAlarms); i++)
449 {
450 pdwAlarmList[i] = m_wndListCtrl.GetItemData(iItem);
451 iItem = m_wndListCtrl.GetNextItem(iItem, LVNI_SELECTED);
452 }
453
454 dwResult = DoRequestArg2(AcknowlegeAlarms, (void *)dwNumAlarms, pdwAlarmList,
455 _T("Acknowleging alarm..."));
456 if (dwResult != RCC_SUCCESS)
457 theApp.ErrorBox(dwResult, _T("Cannot acknowlege alarm: %s"));
458 free(pdwAlarmList);
459 }
460
461 void CAlarmBrowser::OnUpdateAlarmAcknowlege(CCmdUI* pCmdUI)
462 {
463 pCmdUI->Enable(m_wndListCtrl.GetSelectedCount() > 0);
464 }
465
466
467 //
468 // Update status bar information
469 //
470
471 void CAlarmBrowser::UpdateStatusBar(void)
472 {
473 int i, iSum;
474 TCHAR szBuffer[64];
475
476 for(i = 0, iSum = 0; i < 5; i++)
477 {
478 _stprintf(szBuffer, _T("%d"), m_iNumAlarms[i]);
479 m_wndStatusBar.SetText(szBuffer, i, 0);
480 iSum += m_iNumAlarms[i];
481 }
482 _stprintf(szBuffer, _T("Total: %d"), iSum);
483 m_wndStatusBar.SetText(szBuffer, 5, 0);
484 }
485
486
487 //
488 // WM_CLOSE message handler
489 //
490
491 void CAlarmBrowser::OnClose()
492 {
493 WINDOWPLACEMENT wp;
494
495 GetWindowPlacement(&wp);
496 theApp.WriteProfileBinary(_T("AlarmBrowser"), _T("WindowPlacement"),
497 (BYTE *)&wp, sizeof(WINDOWPLACEMENT));
498 CMDIChildWnd::OnClose();
499 }
500
501
502 //
503 // Get save info for desktop saving
504 //
505
506 LRESULT CAlarmBrowser::OnGetSaveInfo(WPARAM wParam, WINDOW_SAVE_INFO *pInfo)
507 {
508 pInfo->iWndClass = WNDC_ALARM_BROWSER;
509 GetWindowPlacement(&pInfo->placement);
510 _sntprintf(pInfo->szParameters, MAX_DB_STRING, _T("SM:%d\x7FSD:%d"),
511 m_iSortMode, m_iSortDir);
512 return 1;
513 }
514
515
516 //
517 // WM_NOTIFY::LVN_COLUMNCLICK message handler
518 //
519
520 void CAlarmBrowser::OnListViewColumnClick(LPNMLISTVIEW pNMHDR, LRESULT *pResult)
521 {
522 LVCOLUMN lvCol;
523
524 // Unmark old sorting column
525 lvCol.mask = LVCF_FMT;
526 lvCol.fmt = LVCFMT_LEFT;
527 m_wndListCtrl.SetColumn(m_iSortMode, &lvCol);
528
529 // Change current sort mode and resort list
530 if (m_iSortMode == pNMHDR->iSubItem)
531 {
532 // Same column, change sort direction
533 m_iSortDir = 1 - m_iSortDir;
534 }
535 else
536 {
537 // Another sorting column
538 m_iSortMode = pNMHDR->iSubItem;
539 }
540 m_wndListCtrl.SortItems(CompareListItems, (LPARAM)this);
541
542 // Mark new sorting column
543 lvCol.mask = LVCF_IMAGE | LVCF_FMT;
544 lvCol.fmt = LVCFMT_BITMAP_ON_RIGHT | LVCFMT_IMAGE | LVCFMT_LEFT;
545 lvCol.iImage = (m_iSortDir == 0) ? m_iSortImageBase : (m_iSortImageBase + 1);
546 m_wndListCtrl.SetColumn(pNMHDR->iSubItem, &lvCol);
547
548 *pResult = 0;
549 }
550
551
552 //
553 // Delete alarm from internal alarms list
554 //
555
556 void CAlarmBrowser::DeleteAlarmFromList(DWORD dwAlarmId)
557 {
558 DWORD i;
559
560 for(i = 0; i < m_dwNumAlarms; i++)
561 if (m_pAlarmList[i].dwAlarmId == dwAlarmId)
562 {
563 m_dwNumAlarms--;
564 memmove(&m_pAlarmList[i], &m_pAlarmList[i + 1],
565 sizeof(NXC_ALARM) * (m_dwNumAlarms - i));
566 break;
567 }
568 }
569
570
571 //
572 // Add new alarm to internal list
573 //
574
575 void CAlarmBrowser::AddAlarmToList(NXC_ALARM *pAlarm)
576 {
577 m_pAlarmList = (NXC_ALARM *)realloc(m_pAlarmList, sizeof(NXC_ALARM) * (m_dwNumAlarms + 1));
578 memcpy(&m_pAlarmList[m_dwNumAlarms], pAlarm, sizeof(NXC_ALARM));
579 m_dwNumAlarms++;
580 }
581
582
583 //
584 // Find alarm record in internal list
585 //
586
587 NXC_ALARM *CAlarmBrowser::FindAlarmInList(DWORD dwAlarmId)
588 {
589 DWORD i;
590
591 for(i = 0; i < m_dwNumAlarms; i++)
592 if (m_pAlarmList[i].dwAlarmId == dwAlarmId)
593 return &m_pAlarmList[i];
594 return NULL;
595 }