31 get_rendering: () => JEdit_Rendering, |
31 get_rendering: () => JEdit_Rendering, |
32 close_action: () => Unit, |
32 close_action: () => Unit, |
33 get_search_pattern: () => Option[Regex], |
33 get_search_pattern: () => Option[Regex], |
34 caret_update: () => Unit, |
34 caret_update: () => Unit, |
35 caret_visible: Boolean, |
35 caret_visible: Boolean, |
36 enable_hovering: Boolean) |
36 enable_hovering: Boolean |
37 { |
37 ) { |
38 private val buffer = text_area.getBuffer |
38 private val buffer = text_area.getBuffer |
39 |
39 |
40 |
40 |
41 /* robust extension body */ |
41 /* robust extension body */ |
42 |
42 |
43 def check_robust_body: Boolean = |
43 def check_robust_body: Boolean = |
44 GUI_Thread.require { buffer == text_area.getBuffer } |
44 GUI_Thread.require { buffer == text_area.getBuffer } |
45 |
45 |
46 def robust_body[A](default: A)(body: => A): A = |
46 def robust_body[A](default: A)(body: => A): A = { |
47 { |
|
48 try { |
47 try { |
49 if (check_robust_body) body |
48 if (check_robust_body) body |
50 else { |
49 else { |
51 Log.log(Log.ERROR, this, ERROR("Implicit change of text area buffer")) |
50 Log.log(Log.ERROR, this, ERROR("Implicit change of text area buffer")) |
52 default |
51 default |
79 if (caret_focus_modifier) Text.Range.full |
77 if (caret_focus_modifier) Text.Range.full |
80 else JEdit_Lib.visible_range(text_area) getOrElse Text.Range.offside |
78 else JEdit_Lib.visible_range(text_area) getOrElse Text.Range.offside |
81 |
79 |
82 private val key_listener = |
80 private val key_listener = |
83 JEdit_Lib.key_listener( |
81 JEdit_Lib.key_listener( |
84 key_pressed = (evt: KeyEvent) => |
82 key_pressed = (evt: KeyEvent) => { |
85 { |
|
86 val mod = PIDE.options.string("jedit_focus_modifier") |
83 val mod = PIDE.options.string("jedit_focus_modifier") |
87 val old = caret_focus_modifier |
84 val old = caret_focus_modifier |
88 caret_focus_modifier = (mod.nonEmpty && mod == JEdit_Lib.modifier_string(evt)) |
85 caret_focus_modifier = (mod.nonEmpty && mod == JEdit_Lib.modifier_string(evt)) |
89 if (caret_focus_modifier != old) caret_update() |
86 if (caret_focus_modifier != old) caret_update() |
90 }, |
87 }, |
91 key_released = _ => |
88 key_released = _ => { |
92 { |
|
93 if (caret_focus_modifier) { |
89 if (caret_focus_modifier) { |
94 caret_focus_modifier = false |
90 caret_focus_modifier = false |
95 caret_update() |
91 caret_update() |
96 } |
92 } |
97 }) |
93 }) |
101 |
97 |
102 @volatile private var painter_rendering: JEdit_Rendering = null |
98 @volatile private var painter_rendering: JEdit_Rendering = null |
103 @volatile private var painter_clip: Shape = null |
99 @volatile private var painter_clip: Shape = null |
104 @volatile private var caret_focus = Rendering.Focus.empty |
100 @volatile private var caret_focus = Rendering.Focus.empty |
105 |
101 |
106 private val set_state = new TextAreaExtension |
102 private val set_state = new TextAreaExtension { |
107 { |
103 override def paintScreenLineRange( |
108 override def paintScreenLineRange(gfx: Graphics2D, |
104 gfx: Graphics2D, |
109 first_line: Int, last_line: Int, physical_lines: Array[Int], |
105 first_line: Int, |
110 start: Array[Int], end: Array[Int], y: Int, line_height: Int): Unit = |
106 last_line: Int, |
111 { |
107 physical_lines: Array[Int], |
|
108 start: Array[Int], |
|
109 end: Array[Int], |
|
110 y: Int, |
|
111 line_height: Int |
|
112 ): Unit = { |
112 painter_rendering = get_rendering() |
113 painter_rendering = get_rendering() |
113 painter_clip = gfx.getClip |
114 painter_clip = gfx.getClip |
114 caret_focus = |
115 caret_focus = |
115 if (caret_enabled && !painter_rendering.snapshot.is_outdated) { |
116 if (caret_enabled && !painter_rendering.snapshot.is_outdated) { |
116 painter_rendering.caret_focus(JEdit_Lib.caret_range(text_area), caret_focus_range) |
117 painter_rendering.caret_focus(JEdit_Lib.caret_range(text_area), caret_focus_range) |
117 } |
118 } |
118 else Rendering.Focus.empty |
119 else Rendering.Focus.empty |
119 } |
120 } |
120 } |
121 } |
121 |
122 |
122 private val reset_state = new TextAreaExtension |
123 private val reset_state = new TextAreaExtension { |
123 { |
124 override def paintScreenLineRange( |
124 override def paintScreenLineRange(gfx: Graphics2D, |
125 gfx: Graphics2D, |
125 first_line: Int, last_line: Int, physical_lines: Array[Int], |
126 first_line: Int, |
126 start: Array[Int], end: Array[Int], y: Int, line_height: Int): Unit = |
127 last_line: Int, |
127 { |
128 physical_lines: Array[Int], |
|
129 start: Array[Int], |
|
130 end: Array[Int], |
|
131 y: Int, |
|
132 line_height: Int |
|
133 ): Unit = { |
128 painter_rendering = null |
134 painter_rendering = null |
129 painter_clip = null |
135 painter_clip = null |
130 caret_focus = Rendering.Focus.empty |
136 caret_focus = Rendering.Focus.empty |
131 } |
137 } |
132 } |
138 } |
133 |
139 |
134 def robust_rendering(body: JEdit_Rendering => Unit): Unit = |
140 def robust_rendering(body: JEdit_Rendering => Unit): Unit = { |
135 { |
|
136 robust_body(()) { body(painter_rendering) } |
141 robust_body(()) { body(painter_rendering) } |
137 } |
142 } |
138 |
143 |
139 |
144 |
140 /* active areas within the text */ |
145 /* active areas within the text */ |
141 |
146 |
142 private class Active_Area[A]( |
147 private class Active_Area[A]( |
143 rendering: JEdit_Rendering => Text.Range => Option[Text.Info[A]], |
148 rendering: JEdit_Rendering => Text.Range => Option[Text.Info[A]], |
144 cursor: Option[Int]) |
149 cursor: Option[Int] |
145 { |
150 ) { |
146 private var the_text_info: Option[(String, Text.Info[A])] = None |
151 private var the_text_info: Option[(String, Text.Info[A])] = None |
147 |
152 |
148 def is_active: Boolean = the_text_info.isDefined |
153 def is_active: Boolean = the_text_info.isDefined |
149 def text_info: Option[(String, Text.Info[A])] = the_text_info |
154 def text_info: Option[(String, Text.Info[A])] = the_text_info |
150 def info: Option[Text.Info[A]] = the_text_info.map(_._2) |
155 def info: Option[Text.Info[A]] = the_text_info.map(_._2) |
151 |
156 |
152 def update(new_info: Option[Text.Info[A]]): Unit = |
157 def update(new_info: Option[Text.Info[A]]): Unit = { |
153 { |
|
154 val old_text_info = the_text_info |
158 val old_text_info = the_text_info |
155 val new_text_info = |
159 val new_text_info = |
156 new_info.map(info => (text_area.getText(info.range.start, info.range.length), info)) |
160 new_info.map(info => (text_area.getText(info.range.start, info.range.length), info)) |
157 |
161 |
158 if (new_text_info != old_text_info) { |
162 if (new_text_info != old_text_info) { |
208 override def windowIconified(e: WindowEvent): Unit = { robust_body(()) { active_reset() } } |
212 override def windowIconified(e: WindowEvent): Unit = { robust_body(()) { active_reset() } } |
209 override def windowDeactivated(e: WindowEvent): Unit = { robust_body(()) { active_reset() } } |
213 override def windowDeactivated(e: WindowEvent): Unit = { robust_body(()) { active_reset() } } |
210 } |
214 } |
211 |
215 |
212 private val mouse_listener = new MouseAdapter { |
216 private val mouse_listener = new MouseAdapter { |
213 override def mouseClicked(e: MouseEvent): Unit = |
217 override def mouseClicked(e: MouseEvent): Unit = { |
214 { |
|
215 robust_body(()) { |
218 robust_body(()) { |
216 hyperlink_area.info match { |
219 hyperlink_area.info match { |
217 case Some(Text.Info(range, link)) => |
220 case Some(Text.Info(range, link)) => |
218 if (!link.external) { |
221 if (!link.external) { |
219 try { text_area.moveCaretPosition(range.start) } |
222 try { text_area.moveCaretPosition(range.start) } |
245 SwingUtilities.convertPointFromScreen(point, painter) |
248 SwingUtilities.convertPointFromScreen(point, painter) |
246 painter.contains(point) |
249 painter.contains(point) |
247 } |
250 } |
248 |
251 |
249 private val mouse_motion_listener = new MouseMotionAdapter { |
252 private val mouse_motion_listener = new MouseMotionAdapter { |
250 override def mouseDragged(evt: MouseEvent): Unit = |
253 override def mouseDragged(evt: MouseEvent): Unit = { |
251 { |
|
252 robust_body(()) { |
254 robust_body(()) { |
253 active_reset() |
255 active_reset() |
254 Completion_Popup.Text_Area.dismissed(text_area) |
256 Completion_Popup.Text_Area.dismissed(text_area) |
255 Pretty_Tooltip.dismiss_descendant(text_area.getPainter) |
257 Pretty_Tooltip.dismiss_descendant(text_area.getPainter) |
256 } |
258 } |
257 } |
259 } |
258 |
260 |
259 override def mouseMoved(evt: MouseEvent): Unit = |
261 override def mouseMoved(evt: MouseEvent): Unit = { |
260 { |
|
261 robust_body(()) { |
262 robust_body(()) { |
262 val x = evt.getX |
263 val x = evt.getX |
263 val y = evt.getY |
264 val y = evt.getY |
264 val control = JEdit_Lib.command_modifier(evt) |
265 val control = JEdit_Lib.command_modifier(evt) |
265 |
266 |
267 JEdit_Lib.buffer_lock(buffer) { |
268 JEdit_Lib.buffer_lock(buffer) { |
268 JEdit_Lib.pixel_range(text_area, x, y) match { |
269 JEdit_Lib.pixel_range(text_area, x, y) match { |
269 case None => active_reset() |
270 case None => active_reset() |
270 case Some(range) => |
271 case Some(range) => |
271 val rendering = get_rendering() |
272 val rendering = get_rendering() |
272 for ((area, require_control) <- active_areas) |
273 for ((area, require_control) <- active_areas) { |
273 { |
|
274 if (control == require_control && !rendering.snapshot.is_outdated) |
274 if (control == require_control && !rendering.snapshot.is_outdated) |
275 area.update_rendering(rendering, range) |
275 area.update_rendering(rendering, range) |
276 else area.reset() |
276 else area.reset() |
277 } |
277 } |
278 } |
278 } |
308 } |
308 } |
309 |
309 |
310 |
310 |
311 /* text background */ |
311 /* text background */ |
312 |
312 |
313 private val background_painter = new TextAreaExtension |
313 private val background_painter = new TextAreaExtension { |
314 { |
314 override def paintScreenLineRange( |
315 override def paintScreenLineRange(gfx: Graphics2D, |
315 gfx: Graphics2D, |
316 first_line: Int, last_line: Int, physical_lines: Array[Int], |
316 first_line: Int, |
317 start: Array[Int], end: Array[Int], y: Int, line_height: Int): Unit = |
317 last_line: Int, |
318 { |
318 physical_lines: Array[Int], |
|
319 start: Array[Int], |
|
320 end: Array[Int], |
|
321 y: Int, |
|
322 line_height: Int |
|
323 ): Unit = { |
319 robust_rendering { rendering => |
324 robust_rendering { rendering => |
320 val fm = text_area.getPainter.getFontMetrics |
325 val fm = text_area.getPainter.getFontMetrics |
321 |
326 |
322 for (i <- physical_lines.indices) { |
327 for (i <- physical_lines.indices) { |
323 if (physical_lines(i) != -1) { |
328 if (physical_lines(i) != -1) { |
324 val line_range = Text.Range(start(i), end(i) min buffer.getLength) |
329 val line_range = Text.Range(start(i), end(i) min buffer.getLength) |
325 |
330 |
326 // line background color |
331 // line background color |
327 for { (c, separator) <- rendering.line_background(line_range) } |
332 for { (c, separator) <- rendering.line_background(line_range) } { |
328 { |
|
329 gfx.setColor(rendering.color(c)) |
333 gfx.setColor(rendering.color(c)) |
330 val sep = if (separator) 2 min (line_height / 2) else 0 |
334 val sep = if (separator) 2 min (line_height / 2) else 0 |
331 gfx.fillRect(0, y + i * line_height, text_area.getWidth, line_height - sep) |
335 gfx.fillRect(0, y + i * line_height, text_area.getWidth, line_height - sep) |
332 } |
336 } |
333 |
337 |
418 cache += (codepoint -> res) |
420 cache += (codepoint -> res) |
419 res |
421 res |
420 }) |
422 }) |
421 } |
423 } |
422 |
424 |
423 private def paint_chunk_list(rendering: JEdit_Rendering, font_subst: Font_Subst, |
425 private def paint_chunk_list( |
424 gfx: Graphics2D, line_start: Text.Offset, head: Chunk, x: Float, y: Float): Float = |
426 rendering: JEdit_Rendering, |
425 { |
427 font_subst: Font_Subst, |
|
428 gfx: Graphics2D, |
|
429 line_start: Text.Offset, |
|
430 head: Chunk, |
|
431 x: Float, |
|
432 y: Float |
|
433 ): Float = { |
426 val clip_rect = gfx.getClipBounds |
434 val clip_rect = gfx.getClipBounds |
427 |
435 |
428 val caret_range = |
436 val caret_range = |
429 if (caret_enabled) JEdit_Lib.caret_range(text_area) |
437 if (caret_enabled) JEdit_Lib.caret_range(text_area) |
430 else Text.Range.offside |
438 else Text.Range.offside |
432 var w = 0.0f |
440 var w = 0.0f |
433 var chunk = head |
441 var chunk = head |
434 while (chunk != null) { |
442 while (chunk != null) { |
435 val chunk_offset = line_start + chunk.offset |
443 val chunk_offset = line_start + chunk.offset |
436 if (x + w + chunk.width > clip_rect.x && |
444 if (x + w + chunk.width > clip_rect.x && |
437 x + w < clip_rect.x + clip_rect.width && chunk.length > 0) |
445 x + w < clip_rect.x + clip_rect.width && chunk.length > 0) { |
438 { |
|
439 val chunk_range = Text.Range(chunk_offset, chunk_offset + chunk.length) |
446 val chunk_range = Text.Range(chunk_offset, chunk_offset + chunk.length) |
440 val chunk_str = |
447 val chunk_str = |
441 if (chunk.chars == null) Symbol.spaces(chunk.length) |
448 if (chunk.chars == null) Symbol.spaces(chunk.length) |
442 else { |
449 else { |
443 if (chunk.str == null) { chunk.str = new String(chunk.chars) } |
450 if (chunk.str == null) { chunk.str = new String(chunk.chars) } |
484 chunk = chunk.next.asInstanceOf[Chunk] |
491 chunk = chunk.next.asInstanceOf[Chunk] |
485 } |
492 } |
486 w |
493 w |
487 } |
494 } |
488 |
495 |
489 private val text_painter = new TextAreaExtension |
496 private val text_painter = new TextAreaExtension { |
490 { |
497 override def paintScreenLineRange( |
491 override def paintScreenLineRange(gfx: Graphics2D, |
498 gfx: Graphics2D, |
492 first_line: Int, last_line: Int, physical_lines: Array[Int], |
499 first_line: Int, |
493 start: Array[Int], end: Array[Int], y: Int, line_height: Int): Unit = |
500 last_line: Int, |
494 { |
501 physical_lines: Array[Int], |
|
502 start: Array[Int], |
|
503 end: Array[Int], |
|
504 y: Int, |
|
505 line_height: Int |
|
506 ): Unit = { |
495 robust_rendering { rendering => |
507 robust_rendering { rendering => |
496 val painter = text_area.getPainter |
508 val painter = text_area.getPainter |
497 val fm = painter.getFontMetrics |
509 val fm = painter.getFontMetrics |
498 val lm = painter.getFont.getLineMetrics(" ", painter.getFontRenderContext) |
510 val lm = painter.getFont.getLineMetrics(" ", painter.getFontRenderContext) |
499 |
511 |
500 val clip = gfx.getClip |
512 val clip = gfx.getClip |
501 val x0 = text_area.getHorizontalOffset |
513 val x0 = text_area.getHorizontalOffset |
502 var y0 = y + painter.getLineHeight - (fm.getLeading + 1) - fm.getDescent |
514 var y0 = y + painter.getLineHeight - (fm.getLeading + 1) - fm.getDescent |
503 |
515 |
504 val (bullet_x, bullet_y, bullet_w, bullet_h) = |
516 val (bullet_x, bullet_y, bullet_w, bullet_h) = { |
505 { |
|
506 val w = fm.charWidth(' ') |
517 val w = fm.charWidth(' ') |
507 val b = (w / 2) max 1 |
518 val b = (w / 2) max 1 |
508 val c = (lm.getAscent + lm.getStrikethroughOffset).round |
519 val c = (lm.getAscent + lm.getStrikethroughOffset).round |
509 ((w - b + 1) / 2, c - b / 2, w - b, line_height - b) |
520 ((w - b + 1) / 2, c - b / 2, w - b, line_height - b) |
510 } |
521 } |
549 } |
560 } |
550 |
561 |
551 |
562 |
552 /* foreground */ |
563 /* foreground */ |
553 |
564 |
554 private val foreground_painter = new TextAreaExtension |
565 private val foreground_painter = new TextAreaExtension { |
555 { |
566 override def paintScreenLineRange( |
556 override def paintScreenLineRange(gfx: Graphics2D, |
567 gfx: Graphics2D, |
557 first_line: Int, last_line: Int, physical_lines: Array[Int], |
568 first_line: Int, |
558 start: Array[Int], end: Array[Int], y: Int, line_height: Int): Unit = |
569 last_line: Int, |
559 { |
570 physical_lines: Array[Int], |
|
571 start: Array[Int], |
|
572 end: Array[Int], |
|
573 y: Int, |
|
574 line_height: Int |
|
575 ): Unit = { |
560 robust_rendering { rendering => |
576 robust_rendering { rendering => |
561 val search_pattern = get_search_pattern() |
577 val search_pattern = get_search_pattern() |
562 for (i <- physical_lines.indices) { |
578 for (i <- physical_lines.indices) { |
563 if (physical_lines(i) != -1) { |
579 if (physical_lines(i) != -1) { |
564 val line_range = Text.Range(start(i), end(i) min buffer.getLength) |
580 val line_range = Text.Range(start(i), end(i) min buffer.getLength) |
633 } |
649 } |
634 |
650 |
635 |
651 |
636 /* caret -- outside of text range */ |
652 /* caret -- outside of text range */ |
637 |
653 |
638 private class Caret_Painter(before: Boolean) extends TextAreaExtension |
654 private class Caret_Painter(before: Boolean) extends TextAreaExtension { |
639 { |
655 override def paintValidLine( |
640 override def paintValidLine(gfx: Graphics2D, |
656 gfx: Graphics2D, |
641 screen_line: Int, physical_line: Int, start: Int, end: Int, y: Int): Unit = |
657 screen_line: Int, |
642 { |
658 physical_line: Int, |
|
659 start: Int, |
|
660 end: Int, |
|
661 y: Int |
|
662 ): Unit = { |
643 robust_rendering { _ => |
663 robust_rendering { _ => |
644 if (before) gfx.clipRect(0, 0, 0, 0) |
664 if (before) gfx.clipRect(0, 0, 0, 0) |
645 else gfx.setClip(painter_clip) |
665 else gfx.setClip(painter_clip) |
646 } |
666 } |
647 } |
667 } |
650 private val before_caret_painter1 = new Caret_Painter(true) |
670 private val before_caret_painter1 = new Caret_Painter(true) |
651 private val after_caret_painter1 = new Caret_Painter(false) |
671 private val after_caret_painter1 = new Caret_Painter(false) |
652 private val before_caret_painter2 = new Caret_Painter(true) |
672 private val before_caret_painter2 = new Caret_Painter(true) |
653 private val after_caret_painter2 = new Caret_Painter(false) |
673 private val after_caret_painter2 = new Caret_Painter(false) |
654 |
674 |
655 private val caret_painter = new TextAreaExtension |
675 private val caret_painter = new TextAreaExtension { |
656 { |
676 override def paintValidLine( |
657 override def paintValidLine(gfx: Graphics2D, |
677 gfx: Graphics2D, |
658 screen_line: Int, physical_line: Int, start: Int, end: Int, y: Int): Unit = |
678 screen_line: Int, |
659 { |
679 physical_line: Int, |
|
680 start: Int, |
|
681 end: Int, |
|
682 y: Int |
|
683 ): Unit = { |
660 robust_rendering { rendering => |
684 robust_rendering { rendering => |
661 if (caret_visible) { |
685 if (caret_visible) { |
662 val caret = text_area.getCaretPosition |
686 val caret = text_area.getCaretPosition |
663 if (caret_enabled && start <= caret && caret == end - 1) { |
687 if (caret_enabled && start <= caret && caret == end - 1) { |
664 val painter = text_area.getPainter |
688 val painter = text_area.getPainter |
686 } |
710 } |
687 |
711 |
688 |
712 |
689 /* activation */ |
713 /* activation */ |
690 |
714 |
691 def activate(): Unit = |
715 def activate(): Unit = { |
692 { |
|
693 val painter = text_area.getPainter |
716 val painter = text_area.getPainter |
694 painter.addExtension(TextAreaPainter.LOWEST_LAYER, set_state) |
717 painter.addExtension(TextAreaPainter.LOWEST_LAYER, set_state) |
695 painter.addExtension(TextAreaPainter.LINE_BACKGROUND_LAYER + 1, background_painter) |
718 painter.addExtension(TextAreaPainter.LINE_BACKGROUND_LAYER + 1, background_painter) |
696 painter.addExtension(TextAreaPainter.TEXT_LAYER, text_painter) |
719 painter.addExtension(TextAreaPainter.TEXT_LAYER, text_painter) |
697 painter.addExtension(TextAreaPainter.CARET_LAYER - 1, before_caret_painter1) |
720 painter.addExtension(TextAreaPainter.CARET_LAYER - 1, before_caret_painter1) |
707 text_area.addKeyListener(key_listener) |
730 text_area.addKeyListener(key_listener) |
708 text_area.addFocusListener(focus_listener) |
731 text_area.addFocusListener(focus_listener) |
709 view.addWindowListener(window_listener) |
732 view.addWindowListener(window_listener) |
710 } |
733 } |
711 |
734 |
712 def deactivate(): Unit = |
735 def deactivate(): Unit = { |
713 { |
|
714 active_reset() |
736 active_reset() |
715 val painter = text_area.getPainter |
737 val painter = text_area.getPainter |
716 view.removeWindowListener(window_listener) |
738 view.removeWindowListener(window_listener) |
717 text_area.removeFocusListener(focus_listener) |
739 text_area.removeFocusListener(focus_listener) |
718 text_area.removeKeyListener(key_listener) |
740 text_area.removeKeyListener(key_listener) |