001 /* This file is part of the project "Hilbert II" - http://www.qedeq.org
002 *
003 * Copyright 2000-2014, 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.Toolkit;
021 import java.awt.datatransfer.Clipboard;
022 import java.awt.datatransfer.StringSelection;
023 import java.awt.event.ActionEvent;
024 import java.awt.event.ActionListener;
025 import java.awt.event.MouseAdapter;
026 import java.awt.event.MouseEvent;
027
028 import javax.swing.AbstractAction;
029 import javax.swing.BorderFactory;
030 import javax.swing.Icon;
031 import javax.swing.JLabel;
032 import javax.swing.JPanel;
033 import javax.swing.JScrollPane;
034 import javax.swing.JTable;
035 import javax.swing.ListSelectionModel;
036 import javax.swing.SwingUtilities;
037 import javax.swing.event.ListSelectionEvent;
038 import javax.swing.event.ListSelectionListener;
039 import javax.swing.table.AbstractTableModel;
040 import javax.swing.table.DefaultTableCellRenderer;
041 import javax.swing.table.JTableHeader;
042 import javax.swing.table.TableColumnModel;
043 import javax.swing.text.SimpleAttributeSet;
044 import javax.swing.text.StyleConstants;
045
046 import org.qedeq.base.trace.Trace;
047 import org.qedeq.base.utility.DateUtility;
048 import org.qedeq.base.utility.EqualsUtility;
049 import org.qedeq.gui.se.control.SelectionListenerList;
050 import org.qedeq.gui.se.util.GuiHelper;
051 import org.qedeq.kernel.bo.KernelContext;
052 import org.qedeq.kernel.bo.common.QedeqBo;
053 import org.qedeq.kernel.bo.log.ModuleEventListener;
054 import org.qedeq.kernel.se.common.SourceFileException;
055
056 import com.jgoodies.forms.builder.DefaultFormBuilder;
057 import com.jgoodies.forms.layout.CellConstraints;
058 import com.jgoodies.forms.layout.FormLayout;
059 import com.jgoodies.forms.layout.RowSpec;
060
061 /**
062 * Shows QEDEQ module specific error pane.
063 *
064 * @author Michael Meyling
065 */
066
067 public class ModuleErrorAndWarningListPane extends JPanel implements ModuleEventListener,
068 ActionListener {
069
070 /** This class. */
071 private static final Class CLASS = ModuleErrorAndWarningListPane.class;
072
073 /** Currently selected error. */
074 private int selectedLine = -1;
075
076 /** Table model. */
077 private ModuleErrorAndWarningListModel model = new ModuleErrorAndWarningListModel();
078
079 /** This table holds the error descriptions. */
080 private JTable list = new JTable(model) {
081
082 /**
083 * Just return the error message of the row.
084 *
085 * @param e Mouse event.
086 * @return Tool tip text.
087 */
088 public String getToolTipText(final MouseEvent e) {
089 String tip = null;
090 java.awt.Point p = e.getPoint();
091 int row = rowAtPoint(p);
092 SourceFileException sfe = null;
093 if (row >= 0) {
094 try {
095 sfe = model.getSourceFileException(row);
096 } catch (RuntimeException ex) {
097 // ignore
098 }
099 }
100 if (sfe != null) {
101 int col = columnAtPoint(p);
102 switch (col) {
103 case 0: tip = (model.isWarning(row) ? "Warning" : "Error");
104 break;
105 case 1: tip = sfe.getMessage() + "\n";
106 break;
107 case 2: tip = (sfe.getSourceArea() != null
108 ? sfe.getSourceArea().getShortDescription() : "");
109 break;
110 case 3: tip = sfe.getService().getServiceDescription();
111 break;
112 default: tip = sfe.getMessage() + "\n";
113 }
114 tip = GuiHelper.getToolTipText(tip);
115 return tip;
116
117 }
118 return super.getToolTipText(e);
119 }
120
121 };
122
123 /** Context menu for right mouse click. */
124 private final ModuleErrorAndWarningListContextMenu contextMenu;
125
126 /** Write with this font attributes. */
127 private final SimpleAttributeSet errorAttrs = new SimpleAttributeSet();
128
129 /** For this module properties the warnings and errors are shown. */
130 private QedeqBo prop;
131
132 /** Our scroll area. */
133 private JScrollPane scrollPane;
134
135 /** This listener gets all selection messages from us. */
136 private SelectionListenerList listener;
137
138 /**
139 * Creates new panel.
140 *
141 * @param listener Send selecting events to this listener.
142 */
143 public ModuleErrorAndWarningListPane(final SelectionListenerList listener) {
144 super(false);
145 this.listener = listener;
146 this.contextMenu = new ModuleErrorAndWarningListContextMenu(this);
147 setModel(null);
148 setupView();
149 }
150
151 /**
152 * Send selection events.
153 */
154 private void selectLine() {
155 Trace.param(CLASS, this, "selectLine", "selectedLine", selectedLine);
156 if (model.isError(selectedLine)) {
157 listener.selectError(model.getErrorNumber(selectedLine), model.getSourceFileException(selectedLine));
158 } else if (model.isWarning(selectedLine)) {
159 listener.selectWarning(model.getWarningNumber(selectedLine), model.getSourceFileException(selectedLine));
160 }
161 }
162
163 /**
164 * Assembles the GUI components of the panel.
165 */
166 private final void setupView() {
167 FormLayout layout = new FormLayout(
168 "min:grow",
169 "0:grow");
170 final DefaultFormBuilder builder = new DefaultFormBuilder(layout, this);
171 builder.setBorder(BorderFactory.createEmptyBorder());
172 builder.setRowGroupingEnabled(true);
173
174 final CellConstraints cc = new CellConstraints();
175 builder.appendRow(new RowSpec("0:grow"));
176
177 list.setDefaultRenderer(Icon.class, new IconCellRenderer());
178
179 final ListSelectionModel rowSM = list.getSelectionModel();
180 rowSM.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
181
182 // if selection changes remember the error number (= selected row (starting with 0))
183 rowSM.addListSelectionListener(new ListSelectionListener() {
184 public void valueChanged(final ListSelectionEvent e) {
185 if (e.getValueIsAdjusting()) {
186 return;
187 }
188 selectedLine = list.getSelectionModel().getLeadSelectionIndex();
189 Trace.param(CLASS, this, "setupView$valueChanged", "selectedLine" , selectedLine);
190 }
191 });
192
193 // doing a click shall open the edit window
194 list.addMouseListener(new MouseAdapter() {
195 public void mousePressed(final MouseEvent evt) {
196 if (SwingUtilities.isRightMouseButton(evt)) {
197 contextMenu.show(evt.getComponent(),
198 evt.getX(), evt.getY());
199 } else {
200 super.mousePressed(evt);
201 }
202 }
203
204 public void mouseClicked(final MouseEvent e) {
205 if (e.getClickCount() == 2) {
206 Trace.trace(CLASS, this, "setupView$vmouseClicked", "doubleClick");
207 }
208 selectLine();
209 }
210 });
211
212 // pressing the enter key shall open the edit window
213 list.getActionMap().put("selectNextRowCell", new AbstractAction() {
214 public void actionPerformed(final ActionEvent event) {
215 Trace.param(CLASS, this, "setupView$actionPerformed", "event" , event);
216 selectLine();
217 }
218 });
219
220 scrollPane = new JScrollPane(list);
221 builder.add(scrollPane,
222 cc.xywh(builder.getColumn(), builder.getRow(), 1, 2, "fill, fill"));
223
224 // open context menu on right click
225 scrollPane.addMouseListener(new MouseAdapter() {
226 public void mousePressed(final MouseEvent evt) {
227 if (SwingUtilities.isRightMouseButton(evt)) {
228 contextMenu.show(evt.getComponent(),
229 evt.getX(), evt.getY());
230 } else {
231 super.mousePressed(evt);
232 }
233 }
234 });
235
236 StyleConstants.setForeground(this.errorAttrs, Color.red);
237 // because we override getScrollableTracksViewportWidth:
238 scrollPane.getViewport().setBackground(Color.white);
239 StyleConstants.setBackground(this.errorAttrs, Color.white); // TODO mime 20080124: testing
240
241
242 // the default table cell renderer uses a JLabel to render the heading and we want to
243 // left align the header columns
244 // TODO mime 20080415: left align with small spaces would be better
245 final JTableHeader header = list.getTableHeader();
246 ((JLabel) header.getDefaultRenderer()).setHorizontalAlignment(JLabel.LEFT);
247
248 //set height of header correctly
249 header.setPreferredSize(new Dimension(list.getTableHeader().getWidth(),
250 (int) (1.1 * this.getFontMetrics(getFont()).getHeight())));
251
252 changeColumnHeaderWidth();
253 }
254
255 /**
256 * Make 2. column smaller.
257 */
258 private void changeColumnHeaderWidth() {
259 TableColumnModel columnModel = list.getColumnModel();
260 columnModel.getColumn(0).setPreferredWidth(24);
261 columnModel.getColumn(0).setMinWidth(24);
262 columnModel.getColumn(1).setPreferredWidth(2000);
263 columnModel.getColumn(1).setMinWidth(100);
264 columnModel.getColumn(2).setPreferredWidth(50);
265 columnModel.getColumn(2).setMinWidth(50);
266 columnModel.getColumn(3).setPreferredWidth(100);
267 columnModel.getColumn(3).setMinWidth(100);
268 }
269
270 /**
271 * Set new model. To make the new model visible {@link #updateView} must be called.
272 *
273 * @param prop QEDEQ module.
274 */
275 public synchronized void setModel(final QedeqBo prop) {
276 Trace.trace(CLASS, this, "setModel", prop);
277 model.setQedeq(prop);
278 if (!EqualsUtility.equals(this.prop, prop)) {
279 this.prop = prop;
280 Runnable setModel = new Runnable() {
281 public void run() {
282 updateView();
283 }
284 };
285 SwingUtilities.invokeLater(setModel);
286 }
287 }
288
289 /**
290 * Update from model.
291 */
292 public synchronized void updateView() {
293 final String method = "updateView";
294 Trace.begin(CLASS, this, method);
295 ((AbstractTableModel) list.getModel()).fireTableDataChanged();
296 ((AbstractTableModel) list.getModel()).fireTableStructureChanged();
297 changeColumnHeaderWidth();
298 list.invalidate();
299 list.repaint();
300 this.repaint();
301 }
302
303 public void addModule(final QedeqBo p) {
304 if (prop != null && prop.equals(p)) {
305 Runnable addModule = new Runnable() {
306 public void run() {
307 updateView();
308 }
309 };
310 SwingUtilities.invokeLater(addModule);
311 }
312 }
313
314 public void stateChanged(final QedeqBo p) {
315 if (prop != null && prop.equals(p)) {
316 Runnable stateChanged = new Runnable() {
317 public void run() {
318 updateView();
319 }
320 };
321 SwingUtilities.invokeLater(stateChanged);
322 }
323 }
324
325 public void removeModule(final QedeqBo p) {
326 if (prop != null && prop.equals(p)) {
327 prop = null;
328 Runnable removeModule = new Runnable() {
329 public void run() {
330 updateView();
331 }
332 };
333 SwingUtilities.invokeLater(removeModule);
334 }
335 }
336
337 public void actionPerformed(final ActionEvent actionevent) {
338 final String s = actionevent.getActionCommand();
339 if (s.equals("copy")) {
340 if (model.getQedeq() != null) {
341 final StringBuffer sb = new StringBuffer(512);
342 sb.append(KernelContext.getInstance().getDescriptiveKernelVersion() + "\n");
343 sb.append("Date\t" + DateUtility.getGmtTimestamp() + "\n");
344 sb.append("QEDEQ module\t" + prop.getUrl() + "\n");
345 sb.append("Kind\tType\tCode\tFrom\tTo\tDescription\n");
346 for (int i = 0; i < model.getQedeq().getErrors().size(); i++) {
347 SourceFileException e = model.getQedeq().getErrors().get(i);
348 sb.append(e.getService().getServiceAction());
349 sb.append("\t");
350 sb.append("Error");
351 sb.append("\t");
352 sb.append(e.getErrorCode());
353 sb.append("\t");
354 sb.append(e.getSourceArea().getStartPosition());
355 sb.append("\t");
356 sb.append(e.getSourceArea().getEndPosition());
357 sb.append("\t");
358 sb.append(e.getMessage());
359 sb.append("\n");
360 }
361 for (int i = 0; i < model.getQedeq().getWarnings().size(); i++) {
362 SourceFileException e = model.getQedeq().getWarnings().get(i);
363 sb.append(e.getService().getServiceAction());
364 sb.append("\t");
365 sb.append("Warning");
366 sb.append("\t");
367 sb.append(e.getErrorCode());
368 sb.append("\t");
369 sb.append(e.getSourceArea().getStartPosition());
370 sb.append("\t");
371 sb.append(e.getSourceArea().getEndPosition());
372 sb.append("\t");
373 sb.append(e.getMessage());
374 sb.append("\n");
375 }
376 final Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard();
377 StringSelection sel = new StringSelection(sb.toString());
378 cb.setContents(sel, null);
379 }
380 }
381 }
382
383
384 /**
385 * We render our cells with this class.
386 *
387 * @author Michael Meyling
388 */
389 private static class IconCellRenderer extends DefaultTableCellRenderer {
390
391 protected void setValue(final Object value) {
392 if (value instanceof Icon) {
393 setIcon((Icon) value);
394 super.setValue(null);
395 } else {
396 setIcon(null);
397 super.setValue(value);
398 }
399 }
400
401 }
402
403 }
|