Merge Resource Directory into the master
[platform/upstream/iotivity.git] / service / simulator / java / eclipse-plugin / ServiceProviderPlugin / src / oic / simulator / serviceprovider / view / LogView.java
1 /*
2  * Copyright 2015 Samsung Electronics All Rights Reserved.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package oic.simulator.serviceprovider.view;
18
19 import java.io.BufferedWriter;
20 import java.io.FileWriter;
21 import java.io.IOException;
22 import java.text.DateFormat;
23 import java.text.SimpleDateFormat;
24 import java.util.HashMap;
25 import java.util.List;
26
27 import oic.simulator.logger.LogContentProvider;
28 import oic.simulator.logger.LogEntry;
29 import oic.simulator.logger.LogLabelProvider;
30 import oic.simulator.serviceprovider.Activator;
31 import oic.simulator.serviceprovider.listener.ILogUIListener;
32 import oic.simulator.serviceprovider.manager.LogManager;
33 import oic.simulator.serviceprovider.utils.Constants;
34 import oic.simulator.serviceprovider.view.dialogs.FilterDialog;
35 import oic.simulator.serviceprovider.view.dialogs.LogDetailsDialog;
36
37 import org.eclipse.jface.action.Action;
38 import org.eclipse.jface.action.IAction;
39 import org.eclipse.jface.action.IContributionItem;
40 import org.eclipse.jface.action.IMenuManager;
41 import org.eclipse.jface.action.IToolBarManager;
42 import org.eclipse.jface.action.MenuManager;
43 import org.eclipse.jface.action.Separator;
44 import org.eclipse.jface.dialogs.MessageDialog;
45 import org.eclipse.jface.resource.ImageDescriptor;
46 import org.eclipse.jface.viewers.DoubleClickEvent;
47 import org.eclipse.jface.viewers.IDoubleClickListener;
48 import org.eclipse.jface.viewers.ISelectionChangedListener;
49 import org.eclipse.jface.viewers.IStructuredSelection;
50 import org.eclipse.jface.viewers.SelectionChangedEvent;
51 import org.eclipse.jface.viewers.StructuredSelection;
52 import org.eclipse.jface.viewers.TreeViewer;
53 import org.eclipse.jface.viewers.Viewer;
54 import org.eclipse.jface.viewers.ViewerComparator;
55 import org.eclipse.jface.window.Window;
56 import org.eclipse.swt.SWT;
57 import org.eclipse.swt.events.KeyEvent;
58 import org.eclipse.swt.events.KeyListener;
59 import org.eclipse.swt.events.SelectionAdapter;
60 import org.eclipse.swt.events.SelectionEvent;
61 import org.eclipse.swt.layout.GridData;
62 import org.eclipse.swt.widgets.Composite;
63 import org.eclipse.swt.widgets.Display;
64 import org.eclipse.swt.widgets.FileDialog;
65 import org.eclipse.swt.widgets.Text;
66 import org.eclipse.swt.widgets.Tree;
67 import org.eclipse.swt.widgets.TreeColumn;
68 import org.eclipse.swt.widgets.TreeItem;
69 import org.eclipse.ui.IActionBars;
70 import org.eclipse.ui.IWorkbenchActionConstants;
71 import org.eclipse.ui.IWorkbenchPage;
72 import org.eclipse.ui.dialogs.FilteredTree;
73 import org.eclipse.ui.dialogs.PatternFilter;
74 import org.eclipse.ui.part.ViewPart;
75 import org.oic.simulator.ILogger.Level;
76
77 /**
78  * This class manages and shows the log view in the perspective.
79  */
80 public class LogView extends ViewPart {
81
82     public static final String        VIEW_ID              = "oic.simulator.serviceprovider.view.log";
83
84     private LogManager                logManager;
85     private ILogUIListener            logListener;
86
87     private LogContentProvider        treeContentProvider;
88
89     private FilteredTree              tree;
90     private TreeColumn                severityColumn;
91     private TreeColumn                dateColumn;
92     private TreeColumn                messageColumn;
93
94     private IAction                   exportLogAction;
95     private IAction                   clearLogAction;
96     private IAction                   deleteLogAction;
97     private IAction                   scrollLockAction;
98     private IAction                   logDetailsAction;
99     private IAction                   filterAction;
100     private IAction                   activateViewAction;
101     private IAction                   showTextFilter;
102     private IContributionItem         groupByAction;
103
104     private HashMap<Integer, Boolean> shownSeverities      = new HashMap<Integer, Boolean>();
105
106     private boolean                   activateOnChange     = false;
107
108     private boolean                   hideTextFilter       = false;
109
110     private boolean                   scrollLockDisabled;
111
112     public static final int           ORDER_BY_TIME        = 0;
113     public static final int           ORDER_BY_SEVERITY    = 1;
114     public static final int           ORDER_BY_MESSAGE     = 2;
115
116     int                               sortCandidate        = ORDER_BY_TIME;
117
118     SortAction                        sortByTimeAction     = new SortAction(
119                                                                    "Order by Time",
120                                                                    ORDER_BY_TIME);
121     SortAction                        sortBySeverityAction = new SortAction(
122                                                                    "Order by Severity",
123                                                                    ORDER_BY_SEVERITY);
124     SortAction                        sortByMessageAction  = new SortAction(
125                                                                    "Order by Message",
126                                                                    ORDER_BY_MESSAGE);
127
128     private ViewerComparator          dateComparator;
129     private ViewerComparator          severityComparator;
130     private ViewerComparator          messageComparator;
131
132     private TreeColumn                sortColumn           = null;
133     private static int                DOWN                 = 1;
134     private static int                UP                   = -1;
135     private int                       sortDirection        = DOWN;
136
137     public LogView() {
138
139         logListener = new ILogUIListener() {
140
141             @Override
142             public void logChanged(final List<LogEntry> entry) {
143                 Display.getDefault().asyncExec(new Runnable() {
144
145                     @Override
146                     public void run() {
147                         TreeViewer viewer = tree.getViewer();
148                         if (viewer.getControl().isDisposed()) {
149                             return;
150                         }
151                         viewer.setInput(entry);
152                         updateTree(false);
153                     }
154                 });
155             }
156
157             @Override
158             public void logAdded(final LogEntry added) {
159                 Display.getDefault().asyncExec(new Runnable() {
160
161                     @Override
162                     public void run() {
163                         TreeViewer viewer = tree.getViewer();
164                         if (viewer.getControl().isDisposed()) {
165                             return;
166                         }
167                         LogContentProvider provider = (LogContentProvider) viewer
168                                 .getContentProvider();
169                         provider.addLog(added);
170                         tree.getViewer().add(viewer.getInput(), added);
171                         @SuppressWarnings("unchecked")
172                         List<LogEntry> input = (List<LogEntry>) viewer
173                                 .getInput();
174                         if (input.size() > Constants.LOG_SIZE) {
175                             viewer.remove(viewer.getInput(), 0);
176                         }
177                         updateTree(true);
178                     }
179                 });
180             }
181
182             private void updateTree(boolean needscroll) {
183                 if (activateOnChange) {
184                     IWorkbenchPage page = Activator.getDefault().getWorkbench()
185                             .getActiveWorkbenchWindow().getActivePage();
186                     if (page != null) {
187                         page.bringToTop(LogView.this);
188                     }
189                 }
190                 if (scrollLockDisabled && needscroll) {
191                     Tree tree2 = tree.getViewer().getTree();
192                     if (tree2.getItemCount() > 0) {
193                         TreeItem item = tree2.getItem(tree2.getItemCount() - 1);
194                         tree2.setTopItem(item);
195                         deleteLogAction.setEnabled(true);
196                     }
197                 }
198             }
199         };
200
201         logManager = Activator.getDefault().getLogManager();
202
203         // Initially state of scroll lock
204         scrollLockDisabled = true;
205     }
206
207     @Override
208     public void createPartControl(Composite parent) {
209         PatternFilter filter = new PatternFilter() {
210
211             DateFormat dateFormat = new SimpleDateFormat("HH:mm:ss.SSS");
212
213             @Override
214             protected boolean isLeafMatch(Viewer viewer, Object element) {
215                 if (element instanceof LogEntry) {
216                     LogEntry logEntry = (LogEntry) element;
217                     String severity = LogManager.getSeverityName(logEntry
218                             .getSeverity());
219                     String date = dateFormat.format(logEntry.getDate());
220                     String message = logEntry.getMessage();
221                     return wordMatches(severity) || wordMatches(date)
222                             || wordMatches(message);
223                 }
224                 return false;
225             }
226         };
227         filter.setIncludeLeadingWildcard(true);
228         tree = new FilteredTree(parent, SWT.SINGLE | SWT.H_SCROLL
229                 | SWT.V_SCROLL | SWT.FULL_SELECTION, filter, true);
230
231         setupFilteredTree();
232
233         createColumnComparators();
234
235         createActions();
236
237         setDefaultShownSeverities();
238
239         IActionBars actionBars = getViewSite().getActionBars();
240         IToolBarManager toolBarManager = actionBars.getToolBarManager();
241         toolBarManager.add(exportLogAction);
242         toolBarManager.add(clearLogAction);
243         toolBarManager.add(deleteLogAction);
244         toolBarManager.add(scrollLockAction);
245         toolBarManager
246                 .add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
247
248         IMenuManager mgr = actionBars.getMenuManager();
249         mgr.add(groupByAction);
250         mgr.add(new Separator());
251         mgr.add(filterAction);
252         mgr.add(new Separator());
253         mgr.add(activateViewAction);
254         mgr.add(showTextFilter);
255
256         addManagerListeners();
257
258         if (sortCandidate == ORDER_BY_TIME) {
259             sortByTimeAction.run();
260         } else if (sortCandidate == ORDER_BY_SEVERITY) {
261             sortBySeverityAction.run();
262         } else { // order_selected == ORDER_BY_NONE
263             sortByMessageAction.run();
264         }
265
266     }
267
268     private void setupFilteredTree() {
269         tree.setLayoutData(new GridData(GridData.FILL_BOTH));
270         final Tree innerTree = tree.getViewer().getTree();
271         innerTree.setLinesVisible(true);
272
273         severityColumn = new TreeColumn(innerTree, SWT.LEFT);
274         severityColumn.setText("Severity");
275         severityColumn.setWidth(110);
276         severityColumn.addSelectionListener(new SelectionAdapter() {
277
278             @Override
279             public void widgetSelected(SelectionEvent e) {
280                 sortBySeverityAction.run();
281             }
282         });
283         dateColumn = new TreeColumn(innerTree, SWT.LEFT);
284         dateColumn.setText("Time");
285         dateColumn.setWidth(110);
286         dateColumn.addSelectionListener(new SelectionAdapter() {
287
288             @Override
289             public void widgetSelected(SelectionEvent e) {
290                 sortByTimeAction.run();
291             }
292         });
293         messageColumn = new TreeColumn(innerTree, SWT.LEFT);
294         messageColumn.setText("Message");
295         messageColumn.setWidth(180);
296         messageColumn.addSelectionListener(new SelectionAdapter() {
297             @Override
298             public void widgetSelected(SelectionEvent e) {
299                 sortByMessageAction.run();
300             }
301         });
302
303         innerTree.setHeaderVisible(true);
304
305         treeContentProvider = new LogContentProvider();
306
307         tree.getViewer().setContentProvider(treeContentProvider);
308         tree.getViewer().setLabelProvider(new LogLabelProvider());
309
310         tree.getViewer().setInput(logManager.getLogEntries());
311
312         tree.getViewer().addSelectionChangedListener(
313                 new ISelectionChangedListener() {
314
315                     @Override
316                     public void selectionChanged(SelectionChangedEvent event) {
317                         deleteLogAction.setEnabled(!tree.getViewer()
318                                 .getSelection().isEmpty());
319                         logDetailsAction.setEnabled(!tree.getViewer()
320                                 .getSelection().isEmpty());
321                     }
322                 });
323
324         tree.getViewer().getTree().addKeyListener(new KeyListener() {
325
326             @Override
327             public void keyReleased(KeyEvent e) {
328             }
329
330             @Override
331             public void keyPressed(KeyEvent e) {
332                 if (e.character == (char) 127) { // If delete key is pressed
333                     if (deleteLogAction.isEnabled()) {
334                         deleteLogAction.run();
335                     }
336                 }
337             }
338         });
339
340         tree.getViewer().addDoubleClickListener(new IDoubleClickListener() {
341
342             @Override
343             public void doubleClick(DoubleClickEvent event) {
344                 logDetailsAction.run();
345             }
346         });
347     }
348
349     private void createColumnComparators() {
350         dateComparator = new ViewerComparator() {
351
352             @Override
353             public int compare(Viewer viewer, Object e1, Object e2) {
354                 LogEntry l1 = (LogEntry) e1;
355                 LogEntry l2 = (LogEntry) e2;
356                 return l1.getDate().compareTo(l2.getDate()) * sortDirection;
357             }
358         };
359
360         severityComparator = new ViewerComparator() {
361
362             @Override
363             public int compare(Viewer viewer, Object e1, Object e2) {
364                 LogEntry l1 = (LogEntry) e1;
365                 LogEntry l2 = (LogEntry) e2;
366                 if (l1.getSeverity() < l2.getSeverity()) {
367                     return -1 * sortDirection;
368                 }
369                 if (l1.getSeverity() > l2.getSeverity()) {
370                     return 1 * sortDirection;
371                 }
372                 return 0;
373             }
374         };
375
376         messageComparator = new ViewerComparator() {
377
378             @Override
379             public int compare(Viewer viewer, Object e1, Object e2) {
380                 LogEntry l1 = (LogEntry) e1;
381                 LogEntry l2 = (LogEntry) e2;
382                 return l1.getMessage().compareTo(l2.getMessage())
383                         * sortDirection;
384             }
385         };
386
387     }
388
389     private void setDefaultShownSeverities() {
390         shownSeverities.put(Level.INFO.ordinal(), true);
391         shownSeverities.put(Level.DEBUG.ordinal(), true);
392         shownSeverities.put(Level.WARNING.ordinal(), true);
393         shownSeverities.put(Level.ERROR.ordinal(), true);
394     }
395
396     private void addManagerListeners() {
397         logManager.addLogListener(logListener);
398         logManager.applyFilter(shownSeverities);
399     }
400
401     private void createActions() {
402         exportLogAction = createExportLogAction();
403         clearLogAction = createClearLogAction();
404         deleteLogAction = createDeleteLogAction();
405         scrollLockAction = createScrollLockAction();
406         logDetailsAction = createLogDetailsAction();
407
408         filterAction = createFilterAction();
409         activateViewAction = createActivateViewAction();
410         showTextFilter = createShowTextFilter();
411         groupByAction = createGroupByAction();
412     }
413
414     private IAction createExportLogAction() {
415         Action action = new Action("Export log") {
416             @Override
417             public void run() {
418                 FileDialog fd = new FileDialog(Display.getDefault()
419                         .getActiveShell(), SWT.SAVE);
420                 fd.setOverwrite(true);
421                 fd.setFileName("OIC_Simulator_ServerLog.log");
422                 fd.setFilterExtensions(Constants.SAVE_LOG_FILTER_EXTENSIONS);
423                 String name = fd.open();
424                 List<LogEntry> logEntries = logManager.getLogEntries();
425                 StringBuilder sb = new StringBuilder();
426                 for (LogEntry entry : logEntries) {
427                     sb.append(entry.toString());
428                 }
429                 String data = sb.toString();
430                 BufferedWriter out = null;
431                 try {
432                     out = new BufferedWriter(new FileWriter(name));
433                     out.write(data);
434                 } catch (IOException e) {
435                     e.printStackTrace();
436                     MessageDialog.openError(
437                             Display.getDefault().getActiveShell(),
438                             "Export error",
439                             "Could not export log. IO exception: "
440                                     + e.getMessage());
441                 } finally {
442                     try {
443                         if (null != out) {
444                             out.close();
445                         }
446                     } catch (IOException e) {
447                         System.out.println("Error occurred during close.");
448                     }
449                 }
450             }
451         };
452         action.setToolTipText("Export log");
453
454         action.setImageDescriptor(ImageDescriptor.createFromFile(
455                 this.getClass(), "/icons/export_log_e.gif"));
456         action.setDisabledImageDescriptor(ImageDescriptor.createFromFile(
457                 this.getClass(), "/icons/export_log_d.gif"));
458         action.setEnabled(true);
459
460         return action;
461     }
462
463     private IAction createClearLogAction() {
464         Action action = new Action("Clear log") {
465
466             @Override
467             public void run() {
468                 logManager.clearLog();
469             }
470         };
471         action.setToolTipText("Clear log");
472
473         action.setImageDescriptor(ImageDescriptor.createFromFile(
474                 this.getClass(), "/icons/clear_e.gif"));
475         action.setDisabledImageDescriptor(ImageDescriptor.createFromFile(
476                 this.getClass(), "/icons/clear_d.gif"));
477         action.setEnabled(true);
478         return action;
479     }
480
481     private IAction createDeleteLogAction() {
482         Action action = new Action("Delete log entry") {
483
484             @Override
485             @SuppressWarnings("unchecked")
486             public void run() {
487                 IStructuredSelection selection = (IStructuredSelection) tree
488                         .getViewer().getSelection();
489                 List<LogEntry> entries = (List<LogEntry>) tree.getViewer()
490                         .getInput();
491                 LogEntry selectedEntry = (LogEntry) selection.getFirstElement();
492                 if (null != selectedEntry) {
493                     LogEntry toBeShownEntry = null;
494                     for (LogEntry entry : entries) {
495                         if (entry.equals(selectedEntry)) {
496                             int size = entries.size();
497                             int index = entries.indexOf(selectedEntry);
498                             if (index + 1 < size) {
499                                 toBeShownEntry = entries.get(index + 1);
500                             } else if (index > 0) {
501                                 toBeShownEntry = entries.get(index - 1);
502                             }
503                             break;
504                         }
505                     }
506                     logManager.removeEntry(selectedEntry);
507                     if (null != toBeShownEntry) {
508                         tree.getViewer().setSelection(
509                                 new StructuredSelection(toBeShownEntry));
510                     }
511                 }
512             }
513         };
514         action.setToolTipText("Delete log entry");
515         action.setImageDescriptor(ImageDescriptor.createFromFile(
516                 this.getClass(), "/icons/delete_e.gif"));
517         action.setDisabledImageDescriptor(ImageDescriptor.createFromFile(
518                 this.getClass(), "/icons/delete_d.gif"));
519         action.setEnabled(false);
520         return action;
521     }
522
523     private IAction createScrollLockAction() {
524         Action action = new Action("Scroll lock") {
525
526             @Override
527             public void run() {
528                 scrollLockDisabled = !this.isChecked();
529             };
530
531             @Override
532             public int getStyle() {
533                 return IAction.AS_CHECK_BOX;
534             }
535         };
536         action.setToolTipText("Scroll lock");
537         action.setImageDescriptor(ImageDescriptor.createFromFile(
538                 this.getClass(), "/icons/lock_e.gif"));
539         action.setDisabledImageDescriptor(ImageDescriptor.createFromFile(
540                 this.getClass(), "/icons/lock_d.gif"));
541         action.setEnabled(true);
542         return action;
543     }
544
545     private IAction createLogDetailsAction() {
546         Action action = new Action("Details...") {
547
548             @Override
549             public void run() {
550                 Display.getDefault().asyncExec(new Runnable() {
551
552                     @Override
553                     public void run() {
554                         LogEntry x = (LogEntry) ((IStructuredSelection) tree
555                                 .getViewer().getSelection()).getFirstElement();
556
557                         new LogDetailsDialog(Display.getDefault()
558                                 .getActiveShell(), LogManager.getSeverityName(x
559                                 .getSeverity()), LogManager.getSeverityIcon(x
560                                 .getSeverity()), x.getDate(), x.getMessage())
561                                 .open();
562                     }
563                 });
564             }
565         };
566         action.setToolTipText("Details...");
567         action.setImageDescriptor(ImageDescriptor.createFromFile(
568                 this.getClass(), "/icons/log_details_e.gif"));
569         action.setDisabledImageDescriptor(ImageDescriptor.createFromFile(
570                 this.getClass(), "/icons/log_details_e.gif"));
571         action.setEnabled(false);
572         return action;
573     }
574
575     private IAction createFilterAction() {
576         Action action = new Action("Filters ...") {
577
578             @Override
579             public void run() {
580                 FilterDialog fd = new FilterDialog(Display.getDefault()
581                         .getActiveShell(), shownSeverities);
582                 if (fd.open() == Window.OK) {
583                     logManager.applyFilter(shownSeverities);
584                 }
585                 tree.getViewer().refresh();
586             }
587         };
588         action.setToolTipText("Filters ...");
589
590         action.setImageDescriptor(ImageDescriptor.createFromFile(
591                 this.getClass(), "/icons/filter_e.gif"));
592         action.setDisabledImageDescriptor(ImageDescriptor.createFromFile(
593                 this.getClass(), "/icons/filter_d.gif"));
594         action.setEnabled(true);
595         return action;
596     }
597
598     private IAction createActivateViewAction() {
599         Action action = new Action("Activate view on new events",
600                 IAction.AS_CHECK_BOX) {
601
602             @Override
603             public void run() {
604                 activateOnChange = this.isChecked();
605             }
606         };
607         action.setChecked(activateOnChange);
608         action.setToolTipText("Activate view on new events");
609
610         action.setImageDescriptor(ImageDescriptor.createFromFile(
611                 this.getClass(), "/icons/prop_e.gif"));
612         action.setDisabledImageDescriptor(ImageDescriptor.createFromFile(
613                 this.getClass(), "/icons/prop_d.gif"));
614         action.setEnabled(true);
615         return action;
616     }
617
618     private IAction createShowTextFilter() {
619         Action action = new Action("Show text filter", IAction.AS_CHECK_BOX) {
620
621             @Override
622             public void run() {
623                 Text filterControl = tree.getFilterControl();
624                 Composite filterComposite = filterControl.getParent();
625                 GridData gd = (GridData) filterComposite.getLayoutData();
626                 boolean visible = isChecked();
627                 gd.exclude = !visible;
628                 filterComposite.setVisible(visible);
629                 filterControl.setText("");
630                 if (visible) {
631                     filterControl.selectAll();
632                     setFocus();
633                 }
634                 tree.layout(false);
635                 hideTextFilter = !visible;
636             }
637         };
638         action.setToolTipText("Show text filter");
639
640         action.setImageDescriptor(ImageDescriptor.createFromFile(
641                 this.getClass(), "/icons/tree_mode_e.gif"));
642         action.setDisabledImageDescriptor(ImageDescriptor.createFromFile(
643                 this.getClass(), "/icons/tree_mode_d.gif"));
644         action.setEnabled(true);
645         action.setChecked(!hideTextFilter);
646         if (hideTextFilter) {
647             action.run();
648         }
649         return action;
650     }
651
652     private IContributionItem createGroupByAction() {
653         IMenuManager manager = new MenuManager("Order by");
654         manager.add(sortByTimeAction);
655         manager.add(sortBySeverityAction);
656         manager.add(sortByMessageAction);
657         return manager;
658     }
659
660     class SortAction extends Action {
661
662         private final int sortBy;
663
664         public SortAction(String text, int sortBy) {
665             super(text, IAction.AS_RADIO_BUTTON);
666             this.sortBy = sortBy;
667
668             if (sortCandidate == sortBy) {
669                 setChecked(true);
670             }
671         }
672
673         @Override
674         public void run() {
675             sortBySeverityAction.setChecked(false);
676             sortByTimeAction.setChecked(false);
677             sortCandidate = sortBy;
678             setChecked(true);
679
680             ViewerComparator comparator;
681             TreeColumn column;
682             if (sortBy == ORDER_BY_SEVERITY) {
683                 comparator = severityComparator;
684                 column = severityColumn;
685             } else if (sortBy == ORDER_BY_TIME) {
686                 comparator = dateComparator;
687                 column = dateColumn;
688             } else { // Order by message
689                 comparator = messageComparator;
690                 column = messageColumn;
691             }
692             TreeViewer viewer = tree.getViewer();
693             viewer.setComparator(comparator);
694             viewer.getTree().setSortColumn(column);
695             if (column.equals(sortColumn)) { // reverse sorting order
696                 sortDirection = viewer.getTree().getSortDirection() == SWT.UP ? DOWN
697                         : UP;
698                 viewer.getTree().setSortDirection(
699                         sortDirection == UP ? SWT.UP : SWT.DOWN);
700                 viewer.refresh();
701             } else { // set this column as the one to sort by
702                 sortDirection = DOWN;
703                 viewer.getTree().setSortDirection(SWT.DOWN);
704             }
705             sortColumn = column;
706             refresh();
707         }
708     }
709
710     private void refresh() {
711         tree.getViewer().refresh();
712     }
713
714     @Override
715     public void setFocus() {
716         tree.setFocus();
717     }
718
719     @Override
720     public void dispose() {
721         logManager.removeLogListener(logListener);
722         super.dispose();
723     }
724 }