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.BorderLayout;
019 import java.awt.event.MouseEvent;
020 import java.io.IOException;
021 import java.util.List;
022
023 import javax.swing.BorderFactory;
024 import javax.swing.JPanel;
025 import javax.swing.JScrollPane;
026 import javax.swing.JTextArea;
027 import javax.swing.JViewport;
028 import javax.swing.SwingUtilities;
029 import javax.swing.UIManager;
030 import javax.swing.plaf.TextUI;
031 import javax.swing.text.BadLocationException;
032
033 import org.qedeq.base.io.SourceArea;
034 import org.qedeq.base.trace.Trace;
035 import org.qedeq.base.utility.EqualsUtility;
036 import org.qedeq.gui.se.control.SelectionListener;
037 import org.qedeq.gui.se.element.CPTextArea;
038 import org.qedeq.gui.se.util.CurrentLineHighlighterUtility;
039 import org.qedeq.gui.se.util.DocumentMarker;
040 import org.qedeq.gui.se.util.DocumentMarkerPainter;
041 import org.qedeq.gui.se.util.GuiHelper;
042 import org.qedeq.kernel.bo.KernelContext;
043 import org.qedeq.kernel.bo.common.QedeqBo;
044 import org.qedeq.kernel.se.common.SourceFileException;
045 import org.qedeq.kernel.se.common.SourceFileExceptionList;
046
047 /**
048 * View source of QEDEQ module.
049 *
050 * @author Michael Meyling
051 */
052
053 public class QedeqPane extends JPanel implements SelectionListener {
054
055 /** This class. */
056 private static final Class CLASS = QedeqPane.class;
057
058 /** Reference to module properties. */
059 private QedeqBo prop;
060
061 /** Here is the QEDEQ module source. */
062 private JTextArea qedeq = new CPTextArea(false) {
063 public String getToolTipText(final MouseEvent e) {
064 if (errorMarker == null || warningMarker == null) {
065 setToolTipText(null);
066 return super.getToolTipText();
067 }
068 // --- determine locations ---
069 TextUI mapper = qedeq.getUI();
070 final int i = mapper.viewToModel(qedeq, e.getPoint());
071 final List errNos = errorMarker.getBlockNumbersForOffset(i);
072 final List warningNos = warningMarker.getBlockNumbersForOffset(i);
073 if (errNos.size() == 0 && warningNos.size() == 0) {
074 setToolTipText(null);
075 return super.getToolTipText(e);
076 }
077 final StringBuffer buffer = new StringBuffer();
078 for (int j = 0; j < errNos.size(); j++) {
079 if (j > 0) {
080 buffer.append("\n");
081 }
082 buffer.append((prop.getErrors().get(((Integer) errNos.get(j)).intValue())
083 .getMessage()));
084 }
085 for (int j = 0; j < warningNos.size(); j++) {
086 if (j > 0) {
087 buffer.append("\n");
088 }
089 buffer.append((prop.getWarnings().get(((Integer) warningNos.get(j)).intValue())
090 .getMessage()));
091 }
092 setToolTipText(GuiHelper.getToolTipText(buffer.toString()));
093 return getToolTipText();
094 }
095 };
096
097 /** Our error highlighter for the text areas. */
098 private DocumentMarker errorMarker;
099
100 /** Our warning highlighter for the text areas. */
101 private DocumentMarker warningMarker;
102
103 /**
104 * Creates new Panel.
105 */
106 public QedeqPane() {
107 super(false);
108 this.prop = null;
109 setupView();
110 updateView();
111 }
112
113 /**
114 * Assembles the GUI components of the panel.
115 */
116 public void setupView() {
117 final JScrollPane scroller = new JScrollPane();
118 final JViewport vp = scroller.getViewport();
119 vp.add(qedeq);
120 this.setLayout(new BorderLayout(0, 0));
121 this.add(scroller);
122 setBorder(BorderFactory.createEmptyBorder());
123 qedeq.setEditable(false);
124 qedeq.setToolTipText("");
125 qedeq.putClientProperty("JTextArea.infoBackground", Boolean.TRUE);
126 qedeq.setLineWrap(true); // TODO mime 20080509: make this configurable
127 }
128
129 /**
130 * Set new model. To make the new model visible {@link #updateView} must be called.
131 *
132 * @param prop New QEDEQ module.
133 */
134 public synchronized void setModel(final QedeqBo prop) {
135 Trace.trace(CLASS, this, "setModel", prop);
136 if (!EqualsUtility.equals(this.prop, prop)) {
137 this.prop = prop;
138 Runnable setModel = new Runnable() {
139 public void run() {
140 updateView();
141 }
142 };
143 SwingUtilities.invokeLater(setModel);
144 }
145 }
146
147
148 public void setLineWrap(final boolean wrap) {
149 qedeq.setLineWrap(wrap);
150 }
151
152 public boolean getLineWrap() {
153 return qedeq.getLineWrap();
154 }
155
156 /**
157 * Update from model.
158 */
159 public synchronized void updateView() {
160 Trace.begin(CLASS, this, "updateView");
161 if (prop != null) {
162 try {
163 qedeq.setText(KernelContext.getInstance().getSource(prop.getModuleAddress()));
164 CurrentLineHighlighterUtility.install(qedeq);
165 qedeq.setLineWrap(getLineWrap());
166 if (prop.getModuleAddress().isFileAddress()) {
167 qedeq.setEditable(true);
168 } else {
169 // TODO 20100319 m31: show "readonly" as label somewhere
170 qedeq.setEditable(false);
171 }
172 // we want the background be same even if area is not editable
173 qedeq.setBackground(UIManager.getColor("TextArea.background"));
174 qedeq.setCaretPosition(0);
175 qedeq.getCaret().setSelectionVisible(true);
176
177 // TODO m31 20100707: duplicate code, refactor
178 warningMarker = new DocumentMarker(qedeq, new DocumentMarkerPainter(
179 GuiHelper.getWarningTextBackgroundColor()));
180 final SourceFileExceptionList pw = prop.getWarnings();
181 if (pw != null) {
182 for (int i = 0; i < pw.size(); i++) {
183 if (pw.get(i).getSourceArea() != null) {
184 try {
185 final SourceArea sa = pw.get(i).getSourceArea();
186 if (sa != null) {
187 final int from = sa.getStartPosition().getRow() - 1;
188 int to = sa.getEndPosition().getRow() - 1;
189 // for line marking only:
190 // warningMarker.addWarningLines(from, to,
191 // sa.getStartPosition().getColumn() - 1);
192 warningMarker.addMarkedBlock(from,
193 sa.getStartPosition().getColumn() - 1,
194 to, sa.getEndPosition().getColumn() - 1);
195 continue;
196 }
197 } catch (BadLocationException e) { // should not occur
198 Trace.fatal(CLASS, this, "updateView", "Programming error?", e);
199 }
200 } else {
201 warningMarker.addEmptyBlock();
202 }
203 }
204 }
205 errorMarker = new DocumentMarker(qedeq, new DocumentMarkerPainter(
206 GuiHelper.getErrorTextBackgroundColor()));
207 final SourceFileExceptionList pe = prop.getErrors();
208 if (pe != null) {
209 for (int i = 0; i < pe.size(); i++) {
210 if (pe.get(i).getSourceArea() != null) {
211 try {
212 final SourceArea sa = pe.get(i).getSourceArea();
213 if (sa != null) {
214 final int from = sa.getStartPosition().getRow() - 1;
215 int to = sa.getEndPosition().getRow() - 1;
216 // for line marking only:
217 // errorMarker.addMarkedLines(from, to,
218 // sa.getStartPosition().getColumn() - 1);
219 errorMarker.addMarkedBlock(from,
220 sa.getStartPosition().getColumn() - 1,
221 to, sa.getEndPosition().getColumn() - 1);
222 continue;
223 }
224 } catch (BadLocationException e) { // should not occur
225 Trace.fatal(CLASS, this, "updateView", "Programming error?", e);
226 }
227 } else {
228 errorMarker.addEmptyBlock();
229 }
230 }
231 }
232 Trace.trace(CLASS, this, "updateView", "Text updated");
233 } catch (IOException ioException) {
234 qedeq.setEditable(false);
235 qedeq.setText("");
236 Trace.trace(CLASS, this, "updateView", ioException);
237 }
238 } else {
239 errorMarker = null;
240 warningMarker = null;
241 CurrentLineHighlighterUtility.uninstall(qedeq);
242 qedeq.setEditable(false);
243 qedeq.setText("");
244 Trace.end(CLASS, this, "updateView");
245 }
246 this.repaint();
247 }
248
249
250 /**
251 * Get QEDEQ text, if QEDEQ text is editable.
252 *
253 * @return QEDEQ text source.
254 * @throws IllegalStateException QEDEQ text is not editable.
255 */
256 public final String getEditedQedeq() {
257 if (qedeq.isEditable()) {
258 return this.qedeq.getText();
259 } else {
260 throw new IllegalStateException("no editable QEDEQ text");
261 }
262 }
263
264
265 /**
266 * Was the content changed?
267 *
268 * @return content modified?
269 */
270 public final boolean isContentChanged() {
271 return !this.qedeq.isEditable();
272 }
273
274
275 // LATER m31 20100830: not used any longer
276 /*
277 private final int findCaretPosition(final int i) {
278 if (i == 1) {
279 return 0;
280 }
281 final String s = qedeq.getText();
282 int j = 0;
283 int k = 0;
284 for (; j < s.length(); j++) {
285 if (s.charAt(j) == '\n') {
286 k++;
287 }
288 if (k == i - 1) {
289 return j + 1;
290 }
291 }
292 return 0;
293 }
294
295 private final void highlightLine(final int line) {
296 Trace.param(CLASS, this, "highlightLine", "line", line);
297 int j;
298 try {
299 j = qedeq.getLineStartOffset(line - 1);
300 } catch (BadLocationException e) {
301 Trace.trace(CLASS, this, "highlightLine", e);
302 j = 0;
303 }
304 int k;
305 try {
306 k = qedeq.getLineEndOffset(line - 1);
307 } catch (BadLocationException e) {
308 Trace.trace(CLASS, this, "highlightLine", e);
309 k = qedeq.getText().length();
310 }
311 Trace.trace(CLASS, this, "highlightLine", "from " + j + " to " + k);
312
313 qedeq.setCaretPosition(j);
314 qedeq.moveCaretPosition(k);
315 }
316
317 // public void selectError(final int number) {
318 // if (prop != null && prop.getException() != null && number < prop.getException().size()) {
319 // final SourceFileException sfe = prop.getException().get(number);
320 // if (sfe.getSourceArea() != null && sfe.getSourceArea().getStartPosition() != null) {
321 // highlightLine(sfe.getSourceArea().getStartPosition().getLine());
322 // }
323 // }
324 // }
325 */
326
327 /**
328 * Jump to error location. Uses only <code>error</code> to select correct marker.
329 *
330 * @param error Selected error number. Starts with 0.
331 * @param sf Selected error.
332 */
333 public synchronized void selectError(final int error, final SourceFileException sf) {
334 if (errorMarker != null) {
335 this.requestFocus();
336 qedeq.requestFocus();
337 qedeq.setCaretPosition(errorMarker.getLandmarkOffsetForBlock(error));
338 } else {
339 Trace.paramInfo(CLASS, "selectError", "errorMarker", "null");
340 }
341 }
342
343 /**
344 * Jump to warning location. Uses only <code>warning</code> to select correct marker.
345 *
346 * @param warning Selected warning number. Starts with 0.
347 * @param sf Selected warning.
348 */
349 public synchronized void selectWarning(final int warning, final SourceFileException sf) {
350 int block = warning;
351 // if (prop != null && prop.getErrors().size() > 0) {
352 // block += prop.getErrors().size();
353 // }
354 if (warningMarker != null) {
355 this.requestFocus();
356 qedeq.requestFocus();
357 qedeq.setCaretPosition(warningMarker.getLandmarkOffsetForBlock(block));
358 } else {
359 Trace.paramInfo(CLASS, "selectWarning", "warningMarker", "null");
360 }
361 }
362
363 }
|