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(), 1, 2, "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 ((JLabel) header.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 ((AbstractTableModel) list.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((Icon) value);
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 }
|