author | immler@in.tum.de |
Thu, 27 Aug 2009 10:51:09 +0200 | |
changeset 34670 | c99878d7bec7 |
parent 34669 | 73727c7eec64 |
child 34671 | d179fcb04cbc |
permissions | -rw-r--r-- |
34407 | 1 |
/* |
34408 | 2 |
* jEdit text area as document text source |
34407 | 3 |
* |
4 |
* @author Fabian Immler, TU Munich |
|
5 |
* @author Johannes Hölzl, TU Munich |
|
34447 | 6 |
* @author Makarius |
34407 | 7 |
*/ |
8 |
||
34318
c13e168a8ae6
original sources from Johannes Hölzl a48e0c6ab1aea77c52d596f7efc007a543d3d10c with minor modifications of directory layout;
wenzelm
parents:
diff
changeset
|
9 |
package isabelle.jedit |
c13e168a8ae6
original sources from Johannes Hölzl a48e0c6ab1aea77c52d596f7efc007a543d3d10c with minor modifications of directory layout;
wenzelm
parents:
diff
changeset
|
10 |
|
34532 | 11 |
import scala.actors.Actor |
34582 | 12 |
import scala.actors.Actor._ |
34446 | 13 |
|
34660 | 14 |
import isabelle.proofdocument.{ProofDocument, Change, Edit, Insert, Remove} |
34649 | 15 |
import isabelle.prover.{Prover, ProverEvents, Command} |
34318
c13e168a8ae6
original sources from Johannes Hölzl a48e0c6ab1aea77c52d596f7efc007a543d3d10c with minor modifications of directory layout;
wenzelm
parents:
diff
changeset
|
16 |
|
c13e168a8ae6
original sources from Johannes Hölzl a48e0c6ab1aea77c52d596f7efc007a543d3d10c with minor modifications of directory layout;
wenzelm
parents:
diff
changeset
|
17 |
import java.awt.Graphics2D |
34446 | 18 |
import java.awt.event.{ActionEvent, ActionListener} |
19 |
import java.awt.Color |
|
34447 | 20 |
import javax.swing.Timer |
21 |
import javax.swing.event.{CaretListener, CaretEvent} |
|
34318
c13e168a8ae6
original sources from Johannes Hölzl a48e0c6ab1aea77c52d596f7efc007a543d3d10c with minor modifications of directory layout;
wenzelm
parents:
diff
changeset
|
22 |
|
34446 | 23 |
import org.gjt.sp.jedit.buffer.{BufferListener, JEditBuffer} |
24 |
import org.gjt.sp.jedit.textarea.{JEditTextArea, TextAreaExtension, TextAreaPainter} |
|
34649 | 25 |
import org.gjt.sp.jedit.syntax.{ModeProvider, SyntaxStyle} |
34318
c13e168a8ae6
original sources from Johannes Hölzl a48e0c6ab1aea77c52d596f7efc007a543d3d10c with minor modifications of directory layout;
wenzelm
parents:
diff
changeset
|
26 |
|
34446 | 27 |
|
34588 | 28 |
object TheoryView |
29 |
{ |
|
34574
d68352286c91
split large changes for faster responses of prover
immler@in.tum.de
parents:
34568
diff
changeset
|
30 |
|
34653 | 31 |
def choose_color(cmd: Command, doc: ProofDocument): Color = { |
32 |
cmd.status(doc) match { |
|
33 |
case Command.Status.UNPROCESSED => new Color(255, 228, 225) |
|
34 |
case Command.Status.FINISHED => new Color(234, 248, 255) |
|
35 |
case Command.Status.FAILED => new Color(255, 192, 192) |
|
36 |
case _ => Color.red |
|
37 |
} |
|
34318
c13e168a8ae6
original sources from Johannes Hölzl a48e0c6ab1aea77c52d596f7efc007a543d3d10c with minor modifications of directory layout;
wenzelm
parents:
diff
changeset
|
38 |
} |
c13e168a8ae6
original sources from Johannes Hölzl a48e0c6ab1aea77c52d596f7efc007a543d3d10c with minor modifications of directory layout;
wenzelm
parents:
diff
changeset
|
39 |
} |
c13e168a8ae6
original sources from Johannes Hölzl a48e0c6ab1aea77c52d596f7efc007a543d3d10c with minor modifications of directory layout;
wenzelm
parents:
diff
changeset
|
40 |
|
34446 | 41 |
|
34532 | 42 |
class TheoryView (text_area: JEditTextArea, document_actor: Actor) |
34588 | 43 |
extends TextAreaExtension with BufferListener |
44 |
{ |
|
34406
f81cd75ae331
restructured: independent provers in different buffers
immler@in.tum.de
parents:
34405
diff
changeset
|
45 |
|
34603
83a37e3b8c9c
produce ids via Isabelle.system (http://isabelle.in.tum.de/repos/isabelle/rev/c23663825e23);
wenzelm
parents:
34598
diff
changeset
|
46 |
def id() = Isabelle.system.id() |
34541 | 47 |
|
34446 | 48 |
private val buffer = text_area.getBuffer |
34475
f963335dbc6b
implemented IsabelleSideKickParser.complete
immler@in.tum.de
parents:
34458
diff
changeset
|
49 |
private val prover = Isabelle.prover_setup(buffer).get.prover |
34446 | 50 |
|
51 |
||
34660 | 52 |
private var edits: List[Edit] = Nil |
34446 | 53 |
private val col_timer = new Timer(300, new ActionListener() { |
34515
3be515f1379d
use FontMetrics.getMaxAdvance if available; tuned
immler@in.tum.de
parents:
34514
diff
changeset
|
54 |
override def actionPerformed(e: ActionEvent) = commit |
34318
c13e168a8ae6
original sources from Johannes Hölzl a48e0c6ab1aea77c52d596f7efc007a543d3d10c with minor modifications of directory layout;
wenzelm
parents:
diff
changeset
|
55 |
}) |
34406
f81cd75ae331
restructured: independent provers in different buffers
immler@in.tum.de
parents:
34405
diff
changeset
|
56 |
|
34446 | 57 |
col_timer.stop |
58 |
col_timer.setRepeats(true) |
|
59 |
||
60 |
||
34566 | 61 |
private val phase_overview = new PhaseOverviewPanel(prover, text_area, to_current) |
34446 | 62 |
|
34318
c13e168a8ae6
original sources from Johannes Hölzl a48e0c6ab1aea77c52d596f7efc007a543d3d10c with minor modifications of directory layout;
wenzelm
parents:
diff
changeset
|
63 |
|
34446 | 64 |
/* activation */ |
65 |
||
66 |
private val selected_state_controller = new CaretListener { |
|
67 |
override def caretUpdate(e: CaretEvent) = { |
|
34650 | 68 |
val doc = current_document() |
34554
7dc6c231da40
abs. stops, markup nodes depend on doc-version;
immler@in.tum.de
parents:
34549
diff
changeset
|
69 |
val cmd = doc.find_command_at(e.getDot) |
7dc6c231da40
abs. stops, markup nodes depend on doc-version;
immler@in.tum.de
parents:
34549
diff
changeset
|
70 |
if (cmd != null && doc.token_start(cmd.tokens.first) <= e.getDot && |
34669 | 71 |
Isabelle.plugin.selected_state != cmd) |
72 |
Isabelle.plugin.selected_state = cmd |
|
34318
c13e168a8ae6
original sources from Johannes Hölzl a48e0c6ab1aea77c52d596f7efc007a543d3d10c with minor modifications of directory layout;
wenzelm
parents:
diff
changeset
|
73 |
} |
c13e168a8ae6
original sources from Johannes Hölzl a48e0c6ab1aea77c52d596f7efc007a543d3d10c with minor modifications of directory layout;
wenzelm
parents:
diff
changeset
|
74 |
} |
c13e168a8ae6
original sources from Johannes Hölzl a48e0c6ab1aea77c52d596f7efc007a543d3d10c with minor modifications of directory layout;
wenzelm
parents:
diff
changeset
|
75 |
|
34582 | 76 |
def activate() { |
34446 | 77 |
text_area.addCaretListener(selected_state_controller) |
78 |
text_area.addLeftOfScrollBar(phase_overview) |
|
79 |
text_area.getPainter.addExtension(TextAreaPainter.LINE_BACKGROUND_LAYER + 1, this) |
|
34538
20bfcca24658
Prover as actor managing ProofDocument-versions (removed EventBus structural_changes);
immler@in.tum.de
parents:
34532
diff
changeset
|
80 |
buffer.setTokenMarker(new DynamicTokenMarker(buffer, prover)) |
34649 | 81 |
buffer.addBufferListener(this) |
34446 | 82 |
} |
83 |
||
34582 | 84 |
def deactivate() { |
34649 | 85 |
buffer.setTokenMarker(buffer.getMode.getTokenMarker) |
86 |
buffer.removeBufferListener(this) |
|
34446 | 87 |
text_area.getPainter.removeExtension(this) |
88 |
text_area.removeLeftOfScrollBar(phase_overview) |
|
89 |
text_area.removeCaretListener(selected_state_controller) |
|
90 |
} |
|
91 |
||
92 |
||
93 |
/* painting */ |
|
94 |
||
34643
3896caeedf82
renamed Delay to clarified version Swing_Thread.delay;
wenzelm
parents:
34642
diff
changeset
|
95 |
val update_delay = Swing_Thread.delay(500){ buffer.propertiesChanged() } |
3896caeedf82
renamed Delay to clarified version Swing_Thread.delay;
wenzelm
parents:
34642
diff
changeset
|
96 |
val repaint_delay = Swing_Thread.delay(100){ repaint_all() } |
34538
20bfcca24658
Prover as actor managing ProofDocument-versions (removed EventBus structural_changes);
immler@in.tum.de
parents:
34532
diff
changeset
|
97 |
|
34582 | 98 |
val change_receiver = actor { |
99 |
loop { |
|
100 |
react { |
|
34649 | 101 |
case ProverEvents.Activate => |
34670 | 102 |
edits = List(Insert(0, buffer.getText(0, buffer.getLength))) |
103 |
commit |
|
34649 | 104 |
case c: Command => |
34643
3896caeedf82
renamed Delay to clarified version Swing_Thread.delay;
wenzelm
parents:
34642
diff
changeset
|
105 |
update_delay() |
3896caeedf82
renamed Delay to clarified version Swing_Thread.delay;
wenzelm
parents:
34642
diff
changeset
|
106 |
repaint_delay() |
3896caeedf82
renamed Delay to clarified version Swing_Thread.delay;
wenzelm
parents:
34642
diff
changeset
|
107 |
phase_overview.repaint_delay() |
34649 | 108 |
case x => System.err.println("warning: change_receiver ignored " + x) |
34538
20bfcca24658
Prover as actor managing ProofDocument-versions (removed EventBus structural_changes);
immler@in.tum.de
parents:
34532
diff
changeset
|
109 |
} |
20bfcca24658
Prover as actor managing ProofDocument-versions (removed EventBus structural_changes);
immler@in.tum.de
parents:
34532
diff
changeset
|
110 |
} |
34583
17c83413b7fe
change_receiver: start only once (already done in actor function);
wenzelm
parents:
34582
diff
changeset
|
111 |
} |
34446 | 112 |
|
113 |
||
34660 | 114 |
def changes_to(doc: ProofDocument) = |
115 |
edits ::: current_change.ancestors(_.id == doc.id).flatten(_.toList) |
|
34545
b928628742ed
implemented to_current and from_current in dependancy of document-versions
immler@in.tum.de
parents:
34544
diff
changeset
|
116 |
|
34660 | 117 |
def from_current(doc: ProofDocument, pos: Int) = |
118 |
(pos /: changes_to(doc)) ((p, c) => c from_where p) |
|
119 |
||
120 |
def to_current(doc: ProofDocument, pos: Int) = |
|
121 |
(pos /: changes_to(doc).reverse) ((p, c) => c where_to p) |
|
34446 | 122 |
|
123 |
def repaint(cmd: Command) = |
|
34318
c13e168a8ae6
original sources from Johannes Hölzl a48e0c6ab1aea77c52d596f7efc007a543d3d10c with minor modifications of directory layout;
wenzelm
parents:
diff
changeset
|
124 |
{ |
34650 | 125 |
val document = current_document() |
34507 | 126 |
if (text_area != null) { |
34654 | 127 |
val start = text_area.getLineOfOffset(to_current(document, cmd.start(document))) |
128 |
val stop = text_area.getLineOfOffset(to_current(document, cmd.stop(document)) - 1) |
|
34406
f81cd75ae331
restructured: independent provers in different buffers
immler@in.tum.de
parents:
34405
diff
changeset
|
129 |
text_area.invalidateLineRange(start, stop) |
34446 | 130 |
|
34669 | 131 |
if (Isabelle.plugin.selected_state == cmd) |
132 |
Isabelle.plugin.selected_state = cmd // update State view |
|
34318
c13e168a8ae6
original sources from Johannes Hölzl a48e0c6ab1aea77c52d596f7efc007a543d3d10c with minor modifications of directory layout;
wenzelm
parents:
diff
changeset
|
133 |
} |
c13e168a8ae6
original sources from Johannes Hölzl a48e0c6ab1aea77c52d596f7efc007a543d3d10c with minor modifications of directory layout;
wenzelm
parents:
diff
changeset
|
134 |
} |
34446 | 135 |
|
34588 | 136 |
def repaint_all() |
34318
c13e168a8ae6
original sources from Johannes Hölzl a48e0c6ab1aea77c52d596f7efc007a543d3d10c with minor modifications of directory layout;
wenzelm
parents:
diff
changeset
|
137 |
{ |
34406
f81cd75ae331
restructured: independent provers in different buffers
immler@in.tum.de
parents:
34405
diff
changeset
|
138 |
if (text_area != null) |
34446 | 139 |
text_area.invalidateLineRange(text_area.getFirstPhysicalLine, text_area.getLastPhysicalLine) |
34318
c13e168a8ae6
original sources from Johannes Hölzl a48e0c6ab1aea77c52d596f7efc007a543d3d10c with minor modifications of directory layout;
wenzelm
parents:
diff
changeset
|
140 |
} |
34391 | 141 |
|
34446 | 142 |
def encolor(gfx: Graphics2D, |
34588 | 143 |
y: Int, height: Int, begin: Int, finish: Int, color: Color, fill: Boolean) |
34446 | 144 |
{ |
145 |
val start = text_area.offsetToXY(begin) |
|
146 |
val stop = |
|
147 |
if (finish < buffer.getLength) text_area.offsetToXY(finish) |
|
148 |
else { |
|
149 |
val p = text_area.offsetToXY(finish - 1) |
|
34515
3be515f1379d
use FontMetrics.getMaxAdvance if available; tuned
immler@in.tum.de
parents:
34514
diff
changeset
|
150 |
val metrics = text_area.getPainter.getFontMetrics |
3be515f1379d
use FontMetrics.getMaxAdvance if available; tuned
immler@in.tum.de
parents:
34514
diff
changeset
|
151 |
p.x = p.x + (metrics.charWidth(' ') max metrics.getMaxAdvance) |
34446 | 152 |
p |
34391 | 153 |
} |
34446 | 154 |
|
155 |
if (start != null && stop != null) { |
|
156 |
gfx.setColor(color) |
|
157 |
if (fill) gfx.fillRect(start.x, y, stop.x - start.x, height) |
|
158 |
else gfx.drawRect(start.x, y, stop.x - start.x, height) |
|
159 |
} |
|
34391 | 160 |
} |
161 |
||
34446 | 162 |
|
163 |
/* TextAreaExtension methods */ |
|
164 |
||
165 |
override def paintValidLine(gfx: Graphics2D, |
|
34588 | 166 |
screen_line: Int, physical_line: Int, start: Int, end: Int, y: Int) |
34446 | 167 |
{ |
34650 | 168 |
val document = current_document() |
34654 | 169 |
def from_current(pos: Int) = this.from_current(document, pos) |
170 |
def to_current(pos: Int) = this.to_current(document, pos) |
|
34446 | 171 |
val saved_color = gfx.getColor |
172 |
||
173 |
val metrics = text_area.getPainter.getFontMetrics |
|
34596 | 174 |
|
175 |
// encolor phase |
|
34545
b928628742ed
implemented to_current and from_current in dependancy of document-versions
immler@in.tum.de
parents:
34544
diff
changeset
|
176 |
var e = document.find_command_at(from_current(start)) |
34596 | 177 |
while (e != null && e.start(document) < end) { |
34554
7dc6c231da40
abs. stops, markup nodes depend on doc-version;
immler@in.tum.de
parents:
34549
diff
changeset
|
178 |
val begin = start max to_current(e.start(document)) |
7dc6c231da40
abs. stops, markup nodes depend on doc-version;
immler@in.tum.de
parents:
34549
diff
changeset
|
179 |
val finish = end - 1 min to_current(e.stop(document)) |
34653 | 180 |
encolor(gfx, y, metrics.getHeight, begin, finish, |
181 |
TheoryView.choose_color(e, document), true) |
|
34596 | 182 |
e = document.commands.next(e).getOrElse(null) |
34318
c13e168a8ae6
original sources from Johannes Hölzl a48e0c6ab1aea77c52d596f7efc007a543d3d10c with minor modifications of directory layout;
wenzelm
parents:
diff
changeset
|
183 |
} |
34391 | 184 |
|
34446 | 185 |
gfx.setColor(saved_color) |
34318
c13e168a8ae6
original sources from Johannes Hölzl a48e0c6ab1aea77c52d596f7efc007a543d3d10c with minor modifications of directory layout;
wenzelm
parents:
diff
changeset
|
186 |
} |
34446 | 187 |
|
34562 | 188 |
override def getToolTipText(x: Int, y: Int) = { |
34650 | 189 |
val document = current_document() |
34654 | 190 |
val offset = from_current(document, text_area.xyToOffset(x, y)) |
34562 | 191 |
val cmd = document.find_command_at(offset) |
192 |
if (cmd != null) { |
|
193 |
document.token_start(cmd.tokens.first) |
|
34653 | 194 |
cmd.type_at(document, offset - cmd.start(document)) |
34562 | 195 |
} else null |
196 |
} |
|
197 |
||
34654 | 198 |
/* history of changes - TODO: seperate class?*/ |
199 |
||
34660 | 200 |
val change_0 = new Change(prover.document_0.id, None, Nil) |
34654 | 201 |
private var changes = List(change_0) |
202 |
private var current_change = change_0 |
|
203 |
def get_changes = changes |
|
34660 | 204 |
|
205 |
private def doc_or_pred(c: Change): ProofDocument = |
|
206 |
prover.document(c.id).getOrElse(doc_or_pred(c.parent.get)) |
|
34654 | 207 |
def current_document() = doc_or_pred(current_change) |
208 |
||
209 |
/* update to desired version */ |
|
210 |
||
34660 | 211 |
def set_version(goal: Change) { |
34654 | 212 |
// changes in buffer must be ignored |
213 |
buffer.removeBufferListener(this) |
|
214 |
||
34660 | 215 |
def apply(c: Change) = c.map { |
216 |
case Insert(start, added) => buffer.insert(start, added) |
|
217 |
case Remove(start, removed) => buffer.remove(start, removed.length) |
|
218 |
} |
|
219 |
||
220 |
def unapply(c: Change) = c.map { |
|
221 |
case Insert(start, added) => buffer.remove(start, added.length) |
|
222 |
case Remove(start, removed) => buffer.insert(start, removed) |
|
223 |
} |
|
34318
c13e168a8ae6
original sources from Johannes Hölzl a48e0c6ab1aea77c52d596f7efc007a543d3d10c with minor modifications of directory layout;
wenzelm
parents:
diff
changeset
|
224 |
|
34654 | 225 |
// undo/redo changes |
226 |
val ancs_current = current_change.ancestors |
|
227 |
val ancs_goal = goal.ancestors |
|
228 |
val paired = ancs_current.reverse zip ancs_goal.reverse |
|
229 |
def last_common[A](xs: List[(A, A)]): Option[A] = { |
|
230 |
xs match { |
|
231 |
case (x, y) :: xs => |
|
232 |
if (x == y) |
|
233 |
xs match { |
|
234 |
case (a, b) :: ys => |
|
235 |
if (a == b) last_common(xs) |
|
236 |
else Some(x) |
|
237 |
case _ => Some(x) |
|
238 |
} |
|
239 |
else None |
|
240 |
case _ => None |
|
241 |
} |
|
242 |
} |
|
243 |
val common_anc = last_common(paired).get |
|
244 |
||
245 |
ancs_current.takeWhile(_ != common_anc) map unapply |
|
246 |
ancs_goal.takeWhile(_ != common_anc).reverse map apply |
|
247 |
||
248 |
current_change = goal |
|
249 |
// invoke repaint |
|
250 |
update_delay() |
|
251 |
repaint_delay() |
|
252 |
phase_overview.repaint_delay() |
|
253 |
||
254 |
//track changes in buffer |
|
255 |
buffer.addBufferListener(this) |
|
256 |
} |
|
257 |
||
258 |
/* BufferListener methods */ |
|
34545
b928628742ed
implemented to_current and from_current in dependancy of document-versions
immler@in.tum.de
parents:
34544
diff
changeset
|
259 |
|
34547 | 260 |
private def commit: Unit = synchronized { |
34660 | 261 |
if (!edits.isEmpty) { |
262 |
val change = new Change(Isabelle.system.id(), Some(current_change), edits) |
|
263 |
changes ::= change |
|
264 |
document_actor ! change |
|
265 |
current_change = change |
|
34545
b928628742ed
implemented to_current and from_current in dependancy of document-versions
immler@in.tum.de
parents:
34544
diff
changeset
|
266 |
} |
34660 | 267 |
edits = Nil |
34446 | 268 |
if (col_timer.isRunning()) |
269 |
col_timer.stop() |
|
270 |
} |
|
271 |
||
34515
3be515f1379d
use FontMetrics.getMaxAdvance if available; tuned
immler@in.tum.de
parents:
34514
diff
changeset
|
272 |
private def delay_commit { |
34446 | 273 |
if (col_timer.isRunning()) |
274 |
col_timer.restart() |
|
34318
c13e168a8ae6
original sources from Johannes Hölzl a48e0c6ab1aea77c52d596f7efc007a543d3d10c with minor modifications of directory layout;
wenzelm
parents:
diff
changeset
|
275 |
else |
34446 | 276 |
col_timer.start() |
34318
c13e168a8ae6
original sources from Johannes Hölzl a48e0c6ab1aea77c52d596f7efc007a543d3d10c with minor modifications of directory layout;
wenzelm
parents:
diff
changeset
|
277 |
} |
34446 | 278 |
|
279 |
||
280 |
override def contentInserted(buffer: JEditBuffer, |
|
281 |
start_line: Int, offset: Int, num_lines: Int, length: Int) { } |
|
282 |
||
283 |
override def contentRemoved(buffer: JEditBuffer, |
|
284 |
start_line: Int, offset: Int, num_lines: Int, length: Int) { } |
|
34318
c13e168a8ae6
original sources from Johannes Hölzl a48e0c6ab1aea77c52d596f7efc007a543d3d10c with minor modifications of directory layout;
wenzelm
parents:
diff
changeset
|
285 |
|
34446 | 286 |
override def preContentInserted(buffer: JEditBuffer, |
34588 | 287 |
start_line: Int, offset: Int, num_lines: Int, length: Int) |
34446 | 288 |
{ |
34660 | 289 |
edits ::= Insert(offset, buffer.getText(offset, length)) |
34515
3be515f1379d
use FontMetrics.getMaxAdvance if available; tuned
immler@in.tum.de
parents:
34514
diff
changeset
|
290 |
delay_commit |
34446 | 291 |
} |
292 |
||
293 |
override def preContentRemoved(buffer: JEditBuffer, |
|
34648 | 294 |
start_line: Int, start: Int, num_lines: Int, removed_length: Int) |
34446 | 295 |
{ |
34660 | 296 |
edits ::= Remove(start, buffer.getText(start, removed_length)) |
34515
3be515f1379d
use FontMetrics.getMaxAdvance if available; tuned
immler@in.tum.de
parents:
34514
diff
changeset
|
297 |
delay_commit |
34318
c13e168a8ae6
original sources from Johannes Hölzl a48e0c6ab1aea77c52d596f7efc007a543d3d10c with minor modifications of directory layout;
wenzelm
parents:
diff
changeset
|
298 |
} |
c13e168a8ae6
original sources from Johannes Hölzl a48e0c6ab1aea77c52d596f7efc007a543d3d10c with minor modifications of directory layout;
wenzelm
parents:
diff
changeset
|
299 |
|
34446 | 300 |
override def bufferLoaded(buffer: JEditBuffer) { } |
301 |
override def foldHandlerChanged(buffer: JEditBuffer) { } |
|
302 |
override def foldLevelChanged(buffer: JEditBuffer, start_line: Int, end_line: Int) { } |
|
303 |
override def transactionComplete(buffer: JEditBuffer) { } |
|
34588 | 304 |
|
34447 | 305 |
} |