added 'clone' as keyword
[phpeclipse.git] / net.sourceforge.phpeclipse.debug.core / src / net / sourceforge / phpdt / internal / debug / core / logview / LogView.java
1 /***********************************************************************************************************************************
2  * Copyright (c) 2000, 2004 IBM Corporation and others. All rights reserved. This program and the accompanying materials are made
3  * available under the terms of the Common Public License v1.0 which accompanies this distribution, and is available at
4  * http://www.eclipse.org/legal/cpl-v10.html
5  * 
6  * Contributors: IBM Corporation - initial API and implementation
7  **********************************************************************************************************************************/
8
9 package net.sourceforge.phpdt.internal.debug.core.logview;
10
11 import java.io.BufferedReader;
12 import java.io.BufferedWriter;
13 import java.io.File;
14 import java.io.FileInputStream;
15 import java.io.FileOutputStream;
16 import java.io.IOException;
17 import java.io.InputStreamReader;
18 import java.io.OutputStreamWriter;
19 import java.io.PrintWriter;
20 import java.io.StringWriter;
21 import java.lang.reflect.InvocationTargetException;
22 import java.text.Collator;
23 import java.text.ParseException;
24 import java.text.SimpleDateFormat;
25 import java.util.ArrayList;
26 import java.util.Arrays;
27 import java.util.Comparator;
28 import java.util.Date;
29
30 import net.sourceforge.phpdt.internal.debug.core.PHPDegugCorePluginImages;
31 import net.sourceforge.phpdt.internal.debug.core.PHPDebugCorePlugin;
32
33 import org.eclipse.core.runtime.ILogListener;
34 import org.eclipse.core.runtime.IProgressMonitor;
35 import org.eclipse.core.runtime.IStatus;
36 import org.eclipse.core.runtime.Path;
37 import org.eclipse.core.runtime.Platform;
38 import org.eclipse.jface.action.Action;
39 import org.eclipse.jface.action.IMenuListener;
40 import org.eclipse.jface.action.IMenuManager;
41 import org.eclipse.jface.action.IStatusLineManager;
42 import org.eclipse.jface.action.IToolBarManager;
43 import org.eclipse.jface.action.MenuManager;
44 import org.eclipse.jface.action.Separator;
45 import org.eclipse.jface.dialogs.MessageDialog;
46 import org.eclipse.jface.dialogs.ProgressMonitorDialog;
47 import org.eclipse.jface.operation.IRunnableWithProgress;
48 import org.eclipse.jface.viewers.ColumnPixelData;
49 import org.eclipse.jface.viewers.DoubleClickEvent;
50 import org.eclipse.jface.viewers.IDoubleClickListener;
51 import org.eclipse.jface.viewers.ISelection;
52 import org.eclipse.jface.viewers.ISelectionChangedListener;
53 import org.eclipse.jface.viewers.IStructuredSelection;
54 import org.eclipse.jface.viewers.ITreeViewerListener;
55 import org.eclipse.jface.viewers.SelectionChangedEvent;
56 import org.eclipse.jface.viewers.TableLayout;
57 import org.eclipse.jface.viewers.TableTreeViewer;
58 import org.eclipse.jface.viewers.TreeExpansionEvent;
59 import org.eclipse.jface.viewers.Viewer;
60 import org.eclipse.jface.viewers.ViewerSorter;
61 import org.eclipse.swt.SWT;
62 import org.eclipse.swt.custom.BusyIndicator;
63 import org.eclipse.swt.custom.TableTree;
64 import org.eclipse.swt.custom.TableTreeItem;
65 import org.eclipse.swt.dnd.Clipboard;
66 import org.eclipse.swt.dnd.TextTransfer;
67 import org.eclipse.swt.dnd.Transfer;
68 import org.eclipse.swt.events.DisposeEvent;
69 import org.eclipse.swt.events.DisposeListener;
70 import org.eclipse.swt.events.SelectionAdapter;
71 import org.eclipse.swt.events.SelectionEvent;
72 import org.eclipse.swt.graphics.Color;
73 import org.eclipse.swt.graphics.Font;
74 import org.eclipse.swt.graphics.FontData;
75 import org.eclipse.swt.graphics.Point;
76 import org.eclipse.swt.layout.GridData;
77 import org.eclipse.swt.layout.GridLayout;
78 import org.eclipse.swt.program.Program;
79 import org.eclipse.swt.widgets.Composite;
80 import org.eclipse.swt.widgets.Control;
81 import org.eclipse.swt.widgets.Display;
82 import org.eclipse.swt.widgets.Event;
83 import org.eclipse.swt.widgets.FileDialog;
84 import org.eclipse.swt.widgets.Listener;
85 import org.eclipse.swt.widgets.Menu;
86 import org.eclipse.swt.widgets.Shell;
87 import org.eclipse.swt.widgets.Table;
88 import org.eclipse.swt.widgets.TableColumn;
89 import org.eclipse.swt.widgets.TableItem;
90 import org.eclipse.swt.widgets.Text;
91 import org.eclipse.ui.IActionBars;
92 import org.eclipse.ui.IMemento;
93 import org.eclipse.ui.ISharedImages;
94 import org.eclipse.ui.IViewPart;
95 import org.eclipse.ui.IViewSite;
96 import org.eclipse.ui.IWorkbenchPage;
97 import org.eclipse.ui.PartInitException;
98 import org.eclipse.ui.PlatformUI;
99 import org.eclipse.ui.XMLMemento;
100 import org.eclipse.ui.actions.ActionFactory;
101 import org.eclipse.ui.part.ViewPart;
102
103 public class LogView extends ViewPart implements ILogListener {
104   public final static String ID_LOGVIEW = "net.sourceforge.phpdt.internal.debug.core.logview.LogView";
105
106   private TableTreeViewer tableTreeViewer;
107
108   private ArrayList logs = new ArrayList();
109
110   public static final String P_LOG_WARNING = "warning"; //$NON-NLS-1$
111
112   public static final String P_LOG_ERROR = "error"; //$NON-NLS-1$
113
114   public static final String P_LOG_INFO = "info"; //$NON-NLS-1$
115
116   public static final String P_LOG_LIMIT = "limit"; //$NON-NLS-1$
117
118   public static final String P_USE_LIMIT = "useLimit"; //$NON-NLS-1$
119
120   public static final String P_SHOW_ALL_SESSIONS = "allSessions"; //$NON-NLS-1$
121
122   private static final String P_COLUMN_1 = "column1"; //$NON-NLS-1$
123
124   private static final String P_COLUMN_2 = "column2"; //$NON-NLS-1$
125
126   private static final String P_COLUMN_3 = "column3"; //$NON-NLS-1$
127
128   private static final String P_COLUMN_4 = "column4"; //$NON-NLS-1$
129
130   public static final String P_ACTIVATE = "activate"; //$NON-NLS-1$
131
132   private int MESSAGE_ORDER = -1;
133
134   private int PLUGIN_ORDER = -1;
135
136   private int DATE_ORDER = -1;
137
138   public static byte MESSAGE = 0x0;
139
140   public static byte PLUGIN = 0x1;
141
142   public static byte DATE = 0x2;
143
144   private static int ASCENDING = 1;
145
146   private static int DESCENDING = -1;
147
148   private Action clearAction;
149
150   private Action copyAction;
151
152   private Action readLogAction;
153
154   private Action deleteLogAction;
155
156   private Action exportAction;
157
158   private Action importAction;
159
160   private Action activateViewAction;
161
162   private Action propertiesAction;
163
164   private Action viewLogAction;
165
166   private Action filterAction;
167
168   private Clipboard clipboard;
169
170   private IMemento memento;
171
172   private File inputFile;
173
174   private String directory;
175
176   private TableColumn column0;
177
178   private TableColumn column1;
179
180   private TableColumn column2;
181
182   private TableColumn column3;
183
184   private TableColumn column4;
185
186   private static Font boldFont;
187
188   private Comparator comparator;
189
190   private Collator collator;
191
192   // hover text
193   private boolean canOpenTextShell;
194
195   private Text textLabel;
196
197   private Shell textShell;
198
199   private boolean firstEvent = true;
200
201   public LogView() {
202     logs = new ArrayList();
203     inputFile = Platform.getLogFileLocation().toFile();
204   }
205
206   public void createPartControl(Composite parent) {
207     readLogFile();
208     TableTree tableTree = new TableTree(parent, SWT.FULL_SELECTION);
209     tableTree.setLayoutData(new GridData(GridData.FILL_BOTH));
210     createColumns(tableTree.getTable());
211     createViewer(tableTree);
212     createPopupMenuManager(tableTree);
213     makeActions(tableTree.getTable());
214     fillToolBar();
215     Platform.addLogListener(this);
216     getSite().setSelectionProvider(tableTreeViewer);
217     clipboard = new Clipboard(tableTree.getDisplay());
218     //        WorkbenchHelp.setHelp(tableTree, IHelpContextIds.LOG_VIEW);
219     tableTreeViewer.getTableTree().getTable().setToolTipText(""); //$NON-NLS-1$
220     initializeFonts();
221     applyFonts();
222   }
223
224   private void initializeFonts() {
225     Font tableFont = tableTreeViewer.getTableTree().getFont();
226     FontData[] fontDataList = tableFont.getFontData();
227     FontData fontData;
228     if (fontDataList.length > 0)
229       fontData = fontDataList[0];
230     else
231       fontData = new FontData();
232     fontData.setStyle(SWT.BOLD);
233     boldFont = new Font(tableTreeViewer.getTableTree().getDisplay(), fontData);
234   }
235
236   /*
237    * Set all rows where the tableTreeItem has children to have a <b>bold </b> font.
238    */
239   private void applyFonts() {
240     if (tableTreeViewer == null || tableTreeViewer.getTableTree().isDisposed())
241       return;
242     int max = tableTreeViewer.getTableTree().getItemCount();
243     int index = 0, tableIndex = 0;
244     while (index < max) {
245       LogEntry entry = (LogEntry) tableTreeViewer.getElementAt(index);
246       if (entry == null)
247         return;
248       if (entry.hasChildren()) {
249         tableTreeViewer.getTableTree().getItems()[index].setFont(boldFont);
250         tableIndex = applyChildFonts(entry, tableIndex);
251       } else {
252         tableTreeViewer.getTableTree().getItems()[index].setFont(tableTreeViewer.getTableTree().getFont());
253       }
254       index++;
255       tableIndex++;
256     }
257   }
258
259   private int applyChildFonts(LogEntry parent, int index) {
260     if (!tableTreeViewer.getExpandedState(parent) || !parent.hasChildren())
261       return index;
262     LogEntry[] children = getEntryChildren(parent);
263     for (int i = 0; i < children.length; i++) {
264       index++;
265       if (children[i].hasChildren()) {
266         TableItem tableItem = getTableItem(index);
267         if (tableItem != null) {
268           tableItem.setFont(boldFont);
269         }
270         index = applyChildFonts(children[i], index);
271       } else {
272         TableItem tableItem = getTableItem(index);
273         if (tableItem != null) {
274           tableItem.setFont(tableTreeViewer.getTableTree().getFont());
275         }
276       }
277     }
278     return index;
279   }
280
281   private LogEntry[] getEntryChildren(LogEntry parent) {
282     Object[] entryChildren = parent.getChildren(parent);
283     if (comparator != null)
284       Arrays.sort(entryChildren, comparator);
285     LogEntry[] children = new LogEntry[entryChildren.length];
286     System.arraycopy(entryChildren, 0, children, 0, entryChildren.length);
287     return children;
288   }
289
290   private TableItem getTableItem(int index) {
291     TableItem[] tableItems = tableTreeViewer.getTableTree().getTable().getItems();
292     if (index > tableItems.length - 1)
293       return null;
294     return tableItems[index];
295   }
296
297   private void fillToolBar() {
298     IActionBars bars = getViewSite().getActionBars();
299     bars.setGlobalActionHandler(ActionFactory.COPY.getId(), copyAction);
300     IToolBarManager toolBarManager = bars.getToolBarManager();
301     toolBarManager.add(exportAction);
302     toolBarManager.add(importAction);
303     toolBarManager.add(new Separator());
304     toolBarManager.add(clearAction);
305     toolBarManager.add(deleteLogAction);
306     toolBarManager.add(viewLogAction);
307     toolBarManager.add(readLogAction);
308     toolBarManager.add(new Separator());
309     IMenuManager mgr = bars.getMenuManager();
310     mgr.add(filterAction);
311     mgr.add(new Separator());
312     mgr.add(activateViewAction);
313   }
314
315   private void createViewer(TableTree tableTree) {
316     tableTreeViewer = new TableTreeViewer(tableTree);
317     tableTreeViewer.setContentProvider(new LogViewContentProvider(this));
318     tableTreeViewer.setLabelProvider(new LogViewLabelProvider());
319     tableTreeViewer.addSelectionChangedListener(new ISelectionChangedListener() {
320       public void selectionChanged(SelectionChangedEvent e) {
321         handleSelectionChanged(e.getSelection());
322         if (propertiesAction.isEnabled())
323           ((EventDetailsDialogAction) propertiesAction).resetSelection();
324       }
325     });
326     tableTreeViewer.addDoubleClickListener(new IDoubleClickListener() {
327       public void doubleClick(DoubleClickEvent event) {
328         ((EventDetailsDialogAction) propertiesAction).setComparator(comparator);
329         propertiesAction.run();
330       }
331     });
332     tableTreeViewer.addTreeListener(new ITreeViewerListener() {
333       public void treeCollapsed(TreeExpansionEvent event) {
334         applyFonts();
335       }
336
337       public void treeExpanded(TreeExpansionEvent event) {
338         applyFonts();
339       }
340     });
341     addMouseListeners();
342     tableTreeViewer.setInput(Platform.class);
343   }
344
345   private void createPopupMenuManager(TableTree tableTree) {
346     MenuManager popupMenuManager = new MenuManager();
347     IMenuListener listener = new IMenuListener() {
348       public void menuAboutToShow(IMenuManager mng) {
349         fillContextMenu(mng);
350       }
351     };
352     popupMenuManager.addMenuListener(listener);
353     popupMenuManager.setRemoveAllWhenShown(true);
354     Menu menu = popupMenuManager.createContextMenu(tableTree);
355     tableTree.setMenu(menu);
356   }
357
358   private void createColumns(Table table) {
359     column0 = new TableColumn(table, SWT.NULL);
360     column0.setText(""); //$NON-NLS-1$
361     column1 = new TableColumn(table, SWT.NULL);
362     column1.setText(PHPDebugCorePlugin.getResourceString("LogView.column.severity")); //$NON-NLS-1$
363     column2 = new TableColumn(table, SWT.NULL);
364     column2.setText(PHPDebugCorePlugin.getResourceString("LogView.column.message")); //$NON-NLS-1$
365     column2.addSelectionListener(new SelectionAdapter() {
366       public void widgetSelected(SelectionEvent e) {
367         MESSAGE_ORDER *= -1;
368         ViewerSorter sorter = getViewerSorter(MESSAGE);
369         tableTreeViewer.setSorter(sorter);
370         collator = sorter.getCollator();
371         boolean isComparatorSet = ((EventDetailsDialogAction) propertiesAction).resetSelection(MESSAGE, MESSAGE_ORDER);
372         setComparator(MESSAGE);
373         if (!isComparatorSet)
374           ((EventDetailsDialogAction) propertiesAction).setComparator(comparator);
375         applyFonts();
376       }
377     });
378     column3 = new TableColumn(table, SWT.NULL);
379     column3.setText(PHPDebugCorePlugin.getResourceString("LogView.column.plugin")); //$NON-NLS-1$
380     column3.addSelectionListener(new SelectionAdapter() {
381       public void widgetSelected(SelectionEvent e) {
382         PLUGIN_ORDER *= -1;
383         ViewerSorter sorter = getViewerSorter(PLUGIN);
384         tableTreeViewer.setSorter(sorter);
385         collator = sorter.getCollator();
386         boolean isComparatorSet = ((EventDetailsDialogAction) propertiesAction).resetSelection(PLUGIN, PLUGIN_ORDER);
387         setComparator(PLUGIN);
388         if (!isComparatorSet)
389           ((EventDetailsDialogAction) propertiesAction).setComparator(comparator);
390         applyFonts();
391       }
392     });
393     column4 = new TableColumn(table, SWT.NULL);
394     column4.setText(PHPDebugCorePlugin.getResourceString("LogView.column.date")); //$NON-NLS-1$
395     column4.addSelectionListener(new SelectionAdapter() {
396       public void widgetSelected(SelectionEvent e) {
397         if (DATE_ORDER == ASCENDING) {
398           DATE_ORDER = DESCENDING;
399         } else {
400           DATE_ORDER = ASCENDING;
401         }
402         ViewerSorter sorter = getViewerSorter(DATE);
403         tableTreeViewer.setSorter(sorter);
404         collator = sorter.getCollator();
405         boolean isComparatorSet = ((EventDetailsDialogAction) propertiesAction).resetSelection(DATE, DATE_ORDER);
406         setComparator(DATE);
407         if (!isComparatorSet)
408           ((EventDetailsDialogAction) propertiesAction).setComparator(comparator);
409         applyFonts();
410       }
411     });
412     TableLayout tlayout = new TableLayout();
413     tlayout.addColumnData(new ColumnPixelData(21));
414     tlayout.addColumnData(new ColumnPixelData(memento.getInteger(P_COLUMN_1).intValue()));
415     tlayout.addColumnData(new ColumnPixelData(memento.getInteger(P_COLUMN_2).intValue()));
416     tlayout.addColumnData(new ColumnPixelData(memento.getInteger(P_COLUMN_3).intValue()));
417     tlayout.addColumnData(new ColumnPixelData(memento.getInteger(P_COLUMN_4).intValue()));
418     table.setLayout(tlayout);
419     table.setHeaderVisible(true);
420   }
421
422   private void makeActions(Table table) {
423     propertiesAction = new EventDetailsDialogAction(table.getShell(), tableTreeViewer);
424     propertiesAction.setImageDescriptor(PHPDegugCorePluginImages.DESC_PROPERTIES);
425     propertiesAction.setDisabledImageDescriptor(PHPDegugCorePluginImages.DESC_PROPERTIES_DISABLED);
426     propertiesAction.setToolTipText(PHPDebugCorePlugin.getResourceString("LogView.properties.tooltip")); //$NON-NLS-1$
427     propertiesAction.setEnabled(false);
428     clearAction = new Action(PHPDebugCorePlugin.getResourceString("LogView.clear")) { //$NON-NLS-1$
429       public void run() {
430         handleClear();
431       }
432     };
433     clearAction.setImageDescriptor(PHPDegugCorePluginImages.DESC_CLEAR);
434     clearAction.setDisabledImageDescriptor(PHPDegugCorePluginImages.DESC_CLEAR_DISABLED);
435     clearAction.setToolTipText(PHPDebugCorePlugin.getResourceString("LogView.clear.tooltip")); //$NON-NLS-1$
436     clearAction.setText(PHPDebugCorePlugin.getResourceString("LogView.clear")); //$NON-NLS-1$
437     readLogAction = new Action(PHPDebugCorePlugin.getResourceString("LogView.readLog.restore")) { //$NON-NLS-1$
438       public void run() {
439         inputFile = Platform.getLogFileLocation().toFile();
440         reloadLog();
441       }
442     };
443     readLogAction.setToolTipText(PHPDebugCorePlugin.getResourceString("LogView.readLog.restore.tooltip")); //$NON-NLS-1$
444     readLogAction.setImageDescriptor(PHPDegugCorePluginImages.DESC_READ_LOG);
445     readLogAction.setDisabledImageDescriptor(PHPDegugCorePluginImages.DESC_READ_LOG_DISABLED);
446     deleteLogAction = new Action(PHPDebugCorePlugin.getResourceString("LogView.delete")) { //$NON-NLS-1$
447       public void run() {
448         doDeleteLog();
449       }
450     };
451     deleteLogAction.setToolTipText(PHPDebugCorePlugin.getResourceString("LogView.delete.tooltip")); //$NON-NLS-1$
452     deleteLogAction.setImageDescriptor(PHPDegugCorePluginImages.DESC_REMOVE_LOG);
453     deleteLogAction.setDisabledImageDescriptor(PHPDegugCorePluginImages.DESC_REMOVE_LOG_DISABLED);
454     deleteLogAction.setEnabled(inputFile.exists() && inputFile.equals(Platform.getLogFileLocation().toFile()));
455     copyAction = new Action(PHPDebugCorePlugin.getResourceString("LogView.copy")) { //$NON-NLS-1$
456       public void run() {
457         copyToClipboard(tableTreeViewer.getSelection());
458       }
459     };
460     copyAction.setImageDescriptor(PlatformUI.getWorkbench().getSharedImages().getImageDescriptor(ISharedImages.IMG_TOOL_COPY));
461     filterAction = new Action(PHPDebugCorePlugin.getResourceString("LogView.filter")) { //$NON-NLS-1$
462       public void run() {
463         handleFilter();
464       }
465     };
466     filterAction.setToolTipText(PHPDebugCorePlugin.getResourceString("LogView.filter")); //$NON-NLS-1$
467     filterAction.setImageDescriptor(PHPDegugCorePluginImages.DESC_FILTER);
468     filterAction.setDisabledImageDescriptor(PHPDegugCorePluginImages.DESC_FILTER_DISABLED);
469     exportAction = new Action(PHPDebugCorePlugin.getResourceString("LogView.export")) { //$NON-NLS-1$
470       public void run() {
471         handleExport();
472       }
473     };
474     exportAction.setToolTipText(PHPDebugCorePlugin.getResourceString("LogView.export.tooltip")); //$NON-NLS-1$
475     exportAction.setImageDescriptor(PHPDegugCorePluginImages.DESC_EXPORT);
476     exportAction.setDisabledImageDescriptor(PHPDegugCorePluginImages.DESC_EXPORT_DISABLED);
477     importAction = new Action(PHPDebugCorePlugin.getResourceString("LogView.import")) { //$NON-NLS-1$
478       public void run() {
479         handleImport();
480       }
481     };
482     importAction.setToolTipText(PHPDebugCorePlugin.getResourceString("LogView.import.tooltip")); //$NON-NLS-1$
483     importAction.setImageDescriptor(PHPDegugCorePluginImages.DESC_IMPORT);
484     importAction.setDisabledImageDescriptor(PHPDegugCorePluginImages.DESC_IMPORT_DISABLED);
485     activateViewAction = new Action(PHPDebugCorePlugin.getResourceString("LogView.activate")) { //$NON-NLS-1$
486       public void run() {
487       }
488     };
489     activateViewAction.setChecked(memento.getString(P_ACTIVATE).equals("true")); //$NON-NLS-1$
490     viewLogAction = new Action(PHPDebugCorePlugin.getResourceString("LogView.view.currentLog")) { //$NON-NLS-1$
491       public void run() {
492         if (inputFile.exists()) {
493           if (inputFile.length() > LogReader.MAX_FILE_LENGTH) {
494             OpenLogDialog openDialog = new OpenLogDialog(getViewSite().getShell(), inputFile);
495             openDialog.create();
496             openDialog.open();
497           } else {
498             boolean canLaunch = Program.launch(inputFile.getAbsolutePath());
499             if (!canLaunch) {
500               Program p = Program.findProgram(".txt"); //$NON-NLS-1$
501               if (p != null)
502                 p.execute(inputFile.getAbsolutePath());
503               else {
504                 OpenLogDialog openDialog = new OpenLogDialog(getViewSite().getShell(), inputFile);
505                 openDialog.create();
506                 openDialog.open();
507               }
508             }
509           }
510         }
511       }
512     };
513     viewLogAction.setImageDescriptor(PHPDegugCorePluginImages.DESC_OPEN_LOG);
514     viewLogAction.setDisabledImageDescriptor(PHPDegugCorePluginImages.DESC_OPEN_LOG_DISABLED);
515     viewLogAction.setEnabled(inputFile.exists());
516     viewLogAction.setToolTipText(PHPDebugCorePlugin.getResourceString("LogView.view.currentLog.tooltip")); //$NON-NLS-1$
517   }
518
519   public void dispose() {
520     Platform.removeLogListener(this);
521     clipboard.dispose();
522     LogReader.reset();
523     boldFont.dispose();
524     super.dispose();
525   }
526
527   private void handleImport() {
528     FileDialog dialog = new FileDialog(getViewSite().getShell());
529     dialog.setFilterExtensions(new String[] { "*.log" }); //$NON-NLS-1$
530     if (directory != null)
531       dialog.setFilterPath(directory);
532     String path = dialog.open();
533     if (path != null && new Path(path).toFile().exists()) {
534       inputFile = new Path(path).toFile();
535       directory = inputFile.getParent();
536       IRunnableWithProgress op = new IRunnableWithProgress() {
537         public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
538           monitor.beginTask(PHPDebugCorePlugin.getResourceString("LogView.operation.importing"), IProgressMonitor.UNKNOWN); //$NON-NLS-1$
539           readLogFile();
540         }
541       };
542       ProgressMonitorDialog pmd = new ProgressMonitorDialog(getViewSite().getShell());
543       try {
544         pmd.run(true, true, op);
545       } catch (InvocationTargetException e) {
546       } catch (InterruptedException e) {
547       } finally {
548         readLogAction.setText(PHPDebugCorePlugin.getResourceString("LogView.readLog.reload")); //$NON-NLS-1$
549         readLogAction.setToolTipText(PHPDebugCorePlugin.getResourceString("LogView.readLog.reload")); //$NON-NLS-1$
550         asyncRefresh(false);
551         resetDialogButtons();
552       }
553     }
554   }
555
556   private void handleExport() {
557     FileDialog dialog = new FileDialog(getViewSite().getShell(), SWT.SAVE);
558     dialog.setFilterExtensions(new String[] { "*.log" }); //$NON-NLS-1$
559     if (directory != null)
560       dialog.setFilterPath(directory);
561     String path = dialog.open();
562     if (path != null) {
563       if (!path.endsWith(".log")) //$NON-NLS-1$
564         path += ".log"; //$NON-NLS-1$
565       File outputFile = new Path(path).toFile();
566       directory = outputFile.getParent();
567       if (outputFile.exists()) {
568         String message = PHPDebugCorePlugin.getFormattedMessage("LogView.confirmOverwrite.message", //$NON-NLS-1$
569             outputFile.toString());
570         if (!MessageDialog.openQuestion(getViewSite().getShell(), exportAction.getText(), message))
571           return;
572       }
573       copy(inputFile, outputFile);
574     }
575   }
576
577   private void copy(File inputFile, File outputFile) {
578     BufferedReader reader = null;
579     BufferedWriter writer = null;
580     try {
581       reader = new BufferedReader(new InputStreamReader(new FileInputStream(inputFile), "UTF-8")); //$NON-NLS-1$
582       writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outputFile), "UTF-8")); //$NON-NLS-1$
583       while (reader.ready()) {
584         writer.write(reader.readLine());
585         writer.write(System.getProperty("line.separator")); //$NON-NLS-1$
586       }
587     } catch (IOException e) {
588     } finally {
589       try {
590         if (reader != null)
591           reader.close();
592         if (writer != null)
593           writer.close();
594       } catch (IOException e1) {
595       }
596     }
597   }
598
599   private void handleFilter() {
600     FilterDialog dialog = new FilterDialog(PHPDebugCorePlugin.getActiveWorkbenchShell(), memento);
601     dialog.create();
602     dialog.getShell().setText(PHPDebugCorePlugin.getResourceString("LogView.FilterDialog.title")); //$NON-NLS-1$
603     if (dialog.open() == FilterDialog.OK)
604       reloadLog();
605   }
606
607   private void doDeleteLog() {
608     String title = PHPDebugCorePlugin.getResourceString("LogView.confirmDelete.title"); //$NON-NLS-1$
609     String message = PHPDebugCorePlugin.getResourceString("LogView.confirmDelete.message"); //$NON-NLS-1$
610     if (!MessageDialog.openConfirm(tableTreeViewer.getControl().getShell(), title, message))
611       return;
612     if (inputFile.delete()) {
613       logs.clear();
614       asyncRefresh(false);
615       resetDialogButtons();
616     }
617   }
618
619   public void fillContextMenu(IMenuManager manager) {
620     manager.add(copyAction);
621     manager.add(new Separator());
622     manager.add(clearAction);
623     manager.add(deleteLogAction);
624     manager.add(viewLogAction);
625     manager.add(readLogAction);
626     manager.add(new Separator());
627     manager.add(exportAction);
628     manager.add(importAction);
629     manager.add(new Separator());
630     ((EventDetailsDialogAction) propertiesAction).setComparator(comparator);
631     manager.add(propertiesAction);
632   }
633
634   public LogEntry[] getLogs() {
635     return (LogEntry[]) logs.toArray(new LogEntry[logs.size()]);
636   }
637
638   protected void handleClear() {
639     BusyIndicator.showWhile(tableTreeViewer.getControl().getDisplay(), new Runnable() {
640       public void run() {
641         logs.clear();
642         asyncRefresh(false);
643         resetDialogButtons();
644       }
645     });
646   }
647
648   protected void reloadLog() {
649     IRunnableWithProgress op = new IRunnableWithProgress() {
650       public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
651         monitor.beginTask(PHPDebugCorePlugin.getResourceString("LogView.operation.reloading"), //$NON-NLS-1$
652             IProgressMonitor.UNKNOWN);
653         readLogFile();
654       }
655     };
656     ProgressMonitorDialog pmd = new ProgressMonitorDialog(getViewSite().getShell());
657     try {
658       pmd.run(true, true, op);
659     } catch (InvocationTargetException e) {
660     } catch (InterruptedException e) {
661     } finally {
662       readLogAction.setText(PHPDebugCorePlugin.getResourceString("LogView.readLog.restore")); //$NON-NLS-1$
663       readLogAction.setToolTipText(PHPDebugCorePlugin.getResourceString("LogView.readLog.restore")); //$NON-NLS-1$
664       asyncRefresh(false);
665       resetDialogButtons();
666     }
667   }
668
669   private void readLogFile() {
670     logs.clear();
671     if (!inputFile.exists())
672       return;
673     if (inputFile.length() > LogReader.MAX_FILE_LENGTH)
674       LogReader.parseLargeFile(inputFile, logs, memento);
675     else
676       LogReader.parseLogFile(inputFile, logs, memento);
677   }
678
679   public void logging(IStatus status, String plugin) { 
680     if (!inputFile.equals(Platform.getLogFileLocation().toFile()))
681       return;
682     if (firstEvent) {
683       readLogFile();
684       asyncRefresh();
685       firstEvent = false;
686     } else {
687       pushStatus(status);
688     }
689   }
690
691   private void pushStatus(IStatus status) {
692       LogEntry entry = new LogEntry(status);
693       LogReader.addEntry(entry, logs, memento, true);
694       asyncRefresh();
695   }
696
697   private void asyncRefresh() {
698     asyncRefresh(true);
699   }
700
701   private void asyncRefresh(final boolean activate) {
702     final Control control = tableTreeViewer.getControl();
703     if (control.isDisposed())
704       return;
705     Display display = control.getDisplay();
706     final ViewPart view = this;
707     if (display != null) {
708       display.asyncExec(new Runnable() {
709         public void run() {
710           if (!control.isDisposed()) {
711             tableTreeViewer.refresh();
712             deleteLogAction.setEnabled(inputFile.exists() && inputFile.equals(Platform.getLogFileLocation().toFile()));
713             viewLogAction.setEnabled(inputFile.exists());
714             if (activate && activateViewAction.isChecked()) {
715               IWorkbenchPage page = PHPDebugCorePlugin.getActivePage();
716               if (page != null)
717                 page.bringToTop(view);
718             }
719           }
720           applyFonts();
721         }
722       });
723     }
724   }
725
726   public void setFocus() {
727     if (tableTreeViewer != null && !tableTreeViewer.getTableTree().isDisposed())
728       tableTreeViewer.getTableTree().getTable().setFocus();
729   }
730
731   private void handleSelectionChanged(ISelection selection) {
732     updateStatus(selection);
733     copyAction.setEnabled(!selection.isEmpty());
734     propertiesAction.setEnabled(!selection.isEmpty());
735   }
736
737   private void updateStatus(ISelection selection) {
738     IStatusLineManager status = getViewSite().getActionBars().getStatusLineManager();
739     if (selection.isEmpty())
740       status.setMessage(null);
741     else {
742       LogEntry entry = (LogEntry) ((IStructuredSelection) selection).getFirstElement();
743       status.setMessage(((LogViewLabelProvider) tableTreeViewer.getLabelProvider()).getColumnText(entry, 2));
744     }
745   }
746
747   private void copyToClipboard(ISelection selection) {
748     StringWriter writer = new StringWriter();
749     PrintWriter pwriter = new PrintWriter(writer);
750     if (selection.isEmpty())
751       return;
752     LogEntry entry = (LogEntry) ((IStructuredSelection) selection).getFirstElement();
753     entry.write(pwriter);
754     pwriter.flush();
755     String textVersion = writer.toString();
756     try {
757       pwriter.close();
758       writer.close();
759     } catch (IOException e) {
760     }
761     if (textVersion.trim().length() > 0) {
762       // set the clipboard contents
763       clipboard.setContents(new Object[] { textVersion }, new Transfer[] { TextTransfer.getInstance() });
764     }
765   }
766
767   public void init(IViewSite site, IMemento memento) throws PartInitException {
768     super.init(site, memento);
769     if (memento == null)
770       this.memento = XMLMemento.createWriteRoot("LOGVIEW"); //$NON-NLS-1$
771     else
772       this.memento = memento;
773     initializeMemento();
774   }
775
776   private void initializeMemento() {
777     if (memento.getString(P_USE_LIMIT) == null)
778       memento.putString(P_USE_LIMIT, "true"); //$NON-NLS-1$
779     if (memento.getInteger(P_LOG_LIMIT) == null)
780       memento.putInteger(P_LOG_LIMIT, 50);
781     if (memento.getString(P_LOG_INFO) == null)
782       memento.putString(P_LOG_INFO, "true"); //$NON-NLS-1$
783     if (memento.getString(P_LOG_WARNING) == null)
784       memento.putString(P_LOG_WARNING, "true"); //$NON-NLS-1$
785     if (memento.getString(P_LOG_ERROR) == null)
786       memento.putString(P_LOG_ERROR, "true"); //$NON-NLS-1$
787     if (memento.getString(P_SHOW_ALL_SESSIONS) == null)
788       memento.putString(P_SHOW_ALL_SESSIONS, "true"); //$NON-NLS-1$
789     Integer width = memento.getInteger(P_COLUMN_1);
790     if (width == null || width.intValue() == 0)
791       memento.putInteger(P_COLUMN_1, 20);
792     width = memento.getInteger(P_COLUMN_2);
793     if (width == null || width.intValue() == 0)
794       memento.putInteger(P_COLUMN_2, 300);
795     width = memento.getInteger(P_COLUMN_3);
796     if (width == null || width.intValue() == 0)
797       memento.putInteger(P_COLUMN_3, 150);
798     width = memento.getInteger(P_COLUMN_4);
799     if (width == null || width.intValue() == 0)
800       memento.putInteger(P_COLUMN_4, 150);
801     if (memento.getString(P_ACTIVATE) == null)
802       memento.putString(P_ACTIVATE, "true"); //$NON-NLS-1$
803   }
804
805   public void saveState(IMemento memento) {
806     if (this.memento == null || memento == null)
807       return;
808     this.memento.putInteger(P_COLUMN_1, column1.getWidth());
809     this.memento.putInteger(P_COLUMN_2, column2.getWidth());
810     this.memento.putInteger(P_COLUMN_3, column3.getWidth());
811     this.memento.putInteger(P_COLUMN_4, column4.getWidth());
812     this.memento.putString(P_ACTIVATE, activateViewAction.isChecked() ? "true" : "false"); //$NON-NLS-1$ //$NON-NLS-2$
813     memento.putMemento(this.memento);
814   }
815
816   private void addMouseListeners() {
817     Listener tableListener = new Listener() {
818       public void handleEvent(Event e) {
819         switch (e.type) {
820         case SWT.MouseMove:
821           onMouseMove(e);
822           break;
823         case SWT.MouseHover:
824           onMouseHover(e);
825           break;
826         case SWT.MouseDown:
827           onMouseDown(e);
828           break;
829         }
830       }
831     };
832     int[] tableEvents = new int[] { SWT.MouseDown, SWT.MouseMove, SWT.MouseHover };
833     for (int i = 0; i < tableEvents.length; i++) {
834       tableTreeViewer.getTableTree().getTable().addListener(tableEvents[i], tableListener);
835     }
836   }
837
838   private void makeHoverShell() {
839     Control control = tableTreeViewer.getControl();
840     textShell = new Shell(control.getShell(), SWT.NO_FOCUS | SWT.ON_TOP);
841     Display display = textShell.getDisplay();
842     textShell.setBackground(display.getSystemColor(SWT.COLOR_INFO_BACKGROUND));
843     GridLayout layout = new GridLayout(1, false);
844     int border = ((control.getShell().getStyle() & SWT.NO_TRIM) == 0) ? 0 : 1;
845     layout.marginHeight = border;
846     layout.marginWidth = border;
847     textShell.setLayout(layout);
848     textShell.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
849     Composite shellComposite = new Composite(textShell, SWT.NONE);
850     layout = new GridLayout();
851     layout.marginHeight = 0;
852     layout.marginWidth = 0;
853     shellComposite.setLayout(layout);
854     shellComposite.setLayoutData(new GridData(GridData.FILL_BOTH | GridData.VERTICAL_ALIGN_BEGINNING));
855     textLabel = new Text(shellComposite, SWT.WRAP | SWT.MULTI);
856     GridData gd = new GridData(GridData.FILL_BOTH);
857     gd.widthHint = 100;
858     gd.grabExcessHorizontalSpace = true;
859     textLabel.setLayoutData(gd);
860     Color c = control.getDisplay().getSystemColor(SWT.COLOR_INFO_BACKGROUND);
861     textLabel.setBackground(c);
862     c = control.getDisplay().getSystemColor(SWT.COLOR_INFO_FOREGROUND);
863     textLabel.setForeground(c);
864     textLabel.setEditable(false);
865     textShell.addDisposeListener(new DisposeListener() {
866       public void widgetDisposed(DisposeEvent e) {
867         onTextShellDispose(e);
868       }
869     });
870   }
871
872   void onTextShellDispose(DisposeEvent e) {
873     canOpenTextShell = true;
874     setFocus();
875   }
876
877   void onMouseDown(Event e) {
878     if (textShell != null && !textShell.isDisposed() && !textShell.isFocusControl()) {
879       textShell.close();
880       canOpenTextShell = true;
881     }
882   }
883
884   void onMouseHover(Event e) {
885     if (!canOpenTextShell)
886       return;
887     canOpenTextShell = false;
888     Point point = new Point(e.x, e.y);
889     TableTree table = tableTreeViewer.getTableTree();
890     TableTreeItem item = table.getItem(point);
891     if (item == null)
892       return;
893     String message = ((LogEntry) item.getData()).getStack();
894     if (message == null)
895       return;
896     makeHoverShell();
897     textLabel.setText(message);
898     int x = point.x + 5;
899     int y = point.y - (table.getItemHeight() * 2) - 20;
900     textShell.setLocation(table.toDisplay(x, y));
901     textShell.setSize(tableTreeViewer.getTableTree().getSize().x - x, 125);
902     textShell.open();
903     setFocus();
904   }
905
906   void onMouseMove(Event e) {
907     if (textShell != null && !textShell.isDisposed()) {
908       textShell.close();
909       canOpenTextShell = textShell.isDisposed() && e.x > column0.getWidth() && e.x < (column0.getWidth() + column1.getWidth());
910     } else {
911       canOpenTextShell = e.x > column0.getWidth() && e.x < (column0.getWidth() + column1.getWidth());
912     }
913   }
914
915   public Comparator getComparator() {
916     return comparator;
917   }
918
919   private void setComparator(byte sortType) {
920     if (sortType == DATE) {
921       comparator = new Comparator() {
922         public int compare(Object e1, Object e2) {
923           try {
924             SimpleDateFormat formatter = new SimpleDateFormat("MMM dd, yyyy HH:mm:ss.SS"); //$NON-NLS-1$
925             Date date1 = formatter.parse(((LogEntry) e1).getDate());
926             Date date2 = formatter.parse(((LogEntry) e2).getDate());
927             if (DATE_ORDER == ASCENDING)
928               return date1.before(date2) ? -1 : 1;
929             return date1.after(date2) ? -1 : 1;
930           } catch (ParseException e) {
931           }
932           return 0;
933         }
934       };
935     } else if (sortType == PLUGIN) {
936       comparator = new Comparator() {
937         public int compare(Object e1, Object e2) {
938           LogEntry entry1 = (LogEntry) e1;
939           LogEntry entry2 = (LogEntry) e2;
940           return collator.compare(entry1.getPluginId(), entry2.getPluginId()) * PLUGIN_ORDER;
941         }
942       };
943     } else {
944       comparator = new Comparator() {
945         public int compare(Object e1, Object e2) {
946           LogEntry entry1 = (LogEntry) e1;
947           LogEntry entry2 = (LogEntry) e2;
948           return collator.compare(entry1.getMessage(), entry2.getMessage()) * MESSAGE_ORDER;
949         }
950       };
951     }
952   }
953
954   private ViewerSorter getViewerSorter(byte sortType) {
955     if (sortType == PLUGIN) {
956       return new ViewerSorter() {
957         public int compare(Viewer viewer, Object e1, Object e2) {
958           LogEntry entry1 = (LogEntry) e1;
959           LogEntry entry2 = (LogEntry) e2;
960           return super.compare(viewer, entry1.getPluginId(), entry2.getPluginId()) * PLUGIN_ORDER;
961         }
962       };
963     } else if (sortType == MESSAGE) {
964       return new ViewerSorter() {
965         public int compare(Viewer viewer, Object e1, Object e2) {
966           LogEntry entry1 = (LogEntry) e1;
967           LogEntry entry2 = (LogEntry) e2;
968           return super.compare(viewer, entry1.getMessage(), entry2.getMessage()) * MESSAGE_ORDER;
969         }
970       };
971     } else {
972       return new ViewerSorter() {
973         public int compare(Viewer viewer, Object e1, Object e2) {
974           try {
975             SimpleDateFormat formatter = new SimpleDateFormat("MMM dd, yyyy HH:mm:ss.SS"); //$NON-NLS-1$
976             Date date1 = formatter.parse(((LogEntry) e1).getDate());
977             Date date2 = formatter.parse(((LogEntry) e2).getDate());
978             if (DATE_ORDER == ASCENDING)
979               return date1.before(date2) ? -1 : 1;
980             return date1.after(date2) ? -1 : 1;
981           } catch (ParseException e) {
982           }
983           return 0;
984         }
985       };
986     }
987   }
988
989   private void resetDialogButtons() {
990     ((EventDetailsDialogAction) propertiesAction).resetDialogButtons();
991   }
992 }