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(), 1, 2, "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 ((JLabel) header.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 ((AbstractTableModel) list.getModel()).fireTableDataChanged();
294 ((AbstractTableModel) list.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((Icon) value);
389 super.setValue(null);
390 } else {
391 setIcon(null);
392 super.setValue(value);
393 }
394 }
395
396 }
397
398 }
|