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