ProcessListPane.java
001 /* This file is part of the project "Hilbert II" - http://www.qedeq.org
002  *
003  * Copyright 2000-2013,  Michael Meyling <mime@qedeq.org>.
004  *
005  * "Hilbert II" is free software; you can redistribute
006  * it and/or modify it under the terms of the GNU General Public
007  * License as published by the Free Software Foundation; either
008  * version 2 of the License, or (at your option) any later version.
009  *
010  * This program is distributed in the hope that it will be useful,
011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
013  * GNU General Public License for more details.
014  */
015 
016 package org.qedeq.gui.se.pane;
017 
018 import java.awt.Color;
019 import java.awt.Dimension;
020 import java.awt.Font;
021 import java.awt.event.ActionEvent;
022 import java.awt.event.MouseAdapter;
023 import java.awt.event.MouseEvent;
024 import java.lang.reflect.InvocationTargetException;
025 
026 import javax.swing.AbstractAction;
027 import javax.swing.BorderFactory;
028 import javax.swing.Icon;
029 import javax.swing.JLabel;
030 import javax.swing.JPanel;
031 import javax.swing.JScrollPane;
032 import javax.swing.JTable;
033 import javax.swing.ListSelectionModel;
034 import javax.swing.SwingUtilities;
035 import javax.swing.event.ListSelectionEvent;
036 import javax.swing.event.ListSelectionListener;
037 import javax.swing.table.AbstractTableModel;
038 import javax.swing.table.DefaultTableCellRenderer;
039 import javax.swing.table.JTableHeader;
040 import javax.swing.table.TableColumnModel;
041 import javax.swing.text.SimpleAttributeSet;
042 import javax.swing.text.StyleConstants;
043 
044 import org.qedeq.base.io.IoUtility;
045 import org.qedeq.base.trace.Trace;
046 import org.qedeq.base.utility.StringUtility;
047 import org.qedeq.base.utility.YodaUtility;
048 import org.qedeq.gui.se.util.GuiHelper;
049 import org.qedeq.kernel.bo.common.ServiceProcess;
050 
051 import com.jgoodies.forms.builder.DefaultFormBuilder;
052 import com.jgoodies.forms.layout.CellConstraints;
053 import com.jgoodies.forms.layout.FormLayout;
054 import com.jgoodies.forms.layout.RowSpec;
055 
056 /**
057  * Shows QEDEQ module specific error pane.
058  *
059  @author  Michael Meyling
060  */
061 
062 public class ProcessListPane extends JPanel  {
063 
064     /** This class. */
065     private static final Class CLASS = ProcessListPane.class;
066 
067     /** Currently selected error. */
068     private int selectedLine = -1;
069 
070     /** Start automatic refresh thread? */
071     private boolean automaticRefresh = true;
072 
073     /** Table model. */
074     private ProcessListModel model = new ProcessListModel();
075 
076     /** This table holds the error descriptions. */
077     private JTable list = new JTable(model) {
078 
079         /**
080          * Just return the service process of the row.
081          *
082          @param  e   Mouse event.
083          @return Tool tip text.
084          */
085         public String getToolTipText(final MouseEvent e) {
086             String tip = null;
087             java.awt.Point p = e.getPoint();
088             final int row = rowAtPoint(p);
089             final int col = columnAtPoint(p);
090             try {
091                 final ServiceProcess process = model.getServiceProcess(row);
092                 switch (col) {
093                 case 0:
094                      if (process.isBlocked()) {
095                          tip = "Process is waiting";
096                      else if (process.isRunning()) {
097                          tip = "Process is running";
098                      else if (process.wasFailure()) {
099                          tip = "Process was stopped.";
100                      else if (process.wasSuccess()) {
101                          tip = "Process has finished";
102                      }
103                      break;
104                 case 1: tip = process.getService().getPluginDescription();
105                      break;
106                 case 2:
107                 case 3:
108                 case 4:
109                 case 5: tip = process.getQedeq().getUrl();
110                      break;
111                 case 7: tip = "<html>" + StringUtility.replace(process.getExecutionActionDescription(),
112                      "\n""<br>""</html>";
113                 break;
114                 case 8: tip = process.getParameterString() "\n";
115                     break;
116                 default: tip = "";
117                 }
118             catch (RuntimeException ex) {
119                 return super.getToolTipText(e);
120             }
121             return tip;
122         }
123 
124     };
125 
126     /** Write with this font attributes. */
127     private final SimpleAttributeSet errorAttrs = new SimpleAttributeSet();
128 
129     /** Our scroll area. */
130     private JScrollPane scrollPane;
131 
132     /**
133      * Creates new panel.
134      */
135     public ProcessListPane() {
136         super(false);
137         setupView();
138     }
139 
140     /**
141      * Send selection events.
142      */
143     private void selectLine() {
144         Trace.param(CLASS, this, "selectLine""selectedLine", selectedLine);
145     }
146 
147     /**
148      * Assembles the GUI components of the panel.
149      */
150     private final void setupView() {
151         list.setFont(new Font("Lucida Sans Unicode", Font.PLAIN, list.getFont().getSize()));
152 
153         FormLayout layout = new FormLayout(
154             "min:grow",
155             "0:grow");
156         final DefaultFormBuilder builder = new DefaultFormBuilder(layout, this);
157         builder.setBorder(BorderFactory.createEmptyBorder());
158         builder.setRowGroupingEnabled(true);
159 
160         final CellConstraints cc = new CellConstraints();
161         builder.appendRow(new RowSpec("0:grow"));
162 
163         list.setDefaultRenderer(Icon.class, new IconCellRenderer());
164 
165         final ListSelectionModel rowSM = list.getSelectionModel();
166         rowSM.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
167 
168         // if selection changes remember the error number (= selected row (starting with 0))
169         rowSM.addListSelectionListener(new ListSelectionListener() {
170             public void valueChanged(final ListSelectionEvent e) {
171                 if (e.getValueIsAdjusting()) {
172                     return;
173                 }
174                 selectedLine = list.getSelectionModel().getLeadSelectionIndex();
175                 Trace.param(CLASS, this, "setupView$valueChanged""selectedLine" , selectedLine);
176             }
177         });
178 
179         // doing a click shall open the edit window
180         list.addMouseListener(new MouseAdapter()  {
181             public void mouseClicked(final MouseEvent e) {
182                 if (e.getClickCount() == 2) {
183                     Trace.trace(CLASS, this, "setupView$vmouseClicked""doubleClick");
184                 }
185                 selectLine();
186             }
187         });
188 
189         // pressing the enter key shall open the edit window
190         list.getActionMap().put("selectNextRowCell"new AbstractAction() {
191             public void actionPerformed(final ActionEvent event) {
192                 Trace.param(CLASS, this, "setupView$actionPerformed""event" , event);
193                 selectLine();
194             }
195         });
196 
197         scrollPane = new JScrollPane(list);
198         builder.add(scrollPane,
199             cc.xywh(builder.getColumn(), builder.getRow()12"fill, fill"));
200 
201         StyleConstants.setForeground(this.errorAttrs, Color.red);
202         // because we override getScrollableTracksViewportWidth:
203         scrollPane.getViewport().setBackground(Color.white);
204         StyleConstants.setBackground(this.errorAttrs, Color.white);  // TODO mime 20080124: testing
205 
206 
207         // the default table cell renderer uses a JLabel to render the heading and we want to
208         // left align the header columns
209         // TODO mime 20080415: left align with small spaces would be better
210         final JTableHeader header = list.getTableHeader();
211         ((JLabelheader.getDefaultRenderer()).setHorizontalAlignment(JLabel.LEFT);
212         //set height of header correctly
213         header.setPreferredSize(new Dimension(list.getTableHeader().getWidth(),
214             (int) (1.1 this.getFontMetrics(getFont()).getHeight())));
215 
216         changeHeaderWidth();
217         final Thread refresh = new Thread() {
218             public void run() {
219                 while (automaticRefresh) {
220                     updateView();
221                     IoUtility.sleep(5000);
222                 }
223             }
224         };
225         refresh.setDaemon(true);
226         refresh.start();
227     }
228 
229     /**
230      * Make 2. column smaller.
231      */
232     private void changeHeaderWidth() {
233         TableColumnModel columnModel = list.getColumnModel();
234         columnModel.getColumn(0).setPreferredWidth(24);
235         columnModel.getColumn(0).setMinWidth(24);
236         columnModel.getColumn(1).setPreferredWidth(200);
237         columnModel.getColumn(1).setMinWidth(150);
238         columnModel.getColumn(2).setPreferredWidth(200);
239         columnModel.getColumn(2).setMinWidth(150);
240         columnModel.getColumn(3).setPreferredWidth(100);
241         columnModel.getColumn(3).setMinWidth(100);
242         columnModel.getColumn(4).setPreferredWidth(100);
243         columnModel.getColumn(4).setMinWidth(100);
244         columnModel.getColumn(5).setPreferredWidth(100);
245         columnModel.getColumn(5).setMinWidth(100);
246         columnModel.getColumn(6).setPreferredWidth(60);
247         columnModel.getColumn(6).setMinWidth(60);
248         columnModel.getColumn(7).setPreferredWidth(2000);
249         columnModel.getColumn(7).setMinWidth(100);
250         columnModel.getColumn(8).setPreferredWidth(200);
251         columnModel.getColumn(8).setMinWidth(100);
252     }
253 
254     /**
255      * Update from model.
256      */
257     public synchronized void updateView() {
258         final String method = "updateView";
259         Trace.begin(CLASS, this, method);
260         ((AbstractTableModellist.getModel()).fireTableDataChanged();
261         list.invalidate();
262         list.repaint();
263         this.repaint();
264     }
265 
266     public void refreshStates() {
267         Runnable stateChanged = new Runnable() {
268             public void run() {
269                 updateView();
270             }
271         };
272         SwingUtilities.invokeLater(stateChanged);
273     }
274 
275     /**
276      * We render our cells with this class.
277      *
278      @author  Michael Meyling
279      */
280     private static class IconCellRenderer extends DefaultTableCellRenderer {
281 
282         protected void setValue(final Object value) {
283             if (value instanceof Icon) {
284                 setIcon((Iconvalue);
285                 super.setValue(null);
286             else {
287                 setIcon(null);
288                 super.setValue(value);
289             }
290         }
291 
292     }
293 
294     public void stopSelected() {
295         if (selectedLine >= 0) {
296             final ServiceProcess process = model.getServiceProcess(selectedLine);
297             if (process != null && process.isRunning()) {
298                 process.interrupt();
299             }
300         }
301     }
302 
303     /**
304      * Print stack trace of selected service process thread to <code>System.out</code> if the
305      * method <code>Thread.getStackTrace()</code> is supported form the currently running java
306      * version.
307      */
308     public void stackTraceSelected() {
309         if (selectedLine >= 0) {
310             StringBuffer result = new StringBuffer();
311             final ServiceProcess process = model.getServiceProcess(selectedLine);
312             if (process != null && process.isRunning()) {
313                 if (YodaUtility.existsMethod(Thread.class, "getStackTrace"new Class[] {})) {
314                     StackTraceElement[] trace = new StackTraceElement[] {};
315                     try {
316                         trace = (StackTraceElement[]) YodaUtility.executeMethod(
317                             process.getThread()"getStackTrace"new Class[] {}new Object[] {});
318                     catch (NoSuchMethodException e) {
319                         // ignore
320                     catch (InvocationTargetException e) {
321                         // ignore
322                     }
323                     for (int i = 0; i < trace.length; i++)  {
324                         System.out.println(trace[i]);
325                         result.append(trace[i]);
326                         result.append("\n");
327                     }
328                     (new TextPaneWindow("Stacktrace",
329                             GuiHelper.readImageIcon("tango/16x16/devices/video-display.png"),
330                             result.toString())).setVisible(true);
331                 }
332             }
333         }
334     }
335 
336 }