1 // Scintilla source code edit control
2 // ScintillaGTK.cxx - GTK+ specific subclass of ScintillaBase
3 // Copyright 1998-2004 by Neil Hodgson <neilh@scintilla.org>
4 // The License.txt file describes the conditions under which this software may be distributed.
27 #include <gdk/gdkkeysyms.h>
28 #if defined(GDK_WINDOWING_WAYLAND)
29 #include <gdk/gdkwayland.h>
32 #if defined(__WIN32__) || defined(_MSC_VER)
39 #include "Scintilla.h"
40 #include "ScintillaWidget.h"
44 #include "StringCopy.h"
46 #include "LexerModule.h"
49 #include "UniqueString.h"
50 #include "SplitVector.h"
51 #include "Partitioning.h"
52 #include "RunStyles.h"
53 #include "ContractionState.h"
54 #include "CellBuffer.h"
57 #include "Indicator.h"
59 #include "LineMarker.h"
61 #include "ViewStyle.h"
62 #include "CharClassify.h"
63 #include "Decoration.h"
64 #include "CaseFolder.h"
66 #include "CaseConvert.h"
67 #include "UniConversion.h"
68 #include "UnicodeFromUTF8.h"
69 #include "Selection.h"
70 #include "PositionCache.h"
71 #include "EditModel.h"
72 #include "MarginView.h"
75 #include "AutoComplete.h"
76 #include "ScintillaBase.h"
79 #include "ExternalLexer.h"
82 #include "ScintillaGTK.h"
83 #include "scintilla-marshal.h"
84 #include "ScintillaGTKAccessible.h"
86 #include "Converter.h"
88 #if GTK_CHECK_VERSION(2,20,0)
89 #define IS_WIDGET_REALIZED(w) (gtk_widget_get_realized(GTK_WIDGET(w)))
90 #define IS_WIDGET_MAPPED(w) (gtk_widget_get_mapped(GTK_WIDGET(w)))
92 #define IS_WIDGET_REALIZED(w) (GTK_WIDGET_REALIZED(w))
93 #define IS_WIDGET_MAPPED(w) (GTK_WIDGET_MAPPED(w))
96 #define SC_INDICATOR_INPUT INDIC_IME
97 #define SC_INDICATOR_TARGET INDIC_IME+1
98 #define SC_INDICATOR_CONVERTED INDIC_IME+2
99 #define SC_INDICATOR_UNKNOWN INDIC_IME_MAX
101 static GdkWindow
*WindowFromWidget(GtkWidget
*w
) {
102 return gtk_widget_get_window(w
);
106 // Constant conditional expressions are because of GTK+ headers
107 #pragma warning(disable: 4127)
108 // Ignore unreferenced local functions in GTK+ headers
109 #pragma warning(disable: 4505)
113 using namespace Scintilla
;
116 static GdkWindow
*PWindow(const Window
&w
) {
117 GtkWidget
*widget
= static_cast<GtkWidget
*>(w
.GetID());
118 return gtk_widget_get_window(widget
);
121 extern std::string
UTF8FromLatin1(const char *s
, int len
);
129 static gint scintilla_signals
[LAST_SIGNAL
] = { 0 };
134 TARGET_COMPOUND_TEXT
,
139 GdkAtom
ScintillaGTK::atomClipboard
= 0;
140 GdkAtom
ScintillaGTK::atomUTF8
= 0;
141 GdkAtom
ScintillaGTK::atomString
= 0;
142 GdkAtom
ScintillaGTK::atomUriList
= 0;
143 GdkAtom
ScintillaGTK::atomDROPFILES_DND
= 0;
145 static const GtkTargetEntry clipboardCopyTargets
[] = {
146 { (gchar
*) "UTF8_STRING", 0, TARGET_UTF8_STRING
},
147 { (gchar
*) "STRING", 0, TARGET_STRING
},
149 static const gint nClipboardCopyTargets
= ELEMENTS(clipboardCopyTargets
);
151 static const GtkTargetEntry clipboardPasteTargets
[] = {
152 { (gchar
*) "text/uri-list", 0, TARGET_URI
},
153 { (gchar
*) "UTF8_STRING", 0, TARGET_UTF8_STRING
},
154 { (gchar
*) "STRING", 0, TARGET_STRING
},
156 static const gint nClipboardPasteTargets
= ELEMENTS(clipboardPasteTargets
);
158 static GtkWidget
*PWidget(Window
&w
) {
159 return static_cast<GtkWidget
*>(w
.GetID());
162 ScintillaGTK
*ScintillaGTK::FromWidget(GtkWidget
*widget
) {
163 ScintillaObject
*scio
= SCINTILLA(widget
);
164 return static_cast<ScintillaGTK
*>(scio
->pscin
);
167 ScintillaGTK::ScintillaGTK(_ScintillaObject
*sci_
) :
168 adjustmentv(0), adjustmenth(0),
169 verticalScrollBarWidth(30), horizontalScrollBarHeight(30),
170 evbtn(0), capturedMouse(false), dragWasDropped(false),
171 lastKey(0), rectangularSelectionModifier(SCMOD_CTRL
), parentClass(0),
172 im_context(NULL
), lastNonCommonScript(PANGO_SCRIPT_INVALID_CODE
),
173 lastWheelMouseDirection(0),
174 wheelMouseIntensity(0),
178 repaintFullWindow(false),
180 accessibilityEnabled(SC_ACCESSIBILITY_ENABLED
),
183 wMain
= GTK_WIDGET(sci
);
186 rectangularSelectionModifier
= SCMOD_ALT
;
188 rectangularSelectionModifier
= SCMOD_CTRL
;
192 // There does not seem to be a real standard for indicating that the clipboard
193 // contains a rectangular selection, so copy Developer Studio.
194 cfColumnSelect
= static_cast<CLIPFORMAT
>(
195 ::RegisterClipboardFormat("MSDEVColumnSelect"));
197 // Get intellimouse parameters when running on win32; otherwise use
198 // reasonable default
199 #ifndef SPI_GETWHEELSCROLLLINES
200 #define SPI_GETWHEELSCROLLLINES 104
202 ::SystemParametersInfo(SPI_GETWHEELSCROLLLINES
, 0, &linesPerScroll
, 0);
206 lastWheelMouseTime
.tv_sec
= 0;
207 lastWheelMouseTime
.tv_usec
= 0;
212 ScintillaGTK::~ScintillaGTK() {
214 g_source_remove(styleIdleID
);
218 gdk_event_free(reinterpret_cast<GdkEvent
*>(evbtn
));
224 static void UnRefCursor(GdkCursor
*cursor
) {
225 #if GTK_CHECK_VERSION(3,0,0)
226 g_object_unref(cursor
);
228 gdk_cursor_unref(cursor
);
232 void ScintillaGTK::RealizeThis(GtkWidget
*widget
) {
233 //Platform::DebugPrintf("ScintillaGTK::realize this\n");
234 #if GTK_CHECK_VERSION(2,20,0)
235 gtk_widget_set_realized(widget
, TRUE
);
237 GTK_WIDGET_SET_FLAGS(widget
, GTK_REALIZED
);
240 attrs
.window_type
= GDK_WINDOW_CHILD
;
241 GtkAllocation allocation
;
242 gtk_widget_get_allocation(widget
, &allocation
);
243 attrs
.x
= allocation
.x
;
244 attrs
.y
= allocation
.y
;
245 attrs
.width
= allocation
.width
;
246 attrs
.height
= allocation
.height
;
247 attrs
.wclass
= GDK_INPUT_OUTPUT
;
248 attrs
.visual
= gtk_widget_get_visual(widget
);
249 #if !GTK_CHECK_VERSION(3,0,0)
250 attrs
.colormap
= gtk_widget_get_colormap(widget
);
252 attrs
.event_mask
= gtk_widget_get_events(widget
) | GDK_EXPOSURE_MASK
;
253 GdkDisplay
*pdisplay
= gtk_widget_get_display(widget
);
254 GdkCursor
*cursor
= gdk_cursor_new_for_display(pdisplay
, GDK_XTERM
);
255 attrs
.cursor
= cursor
;
256 #if GTK_CHECK_VERSION(3,0,0)
257 gtk_widget_set_window(widget
, gdk_window_new(gtk_widget_get_parent_window(widget
), &attrs
,
258 GDK_WA_X
| GDK_WA_Y
| GDK_WA_VISUAL
| GDK_WA_CURSOR
));
259 #if GTK_CHECK_VERSION(3,8,0)
260 gtk_widget_register_window(widget
, gtk_widget_get_window(widget
));
262 gdk_window_set_user_data(gtk_widget_get_window(widget
), widget
);
264 #if !GTK_CHECK_VERSION(3,18,0)
265 gtk_style_context_set_background(gtk_widget_get_style_context(widget
),
266 gtk_widget_get_window(widget
));
268 gdk_window_show(gtk_widget_get_window(widget
));
271 widget
->window
= gdk_window_new(gtk_widget_get_parent_window(widget
), &attrs
,
272 GDK_WA_X
| GDK_WA_Y
| GDK_WA_VISUAL
| GDK_WA_COLORMAP
| GDK_WA_CURSOR
);
273 gdk_window_set_user_data(widget
->window
, widget
);
274 widget
->style
= gtk_style_attach(widget
->style
, widget
->window
);
275 gdk_window_set_background(widget
->window
, &widget
->style
->bg
[GTK_STATE_NORMAL
]);
276 gdk_window_show(widget
->window
);
279 gtk_widget_realize(PWidget(wPreedit
));
280 gtk_widget_realize(PWidget(wPreeditDraw
));
282 im_context
= gtk_im_multicontext_new();
283 g_signal_connect(G_OBJECT(im_context
), "commit",
284 G_CALLBACK(Commit
), this);
285 g_signal_connect(G_OBJECT(im_context
), "preedit_changed",
286 G_CALLBACK(PreeditChanged
), this);
287 gtk_im_context_set_client_window(im_context
, WindowFromWidget(widget
));
288 GtkWidget
*widtxt
= PWidget(wText
); // // No code inside the G_OBJECT macro
289 g_signal_connect_after(G_OBJECT(widtxt
), "style_set",
290 G_CALLBACK(ScintillaGTK::StyleSetText
), NULL
);
291 g_signal_connect_after(G_OBJECT(widtxt
), "realize",
292 G_CALLBACK(ScintillaGTK::RealizeText
), NULL
);
293 gtk_widget_realize(widtxt
);
294 gtk_widget_realize(PWidget(scrollbarv
));
295 gtk_widget_realize(PWidget(scrollbarh
));
297 cursor
= gdk_cursor_new_for_display(pdisplay
, GDK_XTERM
);
298 gdk_window_set_cursor(PWindow(wText
), cursor
);
301 cursor
= gdk_cursor_new_for_display(pdisplay
, GDK_LEFT_PTR
);
302 gdk_window_set_cursor(PWindow(scrollbarv
), cursor
);
305 cursor
= gdk_cursor_new_for_display(pdisplay
, GDK_LEFT_PTR
);
306 gdk_window_set_cursor(PWindow(scrollbarh
), cursor
);
309 gtk_selection_add_targets(widget
, GDK_SELECTION_PRIMARY
,
310 clipboardCopyTargets
, nClipboardCopyTargets
);
313 void ScintillaGTK::Realize(GtkWidget
*widget
) {
314 ScintillaGTK
*sciThis
= FromWidget(widget
);
315 sciThis
->RealizeThis(widget
);
318 void ScintillaGTK::UnRealizeThis(GtkWidget
*widget
) {
320 gtk_selection_clear_targets(widget
, GDK_SELECTION_PRIMARY
);
322 if (IS_WIDGET_MAPPED(widget
)) {
323 gtk_widget_unmap(widget
);
325 #if GTK_CHECK_VERSION(2,20,0)
326 gtk_widget_set_realized(widget
, FALSE
);
328 GTK_WIDGET_UNSET_FLAGS(widget
, GTK_REALIZED
);
330 gtk_widget_unrealize(PWidget(wText
));
331 if (PWidget(scrollbarv
))
332 gtk_widget_unrealize(PWidget(scrollbarv
));
333 if (PWidget(scrollbarh
))
334 gtk_widget_unrealize(PWidget(scrollbarh
));
335 gtk_widget_unrealize(PWidget(wPreedit
));
336 gtk_widget_unrealize(PWidget(wPreeditDraw
));
337 g_object_unref(im_context
);
339 if (GTK_WIDGET_CLASS(parentClass
)->unrealize
)
340 GTK_WIDGET_CLASS(parentClass
)->unrealize(widget
);
344 errorStatus
= SC_STATUS_FAILURE
;
348 void ScintillaGTK::UnRealize(GtkWidget
*widget
) {
349 ScintillaGTK
*sciThis
= FromWidget(widget
);
350 sciThis
->UnRealizeThis(widget
);
353 static void MapWidget(GtkWidget
*widget
) {
355 gtk_widget_get_visible(GTK_WIDGET(widget
)) &&
356 !IS_WIDGET_MAPPED(widget
)) {
357 gtk_widget_map(widget
);
361 void ScintillaGTK::MapThis() {
363 //Platform::DebugPrintf("ScintillaGTK::map this\n");
364 #if GTK_CHECK_VERSION(2,20,0)
365 gtk_widget_set_mapped(PWidget(wMain
), TRUE
);
367 GTK_WIDGET_SET_FLAGS(PWidget(wMain
), GTK_MAPPED
);
369 MapWidget(PWidget(wText
));
370 MapWidget(PWidget(scrollbarh
));
371 MapWidget(PWidget(scrollbarv
));
372 wMain
.SetCursor(Window::cursorArrow
);
373 scrollbarv
.SetCursor(Window::cursorArrow
);
374 scrollbarh
.SetCursor(Window::cursorArrow
);
376 gdk_window_show(PWindow(wMain
));
378 errorStatus
= SC_STATUS_FAILURE
;
382 void ScintillaGTK::Map(GtkWidget
*widget
) {
383 ScintillaGTK
*sciThis
= FromWidget(widget
);
387 void ScintillaGTK::UnMapThis() {
389 //Platform::DebugPrintf("ScintillaGTK::unmap this\n");
390 #if GTK_CHECK_VERSION(2,20,0)
391 gtk_widget_set_mapped(PWidget(wMain
), FALSE
);
393 GTK_WIDGET_UNSET_FLAGS(PWidget(wMain
), GTK_MAPPED
);
396 gdk_window_hide(PWindow(wMain
));
397 gtk_widget_unmap(PWidget(wText
));
398 if (PWidget(scrollbarh
))
399 gtk_widget_unmap(PWidget(scrollbarh
));
400 if (PWidget(scrollbarv
))
401 gtk_widget_unmap(PWidget(scrollbarv
));
403 errorStatus
= SC_STATUS_FAILURE
;
407 void ScintillaGTK::UnMap(GtkWidget
*widget
) {
408 ScintillaGTK
*sciThis
= FromWidget(widget
);
409 sciThis
->UnMapThis();
412 void ScintillaGTK::ForAll(GtkCallback callback
, gpointer callback_data
) {
414 (*callback
) (PWidget(wText
), callback_data
);
415 if (PWidget(scrollbarv
))
416 (*callback
) (PWidget(scrollbarv
), callback_data
);
417 if (PWidget(scrollbarh
))
418 (*callback
) (PWidget(scrollbarh
), callback_data
);
420 errorStatus
= SC_STATUS_FAILURE
;
424 void ScintillaGTK::MainForAll(GtkContainer
*container
, gboolean include_internals
, GtkCallback callback
, gpointer callback_data
) {
425 ScintillaGTK
*sciThis
= FromWidget((GtkWidget
*)container
);
427 if (callback
!= NULL
&& include_internals
) {
428 sciThis
->ForAll(callback
, callback_data
);
434 class PreEditString
{
438 PangoAttrList
*attrs
;
444 explicit PreEditString(GtkIMContext
*im_context
) {
445 gtk_im_context_get_preedit_string(im_context
, &str
, &attrs
, &cursor_pos
);
446 validUTF8
= g_utf8_validate(str
, strlen(str
), NULL
);
447 uniStr
= g_utf8_to_ucs4_fast(str
, strlen(str
), &uniStrLen
);
448 pscript
= pango_script_for_unichar(uniStr
[0]);
453 pango_attr_list_unref(attrs
);
459 gint
ScintillaGTK::FocusInThis(GtkWidget
*) {
462 if (im_context
!= NULL
) {
463 PreEditString
pes(im_context
);
464 if (PWidget(wPreedit
) != NULL
) {
465 if (strlen(pes
.str
) > 0) {
466 gtk_widget_show(PWidget(wPreedit
));
468 gtk_widget_hide(PWidget(wPreedit
));
471 gtk_im_context_focus_in(im_context
);
475 errorStatus
= SC_STATUS_FAILURE
;
480 gint
ScintillaGTK::FocusIn(GtkWidget
*widget
, GdkEventFocus
* /*event*/) {
481 ScintillaGTK
*sciThis
= FromWidget(widget
);
482 return sciThis
->FocusInThis(widget
);
485 gint
ScintillaGTK::FocusOutThis(GtkWidget
*) {
487 SetFocusState(false);
489 if (PWidget(wPreedit
) != NULL
)
490 gtk_widget_hide(PWidget(wPreedit
));
491 if (im_context
!= NULL
)
492 gtk_im_context_focus_out(im_context
);
495 errorStatus
= SC_STATUS_FAILURE
;
500 gint
ScintillaGTK::FocusOut(GtkWidget
*widget
, GdkEventFocus
* /*event*/) {
501 ScintillaGTK
*sciThis
= FromWidget(widget
);
502 return sciThis
->FocusOutThis(widget
);
505 void ScintillaGTK::SizeRequest(GtkWidget
*widget
, GtkRequisition
*requisition
) {
506 ScintillaGTK
*sciThis
= FromWidget(widget
);
507 requisition
->width
= 1;
508 requisition
->height
= 1;
509 GtkRequisition child_requisition
;
510 #if GTK_CHECK_VERSION(3,0,0)
511 gtk_widget_get_preferred_size(PWidget(sciThis
->scrollbarh
), NULL
, &child_requisition
);
512 gtk_widget_get_preferred_size(PWidget(sciThis
->scrollbarv
), NULL
, &child_requisition
);
514 gtk_widget_size_request(PWidget(sciThis
->scrollbarh
), &child_requisition
);
515 gtk_widget_size_request(PWidget(sciThis
->scrollbarv
), &child_requisition
);
519 #if GTK_CHECK_VERSION(3,0,0)
521 void ScintillaGTK::GetPreferredWidth(GtkWidget
*widget
, gint
*minimalWidth
, gint
*naturalWidth
) {
522 GtkRequisition requisition
;
523 SizeRequest(widget
, &requisition
);
524 *minimalWidth
= *naturalWidth
= requisition
.width
;
527 void ScintillaGTK::GetPreferredHeight(GtkWidget
*widget
, gint
*minimalHeight
, gint
*naturalHeight
) {
528 GtkRequisition requisition
;
529 SizeRequest(widget
, &requisition
);
530 *minimalHeight
= *naturalHeight
= requisition
.height
;
535 void ScintillaGTK::SizeAllocate(GtkWidget
*widget
, GtkAllocation
*allocation
) {
536 ScintillaGTK
*sciThis
= FromWidget(widget
);
538 gtk_widget_set_allocation(widget
, allocation
);
539 if (IS_WIDGET_REALIZED(widget
))
540 gdk_window_move_resize(WindowFromWidget(widget
),
546 sciThis
->Resize(allocation
->width
, allocation
->height
);
549 sciThis
->errorStatus
= SC_STATUS_FAILURE
;
553 void ScintillaGTK::Init() {
554 parentClass
= reinterpret_cast<GtkWidgetClass
*>(
555 g_type_class_ref(gtk_container_get_type()));
558 #if defined(GDK_WINDOWING_WAYLAND)
559 GdkDisplay
*pdisplay
= gdk_display_get_default();
560 if (GDK_IS_WAYLAND_DISPLAY(pdisplay
)) {
561 // On Wayland, touch pads only produce smooth scroll events
562 maskSmooth
= GDK_SMOOTH_SCROLL_MASK
;
566 gtk_widget_set_can_focus(PWidget(wMain
), TRUE
);
567 gtk_widget_set_sensitive(PWidget(wMain
), TRUE
);
568 gtk_widget_set_events(PWidget(wMain
),
574 | GDK_KEY_RELEASE_MASK
575 | GDK_FOCUS_CHANGE_MASK
576 | GDK_LEAVE_NOTIFY_MASK
577 | GDK_BUTTON_PRESS_MASK
578 | GDK_BUTTON_RELEASE_MASK
579 | GDK_POINTER_MOTION_MASK
580 | GDK_POINTER_MOTION_HINT_MASK
);
582 wText
= gtk_drawing_area_new();
583 gtk_widget_set_parent(PWidget(wText
), PWidget(wMain
));
584 GtkWidget
*widtxt
= PWidget(wText
); // No code inside the G_OBJECT macro
585 gtk_widget_show(widtxt
);
586 #if GTK_CHECK_VERSION(3,0,0)
587 g_signal_connect(G_OBJECT(widtxt
), "draw",
588 G_CALLBACK(ScintillaGTK::DrawText
), this);
590 g_signal_connect(G_OBJECT(widtxt
), "expose_event",
591 G_CALLBACK(ScintillaGTK::ExposeText
), this);
593 #if GTK_CHECK_VERSION(3,0,0)
594 // we need a runtime check because we don't want double buffering when
595 // running on >= 3.9.2
596 if (gtk_check_version(3,9,2) != NULL
/* on < 3.9.2 */)
599 #if !GTK_CHECK_VERSION(3,14,0)
600 // Avoid background drawing flash/missing redraws
601 gtk_widget_set_double_buffered(widtxt
, FALSE
);
604 gtk_widget_set_events(widtxt
, GDK_EXPOSURE_MASK
);
605 gtk_widget_set_size_request(widtxt
, 100, 100);
606 adjustmentv
= GTK_ADJUSTMENT(gtk_adjustment_new(0.0, 0.0, 201.0, 1.0, 20.0, 20.0));
607 #if GTK_CHECK_VERSION(3,0,0)
608 scrollbarv
= gtk_scrollbar_new(GTK_ORIENTATION_VERTICAL
, GTK_ADJUSTMENT(adjustmentv
));
610 scrollbarv
= gtk_vscrollbar_new(GTK_ADJUSTMENT(adjustmentv
));
612 gtk_widget_set_can_focus(PWidget(scrollbarv
), FALSE
);
613 g_signal_connect(G_OBJECT(adjustmentv
), "value_changed",
614 G_CALLBACK(ScrollSignal
), this);
615 gtk_widget_set_parent(PWidget(scrollbarv
), PWidget(wMain
));
616 gtk_widget_show(PWidget(scrollbarv
));
618 adjustmenth
= GTK_ADJUSTMENT(gtk_adjustment_new(0.0, 0.0, 101.0, 1.0, 20.0, 20.0));
619 #if GTK_CHECK_VERSION(3,0,0)
620 scrollbarh
= gtk_scrollbar_new(GTK_ORIENTATION_HORIZONTAL
, GTK_ADJUSTMENT(adjustmenth
));
622 scrollbarh
= gtk_hscrollbar_new(GTK_ADJUSTMENT(adjustmenth
));
624 gtk_widget_set_can_focus(PWidget(scrollbarh
), FALSE
);
625 g_signal_connect(G_OBJECT(adjustmenth
), "value_changed",
626 G_CALLBACK(ScrollHSignal
), this);
627 gtk_widget_set_parent(PWidget(scrollbarh
), PWidget(wMain
));
628 gtk_widget_show(PWidget(scrollbarh
));
630 gtk_widget_grab_focus(PWidget(wMain
));
632 gtk_drag_dest_set(GTK_WIDGET(PWidget(wMain
)),
633 GTK_DEST_DEFAULT_ALL
, clipboardPasteTargets
, nClipboardPasteTargets
,
634 static_cast<GdkDragAction
>(GDK_ACTION_COPY
| GDK_ACTION_MOVE
));
636 /* create pre-edit window */
637 wPreedit
= gtk_window_new(GTK_WINDOW_POPUP
);
638 wPreeditDraw
= gtk_drawing_area_new();
639 GtkWidget
*predrw
= PWidget(wPreeditDraw
); // No code inside the G_OBJECT macro
640 #if GTK_CHECK_VERSION(3,0,0)
641 g_signal_connect(G_OBJECT(predrw
), "draw",
642 G_CALLBACK(DrawPreedit
), this);
644 g_signal_connect(G_OBJECT(predrw
), "expose_event",
645 G_CALLBACK(ExposePreedit
), this);
647 gtk_container_add(GTK_CONTAINER(PWidget(wPreedit
)), predrw
);
648 gtk_widget_show(predrw
);
650 // Set caret period based on GTK settings
651 gboolean blinkOn
= false;
652 if (g_object_class_find_property(G_OBJECT_GET_CLASS(
653 G_OBJECT(gtk_settings_get_default())), "gtk-cursor-blink")) {
654 g_object_get(G_OBJECT(
655 gtk_settings_get_default()), "gtk-cursor-blink", &blinkOn
, NULL
);
658 g_object_class_find_property(G_OBJECT_GET_CLASS(
659 G_OBJECT(gtk_settings_get_default())), "gtk-cursor-blink-time")) {
661 g_object_get(G_OBJECT(
662 gtk_settings_get_default()), "gtk-cursor-blink-time", &value
, NULL
);
663 caret
.period
= gint(value
/ 1.75);
668 for (TickReason tr
= tickCaret
; tr
<= tickDwell
; tr
= static_cast<TickReason
>(tr
+ 1)) {
669 timers
[tr
].reason
= tr
;
670 timers
[tr
].scintilla
= this;
672 vs
.indicators
[SC_INDICATOR_UNKNOWN
] = Indicator(INDIC_HIDDEN
, ColourDesired(0, 0, 0xff));
673 vs
.indicators
[SC_INDICATOR_INPUT
] = Indicator(INDIC_DOTS
, ColourDesired(0, 0, 0xff));
674 vs
.indicators
[SC_INDICATOR_CONVERTED
] = Indicator(INDIC_COMPOSITIONTHICK
, ColourDesired(0, 0, 0xff));
675 vs
.indicators
[SC_INDICATOR_TARGET
] = Indicator(INDIC_STRAIGHTBOX
, ColourDesired(0, 0, 0xff));
678 void ScintillaGTK::Finalise() {
679 for (TickReason tr
= tickCaret
; tr
<= tickDwell
; tr
= static_cast<TickReason
>(tr
+ 1)) {
680 FineTickerCancel(tr
);
683 gtk_accessible_set_widget(GTK_ACCESSIBLE(accessible
), NULL
);
684 g_object_unref(accessible
);
688 ScintillaBase::Finalise();
691 bool ScintillaGTK::AbandonPaint() {
692 if ((paintState
== painting
) && !paintingAllText
) {
693 repaintFullWindow
= true;
698 void ScintillaGTK::DisplayCursor(Window::Cursor c
) {
699 if (cursorMode
== SC_CURSORNORMAL
)
702 wText
.SetCursor(static_cast<Window::Cursor
>(cursorMode
));
705 bool ScintillaGTK::DragThreshold(Point ptStart
, Point ptNow
) {
706 return gtk_drag_check_threshold(GTK_WIDGET(PWidget(wMain
)),
707 ptStart
.x
, ptStart
.y
, ptNow
.x
, ptNow
.y
);
710 void ScintillaGTK::StartDrag() {
711 PLATFORM_ASSERT(evbtn
!= 0);
712 dragWasDropped
= false;
713 inDragDrop
= ddDragging
;
714 GtkTargetList
*tl
= gtk_target_list_new(clipboardCopyTargets
, nClipboardCopyTargets
);
715 #if GTK_CHECK_VERSION(3,10,0)
716 gtk_drag_begin_with_coordinates(GTK_WIDGET(PWidget(wMain
)),
718 static_cast<GdkDragAction
>(GDK_ACTION_COPY
| GDK_ACTION_MOVE
),
720 reinterpret_cast<GdkEvent
*>(evbtn
),
723 gtk_drag_begin(GTK_WIDGET(PWidget(wMain
)),
725 static_cast<GdkDragAction
>(GDK_ACTION_COPY
| GDK_ACTION_MOVE
),
727 reinterpret_cast<GdkEvent
*>(evbtn
));
732 namespace Scintilla
{
734 std::string
ConvertText(const char *s
, size_t len
, const char *charSetDest
,
735 const char *charSetSource
, bool transliterations
, bool silent
) {
736 // s is not const because of different versions of iconv disagreeing about const
737 std::string destForm
;
738 Converter
conv(charSetDest
, charSetSource
, transliterations
);
740 gsize outLeft
= len
*3+1;
741 destForm
= std::string(outLeft
, '\0');
742 // g_iconv does not actually write to its input argument so safe to cast away const
743 char *pin
= const_cast<char *>(s
);
745 char *putf
= &destForm
[0];
747 gsize conversions
= conv
.Convert(&pin
, &inLeft
, &pout
, &outLeft
);
748 if (conversions
== sizeFailure
) {
751 fprintf(stderr
, "iconv %s->%s failed for %0x '%s'\n",
752 charSetSource
, charSetDest
, (unsigned char)(*s
), s
);
754 fprintf(stderr
, "iconv %s->%s failed for %s\n",
755 charSetSource
, charSetDest
, s
);
757 destForm
= std::string();
759 destForm
.resize(pout
- putf
);
762 fprintf(stderr
, "Can not iconv %s %s\n", charSetDest
, charSetSource
);
770 // Returns the target converted to UTF8.
771 // Return the length in bytes.
772 int ScintillaGTK::TargetAsUTF8(char *text
) {
773 int targetLength
= targetEnd
- targetStart
;
774 if (IsUnicodeMode()) {
776 pdoc
->GetCharRange(text
, targetStart
, targetLength
);
780 const char *charSetBuffer
= CharacterSetID();
781 if (*charSetBuffer
) {
782 std::string s
= RangeText(targetStart
, targetEnd
);
783 std::string tmputf
= ConvertText(&s
[0], targetLength
, "UTF-8", charSetBuffer
, false);
785 memcpy(text
, tmputf
.c_str(), tmputf
.length());
787 return tmputf
.length();
790 pdoc
->GetCharRange(text
, targetStart
, targetLength
);
797 // Translates a nul terminated UTF8 string into the document encoding.
798 // Return the length of the result in bytes.
799 int ScintillaGTK::EncodedFromUTF8(char *utf8
, char *encoded
) const {
800 int inputLength
= (lengthForEncode
>= 0) ? lengthForEncode
: strlen(utf8
);
801 if (IsUnicodeMode()) {
803 memcpy(encoded
, utf8
, inputLength
);
808 const char *charSetBuffer
= CharacterSetID();
809 if (*charSetBuffer
) {
810 std::string tmpEncoded
= ConvertText(utf8
, inputLength
, charSetBuffer
, "UTF-8", true);
812 memcpy(encoded
, tmpEncoded
.c_str(), tmpEncoded
.length());
814 return tmpEncoded
.length();
817 memcpy(encoded
, utf8
, inputLength
);
826 bool ScintillaGTK::ValidCodePage(int codePage
) const {
828 || codePage
== SC_CP_UTF8
836 sptr_t
ScintillaGTK::WndProc(unsigned int iMessage
, uptr_t wParam
, sptr_t lParam
) {
841 gtk_widget_grab_focus(PWidget(wMain
));
844 case SCI_GETDIRECTFUNCTION
:
845 return reinterpret_cast<sptr_t
>(DirectFunction
);
847 case SCI_GETDIRECTPOINTER
:
848 return reinterpret_cast<sptr_t
>(this);
851 case SCI_LOADLEXERLIBRARY
:
852 LexerManager::GetInstance()->Load(reinterpret_cast<const char*>(lParam
));
855 case SCI_TARGETASUTF8
:
856 return TargetAsUTF8(reinterpret_cast<char*>(lParam
));
858 case SCI_ENCODEDFROMUTF8
:
859 return EncodedFromUTF8(reinterpret_cast<char*>(wParam
),
860 reinterpret_cast<char*>(lParam
));
862 case SCI_SETRECTANGULARSELECTIONMODIFIER
:
863 rectangularSelectionModifier
= wParam
;
866 case SCI_GETRECTANGULARSELECTIONMODIFIER
:
867 return rectangularSelectionModifier
;
869 case SCI_SETREADONLY
: {
870 sptr_t ret
= ScintillaBase::WndProc(iMessage
, wParam
, lParam
);
872 ScintillaGTKAccessible
*sciAccessible
= ScintillaGTKAccessible::FromAccessible(accessible
);
874 sciAccessible
->NotifyReadOnly();
880 case SCI_GETACCESSIBILITY
:
881 return accessibilityEnabled
;
883 case SCI_SETACCESSIBILITY
:
884 accessibilityEnabled
= wParam
;
886 ScintillaGTKAccessible
*sciAccessible
= ScintillaGTKAccessible::FromAccessible(accessible
);
888 sciAccessible
->SetAccessibility();
894 return ScintillaBase::WndProc(iMessage
, wParam
, lParam
);
896 } catch (std::bad_alloc
&) {
897 errorStatus
= SC_STATUS_BADALLOC
;
899 errorStatus
= SC_STATUS_FAILURE
;
904 sptr_t
ScintillaGTK::DefWndProc(unsigned int, uptr_t
, sptr_t
) {
909 * Report that this Editor subclass has a working implementation of FineTickerStart.
911 bool ScintillaGTK::FineTickerAvailable() {
915 bool ScintillaGTK::FineTickerRunning(TickReason reason
) {
916 return timers
[reason
].timer
!= 0;
919 void ScintillaGTK::FineTickerStart(TickReason reason
, int millis
, int /* tolerance */) {
920 FineTickerCancel(reason
);
921 timers
[reason
].timer
= gdk_threads_add_timeout(millis
, TimeOut
, &timers
[reason
]);
924 void ScintillaGTK::FineTickerCancel(TickReason reason
) {
925 if (timers
[reason
].timer
) {
926 g_source_remove(timers
[reason
].timer
);
927 timers
[reason
].timer
= 0;
931 bool ScintillaGTK::SetIdle(bool on
) {
933 // Start idler, if it's not running.
936 idler
.idlerID
= reinterpret_cast<IdlerID
>(
937 gdk_threads_add_idle_full(G_PRIORITY_DEFAULT_IDLE
, IdleCallback
, this, NULL
));
940 // Stop idler, if it's running
943 g_source_remove(GPOINTER_TO_UINT(idler
.idlerID
));
949 void ScintillaGTK::SetMouseCapture(bool on
) {
950 if (mouseDownCaptures
) {
952 gtk_grab_add(GTK_WIDGET(PWidget(wMain
)));
954 gtk_grab_remove(GTK_WIDGET(PWidget(wMain
)));
960 bool ScintillaGTK::HaveMouseCapture() {
961 return capturedMouse
;
964 #if GTK_CHECK_VERSION(3,0,0)
966 // Is crcTest completely in crcContainer?
967 static bool CRectContains(const cairo_rectangle_t
&crcContainer
, const cairo_rectangle_t
&crcTest
) {
969 (crcTest
.x
>= crcContainer
.x
) && ((crcTest
.x
+ crcTest
.width
) <= (crcContainer
.x
+ crcContainer
.width
)) &&
970 (crcTest
.y
>= crcContainer
.y
) && ((crcTest
.y
+ crcTest
.height
) <= (crcContainer
.y
+ crcContainer
.height
));
973 // Is crcTest completely in crcListContainer?
974 // May incorrectly return false if complex shape
975 static bool CRectListContains(const cairo_rectangle_list_t
*crcListContainer
, const cairo_rectangle_t
&crcTest
) {
976 for (int r
=0; r
<crcListContainer
->num_rectangles
; r
++) {
977 if (CRectContains(crcListContainer
->rectangles
[r
], crcTest
))
985 bool ScintillaGTK::PaintContains(PRectangle rc
) {
986 // This allows optimization when a rectangle is completely in the update region.
987 // It is OK to return false when too difficult to determine as that just performs extra drawing
988 bool contains
= true;
989 if (paintState
== painting
) {
990 if (!rcPaint
.Contains(rc
)) {
992 } else if (rgnUpdate
) {
993 #if GTK_CHECK_VERSION(3,0,0)
994 cairo_rectangle_t grc
= {rc
.left
, rc
.top
,
995 rc
.right
- rc
.left
, rc
.bottom
- rc
.top
};
996 contains
= CRectListContains(rgnUpdate
, grc
);
998 GdkRectangle grc
= {static_cast<gint
>(rc
.left
), static_cast<gint
>(rc
.top
),
999 static_cast<gint
>(rc
.right
- rc
.left
), static_cast<gint
>(rc
.bottom
- rc
.top
)};
1000 if (gdk_region_rect_in(rgnUpdate
, &grc
) != GDK_OVERLAP_RECTANGLE_IN
) {
1009 // Redraw all of text area. This paint will not be abandoned.
1010 void ScintillaGTK::FullPaint() {
1011 wText
.InvalidateAll();
1014 PRectangle
ScintillaGTK::GetClientRectangle() const {
1016 PRectangle rc
= win
.GetClientPosition();
1017 if (verticalScrollBarVisible
)
1018 rc
.right
-= verticalScrollBarWidth
;
1019 if (horizontalScrollBarVisible
&& !Wrapping())
1020 rc
.bottom
-= horizontalScrollBarHeight
;
1022 rc
.right
-= rc
.left
;
1023 rc
.bottom
-= rc
.top
;
1033 void ScintillaGTK::ScrollText(Sci::Line linesToMove
) {
1036 #if GTK_CHECK_VERSION(3,22,0)
1039 GtkWidget
*wi
= PWidget(wText
);
1040 if (IS_WIDGET_REALIZED(wi
)) {
1041 const int diff
= vs
.lineHeight
* -linesToMove
;
1042 gdk_window_scroll(WindowFromWidget(wi
), 0, -diff
);
1043 gdk_window_process_updates(WindowFromWidget(wi
), FALSE
);
1048 void ScintillaGTK::SetVerticalScrollPos() {
1050 gtk_adjustment_set_value(GTK_ADJUSTMENT(adjustmentv
), topLine
);
1053 void ScintillaGTK::SetHorizontalScrollPos() {
1055 gtk_adjustment_set_value(GTK_ADJUSTMENT(adjustmenth
), xOffset
);
1058 bool ScintillaGTK::ModifyScrollBars(Sci::Line nMax
, Sci::Line nPage
) {
1059 bool modified
= false;
1060 int pageScroll
= LinesToScroll();
1062 if (gtk_adjustment_get_upper(adjustmentv
) != (nMax
+ 1) ||
1063 gtk_adjustment_get_page_size(adjustmentv
) != nPage
||
1064 gtk_adjustment_get_page_increment(adjustmentv
) != pageScroll
) {
1065 gtk_adjustment_set_upper(adjustmentv
, nMax
+ 1);
1066 gtk_adjustment_set_page_size(adjustmentv
, nPage
);
1067 gtk_adjustment_set_page_increment(adjustmentv
, pageScroll
);
1068 #if !GTK_CHECK_VERSION(3,18,0)
1069 gtk_adjustment_changed(GTK_ADJUSTMENT(adjustmentv
));
1074 PRectangle rcText
= GetTextRectangle();
1075 int horizEndPreferred
= scrollWidth
;
1076 if (horizEndPreferred
< 0)
1077 horizEndPreferred
= 0;
1078 unsigned int pageWidth
= rcText
.Width();
1079 unsigned int pageIncrement
= pageWidth
/ 3;
1080 unsigned int charWidth
= vs
.styles
[STYLE_DEFAULT
].aveCharWidth
;
1081 if (gtk_adjustment_get_upper(adjustmenth
) != horizEndPreferred
||
1082 gtk_adjustment_get_page_size(adjustmenth
) != pageWidth
||
1083 gtk_adjustment_get_page_increment(adjustmenth
) != pageIncrement
||
1084 gtk_adjustment_get_step_increment(adjustmenth
) != charWidth
) {
1085 gtk_adjustment_set_upper(adjustmenth
, horizEndPreferred
);
1086 gtk_adjustment_set_page_size(adjustmenth
, pageWidth
);
1087 gtk_adjustment_set_page_increment(adjustmenth
, pageIncrement
);
1088 gtk_adjustment_set_step_increment(adjustmenth
, charWidth
);
1089 #if !GTK_CHECK_VERSION(3,18,0)
1090 gtk_adjustment_changed(GTK_ADJUSTMENT(adjustmenth
));
1094 if (modified
&& (paintState
== painting
)) {
1095 repaintFullWindow
= true;
1101 void ScintillaGTK::ReconfigureScrollBars() {
1102 PRectangle rc
= wMain
.GetClientPosition();
1103 Resize(rc
.Width(), rc
.Height());
1106 void ScintillaGTK::NotifyChange() {
1107 g_signal_emit(G_OBJECT(sci
), scintilla_signals
[COMMAND_SIGNAL
], 0,
1108 Platform::LongFromTwoShorts(GetCtrlID(), SCEN_CHANGE
), PWidget(wMain
));
1111 void ScintillaGTK::NotifyFocus(bool focus
) {
1112 g_signal_emit(G_OBJECT(sci
), scintilla_signals
[COMMAND_SIGNAL
], 0,
1113 Platform::LongFromTwoShorts
1114 (GetCtrlID(), focus
? SCEN_SETFOCUS
: SCEN_KILLFOCUS
), PWidget(wMain
));
1115 Editor::NotifyFocus(focus
);
1118 void ScintillaGTK::NotifyParent(SCNotification scn
) {
1119 scn
.nmhdr
.hwndFrom
= PWidget(wMain
);
1120 scn
.nmhdr
.idFrom
= GetCtrlID();
1121 g_signal_emit(G_OBJECT(sci
), scintilla_signals
[NOTIFY_SIGNAL
], 0,
1125 void ScintillaGTK::NotifyKey(int key
, int modifiers
) {
1126 SCNotification scn
= {};
1127 scn
.nmhdr
.code
= SCN_KEY
;
1129 scn
.modifiers
= modifiers
;
1134 void ScintillaGTK::NotifyURIDropped(const char *list
) {
1135 SCNotification scn
= {};
1136 scn
.nmhdr
.code
= SCN_URIDROPPED
;
1142 const char *CharacterSetID(int characterSet
);
1144 const char *ScintillaGTK::CharacterSetID() const {
1145 return ::CharacterSetID(vs
.styles
[STYLE_DEFAULT
].characterSet
);
1148 class CaseFolderDBCS
: public CaseFolderTable
{
1149 const char *charSet
;
1151 explicit CaseFolderDBCS(const char *charSet_
) : charSet(charSet_
) {
1154 size_t Fold(char *folded
, size_t sizeFolded
, const char *mixed
, size_t lenMixed
) override
{
1155 if ((lenMixed
== 1) && (sizeFolded
> 0)) {
1156 folded
[0] = mapping
[static_cast<unsigned char>(mixed
[0])];
1158 } else if (*charSet
) {
1159 std::string sUTF8
= ConvertText(mixed
, lenMixed
,
1160 "UTF-8", charSet
, false);
1161 if (!sUTF8
.empty()) {
1162 gchar
*mapped
= g_utf8_casefold(sUTF8
.c_str(), sUTF8
.length());
1163 size_t lenMapped
= strlen(mapped
);
1164 if (lenMapped
< sizeFolded
) {
1165 memcpy(folded
, mapped
, lenMapped
);
1174 // Something failed so return a single NUL byte
1180 CaseFolder
*ScintillaGTK::CaseFolderForEncoding() {
1181 if (pdoc
->dbcsCodePage
== SC_CP_UTF8
) {
1182 return new CaseFolderUnicode();
1184 const char *charSetBuffer
= CharacterSetID();
1185 if (charSetBuffer
) {
1186 if (pdoc
->dbcsCodePage
== 0) {
1187 CaseFolderTable
*pcf
= new CaseFolderTable();
1188 pcf
->StandardASCII();
1189 // Only for single byte encodings
1190 for (int i
=0x80; i
<0x100; i
++) {
1191 char sCharacter
[2] = "A";
1193 // Silent as some bytes have no assigned character
1194 std::string sUTF8
= ConvertText(sCharacter
, 1,
1195 "UTF-8", charSetBuffer
, false, true);
1196 if (!sUTF8
.empty()) {
1197 gchar
*mapped
= g_utf8_casefold(sUTF8
.c_str(), sUTF8
.length());
1199 std::string mappedBack
= ConvertText(mapped
, strlen(mapped
),
1200 charSetBuffer
, "UTF-8", false, true);
1201 if ((mappedBack
.length() == 1) && (mappedBack
[0] != sCharacter
[0])) {
1202 pcf
->SetTranslation(sCharacter
[0], mappedBack
[0]);
1210 return new CaseFolderDBCS(charSetBuffer
);
1220 gchar
*mapped
; // Must be freed with g_free
1221 CaseMapper(const std::string
&sUTF8
, bool toUpperCase
) {
1223 mapped
= g_utf8_strup(sUTF8
.c_str(), sUTF8
.length());
1225 mapped
= g_utf8_strdown(sUTF8
.c_str(), sUTF8
.length());
1235 std::string
ScintillaGTK::CaseMapString(const std::string
&s
, int caseMapping
) {
1236 if ((s
.size() == 0) || (caseMapping
== cmSame
))
1239 if (IsUnicodeMode()) {
1240 std::string
retMapped(s
.length() * maxExpansionCaseConversion
, 0);
1241 size_t lenMapped
= CaseConvertString(&retMapped
[0], retMapped
.length(), s
.c_str(), s
.length(),
1242 (caseMapping
== cmUpper
) ? CaseConversionUpper
: CaseConversionLower
);
1243 retMapped
.resize(lenMapped
);
1247 const char *charSetBuffer
= CharacterSetID();
1249 if (!*charSetBuffer
) {
1250 CaseMapper
mapper(s
, caseMapping
== cmUpper
);
1251 return std::string(mapper
.mapped
, strlen(mapper
.mapped
));
1253 // Change text to UTF-8
1254 std::string sUTF8
= ConvertText(s
.c_str(), s
.length(),
1255 "UTF-8", charSetBuffer
, false);
1256 CaseMapper
mapper(sUTF8
, caseMapping
== cmUpper
);
1257 return ConvertText(mapper
.mapped
, strlen(mapper
.mapped
), charSetBuffer
, "UTF-8", false);
1261 int ScintillaGTK::KeyDefault(int key
, int modifiers
) {
1262 // Pass up to container in case it is an accelerator
1263 NotifyKey(key
, modifiers
);
1267 void ScintillaGTK::CopyToClipboard(const SelectionText
&selectedText
) {
1268 SelectionText
*clipText
= new SelectionText();
1269 clipText
->Copy(selectedText
);
1270 StoreOnClipboard(clipText
);
1273 void ScintillaGTK::Copy() {
1275 SelectionText
*clipText
= new SelectionText();
1276 CopySelectionRange(clipText
);
1277 StoreOnClipboard(clipText
);
1279 if (sel
.IsRectangular()) {
1280 ::OpenClipboard(NULL
);
1281 ::SetClipboardData(cfColumnSelect
, 0);
1288 void ScintillaGTK::Paste() {
1289 atomSought
= atomUTF8
;
1290 GtkClipboard
*clipBoard
=
1291 gtk_widget_get_clipboard(GTK_WIDGET(PWidget(wMain
)), atomClipboard
);
1292 if (clipBoard
== NULL
)
1295 // helper class for the asynchronous paste not to risk calling in a destroyed ScintillaGTK
1296 class Helper
: GObjectWatcher
{
1299 void Destroyed() override
{
1304 Helper(ScintillaGTK
*sci_
) :
1305 GObjectWatcher(G_OBJECT(PWidget(sci_
->wMain
))),
1309 static void ClipboardReceived(GtkClipboard
*, GtkSelectionData
*selection_data
, gpointer data
) {
1310 Helper
*self
= static_cast<Helper
*>(data
);
1311 if (self
->sci
!= 0) {
1312 self
->sci
->ReceivedSelection(selection_data
);
1318 gtk_clipboard_request_contents(clipBoard
, atomSought
, Helper::ClipboardReceived
, new Helper(this));
1321 void ScintillaGTK::CreateCallTipWindow(PRectangle rc
) {
1322 if (!ct
.wCallTip
.Created()) {
1323 ct
.wCallTip
= gtk_window_new(GTK_WINDOW_POPUP
);
1324 ct
.wDraw
= gtk_drawing_area_new();
1325 GtkWidget
*widcdrw
= PWidget(ct
.wDraw
); // // No code inside the G_OBJECT macro
1326 gtk_container_add(GTK_CONTAINER(PWidget(ct
.wCallTip
)), widcdrw
);
1327 #if GTK_CHECK_VERSION(3,0,0)
1328 g_signal_connect(G_OBJECT(widcdrw
), "draw",
1329 G_CALLBACK(ScintillaGTK::DrawCT
), &ct
);
1331 g_signal_connect(G_OBJECT(widcdrw
), "expose_event",
1332 G_CALLBACK(ScintillaGTK::ExposeCT
), &ct
);
1334 g_signal_connect(G_OBJECT(widcdrw
), "button_press_event",
1335 G_CALLBACK(ScintillaGTK::PressCT
), static_cast<void *>(this));
1336 gtk_widget_set_events(widcdrw
,
1337 GDK_EXPOSURE_MASK
| GDK_BUTTON_PRESS_MASK
);
1338 GtkWidget
*top
= gtk_widget_get_toplevel(static_cast<GtkWidget
*>(wMain
.GetID()));
1339 gtk_window_set_transient_for(GTK_WINDOW(static_cast<GtkWidget
*>(PWidget(ct
.wCallTip
))),
1342 gtk_widget_set_size_request(PWidget(ct
.wDraw
), rc
.Width(), rc
.Height());
1344 if (PWindow(ct
.wCallTip
)) {
1345 gdk_window_resize(PWindow(ct
.wCallTip
), rc
.Width(), rc
.Height());
1349 void ScintillaGTK::AddToPopUp(const char *label
, int cmd
, bool enabled
) {
1350 GtkWidget
*menuItem
;
1352 menuItem
= gtk_menu_item_new_with_label(label
);
1354 menuItem
= gtk_separator_menu_item_new();
1355 gtk_menu_shell_append(GTK_MENU_SHELL(popup
.GetID()), menuItem
);
1356 g_object_set_data(G_OBJECT(menuItem
), "CmdNum", GINT_TO_POINTER(cmd
));
1357 g_signal_connect(G_OBJECT(menuItem
),"activate", G_CALLBACK(PopUpCB
), this);
1361 gtk_widget_set_sensitive(menuItem
, enabled
);
1365 bool ScintillaGTK::OwnPrimarySelection() {
1366 return ((gdk_selection_owner_get(GDK_SELECTION_PRIMARY
)
1367 == PWindow(wMain
)) &&
1368 (PWindow(wMain
) != NULL
));
1371 void ScintillaGTK::ClaimSelection() {
1372 // X Windows has a 'primary selection' as well as the clipboard.
1373 // Whenever the user selects some text, we become the primary selection
1374 if (!sel
.Empty() && IS_WIDGET_REALIZED(GTK_WIDGET(PWidget(wMain
)))) {
1375 primarySelection
= true;
1376 gtk_selection_owner_set(GTK_WIDGET(PWidget(wMain
)),
1377 GDK_SELECTION_PRIMARY
, GDK_CURRENT_TIME
);
1379 } else if (OwnPrimarySelection()) {
1380 primarySelection
= true;
1381 if (primary
.Empty())
1382 gtk_selection_owner_set(NULL
, GDK_SELECTION_PRIMARY
, GDK_CURRENT_TIME
);
1384 primarySelection
= false;
1389 static const guchar
*DataOfGSD(GtkSelectionData
*sd
) { return gtk_selection_data_get_data(sd
); }
1390 static gint
LengthOfGSD(GtkSelectionData
*sd
) { return gtk_selection_data_get_length(sd
); }
1391 static GdkAtom
TypeOfGSD(GtkSelectionData
*sd
) { return gtk_selection_data_get_data_type(sd
); }
1392 static GdkAtom
SelectionOfGSD(GtkSelectionData
*sd
) { return gtk_selection_data_get_selection(sd
); }
1394 // Detect rectangular text, convert line ends to current mode, convert from or to UTF-8
1395 void ScintillaGTK::GetGtkSelectionText(GtkSelectionData
*selectionData
, SelectionText
&selText
) {
1396 const char *data
= reinterpret_cast<const char *>(DataOfGSD(selectionData
));
1397 int len
= LengthOfGSD(selectionData
);
1398 GdkAtom selectionTypeData
= TypeOfGSD(selectionData
);
1400 // Return empty string if selection is not a string
1401 if ((selectionTypeData
!= GDK_TARGET_STRING
) && (selectionTypeData
!= atomUTF8
)) {
1406 // Check for "\n\0" ending to string indicating that selection is rectangular
1409 isRectangular
= ::IsClipboardFormatAvailable(cfColumnSelect
) != 0;
1411 isRectangular
= ((len
> 2) && (data
[len
- 1] == 0 && data
[len
- 2] == '\n'));
1413 len
--; // Forget the extra '\0'
1417 // Win32 includes an ending '\0' byte in 'len' for clipboard text from
1418 // external applications; ignore it.
1419 if ((len
> 0) && (data
[len
- 1] == '\0'))
1423 std::string
dest(data
, len
);
1424 if (selectionTypeData
== GDK_TARGET_STRING
) {
1425 if (IsUnicodeMode()) {
1426 // Unknown encoding so assume in Latin1
1427 dest
= UTF8FromLatin1(dest
.c_str(), dest
.length());
1428 selText
.Copy(dest
, SC_CP_UTF8
, 0, isRectangular
, false);
1430 // Assume buffer is in same encoding as selection
1431 selText
.Copy(dest
, pdoc
->dbcsCodePage
,
1432 vs
.styles
[STYLE_DEFAULT
].characterSet
, isRectangular
, false);
1435 const char *charSetBuffer
= CharacterSetID();
1436 if (!IsUnicodeMode() && *charSetBuffer
) {
1437 // Convert to locale
1438 dest
= ConvertText(dest
.c_str(), dest
.length(), charSetBuffer
, "UTF-8", true);
1439 selText
.Copy(dest
, pdoc
->dbcsCodePage
,
1440 vs
.styles
[STYLE_DEFAULT
].characterSet
, isRectangular
, false);
1442 selText
.Copy(dest
, SC_CP_UTF8
, 0, isRectangular
, false);
1447 void ScintillaGTK::ReceivedSelection(GtkSelectionData
*selection_data
) {
1449 if ((SelectionOfGSD(selection_data
) == atomClipboard
) ||
1450 (SelectionOfGSD(selection_data
) == GDK_SELECTION_PRIMARY
)) {
1451 if ((atomSought
== atomUTF8
) && (LengthOfGSD(selection_data
) <= 0)) {
1452 atomSought
= atomString
;
1453 gtk_selection_convert(GTK_WIDGET(PWidget(wMain
)),
1454 SelectionOfGSD(selection_data
), atomSought
, GDK_CURRENT_TIME
);
1455 } else if ((LengthOfGSD(selection_data
) > 0) &&
1456 ((TypeOfGSD(selection_data
) == GDK_TARGET_STRING
) || (TypeOfGSD(selection_data
) == atomUTF8
))) {
1457 SelectionText selText
;
1458 GetGtkSelectionText(selection_data
, selText
);
1461 if (SelectionOfGSD(selection_data
) != GDK_SELECTION_PRIMARY
) {
1462 ClearSelection(multiPasteMode
== SC_MULTIPASTE_EACH
);
1465 InsertPasteShape(selText
.Data(), selText
.Length(),
1466 selText
.rectangular
? pasteRectangular
: pasteStream
);
1467 EnsureCaretVisible();
1470 // else fprintf(stderr, "Target non string %d %d\n", (int)(selection_data->type),
1471 // (int)(atomUTF8));
1474 errorStatus
= SC_STATUS_FAILURE
;
1478 void ScintillaGTK::ReceivedDrop(GtkSelectionData
*selection_data
) {
1479 dragWasDropped
= true;
1480 if (TypeOfGSD(selection_data
) == atomUriList
|| TypeOfGSD(selection_data
) == atomDROPFILES_DND
) {
1481 const char *data
= reinterpret_cast<const char *>(DataOfGSD(selection_data
));
1482 std::vector
<char> drop(data
, data
+ LengthOfGSD(selection_data
));
1483 drop
.push_back('\0');
1484 NotifyURIDropped(&drop
[0]);
1485 } else if ((TypeOfGSD(selection_data
) == GDK_TARGET_STRING
) || (TypeOfGSD(selection_data
) == atomUTF8
)) {
1486 if (LengthOfGSD(selection_data
) > 0) {
1487 SelectionText selText
;
1488 GetGtkSelectionText(selection_data
, selText
);
1489 DropAt(posDrop
, selText
.Data(), selText
.Length(), false, selText
.rectangular
);
1491 } else if (LengthOfGSD(selection_data
) > 0) {
1492 //~ fprintf(stderr, "ReceivedDrop other %p\n", static_cast<void *>(selection_data->type));
1499 void ScintillaGTK::GetSelection(GtkSelectionData
*selection_data
, guint info
, SelectionText
*text
) {
1501 // GDK on Win32 expands any \n into \r\n, so make a copy of
1502 // the clip text now with newlines converted to \n. Use { } to hide symbols
1504 std::unique_ptr
<SelectionText
> newline_normalized
;
1506 std::string tmpstr
= Document::TransformLineEnds(text
->Data(), text
->Length(), SC_EOL_LF
);
1507 newline_normalized
.reset(new SelectionText());
1508 newline_normalized
->Copy(tmpstr
, SC_CP_UTF8
, 0, text
->rectangular
, false);
1509 text
= newline_normalized
.get();
1513 // Convert text to utf8 if it isn't already
1514 std::unique_ptr
<SelectionText
> converted
;
1515 if ((text
->codePage
!= SC_CP_UTF8
) && (info
== TARGET_UTF8_STRING
)) {
1516 const char *charSet
= ::CharacterSetID(text
->characterSet
);
1518 std::string tmputf
= ConvertText(text
->Data(), text
->Length(), "UTF-8", charSet
, false);
1519 converted
.reset(new SelectionText());
1520 converted
->Copy(tmputf
, SC_CP_UTF8
, 0, text
->rectangular
, false);
1521 text
= converted
.get();
1525 // Here is a somewhat evil kludge.
1526 // As I can not work out how to store data on the clipboard in multiple formats
1527 // and need some way to mark the clipping as being stream or rectangular,
1528 // the terminating \0 is included in the length for rectangular clippings.
1529 // All other tested aplications behave benignly by ignoring the \0.
1530 // The #if is here because on Windows cfColumnSelect clip entry is used
1531 // instead as standard indicator of rectangularness (so no need to kludge)
1532 const char *textData
= text
->Data();
1533 int len
= text
->Length();
1534 #if PLAT_GTK_WIN32 == 0
1535 if (text
->rectangular
)
1539 if (info
== TARGET_UTF8_STRING
) {
1540 gtk_selection_data_set_text(selection_data
, textData
, len
);
1542 gtk_selection_data_set(selection_data
,
1543 static_cast<GdkAtom
>(GDK_SELECTION_TYPE_STRING
),
1544 8, reinterpret_cast<const unsigned char *>(textData
), len
);
1548 void ScintillaGTK::StoreOnClipboard(SelectionText
*clipText
) {
1549 GtkClipboard
*clipBoard
=
1550 gtk_widget_get_clipboard(GTK_WIDGET(PWidget(wMain
)), atomClipboard
);
1551 if (clipBoard
== NULL
) // Occurs if widget isn't in a toplevel
1554 if (gtk_clipboard_set_with_data(clipBoard
, clipboardCopyTargets
, nClipboardCopyTargets
,
1555 ClipboardGetSelection
, ClipboardClearSelection
, clipText
)) {
1556 gtk_clipboard_set_can_store(clipBoard
, clipboardCopyTargets
, nClipboardCopyTargets
);
1560 void ScintillaGTK::ClipboardGetSelection(GtkClipboard
*, GtkSelectionData
*selection_data
, guint info
, void *data
) {
1561 GetSelection(selection_data
, info
, static_cast<SelectionText
*>(data
));
1564 void ScintillaGTK::ClipboardClearSelection(GtkClipboard
*, void *data
) {
1565 SelectionText
*obj
= static_cast<SelectionText
*>(data
);
1569 void ScintillaGTK::UnclaimSelection(GdkEventSelection
*selection_event
) {
1571 //Platform::DebugPrintf("UnclaimSelection\n");
1572 if (selection_event
->selection
== GDK_SELECTION_PRIMARY
) {
1573 //Platform::DebugPrintf("UnclaimPrimarySelection\n");
1574 if (!OwnPrimarySelection()) {
1576 primarySelection
= false;
1581 errorStatus
= SC_STATUS_FAILURE
;
1585 void ScintillaGTK::Resize(int width
, int height
) {
1586 //Platform::DebugPrintf("Resize %d %d\n", width, height);
1587 //printf("Resize %d %d\n", width, height);
1589 // GTK+ 3 warns when we allocate smaller than the minimum allocation,
1590 // so we use these variables to store the minimum scrollbar lengths.
1591 int minVScrollBarHeight
, minHScrollBarWidth
;
1593 // Not always needed, but some themes can have different sizes of scrollbars
1594 #if GTK_CHECK_VERSION(3,0,0)
1595 GtkRequisition minimum
, requisition
;
1596 gtk_widget_get_preferred_size(PWidget(scrollbarv
), &minimum
, &requisition
);
1597 minVScrollBarHeight
= minimum
.height
;
1598 verticalScrollBarWidth
= requisition
.width
;
1599 gtk_widget_get_preferred_size(PWidget(scrollbarh
), &minimum
, &requisition
);
1600 minHScrollBarWidth
= minimum
.width
;
1601 horizontalScrollBarHeight
= requisition
.height
;
1603 minVScrollBarHeight
= minHScrollBarWidth
= 1;
1604 verticalScrollBarWidth
= GTK_WIDGET(PWidget(scrollbarv
))->requisition
.width
;
1605 horizontalScrollBarHeight
= GTK_WIDGET(PWidget(scrollbarh
))->requisition
.height
;
1608 // These allocations should never produce negative sizes as they would wrap around to huge
1609 // unsigned numbers inside GTK+ causing warnings.
1610 bool showSBHorizontal
= horizontalScrollBarVisible
&& !Wrapping();
1612 GtkAllocation alloc
;
1613 if (showSBHorizontal
) {
1614 gtk_widget_show(GTK_WIDGET(PWidget(scrollbarh
)));
1616 alloc
.y
= height
- horizontalScrollBarHeight
;
1617 alloc
.width
= Platform::Maximum(minHScrollBarWidth
, width
- verticalScrollBarWidth
);
1618 alloc
.height
= horizontalScrollBarHeight
;
1619 gtk_widget_size_allocate(GTK_WIDGET(PWidget(scrollbarh
)), &alloc
);
1621 gtk_widget_hide(GTK_WIDGET(PWidget(scrollbarh
)));
1622 horizontalScrollBarHeight
= 0; // in case horizontalScrollBarVisible is true.
1625 if (verticalScrollBarVisible
) {
1626 gtk_widget_show(GTK_WIDGET(PWidget(scrollbarv
)));
1627 alloc
.x
= width
- verticalScrollBarWidth
;
1629 alloc
.width
= verticalScrollBarWidth
;
1630 alloc
.height
= Platform::Maximum(minVScrollBarHeight
, height
- horizontalScrollBarHeight
);
1631 gtk_widget_size_allocate(GTK_WIDGET(PWidget(scrollbarv
)), &alloc
);
1633 gtk_widget_hide(GTK_WIDGET(PWidget(scrollbarv
)));
1634 verticalScrollBarWidth
= 0;
1636 if (IS_WIDGET_MAPPED(PWidget(wMain
))) {
1644 #if GTK_CHECK_VERSION(3, 0, 0)
1645 // please GTK 3.20 and ask wText what size it wants, although we know it doesn't really need
1646 // anything special as it's ours.
1647 gtk_widget_get_preferred_size(PWidget(wText
), &requisition
, NULL
);
1648 alloc
.width
= requisition
.width
;
1649 alloc
.height
= requisition
.height
;
1651 alloc
.width
= Platform::Maximum(alloc
.width
, width
- verticalScrollBarWidth
);
1652 alloc
.height
= Platform::Maximum(alloc
.height
, height
- horizontalScrollBarHeight
);
1653 gtk_widget_size_allocate(GTK_WIDGET(PWidget(wText
)), &alloc
);
1656 static void SetAdjustmentValue(GtkAdjustment
*object
, int value
) {
1657 GtkAdjustment
*adjustment
= GTK_ADJUSTMENT(object
);
1658 int maxValue
= static_cast<int>(
1659 gtk_adjustment_get_upper(adjustment
) - gtk_adjustment_get_page_size(adjustment
));
1661 if (value
> maxValue
)
1665 gtk_adjustment_set_value(adjustment
, value
);
1668 static int modifierTranslated(int sciModifier
) {
1669 switch (sciModifier
) {
1671 return GDK_SHIFT_MASK
;
1673 return GDK_CONTROL_MASK
;
1675 return GDK_MOD1_MASK
;
1677 return GDK_MOD4_MASK
;
1683 gint
ScintillaGTK::PressThis(GdkEventButton
*event
) {
1685 //Platform::DebugPrintf("Press %x time=%d state = %x button = %x\n",this,event->time, event->state, event->button);
1686 // Do not use GTK+ double click events as Scintilla has its own double click detection
1687 if (event
->type
!= GDK_BUTTON_PRESS
)
1691 gdk_event_free(reinterpret_cast<GdkEvent
*>(evbtn
));
1694 evbtn
= reinterpret_cast<GdkEventButton
*>(gdk_event_copy(reinterpret_cast<GdkEvent
*>(event
)));
1696 pt
.x
= int(event
->x
);
1697 pt
.y
= int(event
->y
);
1698 PRectangle rcClient
= GetClientRectangle();
1699 //Platform::DebugPrintf("Press %0d,%0d in %0d,%0d %0d,%0d\n",
1700 // pt.x, pt.y, rcClient.left, rcClient.top, rcClient.right, rcClient.bottom);
1701 if ((pt
.x
> rcClient
.right
) || (pt
.y
> rcClient
.bottom
)) {
1702 Platform::DebugPrintf("Bad location\n");
1706 bool shift
= (event
->state
& GDK_SHIFT_MASK
) != 0;
1707 bool ctrl
= (event
->state
& GDK_CONTROL_MASK
) != 0;
1708 // On X, instead of sending literal modifiers use the user specified
1709 // modifier, defaulting to control instead of alt.
1710 // This is because most X window managers grab alt + click for moving
1711 bool alt
= (event
->state
& modifierTranslated(rectangularSelectionModifier
)) != 0;
1713 gtk_widget_grab_focus(PWidget(wMain
));
1714 if (event
->button
== 1) {
1717 // GDK reports the Command modifer key as GDK_MOD2_MASK for button events,
1718 // not GDK_META_MASK like in key events.
1719 ctrl
= (event
->state
& GDK_MOD2_MASK
) != 0;
1723 ButtonDownWithModifiers(pt
, event
->time
, ModifierFlags(shift
, ctrl
, alt
, meta
));
1724 } else if (event
->button
== 2) {
1725 // Grab the primary selection if it exists
1726 SelectionPosition pos
= SPositionFromLocation(pt
, false, false, UserVirtualSpace());
1727 if (OwnPrimarySelection() && primary
.Empty())
1728 CopySelectionRange(&primary
);
1731 SetSelection(pos
, pos
);
1732 atomSought
= atomUTF8
;
1733 gtk_selection_convert(GTK_WIDGET(PWidget(wMain
)), GDK_SELECTION_PRIMARY
,
1734 atomSought
, event
->time
);
1735 } else if (event
->button
== 3) {
1736 if (!PointInSelection(pt
))
1737 SetEmptySelection(PositionFromLocation(pt
));
1738 if (ShouldDisplayPopup(pt
)) {
1740 // Convert to screen
1743 gdk_window_get_origin(PWindow(wMain
), &ox
, &oy
);
1744 ContextMenu(Point(pt
.x
+ ox
, pt
.y
+ oy
));
1748 // GDK reports the Command modifer key as GDK_MOD2_MASK for button events,
1749 // not GDK_META_MASK like in key events.
1750 ctrl
= (event
->state
& GDK_MOD2_MASK
) != 0;
1754 RightButtonDownWithModifiers(pt
, event
->time
, ModifierFlags(shift
, ctrl
, alt
, meta
));
1757 } else if (event
->button
== 4) {
1758 // Wheel scrolling up (only GTK 1.x does it this way)
1760 SetAdjustmentValue(adjustmenth
, xOffset
- 6);
1762 SetAdjustmentValue(adjustmentv
, topLine
- 3);
1763 } else if (event
->button
== 5) {
1764 // Wheel scrolling down (only GTK 1.x does it this way)
1766 SetAdjustmentValue(adjustmenth
, xOffset
+ 6);
1768 SetAdjustmentValue(adjustmentv
, topLine
+ 3);
1771 errorStatus
= SC_STATUS_FAILURE
;
1776 gint
ScintillaGTK::Press(GtkWidget
*widget
, GdkEventButton
*event
) {
1777 if (event
->window
!= WindowFromWidget(widget
))
1779 ScintillaGTK
*sciThis
= FromWidget(widget
);
1780 return sciThis
->PressThis(event
);
1783 gint
ScintillaGTK::MouseRelease(GtkWidget
*widget
, GdkEventButton
*event
) {
1784 ScintillaGTK
*sciThis
= FromWidget(widget
);
1786 //Platform::DebugPrintf("Release %x %d %d\n",sciThis,event->time,event->state);
1787 if (!sciThis
->HaveMouseCapture())
1789 if (event
->button
== 1) {
1791 pt
.x
= int(event
->x
);
1792 pt
.y
= int(event
->y
);
1793 //Platform::DebugPrintf("Up %x %x %d %d %d\n",
1794 // sciThis,event->window,event->time, pt.x, pt.y);
1795 if (event
->window
!= PWindow(sciThis
->wMain
))
1796 // If mouse released on scroll bar then the position is relative to the
1797 // scrollbar, not the drawing window so just repeat the most recent point.
1798 pt
= sciThis
->ptMouseLast
;
1799 sciThis
->ButtonUp(pt
, event
->time
, (event
->state
& GDK_CONTROL_MASK
) != 0);
1802 sciThis
->errorStatus
= SC_STATUS_FAILURE
;
1807 // win32gtk and GTK >= 2 use SCROLL_* events instead of passing the
1808 // button4/5/6/7 events to the GTK app
1809 gint
ScintillaGTK::ScrollEvent(GtkWidget
*widget
, GdkEventScroll
*event
) {
1810 ScintillaGTK
*sciThis
= FromWidget(widget
);
1813 if (widget
== NULL
|| event
== NULL
)
1816 #if defined(GDK_WINDOWING_WAYLAND)
1817 if (event
->direction
== GDK_SCROLL_SMOOTH
&& GDK_IS_WAYLAND_WINDOW(event
->window
)) {
1818 const int smoothScrollFactor
= 4;
1819 sciThis
->smoothScrollY
+= event
->delta_y
* smoothScrollFactor
;
1820 sciThis
->smoothScrollX
+= event
->delta_x
* smoothScrollFactor
;;
1821 if (ABS(sciThis
->smoothScrollY
) >= 1.0) {
1822 const int scrollLines
= trunc(sciThis
->smoothScrollY
);
1823 sciThis
->ScrollTo(sciThis
->topLine
+ scrollLines
);
1824 sciThis
->smoothScrollY
-= scrollLines
;
1826 if (ABS(sciThis
->smoothScrollX
) >= 1.0) {
1827 const int scrollPixels
= trunc(sciThis
->smoothScrollX
);
1828 sciThis
->HorizontalScrollTo(sciThis
->xOffset
+ scrollPixels
);
1829 sciThis
->smoothScrollX
-= scrollPixels
;
1835 // Compute amount and direction to scroll (even tho on win32 there is
1836 // intensity of scrolling info in the native message, gtk doesn't
1837 // support this so we simulate similarly adaptive scrolling)
1838 // Note that this is disabled on OS X (Darwin) with the X11 backend
1839 // where the X11 server already has an adaptive scrolling algorithm
1840 // that fights with this one
1842 #if defined(__APPLE__) && !defined(GDK_WINDOWING_QUARTZ)
1843 cLineScroll
= sciThis
->linesPerScroll
;
1844 if (cLineScroll
== 0)
1846 sciThis
->wheelMouseIntensity
= cLineScroll
;
1848 int timeDelta
= 1000000;
1850 g_get_current_time(&curTime
);
1851 if (curTime
.tv_sec
== sciThis
->lastWheelMouseTime
.tv_sec
)
1852 timeDelta
= curTime
.tv_usec
- sciThis
->lastWheelMouseTime
.tv_usec
;
1853 else if (curTime
.tv_sec
== sciThis
->lastWheelMouseTime
.tv_sec
+ 1)
1854 timeDelta
= 1000000 + (curTime
.tv_usec
- sciThis
->lastWheelMouseTime
.tv_usec
);
1855 if ((event
->direction
== sciThis
->lastWheelMouseDirection
) && (timeDelta
< 250000)) {
1856 if (sciThis
->wheelMouseIntensity
< 12)
1857 sciThis
->wheelMouseIntensity
++;
1858 cLineScroll
= sciThis
->wheelMouseIntensity
;
1860 cLineScroll
= sciThis
->linesPerScroll
;
1861 if (cLineScroll
== 0)
1863 sciThis
->wheelMouseIntensity
= cLineScroll
;
1866 if (event
->direction
== GDK_SCROLL_UP
|| event
->direction
== GDK_SCROLL_LEFT
) {
1869 g_get_current_time(&sciThis
->lastWheelMouseTime
);
1870 sciThis
->lastWheelMouseDirection
= event
->direction
;
1872 // Note: Unpatched versions of win32gtk don't set the 'state' value so
1873 // only regular scrolling is supported there. Also, unpatched win32gtk
1874 // issues spurious button 2 mouse events during wheeling, which can cause
1875 // problems (a patch for both was submitted by archaeopteryx.com on 13Jun2001)
1877 // Data zoom not supported
1878 if (event
->state
& GDK_SHIFT_MASK
) {
1882 #if GTK_CHECK_VERSION(3,4,0)
1883 // Smooth scrolling not supported
1884 if (event
->direction
== GDK_SCROLL_SMOOTH
) {
1889 // Horizontal scrolling
1890 if (event
->direction
== GDK_SCROLL_LEFT
|| event
->direction
== GDK_SCROLL_RIGHT
) {
1891 sciThis
->HorizontalScrollTo(sciThis
->xOffset
+ cLineScroll
);
1893 // Text font size zoom
1894 } else if (event
->state
& GDK_CONTROL_MASK
) {
1895 if (cLineScroll
< 0) {
1896 sciThis
->KeyCommand(SCI_ZOOMIN
);
1898 sciThis
->KeyCommand(SCI_ZOOMOUT
);
1901 // Regular scrolling
1903 sciThis
->ScrollTo(sciThis
->topLine
+ cLineScroll
);
1907 sciThis
->errorStatus
= SC_STATUS_FAILURE
;
1912 gint
ScintillaGTK::Motion(GtkWidget
*widget
, GdkEventMotion
*event
) {
1913 ScintillaGTK
*sciThis
= FromWidget(widget
);
1915 //Platform::DebugPrintf("Motion %x %d\n",sciThis,event->time);
1916 if (event
->window
!= WindowFromWidget(widget
))
1920 GdkModifierType state
;
1921 if (event
->is_hint
) {
1922 #if GTK_CHECK_VERSION(3,0,0)
1923 gdk_window_get_device_position(event
->window
,
1924 event
->device
, &x
, &y
, &state
);
1926 gdk_window_get_pointer(event
->window
, &x
, &y
, &state
);
1929 x
= static_cast<int>(event
->x
);
1930 y
= static_cast<int>(event
->y
);
1931 state
= static_cast<GdkModifierType
>(event
->state
);
1933 //Platform::DebugPrintf("Move %x %x %d %c %d %d\n",
1934 // sciThis,event->window,event->time,event->is_hint? 'h' :'.', x, y);
1936 int modifiers
= ((event
->state
& GDK_SHIFT_MASK
) != 0 ? SCI_SHIFT
: 0) |
1937 ((event
->state
& GDK_CONTROL_MASK
) != 0 ? SCI_CTRL
: 0) |
1938 ((event
->state
& modifierTranslated(sciThis
->rectangularSelectionModifier
)) != 0 ? SCI_ALT
: 0);
1939 sciThis
->ButtonMoveWithModifiers(pt
, modifiers
);
1941 sciThis
->errorStatus
= SC_STATUS_FAILURE
;
1946 // Map the keypad keys to their equivalent functions
1947 static int KeyTranslate(int keyIn
) {
1949 #if GTK_CHECK_VERSION(3,0,0)
1950 case GDK_KEY_ISO_Left_Tab
:
1952 case GDK_KEY_KP_Down
:
1956 case GDK_KEY_KP_Left
:
1958 case GDK_KEY_KP_Right
:
1960 case GDK_KEY_KP_Home
:
1962 case GDK_KEY_KP_End
:
1964 case GDK_KEY_KP_Page_Up
:
1966 case GDK_KEY_KP_Page_Down
:
1968 case GDK_KEY_KP_Delete
:
1970 case GDK_KEY_KP_Insert
:
1972 case GDK_KEY_KP_Enter
:
1987 case GDK_KEY_Page_Up
:
1989 case GDK_KEY_Page_Down
:
1991 case GDK_KEY_Delete
:
1993 case GDK_KEY_Insert
:
1995 case GDK_KEY_Escape
:
1997 case GDK_KEY_BackSpace
:
2001 case GDK_KEY_Return
:
2003 case GDK_KEY_KP_Add
:
2005 case GDK_KEY_KP_Subtract
:
2006 return SCK_SUBTRACT
;
2007 case GDK_KEY_KP_Divide
:
2009 case GDK_KEY_Super_L
:
2011 case GDK_KEY_Super_R
:
2018 case GDK_ISO_Left_Tab
:
2032 case GDK_KP_Page_Up
:
2034 case GDK_KP_Page_Down
:
2073 case GDK_KP_Subtract
:
2074 return SCK_SUBTRACT
;
2089 gboolean
ScintillaGTK::KeyThis(GdkEventKey
*event
) {
2091 //fprintf(stderr, "SC-key: %d %x [%s]\n",
2092 // event->keyval, event->state, (event->length > 0) ? event->string : "empty");
2093 if (gtk_im_context_filter_keypress(im_context
, event
)) {
2096 if (!event
->keyval
) {
2100 bool shift
= (event
->state
& GDK_SHIFT_MASK
) != 0;
2101 bool ctrl
= (event
->state
& GDK_CONTROL_MASK
) != 0;
2102 bool alt
= (event
->state
& GDK_MOD1_MASK
) != 0;
2103 bool super
= (event
->state
& GDK_MOD4_MASK
) != 0;
2104 guint key
= event
->keyval
;
2105 if ((ctrl
|| alt
) && (key
< 128))
2107 #if GTK_CHECK_VERSION(3,0,0)
2108 else if (!ctrl
&& (key
>= GDK_KEY_KP_Multiply
&& key
<= GDK_KEY_KP_9
))
2110 else if (!ctrl
&& (key
>= GDK_KP_Multiply
&& key
<= GDK_KP_9
))
2113 // Hack for keys over 256 and below command keys but makes Hungarian work.
2114 // This will have to change for Unicode
2115 else if (key
>= 0xFE00)
2116 key
= KeyTranslate(key
);
2118 bool consumed
= false;
2119 #if !(PLAT_GTK_MACOSX)
2123 ctrl
= (event
->state
& GDK_META_MASK
) != 0;
2125 bool added
= KeyDownWithModifiers(key
, ModifierFlags(shift
, ctrl
, alt
, meta
, super
), &consumed
) != 0;
2128 //fprintf(stderr, "SK-key: %d %x %x\n",event->keyval, event->state, consumed);
2129 if (event
->keyval
== 0xffffff && event
->length
> 0) {
2131 const int lengthInserted
= pdoc
->InsertString(CurrentPosition(), event
->string
, strlen(event
->string
));
2132 if (lengthInserted
> 0) {
2133 MovePositionTo(CurrentPosition() + lengthInserted
);
2138 errorStatus
= SC_STATUS_FAILURE
;
2143 gboolean
ScintillaGTK::KeyPress(GtkWidget
*widget
, GdkEventKey
*event
) {
2144 ScintillaGTK
*sciThis
= FromWidget(widget
);
2145 return sciThis
->KeyThis(event
);
2148 gboolean
ScintillaGTK::KeyRelease(GtkWidget
*widget
, GdkEventKey
*event
) {
2149 //Platform::DebugPrintf("SC-keyrel: %d %x %3s\n",event->keyval, event->state, event->string);
2150 ScintillaGTK
*sciThis
= FromWidget(widget
);
2151 if (gtk_im_context_filter_keypress(sciThis
->im_context
, event
)) {
2157 #if GTK_CHECK_VERSION(3,0,0)
2159 gboolean
ScintillaGTK::DrawPreeditThis(GtkWidget
*, cairo_t
*cr
) {
2161 PreEditString
pes(im_context
);
2162 PangoLayout
*layout
= gtk_widget_create_pango_layout(PWidget(wText
), pes
.str
);
2163 pango_layout_set_attributes(layout
, pes
.attrs
);
2165 cairo_move_to(cr
, 0, 0);
2166 pango_cairo_show_layout(cr
, layout
);
2168 g_object_unref(layout
);
2170 errorStatus
= SC_STATUS_FAILURE
;
2175 gboolean
ScintillaGTK::DrawPreedit(GtkWidget
*widget
, cairo_t
*cr
, ScintillaGTK
*sciThis
) {
2176 return sciThis
->DrawPreeditThis(widget
, cr
);
2181 gboolean
ScintillaGTK::ExposePreeditThis(GtkWidget
*widget
, GdkEventExpose
*) {
2183 PreEditString
pes(im_context
);
2184 PangoLayout
*layout
= gtk_widget_create_pango_layout(PWidget(wText
), pes
.str
);
2185 pango_layout_set_attributes(layout
, pes
.attrs
);
2187 cairo_t
*context
= gdk_cairo_create(reinterpret_cast<GdkDrawable
*>(WindowFromWidget(widget
)));
2188 cairo_move_to(context
, 0, 0);
2189 pango_cairo_show_layout(context
, layout
);
2190 cairo_destroy(context
);
2191 g_object_unref(layout
);
2193 errorStatus
= SC_STATUS_FAILURE
;
2198 gboolean
ScintillaGTK::ExposePreedit(GtkWidget
*widget
, GdkEventExpose
*ose
, ScintillaGTK
*sciThis
) {
2199 return sciThis
->ExposePreeditThis(widget
, ose
);
2204 bool ScintillaGTK::KoreanIME() {
2205 PreEditString
pes(im_context
);
2206 if (pes
.pscript
!= PANGO_SCRIPT_COMMON
)
2207 lastNonCommonScript
= pes
.pscript
;
2208 return lastNonCommonScript
== PANGO_SCRIPT_HANGUL
;
2211 void ScintillaGTK::MoveImeCarets(int pos
) {
2212 // Move carets relatively by bytes
2213 for (size_t r
=0; r
<sel
.Count(); r
++) {
2214 int positionInsert
= sel
.Range(r
).Start().Position();
2215 sel
.Range(r
).caret
.SetPosition(positionInsert
+ pos
);
2216 sel
.Range(r
).anchor
.SetPosition(positionInsert
+ pos
);
2220 void ScintillaGTK::DrawImeIndicator(int indicator
, int len
) {
2221 // Emulate the visual style of IME characters with indicators.
2222 // Draw an indicator on the character before caret by the character bytes of len
2223 // so it should be called after addCharUTF().
2224 // It does not affect caret positions.
2225 if (indicator
< 8 || indicator
> INDIC_MAX
) {
2228 pdoc
->DecorationSetCurrentIndicator(indicator
);
2229 for (size_t r
=0; r
<sel
.Count(); r
++) {
2230 int positionInsert
= sel
.Range(r
).Start().Position();
2231 pdoc
->DecorationFillRange(positionInsert
- len
, 1, len
);
2235 static std::vector
<int> MapImeIndicators(PangoAttrList
*attrs
, const char *u8Str
) {
2236 // Map input style to scintilla ime indicator.
2237 // Attrs position points between UTF-8 bytes.
2238 // Indicator index to be returned is character based though.
2239 glong charactersLen
= g_utf8_strlen(u8Str
, strlen(u8Str
));
2240 std::vector
<int> indicator(charactersLen
, SC_INDICATOR_UNKNOWN
);
2242 PangoAttrIterator
*iterunderline
= pango_attr_list_get_iterator(attrs
);
2243 if (iterunderline
) {
2245 PangoAttribute
*attrunderline
= pango_attr_iterator_get(iterunderline
, PANGO_ATTR_UNDERLINE
);
2246 if (attrunderline
) {
2247 glong start
= g_utf8_strlen(u8Str
, attrunderline
->start_index
);
2248 glong end
= g_utf8_strlen(u8Str
, attrunderline
->end_index
);
2249 PangoUnderline uline
= (PangoUnderline
)((PangoAttrInt
*)attrunderline
)->value
;
2250 for (glong i
=start
; i
< end
; ++i
) {
2252 case PANGO_UNDERLINE_NONE
:
2253 indicator
[i
] = SC_INDICATOR_UNKNOWN
;
2255 case PANGO_UNDERLINE_SINGLE
: // normal input
2256 indicator
[i
] = SC_INDICATOR_INPUT
;
2258 case PANGO_UNDERLINE_DOUBLE
:
2259 case PANGO_UNDERLINE_LOW
:
2260 case PANGO_UNDERLINE_ERROR
:
2265 } while (pango_attr_iterator_next(iterunderline
));
2266 pango_attr_iterator_destroy(iterunderline
);
2269 PangoAttrIterator
*itercolor
= pango_attr_list_get_iterator(attrs
);
2272 PangoAttribute
*backcolor
= pango_attr_iterator_get(itercolor
, PANGO_ATTR_BACKGROUND
);
2274 glong start
= g_utf8_strlen(u8Str
, backcolor
->start_index
);
2275 glong end
= g_utf8_strlen(u8Str
, backcolor
->end_index
);
2276 for (glong i
=start
; i
< end
; ++i
) {
2277 indicator
[i
] = SC_INDICATOR_TARGET
; // target converted
2280 } while (pango_attr_iterator_next(itercolor
));
2281 pango_attr_iterator_destroy(itercolor
);
2286 void ScintillaGTK::SetCandidateWindowPos() {
2287 // Composition box accompanies candidate box.
2288 Point pt
= PointMainCaret();
2289 GdkRectangle imeBox
= {0}; // No need to set width
2290 imeBox
.x
= pt
.x
; // Only need positiion
2291 imeBox
.y
= pt
.y
+ vs
.lineHeight
; // underneath the first charater
2292 gtk_im_context_set_cursor_location(im_context
, &imeBox
);
2295 void ScintillaGTK::CommitThis(char *commitStr
) {
2297 //~ fprintf(stderr, "Commit '%s'\n", commitStr);
2298 view
.imeCaretBlockOverride
= false;
2300 if (pdoc
->TentativeActive()) {
2301 pdoc
->TentativeUndo();
2304 const char *charSetSource
= CharacterSetID();
2306 glong uniStrLen
= 0;
2307 gunichar
*uniStr
= g_utf8_to_ucs4_fast(commitStr
, strlen(commitStr
), &uniStrLen
);
2308 for (glong i
= 0; i
< uniStrLen
; i
++) {
2309 gchar u8Char
[UTF8MaxBytes
+2] = {0};
2310 gint u8CharLen
= g_unichar_to_utf8(uniStr
[i
], u8Char
);
2311 std::string docChar
= u8Char
;
2312 if (!IsUnicodeMode())
2313 docChar
= ConvertText(u8Char
, u8CharLen
, charSetSource
, "UTF-8", true);
2315 AddCharUTF(docChar
.c_str(), docChar
.size());
2318 ShowCaretAtCurrentPosition();
2320 errorStatus
= SC_STATUS_FAILURE
;
2324 void ScintillaGTK::Commit(GtkIMContext
*, char *str
, ScintillaGTK
*sciThis
) {
2325 sciThis
->CommitThis(str
);
2328 void ScintillaGTK::PreeditChangedInlineThis() {
2329 // Copy & paste by johnsonj with a lot of helps of Neil
2330 // Great thanks for my foreruners, jiniya and BLUEnLIVE
2332 if (pdoc
->IsReadOnly() || SelectionContainsProtected()) {
2333 gtk_im_context_reset(im_context
);
2337 view
.imeCaretBlockOverride
= false; // If backspace.
2339 bool initialCompose
= false;
2340 if (pdoc
->TentativeActive()) {
2341 pdoc
->TentativeUndo();
2343 // No tentative undo means start of this composition so
2344 // fill in any virtual spaces.
2345 initialCompose
= true;
2348 PreEditString
preeditStr(im_context
);
2349 const char *charSetSource
= CharacterSetID();
2351 if (!preeditStr
.validUTF8
|| (charSetSource
== NULL
)) {
2352 ShowCaretAtCurrentPosition();
2356 if (preeditStr
.uniStrLen
== 0 || preeditStr
.uniStrLen
> maxLenInputIME
) {
2357 //fprintf(stderr, "Do not allow over 200 chars: %i\n", preeditStr.uniStrLen);
2358 ShowCaretAtCurrentPosition();
2363 ClearBeforeTentativeStart();
2364 pdoc
->TentativeStart(); // TentativeActive() from now on
2366 std::vector
<int> indicator
= MapImeIndicators(preeditStr
.attrs
, preeditStr
.str
);
2368 bool tmpRecordingMacro
= recordingMacro
;
2369 recordingMacro
= false;
2370 for (glong i
= 0; i
< preeditStr
.uniStrLen
; i
++) {
2371 gchar u8Char
[UTF8MaxBytes
+2] = {0};
2372 gint u8CharLen
= g_unichar_to_utf8(preeditStr
.uniStr
[i
], u8Char
);
2373 std::string docChar
= u8Char
;
2374 if (!IsUnicodeMode())
2375 docChar
= ConvertText(u8Char
, u8CharLen
, charSetSource
, "UTF-8", true);
2377 AddCharUTF(docChar
.c_str(), docChar
.size());
2379 DrawImeIndicator(indicator
[i
], docChar
.size());
2381 recordingMacro
= tmpRecordingMacro
;
2383 // Move caret to ime cursor position.
2384 int imeEndToImeCaretU32
= preeditStr
.cursor_pos
- preeditStr
.uniStrLen
;
2385 int imeCaretPosDoc
= pdoc
->GetRelativePosition(CurrentPosition(), imeEndToImeCaretU32
);
2387 MoveImeCarets(- CurrentPosition() + imeCaretPosDoc
);
2391 if (preeditStr
.cursor_pos
> 0) {
2392 int oneCharBefore
= pdoc
->GetRelativePosition(CurrentPosition(), -1);
2393 MoveImeCarets(- CurrentPosition() + oneCharBefore
);
2396 view
.imeCaretBlockOverride
= true;
2399 EnsureCaretVisible();
2400 SetCandidateWindowPos();
2401 ShowCaretAtCurrentPosition();
2403 errorStatus
= SC_STATUS_FAILURE
;
2407 void ScintillaGTK::PreeditChangedWindowedThis() {
2409 PreEditString
pes(im_context
);
2410 if (strlen(pes
.str
) > 0) {
2411 PangoLayout
*layout
= gtk_widget_create_pango_layout(PWidget(wText
), pes
.str
);
2412 pango_layout_set_attributes(layout
, pes
.attrs
);
2415 pango_layout_get_pixel_size(layout
, &w
, &h
);
2416 g_object_unref(layout
);
2419 gdk_window_get_origin(PWindow(wText
), &x
, &y
);
2421 Point pt
= PointMainCaret();
2427 gtk_window_move(GTK_WINDOW(PWidget(wPreedit
)), x
+ pt
.x
, y
+ pt
.y
);
2428 gtk_window_resize(GTK_WINDOW(PWidget(wPreedit
)), w
, h
);
2429 gtk_widget_show(PWidget(wPreedit
));
2430 gtk_widget_queue_draw_area(PWidget(wPreeditDraw
), 0, 0, w
, h
);
2432 gtk_widget_hide(PWidget(wPreedit
));
2435 errorStatus
= SC_STATUS_FAILURE
;
2439 void ScintillaGTK::PreeditChanged(GtkIMContext
*, ScintillaGTK
*sciThis
) {
2440 if ((sciThis
->imeInteraction
== imeInline
) || (sciThis
->KoreanIME())) {
2441 sciThis
->PreeditChangedInlineThis();
2443 sciThis
->PreeditChangedWindowedThis();
2447 void ScintillaGTK::StyleSetText(GtkWidget
*widget
, GtkStyle
*, void*) {
2448 RealizeText(widget
, NULL
);
2451 void ScintillaGTK::RealizeText(GtkWidget
*widget
, void*) {
2452 // Set NULL background to avoid automatic clearing so Scintilla responsible for all drawing
2453 if (WindowFromWidget(widget
)) {
2454 #if GTK_CHECK_VERSION(3,22,0)
2455 // Appears unnecessary
2456 #elif GTK_CHECK_VERSION(3,0,0)
2457 gdk_window_set_background_pattern(WindowFromWidget(widget
), NULL
);
2459 gdk_window_set_back_pixmap(WindowFromWidget(widget
), NULL
, FALSE
);
2464 static GObjectClass
*scintilla_class_parent_class
;
2466 void ScintillaGTK::Dispose(GObject
*object
) {
2468 ScintillaObject
*scio
= reinterpret_cast<ScintillaObject
*>(object
);
2469 ScintillaGTK
*sciThis
= reinterpret_cast<ScintillaGTK
*>(scio
->pscin
);
2471 if (PWidget(sciThis
->scrollbarv
)) {
2472 gtk_widget_unparent(PWidget(sciThis
->scrollbarv
));
2473 sciThis
->scrollbarv
= NULL
;
2476 if (PWidget(sciThis
->scrollbarh
)) {
2477 gtk_widget_unparent(PWidget(sciThis
->scrollbarh
));
2478 sciThis
->scrollbarh
= NULL
;
2481 scintilla_class_parent_class
->dispose(object
);
2483 // Its dying so nowhere to save the status
2487 void ScintillaGTK::Destroy(GObject
*object
) {
2489 ScintillaObject
*scio
= SCINTILLA(object
);
2491 // This avoids a double destruction
2494 ScintillaGTK
*sciThis
= static_cast<ScintillaGTK
*>(scio
->pscin
);
2495 //Platform::DebugPrintf("Destroying %x %x\n", sciThis, object);
2496 sciThis
->Finalise();
2500 scintilla_class_parent_class
->finalize(object
);
2502 // Its dead so nowhere to save the status
2506 #if GTK_CHECK_VERSION(3,0,0)
2508 gboolean
ScintillaGTK::DrawTextThis(cairo_t
*cr
) {
2510 paintState
= painting
;
2511 repaintFullWindow
= false;
2513 rcPaint
= GetClientRectangle();
2515 PLATFORM_ASSERT(rgnUpdate
== NULL
);
2516 rgnUpdate
= cairo_copy_clip_rectangle_list(cr
);
2517 if (rgnUpdate
&& rgnUpdate
->status
!= CAIRO_STATUS_SUCCESS
) {
2518 // If not successful then ignore
2519 fprintf(stderr
, "DrawTextThis failed to copy update region %d [%d]\n", rgnUpdate
->status
, rgnUpdate
->num_rectangles
);
2520 cairo_rectangle_list_destroy(rgnUpdate
);
2524 double x1
, y1
, x2
, y2
;
2525 cairo_clip_extents(cr
, &x1
, &y1
, &x2
, &y2
);
2529 rcPaint
.bottom
= y2
;
2530 PRectangle rcClient
= GetClientRectangle();
2531 paintingAllText
= rcPaint
.Contains(rcClient
);
2532 std::unique_ptr
<Surface
> surfaceWindow(Surface::Allocate(SC_TECHNOLOGY_DEFAULT
));
2533 surfaceWindow
->Init(cr
, PWidget(wText
));
2534 Paint(surfaceWindow
.get(), rcPaint
);
2535 surfaceWindow
->Release();
2536 if ((paintState
== paintAbandoned
) || repaintFullWindow
) {
2537 // Painting area was insufficient to cover new styling or brace highlight positions
2540 paintState
= notPainting
;
2541 repaintFullWindow
= false;
2544 cairo_rectangle_list_destroy(rgnUpdate
);
2547 paintState
= notPainting
;
2549 errorStatus
= SC_STATUS_FAILURE
;
2555 gboolean
ScintillaGTK::DrawText(GtkWidget
*, cairo_t
*cr
, ScintillaGTK
*sciThis
) {
2556 return sciThis
->DrawTextThis(cr
);
2559 gboolean
ScintillaGTK::DrawThis(cairo_t
*cr
) {
2561 #ifdef GTK_STYLE_CLASS_SCROLLBARS_JUNCTION /* GTK >= 3.4 */
2562 // if both scrollbars are visible, paint the little square on the bottom right corner
2563 if (verticalScrollBarVisible
&& horizontalScrollBarVisible
&& !Wrapping()) {
2564 GtkStyleContext
*styleContext
= gtk_widget_get_style_context(PWidget(wMain
));
2565 PRectangle rc
= GetClientRectangle();
2567 gtk_style_context_save(styleContext
);
2568 gtk_style_context_add_class(styleContext
, GTK_STYLE_CLASS_SCROLLBARS_JUNCTION
);
2570 gtk_render_background(styleContext
, cr
, rc
.right
, rc
.bottom
,
2571 verticalScrollBarWidth
, horizontalScrollBarHeight
);
2572 gtk_render_frame(styleContext
, cr
, rc
.right
, rc
.bottom
,
2573 verticalScrollBarWidth
, horizontalScrollBarHeight
);
2575 gtk_style_context_restore(styleContext
);
2579 gtk_container_propagate_draw(
2580 GTK_CONTAINER(PWidget(wMain
)), PWidget(scrollbarh
), cr
);
2581 gtk_container_propagate_draw(
2582 GTK_CONTAINER(PWidget(wMain
)), PWidget(scrollbarv
), cr
);
2583 // Starting from the following version, the expose event are not propagated
2584 // for double buffered non native windows, so we need to call it ourselves
2585 // or keep the default handler
2586 #if GTK_CHECK_VERSION(3,0,0)
2587 // we want to forward on any >= 3.9.2 runtime
2588 if (gtk_check_version(3,9,2) == NULL
) {
2589 gtk_container_propagate_draw(
2590 GTK_CONTAINER(PWidget(wMain
)), PWidget(wText
), cr
);
2594 errorStatus
= SC_STATUS_FAILURE
;
2599 gboolean
ScintillaGTK::DrawMain(GtkWidget
*widget
, cairo_t
*cr
) {
2600 ScintillaGTK
*sciThis
= FromWidget(widget
);
2601 return sciThis
->DrawThis(cr
);
2606 gboolean
ScintillaGTK::ExposeTextThis(GtkWidget
* /*widget*/, GdkEventExpose
*ose
) {
2608 paintState
= painting
;
2610 rcPaint
.left
= ose
->area
.x
;
2611 rcPaint
.top
= ose
->area
.y
;
2612 rcPaint
.right
= ose
->area
.x
+ ose
->area
.width
;
2613 rcPaint
.bottom
= ose
->area
.y
+ ose
->area
.height
;
2615 PLATFORM_ASSERT(rgnUpdate
== NULL
);
2616 rgnUpdate
= gdk_region_copy(ose
->region
);
2617 PRectangle rcClient
= GetClientRectangle();
2618 paintingAllText
= rcPaint
.Contains(rcClient
);
2619 std::unique_ptr
<Surface
> surfaceWindow(Surface::Allocate(SC_TECHNOLOGY_DEFAULT
));
2620 cairo_t
*cr
= gdk_cairo_create(PWindow(wText
));
2621 surfaceWindow
->Init(cr
, PWidget(wText
));
2622 Paint(surfaceWindow
.get(), rcPaint
);
2623 surfaceWindow
->Release();
2625 if (paintState
== paintAbandoned
) {
2626 // Painting area was insufficient to cover new styling or brace highlight positions
2629 paintState
= notPainting
;
2632 gdk_region_destroy(rgnUpdate
);
2636 errorStatus
= SC_STATUS_FAILURE
;
2642 gboolean
ScintillaGTK::ExposeText(GtkWidget
*widget
, GdkEventExpose
*ose
, ScintillaGTK
*sciThis
) {
2643 return sciThis
->ExposeTextThis(widget
, ose
);
2646 gboolean
ScintillaGTK::ExposeMain(GtkWidget
*widget
, GdkEventExpose
*ose
) {
2647 ScintillaGTK
*sciThis
= FromWidget(widget
);
2648 //Platform::DebugPrintf("Expose Main %0d,%0d %0d,%0d\n",
2649 //ose->area.x, ose->area.y, ose->area.width, ose->area.height);
2650 return sciThis
->Expose(widget
, ose
);
2653 gboolean
ScintillaGTK::Expose(GtkWidget
*, GdkEventExpose
*ose
) {
2655 //fprintf(stderr, "Expose %0d,%0d %0d,%0d\n",
2656 //ose->area.x, ose->area.y, ose->area.width, ose->area.height);
2658 // The text is painted in ExposeText
2659 gtk_container_propagate_expose(
2660 GTK_CONTAINER(PWidget(wMain
)), PWidget(scrollbarh
), ose
);
2661 gtk_container_propagate_expose(
2662 GTK_CONTAINER(PWidget(wMain
)), PWidget(scrollbarv
), ose
);
2665 errorStatus
= SC_STATUS_FAILURE
;
2672 void ScintillaGTK::ScrollSignal(GtkAdjustment
*adj
, ScintillaGTK
*sciThis
) {
2674 sciThis
->ScrollTo(static_cast<int>(gtk_adjustment_get_value(adj
)), false);
2676 sciThis
->errorStatus
= SC_STATUS_FAILURE
;
2680 void ScintillaGTK::ScrollHSignal(GtkAdjustment
*adj
, ScintillaGTK
*sciThis
) {
2682 sciThis
->HorizontalScrollTo(static_cast<int>(gtk_adjustment_get_value(adj
)));
2684 sciThis
->errorStatus
= SC_STATUS_FAILURE
;
2688 void ScintillaGTK::SelectionReceived(GtkWidget
*widget
,
2689 GtkSelectionData
*selection_data
, guint
) {
2690 ScintillaGTK
*sciThis
= FromWidget(widget
);
2691 //Platform::DebugPrintf("Selection received\n");
2692 sciThis
->ReceivedSelection(selection_data
);
2695 void ScintillaGTK::SelectionGet(GtkWidget
*widget
,
2696 GtkSelectionData
*selection_data
, guint info
, guint
) {
2697 ScintillaGTK
*sciThis
= FromWidget(widget
);
2699 //Platform::DebugPrintf("Selection get\n");
2700 if (SelectionOfGSD(selection_data
) == GDK_SELECTION_PRIMARY
) {
2701 if (sciThis
->primary
.Empty()) {
2702 sciThis
->CopySelectionRange(&sciThis
->primary
);
2704 sciThis
->GetSelection(selection_data
, info
, &sciThis
->primary
);
2707 sciThis
->errorStatus
= SC_STATUS_FAILURE
;
2711 gint
ScintillaGTK::SelectionClear(GtkWidget
*widget
, GdkEventSelection
*selection_event
) {
2712 ScintillaGTK
*sciThis
= FromWidget(widget
);
2713 //Platform::DebugPrintf("Selection clear\n");
2714 sciThis
->UnclaimSelection(selection_event
);
2715 if (GTK_WIDGET_CLASS(sciThis
->parentClass
)->selection_clear_event
) {
2716 return GTK_WIDGET_CLASS(sciThis
->parentClass
)->selection_clear_event(widget
, selection_event
);
2721 gboolean
ScintillaGTK::DragMotionThis(GdkDragContext
*context
,
2722 gint x
, gint y
, guint dragtime
) {
2725 SetDragPosition(SPositionFromLocation(npt
, false, false, UserVirtualSpace()));
2726 #if GTK_CHECK_VERSION(2,22,0)
2727 GdkDragAction preferredAction
= gdk_drag_context_get_suggested_action(context
);
2728 GdkDragAction actions
= gdk_drag_context_get_actions(context
);
2730 GdkDragAction preferredAction
= context
->suggested_action
;
2731 GdkDragAction actions
= context
->actions
;
2733 SelectionPosition pos
= SPositionFromLocation(npt
);
2734 if ((inDragDrop
== ddDragging
) && (PositionInSelection(pos
.Position()))) {
2735 // Avoid dragging selection onto itself as that produces a move
2736 // with no real effect but which creates undo actions.
2737 preferredAction
= static_cast<GdkDragAction
>(0);
2738 } else if (actions
== static_cast<GdkDragAction
>
2739 (GDK_ACTION_COPY
| GDK_ACTION_MOVE
)) {
2740 preferredAction
= GDK_ACTION_MOVE
;
2742 gdk_drag_status(context
, preferredAction
, dragtime
);
2744 errorStatus
= SC_STATUS_FAILURE
;
2749 gboolean
ScintillaGTK::DragMotion(GtkWidget
*widget
, GdkDragContext
*context
,
2750 gint x
, gint y
, guint dragtime
) {
2751 ScintillaGTK
*sciThis
= FromWidget(widget
);
2752 return sciThis
->DragMotionThis(context
, x
, y
, dragtime
);
2755 void ScintillaGTK::DragLeave(GtkWidget
*widget
, GdkDragContext
* /*context*/, guint
) {
2756 ScintillaGTK
*sciThis
= FromWidget(widget
);
2758 sciThis
->SetDragPosition(SelectionPosition(Sci::invalidPosition
));
2759 //Platform::DebugPrintf("DragLeave %x\n", sciThis);
2761 sciThis
->errorStatus
= SC_STATUS_FAILURE
;
2765 void ScintillaGTK::DragEnd(GtkWidget
*widget
, GdkDragContext
* /*context*/) {
2766 ScintillaGTK
*sciThis
= FromWidget(widget
);
2768 // If drag did not result in drop here or elsewhere
2769 if (!sciThis
->dragWasDropped
)
2770 sciThis
->SetEmptySelection(sciThis
->posDrag
);
2771 sciThis
->SetDragPosition(SelectionPosition(Sci::invalidPosition
));
2772 //Platform::DebugPrintf("DragEnd %x %d\n", sciThis, sciThis->dragWasDropped);
2773 sciThis
->inDragDrop
= ddNone
;
2775 sciThis
->errorStatus
= SC_STATUS_FAILURE
;
2779 gboolean
ScintillaGTK::Drop(GtkWidget
*widget
, GdkDragContext
* /*context*/,
2780 gint
, gint
, guint
) {
2781 ScintillaGTK
*sciThis
= FromWidget(widget
);
2783 //Platform::DebugPrintf("Drop %x\n", sciThis);
2784 sciThis
->SetDragPosition(SelectionPosition(Sci::invalidPosition
));
2786 sciThis
->errorStatus
= SC_STATUS_FAILURE
;
2791 void ScintillaGTK::DragDataReceived(GtkWidget
*widget
, GdkDragContext
* /*context*/,
2792 gint
, gint
, GtkSelectionData
*selection_data
, guint
/*info*/, guint
) {
2793 ScintillaGTK
*sciThis
= FromWidget(widget
);
2795 sciThis
->ReceivedDrop(selection_data
);
2796 sciThis
->SetDragPosition(SelectionPosition(Sci::invalidPosition
));
2798 sciThis
->errorStatus
= SC_STATUS_FAILURE
;
2802 void ScintillaGTK::DragDataGet(GtkWidget
*widget
, GdkDragContext
*context
,
2803 GtkSelectionData
*selection_data
, guint info
, guint
) {
2804 ScintillaGTK
*sciThis
= FromWidget(widget
);
2806 sciThis
->dragWasDropped
= true;
2807 if (!sciThis
->sel
.Empty()) {
2808 sciThis
->GetSelection(selection_data
, info
, &sciThis
->drag
);
2810 #if GTK_CHECK_VERSION(2,22,0)
2811 GdkDragAction action
= gdk_drag_context_get_selected_action(context
);
2813 GdkDragAction action
= context
->action
;
2815 if (action
== GDK_ACTION_MOVE
) {
2816 for (size_t r
=0; r
<sciThis
->sel
.Count(); r
++) {
2817 if (sciThis
->posDrop
>= sciThis
->sel
.Range(r
).Start()) {
2818 if (sciThis
->posDrop
> sciThis
->sel
.Range(r
).End()) {
2819 sciThis
->posDrop
.Add(-sciThis
->sel
.Range(r
).Length());
2821 sciThis
->posDrop
.Add(-SelectionRange(sciThis
->posDrop
, sciThis
->sel
.Range(r
).Start()).Length());
2825 sciThis
->ClearSelection();
2827 sciThis
->SetDragPosition(SelectionPosition(Sci::invalidPosition
));
2829 sciThis
->errorStatus
= SC_STATUS_FAILURE
;
2833 int ScintillaGTK::TimeOut(gpointer ptt
) {
2834 TimeThunk
*tt
= static_cast<TimeThunk
*>(ptt
);
2835 tt
->scintilla
->TickFor(tt
->reason
);
2839 gboolean
ScintillaGTK::IdleCallback(gpointer pSci
) {
2840 ScintillaGTK
*sciThis
= static_cast<ScintillaGTK
*>(pSci
);
2841 // Idler will be automatically stopped, if there is nothing
2842 // to do while idle.
2843 bool ret
= sciThis
->Idle();
2845 // FIXME: This will remove the idler from GTK, we don't want to
2846 // remove it as it is removed automatically when this function
2847 // returns false (although, it should be harmless).
2848 sciThis
->SetIdle(false);
2853 gboolean
ScintillaGTK::StyleIdle(gpointer pSci
) {
2854 ScintillaGTK
*sciThis
= static_cast<ScintillaGTK
*>(pSci
);
2855 sciThis
->IdleWork();
2856 // Idler will be automatically stopped
2860 void ScintillaGTK::IdleWork() {
2865 void ScintillaGTK::QueueIdleWork(WorkNeeded::workItems items
, Sci::Position upTo
) {
2866 Editor::QueueIdleWork(items
, upTo
);
2868 // Only allow one style needed to be queued
2869 styleIdleID
= gdk_threads_add_idle_full(G_PRIORITY_HIGH_IDLE
, StyleIdle
, this, NULL
);
2873 void ScintillaGTK::SetDocPointer(Document
*document
) {
2874 Document
*oldDoc
= 0;
2875 ScintillaGTKAccessible
*sciAccessible
= 0;
2877 sciAccessible
= ScintillaGTKAccessible::FromAccessible(accessible
);
2878 if (sciAccessible
&& pdoc
) {
2884 Editor::SetDocPointer(document
);
2886 if (sciAccessible
) {
2887 // the accessible needs have the old Document, but also the new one active
2888 sciAccessible
->ChangeDocument(oldDoc
, pdoc
);
2895 void ScintillaGTK::PopUpCB(GtkMenuItem
*menuItem
, ScintillaGTK
*sciThis
) {
2896 guint action
= GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(menuItem
), "CmdNum"));
2898 sciThis
->Command(action
);
2902 gboolean
ScintillaGTK::PressCT(GtkWidget
*widget
, GdkEventButton
*event
, ScintillaGTK
*sciThis
) {
2904 if (event
->window
!= WindowFromWidget(widget
))
2906 if (event
->type
!= GDK_BUTTON_PRESS
)
2909 pt
.x
= int(event
->x
);
2910 pt
.y
= int(event
->y
);
2911 sciThis
->ct
.MouseClick(pt
);
2912 sciThis
->CallTipClick();
2918 #if GTK_CHECK_VERSION(3,0,0)
2920 gboolean
ScintillaGTK::DrawCT(GtkWidget
*widget
, cairo_t
*cr
, CallTip
*ctip
) {
2922 std::unique_ptr
<Surface
> surfaceWindow(Surface::Allocate(SC_TECHNOLOGY_DEFAULT
));
2923 surfaceWindow
->Init(cr
, widget
);
2924 surfaceWindow
->SetUnicodeMode(SC_CP_UTF8
== ctip
->codePage
);
2925 surfaceWindow
->SetDBCSMode(ctip
->codePage
);
2926 ctip
->PaintCT(surfaceWindow
.get());
2927 surfaceWindow
->Release();
2929 // No pointer back to Scintilla to save status
2936 gboolean
ScintillaGTK::ExposeCT(GtkWidget
*widget
, GdkEventExpose
* /*ose*/, CallTip
*ctip
) {
2938 std::unique_ptr
<Surface
> surfaceWindow(Surface::Allocate(SC_TECHNOLOGY_DEFAULT
));
2939 cairo_t
*cr
= gdk_cairo_create(WindowFromWidget(widget
));
2940 surfaceWindow
->Init(cr
, widget
);
2941 surfaceWindow
->SetUnicodeMode(SC_CP_UTF8
== ctip
->codePage
);
2942 surfaceWindow
->SetDBCSMode(ctip
->codePage
);
2943 ctip
->PaintCT(surfaceWindow
.get());
2944 surfaceWindow
->Release();
2947 // No pointer back to Scintilla to save status
2954 AtkObject
* ScintillaGTK::GetAccessibleThis(GtkWidget
*widget
) {
2955 return ScintillaGTKAccessible::WidgetGetAccessibleImpl(widget
, &accessible
, scintilla_class_parent_class
);
2958 AtkObject
* ScintillaGTK::GetAccessible(GtkWidget
*widget
) {
2959 return FromWidget(widget
)->GetAccessibleThis(widget
);
2962 sptr_t
ScintillaGTK::DirectFunction(
2963 sptr_t ptr
, unsigned int iMessage
, uptr_t wParam
, sptr_t lParam
) {
2964 return reinterpret_cast<ScintillaGTK
*>(ptr
)->WndProc(iMessage
, wParam
, lParam
);
2967 /* legacy name for scintilla_object_send_message */
2969 sptr_t
scintilla_send_message(ScintillaObject
*sci
, unsigned int iMessage
, uptr_t wParam
, sptr_t lParam
) {
2970 ScintillaGTK
*psci
= static_cast<ScintillaGTK
*>(sci
->pscin
);
2971 return psci
->WndProc(iMessage
, wParam
, lParam
);
2975 gintptr
scintilla_object_send_message(ScintillaObject
*sci
, unsigned int iMessage
, uptr_t wParam
, sptr_t lParam
) {
2976 return scintilla_send_message(sci
, iMessage
, wParam
, lParam
);
2979 static void scintilla_class_init(ScintillaClass
*klass
);
2980 static void scintilla_init(ScintillaObject
*sci
);
2982 extern void Platform_Initialise();
2983 extern void Platform_Finalise();
2985 /* legacy name for scintilla_object_get_type */
2987 GType
scintilla_get_type() {
2988 static GType scintilla_type
= 0;
2991 if (!scintilla_type
) {
2992 scintilla_type
= g_type_from_name("ScintillaObject");
2993 if (!scintilla_type
) {
2994 static GTypeInfo scintilla_info
= {
2995 (guint16
) sizeof (ScintillaObjectClass
),
2996 NULL
, //(GBaseInitFunc)
2997 NULL
, //(GBaseFinalizeFunc)
2998 (GClassInitFunc
) scintilla_class_init
,
2999 NULL
, //(GClassFinalizeFunc)
3000 NULL
, //gconstpointer data
3001 (guint16
) sizeof (ScintillaObject
),
3003 (GInstanceInitFunc
) scintilla_init
,
3004 NULL
//(GTypeValueTable*)
3006 scintilla_type
= g_type_register_static(
3007 GTK_TYPE_CONTAINER
, "ScintillaObject", &scintilla_info
, (GTypeFlags
) 0);
3013 return scintilla_type
;
3017 GType
scintilla_object_get_type() {
3018 return scintilla_get_type();
3021 void ScintillaGTK::ClassInit(OBJECT_CLASS
* object_class
, GtkWidgetClass
*widget_class
, GtkContainerClass
*container_class
) {
3022 Platform_Initialise();
3024 Scintilla_LinkLexers();
3026 atomClipboard
= gdk_atom_intern("CLIPBOARD", FALSE
);
3027 atomUTF8
= gdk_atom_intern("UTF8_STRING", FALSE
);
3028 atomString
= GDK_SELECTION_TYPE_STRING
;
3029 atomUriList
= gdk_atom_intern("text/uri-list", FALSE
);
3030 atomDROPFILES_DND
= gdk_atom_intern("DROPFILES_DND", FALSE
);
3032 // Define default signal handlers for the class: Could move more
3033 // of the signal handlers here (those that currently attached to wDraw
3034 // in Init() may require coordinate translation?)
3036 object_class
->dispose
= Dispose
;
3037 object_class
->finalize
= Destroy
;
3038 #if GTK_CHECK_VERSION(3,0,0)
3039 widget_class
->get_preferred_width
= GetPreferredWidth
;
3040 widget_class
->get_preferred_height
= GetPreferredHeight
;
3042 widget_class
->size_request
= SizeRequest
;
3044 widget_class
->size_allocate
= SizeAllocate
;
3045 #if GTK_CHECK_VERSION(3,0,0)
3046 widget_class
->draw
= DrawMain
;
3048 widget_class
->expose_event
= ExposeMain
;
3050 widget_class
->motion_notify_event
= Motion
;
3051 widget_class
->button_press_event
= Press
;
3052 widget_class
->button_release_event
= MouseRelease
;
3053 widget_class
->scroll_event
= ScrollEvent
;
3054 widget_class
->key_press_event
= KeyPress
;
3055 widget_class
->key_release_event
= KeyRelease
;
3056 widget_class
->focus_in_event
= FocusIn
;
3057 widget_class
->focus_out_event
= FocusOut
;
3058 widget_class
->selection_received
= SelectionReceived
;
3059 widget_class
->selection_get
= SelectionGet
;
3060 widget_class
->selection_clear_event
= SelectionClear
;
3062 widget_class
->drag_data_received
= DragDataReceived
;
3063 widget_class
->drag_motion
= DragMotion
;
3064 widget_class
->drag_leave
= DragLeave
;
3065 widget_class
->drag_end
= DragEnd
;
3066 widget_class
->drag_drop
= Drop
;
3067 widget_class
->drag_data_get
= DragDataGet
;
3069 widget_class
->realize
= Realize
;
3070 widget_class
->unrealize
= UnRealize
;
3071 widget_class
->map
= Map
;
3072 widget_class
->unmap
= UnMap
;
3074 widget_class
->get_accessible
= GetAccessible
;
3076 container_class
->forall
= MainForAll
;
3079 static void scintilla_class_init(ScintillaClass
*klass
) {
3081 OBJECT_CLASS
*object_class
= (OBJECT_CLASS
*) klass
;
3082 GtkWidgetClass
*widget_class
= (GtkWidgetClass
*) klass
;
3083 GtkContainerClass
*container_class
= (GtkContainerClass
*) klass
;
3085 GSignalFlags sigflags
= GSignalFlags(G_SIGNAL_ACTION
| G_SIGNAL_RUN_LAST
);
3086 scintilla_signals
[COMMAND_SIGNAL
] = g_signal_new(
3088 G_TYPE_FROM_CLASS(object_class
),
3090 G_STRUCT_OFFSET(ScintillaClass
, command
),
3091 NULL
, //(GSignalAccumulator)
3093 scintilla_marshal_VOID__INT_OBJECT
,
3095 2, G_TYPE_INT
, GTK_TYPE_WIDGET
);
3097 scintilla_signals
[NOTIFY_SIGNAL
] = g_signal_new(
3099 G_TYPE_FROM_CLASS(object_class
),
3101 G_STRUCT_OFFSET(ScintillaClass
, notify
),
3102 NULL
, //(GSignalAccumulator)
3104 scintilla_marshal_VOID__INT_BOXED
,
3106 2, G_TYPE_INT
, SCINTILLA_TYPE_NOTIFICATION
);
3108 klass
->command
= NULL
;
3109 klass
->notify
= NULL
;
3110 scintilla_class_parent_class
= G_OBJECT_CLASS(g_type_class_peek_parent(klass
));
3111 ScintillaGTK::ClassInit(object_class
, widget_class
, container_class
);
3116 static void scintilla_init(ScintillaObject
*sci
) {
3118 gtk_widget_set_can_focus(GTK_WIDGET(sci
), TRUE
);
3119 sci
->pscin
= new ScintillaGTK(sci
);
3124 /* legacy name for scintilla_object_new */
3126 GtkWidget
* scintilla_new() {
3127 GtkWidget
*widget
= GTK_WIDGET(g_object_new(scintilla_get_type(), NULL
));
3128 gtk_widget_set_direction(widget
, GTK_TEXT_DIR_LTR
);
3134 GtkWidget
*scintilla_object_new() {
3135 return scintilla_new();
3138 void scintilla_set_id(ScintillaObject
*sci
, uptr_t id
) {
3139 ScintillaGTK
*psci
= static_cast<ScintillaGTK
*>(sci
->pscin
);
3143 void scintilla_release_resources(void) {
3145 Platform_Finalise();
3150 /* Define a dummy boxed type because g-ir-scanner is unable to
3151 * recognize gpointer-derived types. Note that SCNotificaiton
3152 * is always allocated on stack so copying is not appropriate. */
3153 static void *copy_(void *src
) { return src
; }
3154 static void free_(void *) { }
3157 GType
scnotification_get_type(void) {
3158 static gsize type_id
= 0;
3159 if (g_once_init_enter(&type_id
)) {
3160 gsize id
= (gsize
) g_boxed_type_register_static(
3161 g_intern_static_string("SCNotification"),
3162 (GBoxedCopyFunc
) copy_
,
3163 (GBoxedFreeFunc
) free_
);
3164 g_once_init_leave(&type_id
, id
);
3166 return (GType
) type_id
;