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