]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.message.ui/src/org/simantics/message/ui/LogView.java
Externalize strings
[simantics/platform.git] / bundles / org.simantics.message.ui / src / org / simantics / message / ui / LogView.java
1 /*******************************************************************************
2  * Copyright (c) 2007, 2010 Association for Decentralized Information Management
3  * in Industry THTH ry.
4  * All rights reserved. This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License v1.0
6  * which accompanies this distribution, and is available at
7  * http://www.eclipse.org/legal/epl-v10.html
8  *
9  * Contributors:
10  *     VTT Technical Research Centre of Finland - initial API and implementation
11  *******************************************************************************/
12 package org.simantics.message.ui;
13
14 import java.io.BufferedReader;
15 import java.io.BufferedWriter;
16 import java.io.File;
17 import java.io.FileInputStream;
18 import java.io.FileOutputStream;
19 import java.io.IOException;
20 import java.io.InputStreamReader;
21 import java.io.OutputStreamWriter;
22 import java.io.PrintWriter;
23 import java.io.StringWriter;
24 import java.lang.reflect.InvocationTargetException;
25 import java.net.URI;
26 import java.net.URISyntaxException;
27 import java.util.ArrayList;
28 import java.util.Arrays;
29 import java.util.Collections;
30 import java.util.Comparator;
31 import java.util.Date;
32 import java.util.HashMap;
33 import java.util.Iterator;
34 import java.util.List;
35 import java.util.Map;
36
37 import org.eclipse.core.runtime.IProgressMonitor;
38 import org.eclipse.core.runtime.IStatus;
39 import org.eclipse.core.runtime.MultiStatus;
40 import org.eclipse.core.runtime.Path;
41 import org.eclipse.core.runtime.Preferences;
42 import org.eclipse.core.runtime.Status;
43 import org.eclipse.core.runtime.jobs.Job;
44 import org.eclipse.jface.action.Action;
45 import org.eclipse.jface.action.IAction;
46 import org.eclipse.jface.action.IContributionItem;
47 import org.eclipse.jface.action.IMenuListener;
48 import org.eclipse.jface.action.IMenuManager;
49 import org.eclipse.jface.action.IStatusLineManager;
50 import org.eclipse.jface.action.IToolBarManager;
51 import org.eclipse.jface.action.MenuManager;
52 import org.eclipse.jface.action.Separator;
53 import org.eclipse.jface.dialogs.IDialogSettings;
54 import org.eclipse.jface.dialogs.MessageDialog;
55 import org.eclipse.jface.dialogs.ProgressMonitorDialog;
56 import org.eclipse.jface.operation.IRunnableWithProgress;
57 import org.eclipse.jface.resource.ImageDescriptor;
58 import org.eclipse.jface.resource.JFaceResources;
59 import org.eclipse.jface.resource.LocalResourceManager;
60 import org.eclipse.jface.util.Policy;
61 import org.eclipse.jface.viewers.DoubleClickEvent;
62 import org.eclipse.jface.viewers.IDoubleClickListener;
63 import org.eclipse.jface.viewers.ISelection;
64 import org.eclipse.jface.viewers.ISelectionChangedListener;
65 import org.eclipse.jface.viewers.IStructuredSelection;
66 import org.eclipse.jface.viewers.SelectionChangedEvent;
67 import org.eclipse.jface.viewers.TreeSelection;
68 import org.eclipse.jface.viewers.Viewer;
69 import org.eclipse.jface.viewers.ViewerComparator;
70 import org.eclipse.jface.window.Window;
71 import org.eclipse.osgi.util.NLS;
72 import org.eclipse.swt.SWT;
73 import org.eclipse.swt.SWTException;
74 import org.eclipse.swt.browser.Browser;
75 import org.eclipse.swt.browser.LocationEvent;
76 import org.eclipse.swt.browser.LocationListener;
77 import org.eclipse.swt.custom.BusyIndicator;
78 import org.eclipse.swt.custom.SashForm;
79 import org.eclipse.swt.dnd.Clipboard;
80 import org.eclipse.swt.dnd.DND;
81 import org.eclipse.swt.dnd.DragSource;
82 import org.eclipse.swt.dnd.DragSourceAdapter;
83 import org.eclipse.swt.dnd.DragSourceEvent;
84 import org.eclipse.swt.dnd.TextTransfer;
85 import org.eclipse.swt.dnd.Transfer;
86 import org.eclipse.swt.events.ControlAdapter;
87 import org.eclipse.swt.events.ControlEvent;
88 import org.eclipse.swt.events.DisposeEvent;
89 import org.eclipse.swt.events.DisposeListener;
90 import org.eclipse.swt.events.SelectionAdapter;
91 import org.eclipse.swt.events.SelectionEvent;
92 import org.eclipse.swt.graphics.Color;
93 import org.eclipse.swt.graphics.Image;
94 import org.eclipse.swt.graphics.Point;
95 import org.eclipse.swt.graphics.Rectangle;
96 import org.eclipse.swt.layout.GridData;
97 import org.eclipse.swt.layout.GridLayout;
98 import org.eclipse.swt.program.Program;
99 import org.eclipse.swt.widgets.Composite;
100 import org.eclipse.swt.widgets.Display;
101 import org.eclipse.swt.widgets.Event;
102 import org.eclipse.swt.widgets.FileDialog;
103 import org.eclipse.swt.widgets.Listener;
104 import org.eclipse.swt.widgets.Menu;
105 import org.eclipse.swt.widgets.Shell;
106 import org.eclipse.swt.widgets.Text;
107 import org.eclipse.swt.widgets.Tree;
108 import org.eclipse.swt.widgets.TreeColumn;
109 import org.eclipse.swt.widgets.TreeItem;
110 import org.eclipse.ui.IActionBars;
111 import org.eclipse.ui.IMemento;
112 import org.eclipse.ui.IPerspectiveDescriptor;
113 import org.eclipse.ui.IPerspectiveListener2;
114 import org.eclipse.ui.ISharedImages;
115 import org.eclipse.ui.IViewReference;
116 import org.eclipse.ui.IViewSite;
117 import org.eclipse.ui.IWorkbenchActionConstants;
118 import org.eclipse.ui.IWorkbenchPage;
119 import org.eclipse.ui.IWorkbenchPart;
120 import org.eclipse.ui.IWorkbenchPartReference;
121 import org.eclipse.ui.PartInitException;
122 import org.eclipse.ui.PlatformUI;
123 import org.eclipse.ui.XMLMemento;
124 import org.eclipse.ui.actions.ActionFactory;
125 import org.eclipse.ui.dialogs.FilteredTree;
126 import org.eclipse.ui.dialogs.PatternFilter;
127 import org.eclipse.ui.part.ViewPart;
128 import org.simantics.message.DetailStatus;
129 import org.simantics.message.ILogListener;
130 import org.simantics.message.IMessageDataSchemeExtension;
131 import org.simantics.message.IMessageSchemeManager;
132 import org.simantics.message.MessageSchemeException;
133 import org.simantics.message.MessageService;
134 import org.simantics.message.ReferenceSerializationException;
135 import org.simantics.message.util.HtmlUtil;
136 import org.simantics.utils.ui.ErrorLogger;
137
138 import com.ibm.icu.text.DateFormat;
139 import com.ibm.icu.text.SimpleDateFormat;
140
141 /**
142  * A direct rip of Eclipse's own <code>LogView</code> for our own purposes.
143  * 
144  * <p>
145  * The only thing that has changed is the the dependence on
146  * <code>Activator.getLogFile()</code> instead of
147  * <code>Platform.getLogFileLocation()</code>.
148  * 
149  * @author Tuukka Lehtonen
150  * @see org.eclipse.ui.internal.views.LogView
151  */
152 @SuppressWarnings("deprecation")
153 public class LogView extends ViewPart implements ILogListener {
154
155     public static final int      DEFAULT_EXPAND_LEVEL = 1;
156     
157     public static final String   P_LOG_WARNING       = "warning";         //$NON-NLS-1$
158     public static final String   P_LOG_ERROR         = "error";           //$NON-NLS-1$
159     public static final String   P_LOG_INFO          = "info";            //$NON-NLS-1$
160     public static final String   P_LOG_DEBUG         = "debug";           //$NON-NLS-1$
161     public static final String   P_LOG_LIMIT         = "limit";           //$NON-NLS-1$
162     public static final String   P_EXPAND_LEVEL      = "expandLevel";     //$NON-NLS-1$
163     public static final String   P_USE_LIMIT         = "useLimit";        //$NON-NLS-1$
164     public static final String   P_SHOW_ALL_SESSIONS = "allSessions";     //$NON-NLS-1$
165     private static final String  P_COLUMN_1          = "column2";         //$NON-NLS-1$
166     private static final String  P_COLUMN_2          = "column3";         //$NON-NLS-1$
167     private static final String  P_COLUMN_3          = "column4";         //$NON-NLS-1$
168     public static final String   P_ACTIVATE          = "activate";        //$NON-NLS-1$
169     public static final String   P_SHOW_FILTER_TEXT  = "show_filter_text"; //$NON-NLS-1$
170     public static final String   P_ORDER_TYPE        = "orderType";       //$NON-NLS-1$
171     public static final String   P_ORDER_VALUE       = "orderValue";      //$NON-NLS-1$
172     public static final String   P_IMPORT_LOG        = "importLog";       //$NON-NLS-1$
173     public static final String   P_GROUP_BY          = "groupBy";         //$NON-NLS-1$
174
175         private int                  MESSAGE_ORDER;
176     private int                  PLUGIN_ORDER;
177     private int                  DATE_ORDER;
178
179         public final static byte     MESSAGE             = 0x0;
180     public final static byte     PLUGIN              = 0x1;
181     public final static byte     DATE                = 0x2;
182     public static int            ASCENDING           = 1;
183     public static int            DESCENDING          = -1;
184
185         public static final int      GROUP_BY_NONE       = 0;
186     public static final int      GROUP_BY_SESSION    = 1;
187     public static final int      GROUP_BY_PLUGIN     = 2;
188
189         private List<Object>         elements;
190     private Map<Object, Object>  groups;
191     private LogSession           currentSession;
192
193         private List<Object>         batchedEntries;
194     private boolean              batchEntries;
195
196     private Clipboard            fClipboard;
197
198         private IMemento             fMemento;
199     private File                 fInputFile;
200     private String               fDirectory;
201
202         private Comparator<Object>   fComparator;
203
204         // hover text
205     private boolean              fCanOpenTextShell;
206     private Text                 fTextLabel;
207     private Shell                fTextShell;
208
209         private boolean              fFirstEvent         = true;
210
211         private TreeColumn           fColumn1;
212     @SuppressWarnings("unused")
213     private TreeColumn           fColumn2;
214     private TreeColumn           fColumn3;
215
216         private Tree                 fTree;
217         private FilteredTree         fFilteredTree;
218     private LogViewLabelProvider fLabelProvider;
219     //private ScrolledComposite    fMessageDescriptorComposite;
220     //private FormText             fMessageDescription;
221     private Browser              fMessageDescription;
222
223         private Action               fPropertiesAction;
224         private Action               fDeleteLogAction;
225         private Action               fReadLogAction;
226         private Action               fCopyAction;
227         private Action               fActivateViewAction;
228         private Action               fOpenLogAction;
229         private Action               fExportAction;
230
231         private LocalResourceManager resourceManager;
232
233         /**
234          * Action called when user selects "Group by -> ..." from menu.
235          */
236         class GroupByAction extends Action {
237                 private int groupBy;
238
239                 public GroupByAction(String text, int groupBy) {
240                         super(text, IAction.AS_RADIO_BUTTON);
241
242                         this.groupBy = groupBy;
243
244                         if (fMemento.getInteger(LogView.P_GROUP_BY).intValue() == groupBy) {
245                                 setChecked(true);
246                         }
247                 }
248
249                 public void run() {
250                         if (fMemento.getInteger(LogView.P_GROUP_BY).intValue() != groupBy) {
251                                 fMemento.putInteger(LogView.P_GROUP_BY, groupBy);
252                                 reloadLog();
253                         }
254                 }
255         }
256
257         /**
258          * Constructor
259          */
260         public LogView() {
261                 elements = new ArrayList<Object>();
262                 groups = new HashMap<Object, Object>();
263                 batchedEntries = new ArrayList<Object>();
264                 fInputFile = getPlatformLogFile();
265         }
266
267         /* (non-Javadoc)
268          * @see org.eclipse.ui.part.WorkbenchPart#createPartControl(org.eclipse.swt.widgets.Composite)
269          */
270         public void createPartControl(Composite parent) {
271             resourceManager = new LocalResourceManager(JFaceResources.getResources());
272             
273             SashForm sashForm = new SashForm(parent, SWT.VERTICAL | SWT.SMOOTH);
274             
275                 final Composite composite = new Composite(sashForm, SWT.NONE);
276         GridLayout layout = new GridLayout();
277         layout.horizontalSpacing = 0;
278         layout.verticalSpacing = 0;
279         layout.marginWidth = 0;
280         layout.marginHeight = 0;
281         layout.marginBottom = 0;
282         layout.marginTop = 0;
283         layout.marginLeft = 0;
284         layout.marginRight = 0;
285         composite.setLayout(layout);
286
287                 readLogFile();
288                 createViewer(composite);
289                 getSite().setSelectionProvider(fFilteredTree.getViewer());
290                 createActions();
291                 fClipboard = new Clipboard(fTree.getDisplay());
292                 fTree.setToolTipText(""); //$NON-NLS-1$
293                 initializeViewerSorter();
294
295                 makeHoverShell();
296
297                 /*
298                 fMessageDescriptorComposite = new ScrolledComposite(sashForm, SWT.V_SCROLL | SWT.H_SCROLL | SWT.BORDER);
299                 GridDataFactory.fillDefaults().grab(true, false).applyTo(fMessageDescriptorComposite);
300                 TableWrapLayout layout2 = new TableWrapLayout();
301                 layout2.bottomMargin = 0;
302                 layout2.topMargin = 0;
303                 layout2.leftMargin = 0;
304                 layout2.rightMargin = 0;
305                 fMessageDescriptorComposite.setLayout(layout2);
306
307                 fMessageDescription = new FormText(fMessageDescriptorComposite, SWT.NONE);
308                 fMessageDescription.setBackground(parent.getDisplay().getSystemColor(SWT.COLOR_INFO_BACKGROUND));
309                 TableWrapData data = new TableWrapData(TableWrapData.FILL_GRAB, TableWrapData.FILL_GRAB);
310                 fMessageDescription.setLayoutData(data);
311                 TextColors.bindTo(resourceManager, fMessageDescription);
312                 fMessageDescription.addHyperlinkListener(new IHyperlinkListener() {
313                     @Override
314                     public void linkActivated(HyperlinkEvent event) {
315                         String s = (String) event.data;
316                 //System.out.println("link activated: " + s);
317                 try {
318                     URI uri = new URI(s);
319                     String scheme = uri.getScheme();
320                     String schemeSpecificPart = uri.getSchemeSpecificPart();
321
322                     IMessageSchemeManager msm = org.simantics.message.internal.Activator.getDefault().getMessageSchemeManager();
323                     IMessageDataSchemeExtension[] exts = msm.getByScheme(scheme);
324
325                     for (IMessageDataSchemeExtension ext : exts) {
326                         Object data = ext.getSerializer().deserialize(schemeSpecificPart);
327                         ext.getHandler().perform(data);
328                         return;
329                     }
330                 } catch (URISyntaxException e) {
331                     // TODO Auto-generated catch block
332                     e.printStackTrace();
333                 } catch (ReferenceSerializationException e) {
334                     // TODO Auto-generated catch block
335                     e.printStackTrace();
336                 }
337                     }
338                     @Override
339                     public void linkEntered(HyperlinkEvent e) {
340                         //System.out.println("link entered: " + e.data);
341                     }
342                     @Override
343                     public void linkExited(HyperlinkEvent e) {
344                         //System.out.println("link exited: " + e.data);
345                     }
346                 });
347                 fMessageDescription.setText("Select a message to show its description here.", false, false);
348
349         fMessageDescriptorComposite.setContent(fMessageDescription);
350         fMessageDescriptorComposite.setExpandVertical(true);
351         fMessageDescriptorComposite.setExpandHorizontal(true);
352                 fMessageDescriptorComposite.addControlListener(new ControlAdapter() {
353                     public void controlResized(ControlEvent e) {
354                         Rectangle r = fMessageDescriptorComposite.getClientArea();
355                 //System.out.println("scrolled composite resized: " + e + ", client area: " + r);
356                 Point contentSize = fMessageDescription.computeSize(r.width, SWT.DEFAULT);                
357                 //System.out.println("computed content size: " + contentSize);
358                         fMessageDescriptorComposite.setMinSize(contentSize);
359                     }
360                 });
361                 */
362                 fMessageDescription = new Browser(sashForm, SWT.NONE);
363                 fMessageDescription.setBackground(parent.getDisplay().getSystemColor(SWT.COLOR_INFO_BACKGROUND));
364                 fMessageDescription.setText("<html><head></head><body><p>Select a message to show its description here.</p></body></html>"); //$NON-NLS-1$
365                 fMessageDescription.addLocationListener(new LocationListener() {
366                     @Override
367                     public void changed(LocationEvent event) {
368                         //System.out.println("changed: " + event);
369                     }
370                     @Override
371                     public void changing(LocationEvent event) {
372                         //System.out.println("changing: " + event);
373                         String location = event.location;
374                         if ("about:blank".equals(location)) { //$NON-NLS-1$
375                             event.doit = true;
376                         } else {
377                             event.doit = false;
378                             //System.out.println("link activated: " + location);
379                             try {
380                                 URI uri = new URI(location);
381                                 String scheme = uri.getScheme();
382                                 //String schemeSpecificPart = uri.getSchemeSpecificPart();
383
384                                 IMessageSchemeManager msm = org.simantics.message.internal.Activator.getDefault().getMessageSchemeManager();
385                                 IMessageDataSchemeExtension[] exts = msm.getByScheme(scheme);
386
387                                 for (IMessageDataSchemeExtension ext : exts) {
388                                     Object data = ext.getSerializer().deserialize(uri);
389                                     try {
390                                         ext.getHandler().perform(data);
391                                         return;
392                                     } catch (MessageSchemeException e) {
393                                         ErrorLogger.defaultLogError(e);
394                                     }
395                                 }
396                                 return;
397                             } catch (URISyntaxException e) {
398                                 // TODO Auto-generated catch block
399                                 e.printStackTrace();
400                             } catch (ReferenceSerializationException e) {
401                                 // TODO Auto-generated catch block
402                                 e.printStackTrace();
403                             }
404                         }
405                     }
406                 });
407
408                 sashForm.setWeights(new int[] { 5, 2 });
409                 
410                 fFilteredTree.getViewer().addSelectionChangedListener(new ISelectionChangedListener() {
411             @Override
412             public void selectionChanged(SelectionChangedEvent event) {
413                 IStructuredSelection s = (IStructuredSelection) event.getSelection();
414                 if (s.isEmpty()) {
415                     //fMessageDescription.setText("Select a message to show its description here.", false, false);
416                     fMessageDescription.setText("<html><head></head><body><pre>Select a message to show its description here.</pre></body></html>"); //$NON-NLS-1$
417                 } else {
418                     AbstractEntry entry = (AbstractEntry) s.getFirstElement();
419                     if (entry instanceof LogEntry) {
420                         LogEntry logEntry = (LogEntry) entry;
421                         String msg = logEntry.getDetailedDescription();
422                         if (msg == null || msg.trim().isEmpty())
423                             msg = logEntry.getMessage();
424                         
425                         // FormText only supports texts of Short.MAX_VALUE length
426                         // since TextSegment.TextFragment uses shorts. This message
427                         // truncation enables us to show even lengthy messages.
428                         if (msg.length() > Short.MAX_VALUE) {
429                             StringBuilder truncated = new StringBuilder();
430                             truncated.append( NLS.bind(Messages.LogView_Truncated, msg.length() - (Short.MAX_VALUE - 100), msg.length()));                            
431                             msg = msg.substring(0, Short.MAX_VALUE - 100) + truncated;
432                         }
433                         try {
434                             //fMessageDescription.setText(FormTextUtil.form(msg), true, false);
435                             //fMessageDescription.setText(HtmlUtil.html(msg));
436                             fMessageDescription.setText(msg);
437                         } catch (SWTException e) {
438                             // Failed to parse markup, fall back to not parsing the input. 
439                             //fMessageDescription.setText(FormTextUtil.form(msg), false, false);
440                             fMessageDescription.setText(msg);
441                         }
442                     } else if (entry instanceof Group) {
443                         if (entry instanceof LogSession) {
444                             LogSession logSession = (LogSession) entry;
445                             //fMessageDescription.setText(logSession.getSessionData(), false, false);
446                             fMessageDescription.setText(logSession.getSessionData());
447                         } else {
448                             Group grp = (Group) entry;
449                             //fMessageDescription.setText(grp.toString(), false, false);
450                             fMessageDescription.setText(grp.toString());
451                         }
452                     }
453                 }
454                 //fMessageDescription.refresh();
455                 //composite.layout(true);
456             }
457                 });
458
459                 MessageService.getDefault().addLogListener(this);
460 //              PlatformUI.getWorkbench().getHelpSystem().setHelp(fTree, IHelpContextIds.LOG_VIEW);
461                 getSite().getWorkbenchWindow().addPerspectiveListener(new IPerspectiveListener2() {
462
463                         public void perspectiveChanged(IWorkbenchPage page, IPerspectiveDescriptor perspective, IWorkbenchPartReference partRef, String changeId) {
464                                 if (!(partRef instanceof IViewReference))
465                                         return;
466
467                                 IWorkbenchPart part = partRef.getPart(false);
468                                 if (part == null) {
469                                         return;
470                                 }
471
472                                 if (part.equals(LogView.this)) {
473                                         if (changeId.equals(IWorkbenchPage.CHANGE_VIEW_SHOW)) {
474                                                 if (!batchedEntries.isEmpty()) {
475                                                         pushBatchedEntries();
476                                                 }
477
478                                                 batchEntries = false;
479                                         } else if (changeId.equals(IWorkbenchPage.CHANGE_VIEW_HIDE)) {
480                                                 batchEntries = true;
481                                         }
482                                 }
483                         }
484
485                         public void perspectiveActivated(IWorkbenchPage page, IPerspectiveDescriptor perspective) {
486                                 // empty
487                         }
488
489                         public void perspectiveChanged(IWorkbenchPage page, IPerspectiveDescriptor perspective, String changeId) {
490                                 // empty
491                         }
492
493                 });
494         }
495
496         /**
497          * Creates the actions for the viewsite action bars
498          */
499         private void createActions() {
500                 IActionBars bars = getViewSite().getActionBars();
501
502                 fCopyAction = createCopyAction();
503                 bars.setGlobalActionHandler(ActionFactory.COPY.getId(), fCopyAction);
504
505                 IToolBarManager toolBarManager = bars.getToolBarManager();
506
507                 fExportAction = createExportAction();
508                 toolBarManager.add(fExportAction);
509
510                 final Action importLogAction = createImportLogAction();
511                 toolBarManager.add(importLogAction);
512
513         toolBarManager.add(new Separator());
514
515                 final Action clearAction = createClearAction();
516                 toolBarManager.add(clearAction);
517
518                 fDeleteLogAction = createDeleteLogAction();
519                 toolBarManager.add(fDeleteLogAction);
520
521                 fOpenLogAction = createOpenLogAction();
522                 toolBarManager.add(fOpenLogAction);
523
524                 fReadLogAction = createReadLogAction();
525                 toolBarManager.add(fReadLogAction);
526
527                 toolBarManager.add(new Separator());
528                 toolBarManager.add(createShowTextFilter());
529
530         // FOR TESTING
531 //        toolBarManager.add(new Separator());
532 //        final Action testAction = createTestAction();
533 //        toolBarManager.add(testAction);
534         // FOR TESTING ENDS
535                 
536                 IMenuManager mgr = bars.getMenuManager();
537
538                 mgr.add(createGroupByAction());
539
540                 mgr.add(new Separator());
541
542                 mgr.add(createFilterAction());
543
544                 mgr.add(new Separator());
545
546                 fActivateViewAction = createActivateViewAction();
547                 mgr.add(fActivateViewAction);
548
549 //              mgr.add(createShowTextFilter());
550
551                 createPropertiesAction();
552
553                 MenuManager popupMenuManager = new MenuManager("#PopupMenu"); //$NON-NLS-1$
554                 IMenuListener listener = new IMenuListener() {
555                         public void menuAboutToShow(IMenuManager manager) {
556                                 manager.add(fCopyAction);
557                                 manager.add(new Separator());
558                                 manager.add(clearAction);
559                                 manager.add(fDeleteLogAction);
560                                 manager.add(fOpenLogAction);
561                                 manager.add(fReadLogAction);
562                                 manager.add(new Separator());
563                                 manager.add(fExportAction);
564                                 manager.add(createImportLogAction());
565                                 manager.add(new Separator());
566
567                                 ((EventDetailsDialogAction) fPropertiesAction).setComparator(fComparator);
568                                 TreeItem[] selection = fTree.getSelection();
569                                 if ((selection.length > 0) && (selection[0].getData() instanceof LogEntry)) {
570                                         manager.add(fPropertiesAction);
571                                 }
572
573                                 manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
574                         }
575                 };
576                 popupMenuManager.addMenuListener(listener);
577                 popupMenuManager.setRemoveAllWhenShown(true);
578                 getSite().registerContextMenu(popupMenuManager, getSite().getSelectionProvider());
579                 Menu menu = popupMenuManager.createContextMenu(fTree);
580                 fTree.setMenu(menu);
581         }
582
583         private Action createActivateViewAction() {
584                 Action action = new Action(Messages.LogView_activate) { //              
585                         public void run() {
586                                 fMemento.putString(P_ACTIVATE, isChecked() ? "true" : "false"); //$NON-NLS-1$ //$NON-NLS-2$
587                         }
588                 };
589                 action.setChecked(fMemento.getString(P_ACTIVATE).equals("true")); //$NON-NLS-1$
590                 return action;
591         }
592
593     @SuppressWarnings("unused")
594     private Action createTestAction() {
595         Action action = new Action("Test") { //$NON-NLS-1$
596             public void run() {
597                 IStatus s1 = new Status(IStatus.INFO, Activator.PLUGIN_ID, "Test message 1", null);  //$NON-NLS-1$
598                 IStatus s2 = new Status(IStatus.WARNING, Activator.PLUGIN_ID, "Test message 2", null);  //$NON-NLS-1$
599                 IStatus s3 = new DetailStatus(IStatus.ERROR, Activator.PLUGIN_ID, "This is the short message.", HtmlUtil.p("A multi-lined message...\n<br/>continuing...<br/><br/>still...<br/>Error occurred, report at {0}" + HtmlUtil.a("http://www.simantics.org", "simantics.org")), null);  //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-1$
600                 MessageService.defaultLog(s1);
601                 MessageService.defaultLog(s2);
602                 MessageService.defaultLog(s3);
603 //                Activator.getDefault().getLog().log(s1);
604 //                Activator.getDefault().getLog().log(s2);
605 //                Activator.getDefault().getLog().log(s3);
606
607                 MultiStatus s4 = new MultiStatus(Activator.PLUGIN_ID, 0, "Test message 4", new Exception()); //$NON-NLS-1$
608                 s4.merge(new Status(IStatus.INFO, Activator.PLUGIN_ID, "MultiStatus Test 1", null)); //$NON-NLS-1$
609                 s4.merge(new Status(IStatus.WARNING, Activator.PLUGIN_ID, "MultiStatus Test 2", null)); //$NON-NLS-1$
610                 s4.merge(new Status(IStatus.ERROR, Activator.PLUGIN_ID, "MultiStatus Test 3", null)); //$NON-NLS-1$
611                 MessageService.defaultLog(s4);
612 //                Activator.getDefault().getLog().log(s4);
613             }
614         };
615         action.setImageDescriptor(ImageDescriptor.getMissingImageDescriptor());
616         action.setToolTipText("Produce test log entries"); //$NON-NLS-1$
617         return action;
618     }
619
620     private Action createClearAction() {
621         Action action = new Action(Messages.LogView_clear) {
622             public void run() {
623                 handleClear();
624             }
625         };
626         action.setImageDescriptor(SharedImages.getImageDescriptor(SharedImages.DESC_CLEAR));
627         action.setDisabledImageDescriptor(SharedImages.getImageDescriptor(SharedImages.DESC_CLEAR_DISABLED));
628         action.setToolTipText(Messages.LogView_clear_tooltip);
629         action.setText(Messages.LogView_clear);
630         return action;
631     }
632
633         private Action createCopyAction() {
634                 Action action = new Action(Messages.LogView_copy) {
635                         public void run() {
636                                 copyToClipboard(fFilteredTree.getViewer().getSelection());
637                         }
638                 };
639                 action.setImageDescriptor(PlatformUI.getWorkbench().getSharedImages().getImageDescriptor(ISharedImages.IMG_TOOL_COPY));
640                 return action;
641         }
642
643         private Action createDeleteLogAction() {
644                 Action action = new Action(Messages.LogView_delete) {
645                         public void run() {
646                                 doDeleteLog();
647                         }
648                 };
649                 action.setToolTipText(Messages.LogView_delete_tooltip);
650                 action.setImageDescriptor(SharedImages.getImageDescriptor(SharedImages.DESC_REMOVE_LOG));
651                 action.setDisabledImageDescriptor(SharedImages.getImageDescriptor(SharedImages.DESC_REMOVE_LOG_DISABLED));
652                 action.setEnabled(fInputFile.exists() && isPlatformLog(fInputFile));
653                 return action;
654         }
655
656         private Action createExportAction() {
657                 Action action = new Action(Messages.LogView_export) {
658                         public void run() {
659                                 handleExport();
660                         }
661                 };
662                 action.setToolTipText(Messages.LogView_export_tooltip);
663                 action.setImageDescriptor(SharedImages.getImageDescriptor(SharedImages.DESC_EXPORT));
664                 action.setDisabledImageDescriptor(SharedImages.getImageDescriptor(SharedImages.DESC_EXPORT_DISABLED));
665                 action.setEnabled(fInputFile.exists());
666                 return action;
667         }
668
669         private Action createFilterAction() {
670                 Action action = new Action(Messages.LogView_filter) {
671                         public void run() {
672                                 handleFilter();
673                         }
674                 };
675                 action.setToolTipText(Messages.LogView_filter);
676                 action.setImageDescriptor(SharedImages.getImageDescriptor(SharedImages.DESC_FILTER));
677                 action.setDisabledImageDescriptor(SharedImages.getImageDescriptor(SharedImages.DESC_FILTER_DISABLED));
678                 return action;
679         }
680
681         private Action createImportLogAction() {
682                 Action action = new ImportLogAction(this, Messages.LogView_import, fMemento);
683                 action.setToolTipText(Messages.LogView_import_tooltip);
684                 action.setImageDescriptor(SharedImages.getImageDescriptor(SharedImages.DESC_IMPORT));
685                 action.setDisabledImageDescriptor(SharedImages.getImageDescriptor(SharedImages.DESC_IMPORT_DISABLED));
686                 return action;
687         }
688
689         private Action createOpenLogAction() {
690                 Action action = null;
691                 /*
692                 try {
693                         // TODO this isn't the best way to check... we should be smarter and use package admin
694                         // check to see if org.eclipse.ui.ide is available
695                         Class.forName("org.eclipse.ui.ide.IDE"); //$NON-NLS-1$
696                         // check to see if org.eclipse.core.filesystem is available
697                         Class.forName("org.eclipse.core.filesystem.IFileStore"); //$NON-NLS-1$
698                         action = new OpenIDELogFileAction(this);
699                 } catch (ClassNotFoundException e) {
700                 */
701                         action = new Action() {
702                                 public void run() {
703                                         if (fInputFile.exists()) {
704                                                 Job job = getOpenLogFileJob();
705                                                 job.setUser(false);
706                                                 job.setPriority(Job.SHORT);
707                                                 job.schedule();
708                                         }
709                                 }
710                         };
711                 //}
712                 action.setText(Messages.LogView_view_currentLog);
713                 action.setImageDescriptor(SharedImages.getImageDescriptor(SharedImages.DESC_OPEN_LOG));
714                 action.setDisabledImageDescriptor(SharedImages.getImageDescriptor(SharedImages.DESC_OPEN_LOG_DISABLED));
715                 action.setEnabled(fInputFile.exists());
716                 action.setToolTipText(Messages.LogView_view_currentLog_tooltip);
717                 return action;
718         }
719
720         private void createPropertiesAction() {
721                 fPropertiesAction = new EventDetailsDialogAction(fTree.getShell(), fFilteredTree.getViewer(), fMemento);
722                 fPropertiesAction.setImageDescriptor(SharedImages.getImageDescriptor(SharedImages.DESC_PROPERTIES));
723                 fPropertiesAction.setDisabledImageDescriptor(SharedImages.getImageDescriptor(SharedImages.DESC_PROPERTIES_DISABLED));
724                 fPropertiesAction.setToolTipText(Messages.LogView_properties_tooltip);
725                 fPropertiesAction.setEnabled(false);
726         }
727
728         private Action createReadLogAction() {
729                 Action action = new Action(Messages.LogView_readLog_restore) {
730                         public void run() {
731                                 fInputFile = getPlatformLogFile();
732                                 reloadLog();
733                         }
734                 };
735                 action.setToolTipText(Messages.LogView_readLog_restore_tooltip);
736                 action.setImageDescriptor(SharedImages.getImageDescriptor(SharedImages.DESC_READ_LOG));
737                 action.setDisabledImageDescriptor(SharedImages.getImageDescriptor(SharedImages.DESC_READ_LOG_DISABLED));
738                 return action;
739         }
740
741         /**
742          * Creates the Show Text Filter view menu action 
743          * @return the new action for the Show Text Filter 
744          */
745         private Action createShowTextFilter() {
746                 Action action = new Action(Messages.LogView_show_filter_text, Action.AS_CHECK_BOX) {
747                         public void run() {
748                                 showFilterText(isChecked());
749                         }
750                 };
751         action.setToolTipText(Messages.LogView_show_filter_tooltip);
752         action.setImageDescriptor(SharedImages.getImageDescriptor(SharedImages.DESC_SHOW_TEXT_FILTER));
753         boolean visible = fMemento.getBoolean(P_SHOW_FILTER_TEXT).booleanValue();
754         action.setChecked(visible);
755                 showFilterText(visible);
756                 return action;
757         }
758
759         /**
760          * Shows/hides the filter text control from the filtered tree. This method also sets the 
761          * P_SHOW_FILTER_TEXT preference to the visible state
762          * 
763          * @param visible if the filter text control should be shown or not
764          */
765         private void showFilterText(boolean visible) {
766                 fMemento.putBoolean(P_SHOW_FILTER_TEXT, visible);
767                 Composite ctrl = fFilteredTree.getFilterControl().getParent();
768                 GridData gd = (GridData) ctrl.getLayoutData();
769                 gd.exclude = !visible;
770                 ctrl.setVisible(visible);
771                 gd.verticalIndent = 8;
772                 gd.horizontalIndent = 4;
773                 if (!visible) // reset control if we aren't visible
774                         fFilteredTree.getFilterControl().setText(Messages.LogView_show_filter_initialText);
775                 fFilteredTree.layout(false);
776         }
777
778         private IContributionItem createGroupByAction() {
779                 IMenuManager manager = new MenuManager(Messages.LogView_GroupBy);
780                 manager.add(new GroupByAction(Messages.LogView_GroupBySession, LogView.GROUP_BY_SESSION));
781                 manager.add(new GroupByAction(Messages.LogView_GroupByPlugin, LogView.GROUP_BY_PLUGIN));
782                 manager.add(new GroupByAction(Messages.LogView_GroupByNone, LogView.GROUP_BY_NONE));
783                 return manager;
784         }
785
786         private void createViewer(Composite parent) {
787
788                 fFilteredTree = new FilteredTree(parent, SWT.FULL_SELECTION, new PatternFilter() {
789                         protected boolean isLeafMatch(Viewer viewer, Object element) {
790                                 if (element instanceof LogEntry) {
791                                         LogEntry logEntry = (LogEntry) element;
792                                         String message = logEntry.getMessage();
793                                         String plugin = logEntry.getPluginId();
794                                         String date = logEntry.getFormattedDate();
795                                         return wordMatches(message) || wordMatches(plugin) || wordMatches(date);
796                                 }
797                                 return false;
798                         }
799                 });
800                 fFilteredTree.setInitialText(Messages.LogView_show_filter_initialText);
801                 fTree = fFilteredTree.getViewer().getTree();
802                 createColumns(fTree);
803                 fTree.setLinesVisible(true);
804                 fFilteredTree.getViewer().setAutoExpandLevel(fMemento.getInteger(P_EXPAND_LEVEL).intValue());
805                 fFilteredTree.getViewer().setContentProvider(new LogViewContentProvider(this));
806                 fFilteredTree.getViewer().setLabelProvider(fLabelProvider = new LogViewLabelProvider(this));
807                 fLabelProvider.connect(this);
808                 fFilteredTree.getViewer().addSelectionChangedListener(new ISelectionChangedListener() {
809                         public void selectionChanged(SelectionChangedEvent e) {
810                                 handleSelectionChanged(e.getSelection());
811                                 if (fPropertiesAction.isEnabled())
812                                         ((EventDetailsDialogAction) fPropertiesAction).resetSelection();
813                         }
814                 });
815                 fFilteredTree.getViewer().addDoubleClickListener(new IDoubleClickListener() {
816                         public void doubleClick(DoubleClickEvent event) {
817                                 ((EventDetailsDialogAction) fPropertiesAction).setComparator(fComparator);
818                                 fPropertiesAction.run();
819                         }
820                 });
821                 fFilteredTree.getViewer().setInput(this);
822                 addMouseListeners();
823                 addDragSource();
824         }
825
826         private void createColumns(final Tree tree) {
827                 fColumn1 = new TreeColumn(tree, SWT.LEFT);
828                 fColumn1.setText(Messages.LogView_column_message);
829                 fColumn1.setWidth(fMemento.getInteger(P_COLUMN_1).intValue());
830                 fColumn1.addSelectionListener(new SelectionAdapter() {
831                         public void widgetSelected(SelectionEvent e) {
832                                 MESSAGE_ORDER *= -1;
833                                 ViewerComparator comparator = getViewerComparator(MESSAGE);
834                                 fFilteredTree.getViewer().setComparator(comparator);
835                                 boolean isComparatorSet = ((EventDetailsDialogAction) fPropertiesAction).resetSelection(MESSAGE, MESSAGE_ORDER);
836                                 setComparator(MESSAGE);
837                                 if (!isComparatorSet)
838                                         ((EventDetailsDialogAction) fPropertiesAction).setComparator(fComparator);
839                                 fMemento.putInteger(P_ORDER_VALUE, MESSAGE_ORDER);
840                                 fMemento.putInteger(P_ORDER_TYPE, MESSAGE);
841                                 setColumnSorting(fColumn1, MESSAGE_ORDER);
842                         }
843                 });
844                 tree.addControlListener(new ControlAdapter() {
845
846                         @Override
847                         public void controlResized(ControlEvent e) {
848                                 fColumn1.setWidth(tree.getSize().x - 20);
849                         }
850                         
851                 });
852
853 //              fColumn2 = new TreeColumn(tree, SWT.LEFT);
854 //              fColumn2.setText(Messages.LogView_column_plugin);
855 //              fColumn2.setWidth(fMemento.getInteger(P_COLUMN_2).intValue());
856 //              fColumn2.addSelectionListener(new SelectionAdapter() {
857 //                      public void widgetSelected(SelectionEvent e) {
858 //                              PLUGIN_ORDER *= -1;
859 //                              ViewerComparator comparator = getViewerComparator(PLUGIN);
860 //                              fFilteredTree.getViewer().setComparator(comparator);
861 //                              boolean isComparatorSet = ((EventDetailsDialogAction) fPropertiesAction).resetSelection(PLUGIN, PLUGIN_ORDER);
862 //                              setComparator(PLUGIN);
863 //                              if (!isComparatorSet)
864 //                                      ((EventDetailsDialogAction) fPropertiesAction).setComparator(fComparator);
865 //                              fMemento.putInteger(P_ORDER_VALUE, PLUGIN_ORDER);
866 //                              fMemento.putInteger(P_ORDER_TYPE, PLUGIN);
867 //                              setColumnSorting(fColumn2, PLUGIN_ORDER);
868 //                      }
869 //              });
870 //
871 //              fColumn3 = new TreeColumn(tree, SWT.LEFT);
872 //              fColumn3.setText(Messages.LogView_column_date);
873 //              fColumn3.setWidth(fMemento.getInteger(P_COLUMN_3).intValue());
874 //              fColumn3.addSelectionListener(new SelectionAdapter() {
875 //                      public void widgetSelected(SelectionEvent e) {
876 //                              DATE_ORDER *= -1;
877 //                              ViewerComparator comparator = getViewerComparator(DATE);
878 //                              fFilteredTree.getViewer().setComparator(comparator);
879 //                              setComparator(DATE);
880 //                              ((EventDetailsDialogAction) fPropertiesAction).setComparator(fComparator);
881 //                              fMemento.putInteger(P_ORDER_VALUE, DATE_ORDER);
882 //                              fMemento.putInteger(P_ORDER_TYPE, DATE);
883 //                              setColumnSorting(fColumn3, DATE_ORDER);
884 //                      }
885 //              });
886
887                 tree.setHeaderVisible(true);
888         }
889
890         private void initializeViewerSorter() {
891                 byte orderType = fMemento.getInteger(P_ORDER_TYPE).byteValue();
892                 ViewerComparator comparator = getViewerComparator(orderType);
893                 fFilteredTree.getViewer().setComparator(comparator);
894                 if (orderType == MESSAGE)
895                         setColumnSorting(fColumn1, MESSAGE_ORDER);
896 //              else if (orderType == PLUGIN)
897 //                      setColumnSorting(fColumn2, PLUGIN_ORDER);
898 //              else if (orderType == DATE)
899 //                      setColumnSorting(fColumn3, DATE_ORDER);
900         }
901
902         private void setColumnSorting(TreeColumn column, int order) {
903                 fTree.setSortColumn(column);
904                 fTree.setSortDirection(order == ASCENDING ? SWT.UP : SWT.DOWN);
905         }
906
907         public void dispose() {
908                 writeSettings();
909                 MessageService.getDefault().removeLogListener(this);
910                 fClipboard.dispose();
911                 if (fTextShell != null)
912                         fTextShell.dispose();
913                 fLabelProvider.disconnect(this);
914                 fFilteredTree.dispose();
915                 resourceManager.dispose();
916                 super.dispose();
917         }
918
919         void handleImport() {
920                 FileDialog dialog = new FileDialog(getViewSite().getShell());
921                 dialog.setFilterExtensions(new String[] {"*.log"}); //$NON-NLS-1$
922                 if (fDirectory != null)
923                         dialog.setFilterPath(fDirectory);
924                 String path = dialog.open();
925                 if (path == null) { // cancel
926                         return;
927                 }
928
929                 File file = new Path(path).toFile();
930                 if (file.exists()) {
931                         handleImportPath(path);
932                 } else {
933                         String msg = NLS.bind(Messages.LogView_FileCouldNotBeFound, file.getName());
934                         MessageDialog.openError(getViewSite().getShell(), Messages.LogView_OpenFile, msg);
935                 }
936         }
937
938         public void handleImportPath(String path) {
939                 if (path != null && new Path(path).toFile().exists()) {
940                         fInputFile = new Path(path).toFile();
941                         fDirectory = fInputFile.getParent();
942                         IRunnableWithProgress op = new IRunnableWithProgress() {
943                                 public void run(IProgressMonitor monitor) {
944                                         monitor.beginTask(Messages.LogView_operation_importing, IProgressMonitor.UNKNOWN);
945                                         readLogFile();
946                                 }
947                         };
948                         ProgressMonitorDialog pmd = new ProgressMonitorDialog(getViewSite().getShell());
949                         try {
950                                 pmd.run(true, true, op);
951                         } catch (InvocationTargetException e) { // do nothing
952                         } catch (InterruptedException e) { // do nothing
953                         } finally {
954                                 fReadLogAction.setText(Messages.LogView_readLog_reload);
955                                 fReadLogAction.setToolTipText(Messages.LogView_readLog_reload);
956                                 asyncRefresh(false);
957                                 resetDialogButtons();
958                         }
959                 }
960         }
961
962         private void handleExport() {
963                 FileDialog dialog = new FileDialog(getViewSite().getShell(), SWT.SAVE);
964                 dialog.setFilterExtensions(new String[] {"*.log"}); //$NON-NLS-1$
965                 if (fDirectory != null)
966                         dialog.setFilterPath(fDirectory);
967                 String path = dialog.open();
968                 if (path != null) {
969                         if (path.indexOf('.') == -1 && !path.endsWith(".log")) //$NON-NLS-1$
970                                 path += ".log"; //$NON-NLS-1$
971                         File outputFile = new Path(path).toFile();
972                         fDirectory = outputFile.getParent();
973                         if (outputFile.exists()) {
974                                 String message = NLS.bind(Messages.LogView_confirmOverwrite_message, outputFile.toString());
975                                 if (!MessageDialog.openQuestion(getViewSite().getShell(), Messages.LogView_exportLog, message))
976                                         return;
977                         }
978                         copy(fInputFile, outputFile);
979                 }
980         }
981
982         private void copy(File inputFile, File outputFile) {
983                 BufferedReader reader = null;
984                 BufferedWriter writer = null;
985                 try {
986                         reader = new BufferedReader(new InputStreamReader(new FileInputStream(inputFile), "UTF-8")); //$NON-NLS-1$
987                         writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outputFile), "UTF-8")); //$NON-NLS-1$
988                         while (reader.ready()) {
989                                 writer.write(reader.readLine());
990                                 writer.write(System.getProperty("line.separator")); //$NON-NLS-1$
991                         }
992                 } catch (IOException e) { // do nothing
993                 } finally {
994                         try {
995                                 if (reader != null)
996                                         reader.close();
997                                 if (writer != null)
998                                         writer.close();
999                         } catch (IOException e1) { // do nothing
1000                         }
1001                 }
1002         }
1003
1004         private void handleFilter() {
1005                 FilterDialog dialog = new FilterDialog(Activator.getDefault().getWorkbench().getActiveWorkbenchWindow().getShell(), fMemento);
1006                 dialog.create();
1007                 dialog.getShell().setText(Messages.LogView_FilterDialog_title);
1008                 if (dialog.open() == Window.OK)
1009                         reloadLog();
1010         }
1011
1012         private void doDeleteLog() {
1013                 String title = Messages.LogView_confirmDelete_title;
1014                 String message = Messages.LogView_confirmDelete_message;
1015                 if (!MessageDialog.openConfirm(fTree.getShell(), title, message))
1016                         return;
1017                 if (fInputFile.delete() || elements.size() > 0) {
1018                         elements.clear();
1019                         groups.clear();
1020                         currentSession.removeAllChildren();
1021                         asyncRefresh(false);
1022                         resetDialogButtons();
1023                 }
1024         }
1025
1026         public void fillContextMenu(IMenuManager manager) { // nothing
1027         }
1028
1029         public AbstractEntry[] getElements() {
1030                 return (AbstractEntry[]) elements.toArray(new AbstractEntry[elements.size()]);
1031         }
1032
1033         protected void handleClear() {
1034                 BusyIndicator.showWhile(fTree.getDisplay(), new Runnable() {
1035                         public void run() {
1036                                 elements.clear();
1037                                 groups.clear();
1038                                 currentSession.removeAllChildren();
1039                                 asyncRefresh(false);
1040                                 resetDialogButtons();
1041                         }
1042                 });
1043         }
1044
1045         protected void reloadLog() {
1046                 IRunnableWithProgress op = new IRunnableWithProgress() {
1047                         public void run(IProgressMonitor monitor) {
1048                                 monitor.beginTask(Messages.LogView_operation_reloading, IProgressMonitor.UNKNOWN);
1049                                 readLogFile();
1050                         }
1051                 };
1052                 ProgressMonitorDialog pmd = new ProgressMonitorDialog(getViewSite().getShell());
1053                 try {
1054                         pmd.run(true, true, op);
1055                 } catch (InvocationTargetException e) { // do nothing
1056                 } catch (InterruptedException e) { // do nothing
1057                 } finally {
1058                         fReadLogAction.setText(Messages.LogView_readLog_restore);
1059                         fReadLogAction.setToolTipText(Messages.LogView_readLog_restore);
1060                         asyncRefresh(false);
1061                         resetDialogButtons();
1062                 }
1063         }
1064
1065         private void readLogFile() {
1066                 if (!fInputFile.exists())
1067                         return;
1068
1069                 elements.clear();
1070                 groups.clear();
1071
1072                 List<Object> result = new ArrayList<Object>();
1073                 currentSession = LogReader.parseLogFile(fInputFile, result, fMemento);
1074                 group(result);
1075                 limitEntriesCount();
1076
1077                 getSite().getShell().getDisplay().asyncExec(new Runnable() {
1078                         public void run() {
1079                                 setContentDescription(getTitleSummary());
1080                         }
1081                 });
1082
1083         }
1084
1085         private String getTitleSummary() {
1086                 String path = ""; //$NON-NLS-1$
1087                 try {
1088                         path = fInputFile.getCanonicalPath();
1089                 } catch (IOException e) { // log nothing
1090                 }
1091
1092                 if (isPlatformLogOpen()) {
1093                     // Remove the content description in this case
1094                     // to save vertical space from the view.
1095                         //return Messages.LogView_WorkspaceLogFile;
1096                     return ""; //$NON-NLS-1$
1097                 }
1098
1099                 Map<String, File> sources = LogFilesManager.getLogSources();
1100                 if (sources.containsValue(path)) {
1101                         for (Iterator<String> i = sources.keySet().iterator(); i.hasNext();) {
1102                                 String key = i.next();
1103                                 if (sources.get(key).equals(path)) {
1104                                         return NLS.bind(Messages.LogView_LogFileTitle, new String[] {key, path});
1105                                 }
1106                         }
1107                 }
1108
1109                 return path;
1110         }
1111
1112         /**
1113          * Add new entries to correct groups in the view.
1114          * @param entries new entries to show up in groups in the view.
1115          */
1116         private void group(List<?> entries) {
1117                 if (fMemento.getInteger(P_GROUP_BY).intValue() == GROUP_BY_NONE) {
1118                         elements.addAll(entries);
1119                 } else {
1120                         for (Iterator<?> i = entries.iterator(); i.hasNext();) {
1121                                 LogEntry entry = (LogEntry) i.next();
1122                                 Group group = getGroup(entry);
1123                                 group.addChild(entry);
1124                         }
1125                 }
1126         }
1127
1128         /**
1129          * Limits the number of entries according to the max entries limit set in
1130          * memento.
1131          */
1132         private void limitEntriesCount() {
1133                 int limit = Integer.MAX_VALUE;
1134                 if (fMemento.getString(LogView.P_USE_LIMIT).equals("true")) {//$NON-NLS-1$
1135                         limit = fMemento.getInteger(LogView.P_LOG_LIMIT).intValue();
1136                 }
1137
1138                 int entriesCount = getEntriesCount();
1139
1140                 if (entriesCount <= limit) {
1141                         return;
1142                 }
1143                 Comparator<Object> dateComparator = new Comparator<Object>() {
1144                         public int compare(Object o1, Object o2) {
1145                                 Date l1 = ((LogEntry) o1).getDate();
1146                                 Date l2 = ((LogEntry) o2).getDate();
1147                                 if ((l1 != null) && (l2 != null)) {
1148                                         return l1.before(l2) ? -1 : 1;
1149                                 } else if ((l1 == null) && (l2 == null)) {
1150                                         return 0;
1151                                 } else
1152                                         return (l1 == null) ? -1 : 1;
1153                         }
1154                 };
1155
1156                 if (fMemento.getInteger(P_GROUP_BY).intValue() == GROUP_BY_NONE) {
1157                         elements.subList(0, elements.size() - limit).clear();
1158                 } else {
1159                         List<Object> copy = new ArrayList<Object>(entriesCount);
1160                         for (Iterator<?> i = elements.iterator(); i.hasNext();) {
1161                                 AbstractEntry group = (AbstractEntry) i.next();
1162                                 copy.addAll(Arrays.asList(group.getChildren(group)));
1163                         }
1164
1165                         Collections.sort(copy, dateComparator);
1166                         List<?> toRemove = copy.subList(0, copy.size() - limit);
1167
1168                         for (Iterator<?> i = elements.iterator(); i.hasNext();) {
1169                                 AbstractEntry group = (AbstractEntry) i.next();
1170                                 group.removeChildren(toRemove);
1171                         }
1172                 }
1173
1174         }
1175
1176         private int getEntriesCount() {
1177                 if (fMemento.getInteger(P_GROUP_BY).intValue() == GROUP_BY_NONE) {
1178                         return elements.size();
1179                 }
1180                 int size = 0;
1181                 for (Iterator<?> i = elements.iterator(); i.hasNext();) {
1182                         AbstractEntry group = (AbstractEntry) i.next();
1183                         size += group.size();
1184                 }
1185                 return size;
1186         }
1187
1188         /**
1189          * Returns group appropriate for the entry. Group depends on P_GROUP_BY
1190          * preference, or is null if grouping is disabled (GROUP_BY_NONE), or group
1191          * could not be determined. May create group if it haven't existed before.
1192          * 
1193          * @param entry entry to be grouped
1194          * @return group or null if grouping is disabled
1195          */
1196         protected Group getGroup(LogEntry entry) {
1197                 int groupBy = fMemento.getInteger(P_GROUP_BY).intValue();
1198                 Object elementGroupId = null;
1199                 String groupName = null;
1200
1201                 switch (groupBy) {
1202                         case GROUP_BY_PLUGIN :
1203                                 groupName = entry.getPluginId();
1204                                 elementGroupId = groupName;
1205                                 break;
1206
1207                         case GROUP_BY_SESSION :
1208                                 elementGroupId = entry.getSession();
1209                                 break;
1210
1211                         default : // grouping is disabled
1212                                 return null;
1213                 }
1214
1215                 if (elementGroupId == null) { // could not determine group
1216                         return null;
1217                 }
1218
1219                 Group group = (Group) groups.get(elementGroupId);
1220                 if (group == null) {
1221                         if (groupBy == GROUP_BY_SESSION) {
1222                                 group = entry.getSession();
1223                         } else {
1224                                 group = new Group(groupName);
1225                         }
1226                         groups.put(elementGroupId, group);
1227                         elements.add(group);
1228                 }
1229
1230                 return group;
1231         }
1232
1233         public void logging(IStatus status, String plugin) {
1234                 if (!isPlatformLog(fInputFile))
1235                         return;
1236
1237                 if (batchEntries) {
1238                         // create LogEntry immediately to don't loose IStatus creation date.
1239                         LogEntry entry = createLogEntry(status);
1240                         batchedEntries.add(entry);
1241                         return;
1242                 }
1243
1244                 if (fFirstEvent) {
1245                         readLogFile();
1246                         asyncRefresh(true);
1247                         fFirstEvent = false;
1248                 } else {
1249                         LogEntry entry = createLogEntry(status);
1250
1251                         if (!batchedEntries.isEmpty()) {
1252                                 // batch new entry as well, to have only one asyncRefresh()
1253                                 batchedEntries.add(entry);
1254                                 pushBatchedEntries();
1255                         } else {
1256                                 pushEntry(entry);
1257                                 asyncRefresh(true);
1258                         }
1259                 }
1260         }
1261
1262         /**
1263          * Push batched entries to log view.
1264          */
1265         private void pushBatchedEntries() {
1266                 Job job = new Job(Messages.LogView_AddingBatchedEvents) {
1267                         protected IStatus run(IProgressMonitor monitor) {
1268                                 for (int i = 0; i < batchedEntries.size(); i++) {
1269                                         if (!monitor.isCanceled()) {
1270                                                 LogEntry entry = (LogEntry) batchedEntries.get(i);
1271                                                 pushEntry(entry);
1272                                                 batchedEntries.remove(i);
1273                                         }
1274                                 }
1275                                 asyncRefresh(true);
1276                                 return Status.OK_STATUS;
1277                         }
1278                 };
1279                 job.schedule();
1280         }
1281
1282         private LogEntry createLogEntry(IStatus status) {
1283                 LogEntry entry = new LogEntry(status);
1284                 entry.setSession(currentSession);
1285                 return entry;
1286         }
1287
1288         private synchronized void pushEntry(LogEntry entry) {
1289                 if (LogReader.isLogged(entry, fMemento)) {
1290                         group(Collections.singletonList(entry));
1291                         limitEntriesCount();
1292                 }
1293                 asyncRefresh(true);
1294         }
1295
1296         private void asyncRefresh(final boolean activate) {
1297                 if (fTree.isDisposed())
1298                         return;
1299                 Display display = fTree.getDisplay();
1300                 final ViewPart view = this;
1301                 if (display != null) {
1302                         display.asyncExec(new Runnable() {
1303                                 public void run() {
1304                                         if (!fTree.isDisposed()) {
1305                                                 fFilteredTree.getViewer().refresh();
1306                                                 fFilteredTree.getViewer().expandToLevel(fMemento.getInteger(P_EXPAND_LEVEL).intValue());
1307                                                 fDeleteLogAction.setEnabled(fInputFile.exists() && isPlatformLog(fInputFile));
1308                                                 fOpenLogAction.setEnabled(fInputFile.exists());
1309                                                 fExportAction.setEnabled(fInputFile.exists());
1310                                                 if (activate && fActivateViewAction.isChecked()) {
1311                                                         IWorkbenchPage page = Activator.getDefault().getWorkbench().getActiveWorkbenchWindow().getActivePage();
1312                                                         if (page != null)
1313                                                                 page.bringToTop(view);
1314                                                 }
1315                                         }
1316                                 }
1317                         });
1318                 }
1319         }
1320
1321         public void setFocus() {
1322                 if (fFilteredTree != null && !fFilteredTree.isDisposed())
1323                         fFilteredTree.setFocus();
1324         }
1325
1326         private void handleSelectionChanged(ISelection selection) {
1327                 updateStatus(selection);
1328                 fCopyAction.setEnabled((!selection.isEmpty()) && ((IStructuredSelection) selection).getFirstElement() instanceof LogEntry);
1329                 fPropertiesAction.setEnabled(!selection.isEmpty());
1330         }
1331
1332         private void updateStatus(ISelection selection) {
1333                 IStatusLineManager status = getViewSite().getActionBars().getStatusLineManager();
1334                 if (selection.isEmpty())
1335                         status.setMessage(null);
1336                 else {
1337                         Object element = ((IStructuredSelection) selection).getFirstElement();
1338                         status.setMessage(((LogViewLabelProvider) fFilteredTree.getViewer().getLabelProvider()).getColumnText(element, 0));
1339                 }
1340         }
1341
1342         /**
1343          * Converts selected log view element to string.
1344          * @return textual log entry representation or null if selection doesn't contain log entry
1345          */
1346         private static String selectionToString(ISelection selection) {
1347                 StringWriter writer = new StringWriter();
1348                 PrintWriter pwriter = new PrintWriter(writer);
1349                 if (selection.isEmpty())
1350                         return null;
1351                 LogEntry entry = (LogEntry) ((IStructuredSelection) selection).getFirstElement();
1352                 entry.write(pwriter);
1353                 pwriter.flush();
1354                 String textVersion = writer.toString();
1355                 try {
1356                         pwriter.close();
1357                         writer.close();
1358                 } catch (IOException e) {
1359                         // empty
1360                 }
1361
1362                 return textVersion;
1363         }
1364
1365         /**
1366          * Copies selected element to clipboard.
1367          */
1368         private void copyToClipboard(ISelection selection) {
1369                 String textVersion = selectionToString(selection);
1370                 if ((textVersion != null) && (textVersion.trim().length() > 0)) {
1371                         // set the clipboard contents
1372                         fClipboard.setContents(new Object[] {textVersion}, new Transfer[] {TextTransfer.getInstance()});
1373                 }
1374         }
1375
1376         public void init(IViewSite site, IMemento memento) throws PartInitException {
1377                 super.init(site, memento);
1378                 if (memento == null)
1379                         this.fMemento = XMLMemento.createWriteRoot("LOGVIEW"); //$NON-NLS-1$
1380                 else
1381                         this.fMemento = memento;
1382                 readSettings();
1383
1384                 // initialize column ordering 
1385                 final byte type = this.fMemento.getInteger(P_ORDER_TYPE).byteValue();
1386                 switch (type) {
1387                         case DATE :
1388                                 DATE_ORDER = this.fMemento.getInteger(P_ORDER_VALUE).intValue();
1389                                 MESSAGE_ORDER = DESCENDING;
1390                                 PLUGIN_ORDER = DESCENDING;
1391                                 break;
1392                         case MESSAGE :
1393                                 MESSAGE_ORDER = this.fMemento.getInteger(P_ORDER_VALUE).intValue();
1394                                 DATE_ORDER = DESCENDING;
1395                                 PLUGIN_ORDER = DESCENDING;
1396                                 break;
1397                         case PLUGIN :
1398                                 PLUGIN_ORDER = this.fMemento.getInteger(P_ORDER_VALUE).intValue();
1399                                 MESSAGE_ORDER = DESCENDING;
1400                                 DATE_ORDER = DESCENDING;
1401                                 break;
1402                         default :
1403                                 DATE_ORDER = DESCENDING;
1404                                 MESSAGE_ORDER = DESCENDING;
1405                                 PLUGIN_ORDER = DESCENDING;
1406                 }
1407                 setComparator(fMemento.getInteger(P_ORDER_TYPE).byteValue());
1408         }
1409
1410         private void initializeMemento() {
1411         if (fMemento.getInteger(P_EXPAND_LEVEL) == null) {
1412             fMemento.putInteger(P_EXPAND_LEVEL, DEFAULT_EXPAND_LEVEL);
1413         }
1414                 if (fMemento.getString(P_USE_LIMIT) == null) {
1415                         fMemento.putString(P_USE_LIMIT, "true"); //$NON-NLS-1$
1416                 }
1417                 if (fMemento.getInteger(P_LOG_LIMIT) == null) {
1418                         fMemento.putInteger(P_LOG_LIMIT, 50);
1419                 }
1420                 if (fMemento.getString(P_LOG_INFO) == null) {
1421                         fMemento.putString(P_LOG_INFO, "true"); //$NON-NLS-1$
1422                 }
1423                 if (fMemento.getString(P_LOG_WARNING) == null) {
1424                         fMemento.putString(P_LOG_WARNING, "true"); //$NON-NLS-1$
1425                 }
1426         if (fMemento.getString(P_LOG_ERROR) == null) {
1427             fMemento.putString(P_LOG_ERROR, "true"); //$NON-NLS-1$
1428         }
1429         if (fMemento.getString(P_LOG_DEBUG) == null) {
1430             fMemento.putString(P_LOG_DEBUG, "true"); //$NON-NLS-1$
1431         }
1432                 if (fMemento.getString(P_SHOW_ALL_SESSIONS) == null) {
1433                         fMemento.putString(P_SHOW_ALL_SESSIONS, "false"); //$NON-NLS-1$
1434                 }
1435                 Integer width = fMemento.getInteger(P_COLUMN_1);
1436                 if (width == null || width.intValue() == 0) {
1437                         fMemento.putInteger(P_COLUMN_1, 300);
1438                 }
1439                 width = fMemento.getInteger(P_COLUMN_2);
1440                 if (width == null || width.intValue() == 0) {
1441                         fMemento.putInteger(P_COLUMN_2, 150);
1442                 }
1443                 width = fMemento.getInteger(P_COLUMN_3);
1444                 if (width == null || width.intValue() == 0) {
1445                         fMemento.putInteger(P_COLUMN_3, 150);
1446                 }
1447                 if (fMemento.getString(P_ACTIVATE) == null) {
1448                         fMemento.putString(P_ACTIVATE, "false"); //$NON-NLS-1$
1449                 }
1450                 if (fMemento.getBoolean(P_SHOW_FILTER_TEXT) == null) {
1451                         fMemento.putBoolean(P_SHOW_FILTER_TEXT, false);
1452                 }
1453                 fMemento.putInteger(P_ORDER_VALUE, DESCENDING);
1454                 fMemento.putInteger(P_ORDER_TYPE, DATE);
1455                 if (fMemento.getInteger(P_GROUP_BY) == null) {
1456                         fMemento.putInteger(P_GROUP_BY, GROUP_BY_NONE);
1457                 }
1458         }
1459
1460         public void saveState(IMemento memento) {
1461                 if (this.fMemento == null || memento == null)
1462                         return;
1463                 this.fMemento.putInteger(P_COLUMN_1, fColumn1.getWidth());
1464 //              this.fMemento.putInteger(P_COLUMN_2, fColumn2.getWidth());
1465 //              this.fMemento.putInteger(P_COLUMN_3, fColumn3.getWidth());
1466                 this.fMemento.putString(P_ACTIVATE, fActivateViewAction.isChecked() ? "true" : "false"); //$NON-NLS-1$ //$NON-NLS-2$
1467                 memento.putMemento(this.fMemento);
1468                 writeSettings();
1469         }
1470
1471         private void addMouseListeners() {
1472                 Listener tableListener = new Listener() {
1473                         public void handleEvent(Event e) {
1474                                 switch (e.type) {
1475                                         case SWT.MouseMove :
1476                                                 onMouseMove(e);
1477                                                 break;
1478                                         case SWT.MouseHover :
1479                                                 onMouseHover(e);
1480                                                 break;
1481                                         case SWT.MouseDown :
1482                                                 onMouseDown(e);
1483                                                 break;
1484                                 }
1485                         }
1486                 };
1487                 int[] tableEvents = new int[] {SWT.MouseDown, SWT.MouseMove, SWT.MouseHover};
1488                 for (int i = 0; i < tableEvents.length; i++) {
1489                         fTree.addListener(tableEvents[i], tableListener);
1490                 }
1491         }
1492
1493         /**
1494          * Adds drag source support to error log tree.
1495          */
1496         private void addDragSource() {
1497                 DragSource source = new DragSource(fTree, DND.DROP_COPY);
1498                 Transfer[] types = new Transfer[] {TextTransfer.getInstance()};
1499                 source.setTransfer(types);
1500
1501                 source.addDragListener(new DragSourceAdapter() {
1502
1503                         public void dragStart(DragSourceEvent event) {
1504                                 ISelection selection = fFilteredTree.getViewer().getSelection();
1505                                 if (selection.isEmpty()) {
1506                                         event.doit = false;
1507                                         return;
1508                                 }
1509
1510                                 AbstractEntry entry = (AbstractEntry) ((TreeSelection) selection).getFirstElement();
1511                                 if (!(entry instanceof LogEntry)) {
1512                                         event.doit = false;
1513                                         return;
1514                                 }
1515                         }
1516
1517                         public void dragSetData(DragSourceEvent event) {
1518                                 if (!TextTransfer.getInstance().isSupportedType(event.dataType)) {
1519                                         return;
1520                                 }
1521
1522                                 ISelection selection = fFilteredTree.getViewer().getSelection();
1523                                 String textVersion = selectionToString(selection);
1524                                 event.data = textVersion;
1525                         }
1526                 });
1527         }
1528
1529         private void makeHoverShell() {
1530                 fTextShell = new Shell(fTree.getShell(), SWT.NO_FOCUS | SWT.ON_TOP | SWT.TOOL);
1531                 Display display = fTextShell.getDisplay();
1532                 fTextShell.setBackground(display.getSystemColor(SWT.COLOR_INFO_BACKGROUND));
1533                 GridLayout layout = new GridLayout(1, false);
1534                 int border = ((fTree.getShell().getStyle() & SWT.NO_TRIM) == 0) ? 0 : 1;
1535                 layout.marginHeight = border;
1536                 layout.marginWidth = border;
1537                 fTextShell.setLayout(layout);
1538                 fTextShell.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
1539                 Composite shellComposite = new Composite(fTextShell, SWT.NONE);
1540                 layout = new GridLayout();
1541                 layout.marginHeight = 0;
1542                 layout.marginWidth = 0;
1543                 shellComposite.setLayout(layout);
1544                 shellComposite.setLayoutData(new GridData(GridData.FILL_BOTH | GridData.VERTICAL_ALIGN_BEGINNING));
1545                 fTextLabel = new Text(shellComposite, SWT.WRAP | SWT.MULTI | SWT.READ_ONLY);
1546                 GridData gd = new GridData(GridData.FILL_BOTH);
1547                 gd.widthHint = 100;
1548                 gd.grabExcessHorizontalSpace = true;
1549                 fTextLabel.setLayoutData(gd);
1550                 Color c = fTree.getDisplay().getSystemColor(SWT.COLOR_INFO_BACKGROUND);
1551                 fTextLabel.setBackground(c);
1552                 c = fTree.getDisplay().getSystemColor(SWT.COLOR_INFO_FOREGROUND);
1553                 fTextLabel.setForeground(c);
1554                 fTextLabel.setEditable(false);
1555                 fTextShell.addDisposeListener(new DisposeListener() {
1556                         public void widgetDisposed(DisposeEvent e) {
1557                                 onTextShellDispose(e);
1558                         }
1559                 });
1560         }
1561
1562         void onTextShellDispose(DisposeEvent e) {
1563                 fCanOpenTextShell = true;
1564                 setFocus();
1565         }
1566
1567         void onMouseDown(Event e) {
1568                 if (fTextShell != null && !fTextShell.isDisposed() && !fTextShell.isFocusControl()) {
1569                         fTextShell.setVisible(false);
1570                         fCanOpenTextShell = true;
1571                 }
1572         }
1573
1574         void onMouseHover(Event e) {
1575                 if (!fCanOpenTextShell || fTextShell == null || fTextShell.isDisposed())
1576                         return;
1577                 fCanOpenTextShell = false;
1578                 Point point = new Point(e.x, e.y);
1579                 TreeItem item = fTree.getItem(point);
1580                 if (item == null)
1581                         return;
1582
1583                 String message = null;
1584                 if (item.getData() instanceof LogEntry) {
1585                         message = ((LogEntry) item.getData()).getStack();
1586                 } else if (item.getData() instanceof LogSession) {
1587                         LogSession session = ((LogSession) item.getData());
1588                         message = Messages.LogView_SessionStarted;
1589                         if (session.getDate() != null) {
1590                                 DateFormat formatter = new SimpleDateFormat(LogEntry.F_DATE_FORMAT);
1591                                 message += formatter.format(session.getDate());
1592                         }
1593                 }
1594
1595                 if (message == null)
1596                         return;
1597
1598                 fTextLabel.setText(message);
1599                 Rectangle bounds = fTree.getDisplay().getBounds();
1600                 Point cursorPoint = fTree.getDisplay().getCursorLocation();
1601                 int x = point.x;
1602                 int y = point.y + 25;
1603                 int width = fTree.getColumn(0).getWidth();
1604                 int height = 125;
1605                 if (cursorPoint.x + width > bounds.width)
1606                         x -= width;
1607                 if (cursorPoint.y + height + 25 > bounds.height)
1608                         y -= height + 27;
1609
1610                 fTextShell.setLocation(fTree.toDisplay(x, y));
1611                 fTextShell.setSize(width, height);
1612                 fTextShell.setVisible(true);
1613         }
1614
1615         void onMouseMove(Event e) {
1616                 if (fTextShell != null && !fTextShell.isDisposed() && fTextShell.isVisible())
1617                         fTextShell.setVisible(false);
1618
1619                 Point point = new Point(e.x, e.y);
1620                 TreeItem item = fTree.getItem(point);
1621                 if (item == null)
1622                         return;
1623                 Image image = item.getImage();
1624                 Object data = item.getData();
1625                 if (data instanceof LogEntry) {
1626                         LogEntry entry = (LogEntry) data;
1627                         int parentCount = getNumberOfParents(entry);
1628                         int startRange = 20 + Math.max(image.getBounds().width + 2, 7 + 2) * parentCount;
1629                         int endRange = startRange + 16;
1630                         fCanOpenTextShell = e.x >= startRange && e.x <= endRange;
1631                 }
1632         }
1633
1634         private int getNumberOfParents(AbstractEntry entry) {
1635                 AbstractEntry parent = (AbstractEntry) entry.getParent(entry);
1636                 if (parent == null)
1637                         return 0;
1638                 return 1 + getNumberOfParents(parent);
1639         }
1640
1641         public Comparator<Object> getComparator() {
1642                 return fComparator;
1643         }
1644
1645         private void setComparator(byte sortType) {
1646                 if (sortType == DATE) {
1647                         fComparator = new Comparator<Object>() {
1648                                 public int compare(Object e1, Object e2) {
1649                                         long date1 = 0;
1650                                         long date2 = 0;
1651                                         if ((e1 instanceof LogEntry) && (e2 instanceof LogEntry)) {
1652                                                 date1 = ((LogEntry) e1).getDate().getTime();
1653                                                 date2 = ((LogEntry) e2).getDate().getTime();
1654                                         } else if ((e1 instanceof LogSession) && (e2 instanceof LogSession)) {
1655                                                 date1 = ((LogSession) e1).getDate() == null ? 0 : ((LogSession) e1).getDate().getTime();
1656                                                 date2 = ((LogSession) e2).getDate() == null ? 0 : ((LogSession) e2).getDate().getTime();
1657                                         }
1658                                         if (date1 == date2) {
1659                                                 int result = elements.indexOf(e2) - elements.indexOf(e1);
1660                                                 if (DATE_ORDER == DESCENDING)
1661                                                         result *= DESCENDING;
1662                                                 return result;
1663                                         }
1664                                         if (DATE_ORDER == DESCENDING)
1665                                                 return date1 > date2 ? DESCENDING : ASCENDING;
1666                                         return date1 < date2 ? DESCENDING : ASCENDING;
1667                                 }
1668                         };
1669                 } else if (sortType == PLUGIN) {
1670                         fComparator = new Comparator<Object>() {
1671                                 public int compare(Object e1, Object e2) {
1672                                         if ((e1 instanceof LogEntry) && (e2 instanceof LogEntry)) {
1673                                                 LogEntry entry1 = (LogEntry) e1;
1674                                                 LogEntry entry2 = (LogEntry) e2;
1675                                                 return getDefaultComparator().compare(entry1.getPluginId(), entry2.getPluginId()) * PLUGIN_ORDER;
1676                                         }
1677                                         return 0;
1678                                 }
1679                         };
1680                 } else {
1681                         fComparator = new Comparator<Object>() {
1682                                 public int compare(Object e1, Object e2) {
1683                                         if ((e1 instanceof LogEntry) && (e2 instanceof LogEntry)) {
1684                                                 LogEntry entry1 = (LogEntry) e1;
1685                                                 LogEntry entry2 = (LogEntry) e2;
1686                                                 return getDefaultComparator().compare(entry1.getMessage(), entry2.getMessage()) * MESSAGE_ORDER;
1687                                         }
1688                                         return 0;
1689                                 }
1690                         };
1691                 }
1692         }
1693
1694         @SuppressWarnings("unchecked")
1695     private Comparator<Object> getDefaultComparator() {
1696                 return Policy.getComparator();
1697         }
1698
1699         private ViewerComparator getViewerComparator(byte sortType) {
1700                 if (sortType == PLUGIN) {
1701                         return new ViewerComparator() {
1702                                 @SuppressWarnings("unchecked")
1703                 public int compare(Viewer viewer, Object e1, Object e2) {
1704                                         if ((e1 instanceof LogEntry) && (e2 instanceof LogEntry)) {
1705                                                 LogEntry entry1 = (LogEntry) e1;
1706                                                 LogEntry entry2 = (LogEntry) e2;
1707                                                 return getComparator().compare(entry1.getPluginId(), entry2.getPluginId()) * PLUGIN_ORDER;
1708                                         }
1709                                         return 0;
1710                                 }
1711                         };
1712                 } else if (sortType == MESSAGE) {
1713                         return new ViewerComparator() {
1714                                 @SuppressWarnings("unchecked")
1715                 public int compare(Viewer viewer, Object e1, Object e2) {
1716                                         if ((e1 instanceof LogEntry) && (e2 instanceof LogEntry)) {
1717                                                 LogEntry entry1 = (LogEntry) e1;
1718                                                 LogEntry entry2 = (LogEntry) e2;
1719                                                 return getComparator().compare(entry1.getMessage(), entry2.getMessage()) * MESSAGE_ORDER;
1720                                         }
1721                                         return 0;
1722                                 }
1723                         };
1724                 } else {
1725                         return new ViewerComparator() {
1726                             private int indexOf(Object[] array, Object o) {
1727                                 if (o == null)
1728                                     return -1;
1729                                 for (int i = 0; i < array.length; ++i)
1730                                     if (o.equals(array[i]))
1731                                         return i;
1732                                 return -1;
1733                             }
1734                             
1735                                 public int compare(Viewer viewer, Object e1, Object e2) {
1736                                         long date1 = 0;
1737                                         long date2 = 0;
1738                                         if ((e1 instanceof LogEntry) && (e2 instanceof LogEntry)) {
1739                                                 date1 = ((LogEntry) e1).getDate().getTime();
1740                                                 date2 = ((LogEntry) e2).getDate().getTime();
1741                                         } else if ((e1 instanceof LogSession) && (e2 instanceof LogSession)) {
1742                                                 date1 = ((LogSession) e1).getDate() == null ? 0 : ((LogSession) e1).getDate().getTime();
1743                                                 date2 = ((LogSession) e2).getDate() == null ? 0 : ((LogSession) e2).getDate().getTime();
1744                                         }
1745
1746                                         if (date1 == date2) {
1747                             // Everything that appears in logview should be an AbstractEntry.
1748                             AbstractEntry parent = (AbstractEntry) ((AbstractEntry) e1).getParent(null);
1749                         Object[] children = null;
1750                             if (parent != null)
1751                                 children = parent.getChildren(parent);
1752                                             
1753                                             int result = 0;
1754                                             if (children != null) {
1755                                                 // The elements in children seem to be in reverse order,
1756                                                 // i.e. latest log message first, therefore index(e2)-index(e1)
1757                             result = indexOf(children, e2) - indexOf(children, e1);
1758                                             } else {
1759                                 result = elements.indexOf(e1) - elements.indexOf(e2);
1760                                             }
1761                                                 if (DATE_ORDER == DESCENDING)
1762                                                         result *= DESCENDING;
1763                                                 return result;
1764                                         }
1765                                         if (DATE_ORDER == DESCENDING)
1766                                                 return date1 > date2 ? DESCENDING : ASCENDING;
1767                                         return date1 < date2 ? DESCENDING : ASCENDING;
1768                                 }
1769                         };
1770                 }
1771         }
1772
1773         private void resetDialogButtons() {
1774                 ((EventDetailsDialogAction) fPropertiesAction).resetDialogButtons();
1775         }
1776
1777         /**
1778          * Returns the filter dialog settings object used to maintain
1779          * state between filter dialogs
1780          * @return the dialog settings to be used
1781          */
1782         private IDialogSettings getLogSettings() {
1783                 IDialogSettings settings = Activator.getDefault().getDialogSettings();
1784                 return settings.getSection(getClass().getName());
1785         }
1786
1787         /**
1788          * Returns the plugin preferences used to maintain
1789          * state of log view
1790          * @return the plugin preferences
1791          */
1792         private Preferences getLogPreferences() {
1793                 return Activator.getDefault().getPluginPreferences();
1794         }
1795
1796         private void readSettings() {
1797                 IDialogSettings s = getLogSettings();
1798                 Preferences p = getLogPreferences();
1799                 if (s == null || p == null) {
1800                         initializeMemento();
1801                         return;
1802                 }
1803                 try {
1804                         fMemento.putString(P_USE_LIMIT, s.getBoolean(P_USE_LIMIT) ? "true" : "false"); //$NON-NLS-1$ //$NON-NLS-2$
1805                         fMemento.putString(P_LOG_INFO, s.getBoolean(P_LOG_INFO) ? "true" : "false"); //$NON-NLS-1$ //$NON-NLS-2$
1806                         fMemento.putString(P_LOG_WARNING, s.getBoolean(P_LOG_WARNING) ? "true" : "false"); //$NON-NLS-1$ //$NON-NLS-2$
1807             fMemento.putString(P_LOG_ERROR, s.getBoolean(P_LOG_ERROR) ? "true" : "false"); //$NON-NLS-1$ //$NON-NLS-2$
1808             fMemento.putString(P_LOG_DEBUG, s.getBoolean(P_LOG_DEBUG) ? "true" : "false"); //$NON-NLS-1$ //$NON-NLS-2$
1809                         fMemento.putString(P_SHOW_ALL_SESSIONS, s.getBoolean(P_SHOW_ALL_SESSIONS) ? "true" : "false"); //$NON-NLS-1$ //$NON-NLS-2$
1810             fMemento.putInteger(P_LOG_LIMIT, s.getInt(P_LOG_LIMIT));
1811                         fMemento.putInteger(P_COLUMN_1, p.getInt(P_COLUMN_1) > 0 ? p.getInt(P_COLUMN_1) : 300);
1812                         fMemento.putInteger(P_COLUMN_2, p.getInt(P_COLUMN_2) > 0 ? p.getInt(P_COLUMN_2) : 150);
1813                         fMemento.putInteger(P_COLUMN_3, p.getInt(P_COLUMN_3) > 0 ? p.getInt(P_COLUMN_3) : 150);
1814                         fMemento.putString(P_ACTIVATE, p.getBoolean(P_ACTIVATE) ? "true" : "false"); //$NON-NLS-1$ //$NON-NLS-2$
1815                         int order = p.getInt(P_ORDER_VALUE);
1816                         fMemento.putInteger(P_ORDER_VALUE, order == 0 ? DESCENDING : order);
1817                         fMemento.putInteger(P_ORDER_TYPE, p.getInt(P_ORDER_TYPE));
1818                         fMemento.putBoolean(P_SHOW_FILTER_TEXT, p.getBoolean(P_SHOW_FILTER_TEXT));
1819                         fMemento.putInteger(P_GROUP_BY, p.getInt(P_GROUP_BY));
1820             fMemento.putInteger(P_EXPAND_LEVEL, s.getInt(P_EXPAND_LEVEL));
1821                 } catch (NumberFormatException e) {
1822             fMemento.putInteger(P_EXPAND_LEVEL, DEFAULT_EXPAND_LEVEL);
1823                         fMemento.putInteger(P_LOG_LIMIT, 50);
1824                         fMemento.putInteger(P_COLUMN_1, 300);
1825                         fMemento.putInteger(P_COLUMN_2, 150);
1826                         fMemento.putInteger(P_COLUMN_3, 150);
1827                         fMemento.putInteger(P_ORDER_TYPE, DATE);
1828                         fMemento.putInteger(P_ORDER_VALUE, DESCENDING);
1829                         fMemento.putInteger(P_GROUP_BY, GROUP_BY_NONE);
1830                 }
1831         }
1832
1833         private void writeSettings() {
1834                 writeViewSettings();
1835                 writeFilterSettings();
1836         }
1837
1838         private void writeFilterSettings() {
1839                 IDialogSettings settings = getLogSettings();
1840                 if (settings == null)
1841                         settings = Activator.getDefault().getDialogSettings().addNewSection(getClass().getName());
1842                 settings.put(P_USE_LIMIT, fMemento.getString(P_USE_LIMIT).equals("true")); //$NON-NLS-1$
1843         settings.put(P_LOG_LIMIT, fMemento.getInteger(P_LOG_LIMIT).intValue());
1844                 settings.put(P_LOG_INFO, fMemento.getString(P_LOG_INFO).equals("true")); //$NON-NLS-1$
1845                 settings.put(P_LOG_WARNING, fMemento.getString(P_LOG_WARNING).equals("true")); //$NON-NLS-1$
1846         settings.put(P_LOG_ERROR, fMemento.getString(P_LOG_ERROR).equals("true")); //$NON-NLS-1$
1847         settings.put(P_LOG_DEBUG, fMemento.getString(P_LOG_DEBUG).equals("true")); //$NON-NLS-1$
1848                 settings.put(P_SHOW_ALL_SESSIONS, fMemento.getString(P_SHOW_ALL_SESSIONS).equals("true")); //$NON-NLS-1$
1849         }
1850
1851         private void writeViewSettings() {
1852                 Preferences preferences = getLogPreferences();
1853                 preferences.setValue(P_COLUMN_1, fMemento.getInteger(P_COLUMN_1).intValue());
1854                 preferences.setValue(P_COLUMN_2, fMemento.getInteger(P_COLUMN_2).intValue());
1855                 preferences.setValue(P_COLUMN_3, fMemento.getInteger(P_COLUMN_3).intValue());
1856                 preferences.setValue(P_ACTIVATE, fMemento.getString(P_ACTIVATE).equals("true")); //$NON-NLS-1$
1857                 int order = fMemento.getInteger(P_ORDER_VALUE).intValue();
1858                 preferences.setValue(P_ORDER_VALUE, order == 0 ? DESCENDING : order);
1859                 preferences.setValue(P_ORDER_TYPE, fMemento.getInteger(P_ORDER_TYPE).intValue());
1860                 preferences.setValue(P_SHOW_FILTER_TEXT, fMemento.getBoolean(P_SHOW_FILTER_TEXT).booleanValue());
1861                 preferences.setValue(P_GROUP_BY, fMemento.getInteger(P_GROUP_BY).intValue());
1862         preferences.setValue(P_EXPAND_LEVEL, fMemento.getInteger(P_EXPAND_LEVEL).intValue());
1863         }
1864
1865         public void sortByDateDescending() {
1866                 setColumnSorting(fColumn3, DESCENDING);
1867         }
1868
1869         protected Job getOpenLogFileJob() {
1870                 final Shell shell = getViewSite().getShell();
1871                 return new Job(Messages.OpenLogDialog_message) {
1872                         protected IStatus run(IProgressMonitor monitor) {
1873                                 boolean failed = false;
1874                                 if (fInputFile.length() <= LogReader.MAX_FILE_LENGTH) {
1875                                         failed = !Program.launch(fInputFile.getAbsolutePath());
1876                                         if (failed) {
1877                                                 Program p = Program.findProgram(".txt"); //$NON-NLS-1$
1878                                                 if (p != null) {
1879                                                         p.execute(fInputFile.getAbsolutePath());
1880                                                         return Status.OK_STATUS;
1881                                                 }
1882                                         }
1883                                 }
1884                                 if (failed) {
1885                                         final OpenLogDialog openDialog = new OpenLogDialog(shell, fInputFile);
1886                                         Display.getDefault().asyncExec(new Runnable() {
1887                                                 public void run() {
1888                                                         openDialog.create();
1889                                                         openDialog.open();
1890                                                 }
1891                                         });
1892                                 }
1893                                 return Status.OK_STATUS;
1894                         }
1895                 };
1896         }
1897
1898         protected File getLogFile() {
1899                 return fInputFile;
1900         }
1901
1902         /**
1903          * Returns whether given session equals to currently displayed in LogView.
1904          * @param session LogSession
1905          * @return true if given session equals to currently displayed in LogView
1906          */
1907         public boolean isCurrentLogSession(LogSession session) {
1908                 return isPlatformLogOpen() && (currentSession != null) && (currentSession.equals(session));
1909         }
1910
1911     /**
1912      * Returns whether currently open log is platform log or imported file.
1913      * @return true if currently open log is platform log, false otherwise
1914      */
1915     public boolean isPlatformLogOpen() {
1916         return isPlatformLog(fInputFile);
1917     }
1918
1919     /**
1920      * Returns whether currently open log is platform log or imported file.
1921      * @return true if currently open log is platform log, false otherwise
1922      */
1923     public boolean isPlatformLog(File file) {
1924         return (file.equals(getPlatformLogFile()));
1925     }
1926     
1927     public File getPlatformLogFile() {
1928         return org.simantics.message.internal.Activator.getLogFile();
1929     }
1930 }