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.
18 #include <string_view>
30 #include <gdk/gdkkeysyms.h>
31 #if defined(GDK_WINDOWING_WAYLAND)
32 #include <gdk/gdkwayland.h>
36 // On Win32 use windows.h to access clipboard (rectangular format) and systems parameters
42 #include "ScintillaTypes.h"
43 #include "ScintillaMessages.h"
44 #include "ScintillaStructures.h"
48 #include "Debugging.h"
52 #include "Scintilla.h"
53 #include "ScintillaWidget.h"
54 #include "CharacterCategoryMap.h"
56 #include "UniqueString.h"
57 #include "SplitVector.h"
58 #include "Partitioning.h"
59 #include "RunStyles.h"
60 #include "ContractionState.h"
61 #include "CellBuffer.h"
64 #include "Indicator.h"
65 #include "LineMarker.h"
67 #include "ViewStyle.h"
68 #include "CharClassify.h"
69 #include "Decoration.h"
70 #include "CaseFolder.h"
72 #include "CaseConvert.h"
73 #include "UniConversion.h"
74 #include "Selection.h"
75 #include "PositionCache.h"
76 #include "EditModel.h"
77 #include "MarginView.h"
80 #include "AutoComplete.h"
81 #include "ScintillaBase.h"
84 #include "ScintillaGTK.h"
85 #include "scintilla-marshal.h"
86 #include "ScintillaGTKAccessible.h"
87 #include "Converter.h"
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 SC_INDICATOR_INPUT INDICATOR_IME
93 #define SC_INDICATOR_TARGET INDICATOR_IME+1
94 #define SC_INDICATOR_CONVERTED INDICATOR_IME+2
95 #define SC_INDICATOR_UNKNOWN INDICATOR_IME_MAX
97 using namespace Scintilla
;
98 using namespace Scintilla::Internal
;
101 extern std::string
UTF8FromLatin1(std::string_view text
);
102 extern void Platform_Initialise();
103 extern void Platform_Finalise();
113 gint scintilla_signals
[LAST_SIGNAL
] = { 0 };
118 TARGET_COMPOUND_TEXT
,
123 const GtkTargetEntry clipboardCopyTargets
[] = {
124 { (gchar
*) "UTF8_STRING", 0, TARGET_UTF8_STRING
},
125 { (gchar
*) "STRING", 0, TARGET_STRING
},
127 constexpr gint nClipboardCopyTargets
= static_cast<gint
>(std::size(clipboardCopyTargets
));
129 const GtkTargetEntry clipboardPasteTargets
[] = {
130 { (gchar
*) "text/uri-list", 0, TARGET_URI
},
131 { (gchar
*) "UTF8_STRING", 0, TARGET_UTF8_STRING
},
132 { (gchar
*) "STRING", 0, TARGET_STRING
},
134 constexpr gint nClipboardPasteTargets
= static_cast<gint
>(std::size(clipboardPasteTargets
));
136 const GdkDragAction actionCopyOrMove
= static_cast<GdkDragAction
>(GDK_ACTION_COPY
| GDK_ACTION_MOVE
);
138 GtkWidget
*PWidget(const Window
&w
) noexcept
{
139 return static_cast<GtkWidget
*>(w
.GetID());
142 GdkWindow
*PWindow(const Window
&w
) noexcept
{
143 GtkWidget
*widget
= static_cast<GtkWidget
*>(w
.GetID());
144 return gtk_widget_get_window(widget
);
147 void MapWidget(GtkWidget
*widget
) noexcept
{
149 gtk_widget_get_visible(GTK_WIDGET(widget
)) &&
150 !IS_WIDGET_MAPPED(widget
)) {
151 gtk_widget_map(widget
);
155 const guchar
*DataOfGSD(GtkSelectionData
*sd
) noexcept
{
156 return gtk_selection_data_get_data(sd
);
159 gint
LengthOfGSD(GtkSelectionData
*sd
) noexcept
{
160 return gtk_selection_data_get_length(sd
);
163 GdkAtom
TypeOfGSD(GtkSelectionData
*sd
) noexcept
{
164 return gtk_selection_data_get_data_type(sd
);
167 GdkAtom
SelectionOfGSD(GtkSelectionData
*sd
) noexcept
{
168 return gtk_selection_data_get_selection(sd
);
171 bool SettingGet(GtkSettings
*settings
, const gchar
*name
, gpointer value
) noexcept
{
175 if (!g_object_class_find_property(G_OBJECT_GET_CLASS(
176 G_OBJECT(settings
)), name
)) {
179 g_object_get(G_OBJECT(settings
), name
, value
, nullptr);
185 FontOptions::FontOptions(GtkWidget
*widget
) noexcept
{
186 UniquePangoContext
pcontext(gtk_widget_create_pango_context(widget
));
187 PLATFORM_ASSERT(pcontext
);
188 const cairo_font_options_t
*options
= pango_cairo_context_get_font_options(pcontext
.get());
189 // options is owned by the PangoContext so must not be freed.
191 // options is NULL on Win32
192 antialias
= cairo_font_options_get_antialias(options
);
193 order
= cairo_font_options_get_subpixel_order(options
);
194 hint
= cairo_font_options_get_hint_style(options
);
198 bool FontOptions::operator==(const FontOptions
&other
) const noexcept
{
199 return antialias
== other
.antialias
&&
200 order
== other
.order
&&
204 ScintillaGTK
*ScintillaGTK::FromWidget(GtkWidget
*widget
) noexcept
{
205 ScintillaObject
*scio
= SCINTILLA(widget
);
206 return static_cast<ScintillaGTK
*>(scio
->pscin
);
209 ScintillaGTK::ScintillaGTK(_ScintillaObject
*sci_
) :
210 adjustmentv(nullptr), adjustmenth(nullptr),
211 verticalScrollBarWidth(30), horizontalScrollBarHeight(30),
213 capturedMouse(false), dragWasDropped(false),
214 lastKey(0), rectangularSelectionModifier(SCMOD_CTRL
),
215 parentClass(nullptr),
217 preeditInitialized(false),
219 lastNonCommonScript(G_UNICODE_SCRIPT_INVALID_CODE
),
221 settingsHandlerId(0),
222 lastWheelMouseTime(0),
223 lastWheelMouseDirection(0),
224 wheelMouseIntensity(0),
228 repaintFullWindow(false),
230 accessibilityEnabled(SC_ACCESSIBILITY_ENABLED
),
231 accessible(nullptr) {
233 wMain
= GTK_WIDGET(sci
);
235 rectangularSelectionModifier
= SCMOD_ALT
;
238 // There does not seem to be a real standard for indicating that the clipboard
239 // contains a rectangular selection, so copy Developer Studio.
240 cfColumnSelect
= static_cast<CLIPFORMAT
>(
241 ::RegisterClipboardFormatW(L
"MSDEVColumnSelect"));
243 // Get intellimouse parameters when running on win32; otherwise use
244 // reasonable default
245 #ifndef SPI_GETWHEELSCROLLLINES
246 #define SPI_GETWHEELSCROLLLINES 104
248 ::SystemParametersInfo(SPI_GETWHEELSCROLLLINES
, 0, &linesPerScroll
, 0);
252 primarySelection
= false;
257 ScintillaGTK::~ScintillaGTK() {
259 g_source_remove(styleIdleID
);
262 if (scrollBarIdleID
) {
263 g_source_remove(scrollBarIdleID
);
266 ClearPrimarySelection();
268 if (settingsHandlerId
) {
269 g_signal_handler_disconnect(settings
, settingsHandlerId
);
273 void ScintillaGTK::RealizeThis(GtkWidget
*widget
) {
274 //Platform::DebugPrintf("ScintillaGTK::realize this\n");
275 gtk_widget_set_realized(widget
, TRUE
);
276 GdkWindowAttr attrs
{};
277 attrs
.window_type
= GDK_WINDOW_CHILD
;
278 GtkAllocation allocation
;
279 gtk_widget_get_allocation(widget
, &allocation
);
280 attrs
.x
= allocation
.x
;
281 attrs
.y
= allocation
.y
;
282 attrs
.width
= allocation
.width
;
283 attrs
.height
= allocation
.height
;
284 attrs
.wclass
= GDK_INPUT_OUTPUT
;
285 attrs
.visual
= gtk_widget_get_visual(widget
);
286 #if !GTK_CHECK_VERSION(3,0,0)
287 attrs
.colormap
= gtk_widget_get_colormap(widget
);
289 attrs
.event_mask
= gtk_widget_get_events(widget
) | GDK_EXPOSURE_MASK
;
290 GdkDisplay
*pdisplay
= gtk_widget_get_display(widget
);
291 GdkCursor
*cursor
= gdk_cursor_new_for_display(pdisplay
, GDK_XTERM
);
292 attrs
.cursor
= cursor
;
293 #if GTK_CHECK_VERSION(3,0,0)
294 gtk_widget_set_window(widget
, gdk_window_new(gtk_widget_get_parent_window(widget
), &attrs
,
295 GDK_WA_X
| GDK_WA_Y
| GDK_WA_VISUAL
| GDK_WA_CURSOR
));
296 #if GTK_CHECK_VERSION(3,8,0)
297 gtk_widget_register_window(widget
, gtk_widget_get_window(widget
));
299 gdk_window_set_user_data(gtk_widget_get_window(widget
), widget
);
301 #if !GTK_CHECK_VERSION(3,18,0)
302 gtk_style_context_set_background(gtk_widget_get_style_context(widget
),
303 gtk_widget_get_window(widget
));
305 gdk_window_show(gtk_widget_get_window(widget
));
307 widget
->window
= gdk_window_new(gtk_widget_get_parent_window(widget
), &attrs
,
308 GDK_WA_X
| GDK_WA_Y
| GDK_WA_VISUAL
| GDK_WA_COLORMAP
| GDK_WA_CURSOR
);
309 gdk_window_set_user_data(widget
->window
, widget
);
310 widget
->style
= gtk_style_attach(widget
->style
, widget
->window
);
311 gdk_window_set_background(widget
->window
, &widget
->style
->bg
[GTK_STATE_NORMAL
]);
312 gdk_window_show(widget
->window
);
316 preeditInitialized
= false;
317 gtk_widget_realize(PWidget(wPreedit
));
318 gtk_widget_realize(PWidget(wPreeditDraw
));
320 im_context
.reset(gtk_im_multicontext_new());
321 g_signal_connect(G_OBJECT(im_context
.get()), "commit",
322 G_CALLBACK(Commit
), this);
323 g_signal_connect(G_OBJECT(im_context
.get()), "preedit_changed",
324 G_CALLBACK(PreeditChanged
), this);
325 g_signal_connect(G_OBJECT(im_context
.get()), "retrieve-surrounding",
326 G_CALLBACK(RetrieveSurrounding
), this);
327 g_signal_connect(G_OBJECT(im_context
.get()), "delete-surrounding",
328 G_CALLBACK(DeleteSurrounding
), this);
329 gtk_im_context_set_client_window(im_context
.get(), WindowFromWidget(widget
));
331 GtkWidget
*widtxt
= PWidget(wText
); // // No code inside the G_OBJECT macro
332 g_signal_connect_after(G_OBJECT(widtxt
), "style_set",
333 G_CALLBACK(ScintillaGTK::StyleSetText
), nullptr);
334 g_signal_connect_after(G_OBJECT(widtxt
), "realize",
335 G_CALLBACK(ScintillaGTK::RealizeText
), nullptr);
336 gtk_widget_realize(widtxt
);
337 gtk_widget_realize(PWidget(scrollbarv
));
338 gtk_widget_realize(PWidget(scrollbarh
));
340 cursor
= gdk_cursor_new_for_display(pdisplay
, GDK_XTERM
);
341 gdk_window_set_cursor(PWindow(wText
), cursor
);
344 cursor
= gdk_cursor_new_for_display(pdisplay
, GDK_LEFT_PTR
);
345 gdk_window_set_cursor(PWindow(scrollbarv
), cursor
);
348 cursor
= gdk_cursor_new_for_display(pdisplay
, GDK_LEFT_PTR
);
349 gdk_window_set_cursor(PWindow(scrollbarh
), cursor
);
352 using NotifyLambda
= void (*)(GObject
*, GParamSpec
*, ScintillaGTK
*);
354 settingsHandlerId
= g_signal_connect(settings
, "notify::gtk-xft-dpi",
355 G_CALLBACK(static_cast<NotifyLambda
>([](GObject
*, GParamSpec
*, ScintillaGTK
*sciThis
) {
356 sciThis
->InvalidateStyleRedraw();
362 void ScintillaGTK::Realize(GtkWidget
*widget
) {
363 ScintillaGTK
*sciThis
= FromWidget(widget
);
364 sciThis
->RealizeThis(widget
);
367 void ScintillaGTK::UnRealizeThis(GtkWidget
*widget
) {
369 if (IS_WIDGET_MAPPED(widget
)) {
370 gtk_widget_unmap(widget
);
372 gtk_widget_set_realized(widget
, FALSE
);
373 gtk_widget_unrealize(PWidget(wText
));
374 if (PWidget(scrollbarv
))
375 gtk_widget_unrealize(PWidget(scrollbarv
));
376 if (PWidget(scrollbarh
))
377 gtk_widget_unrealize(PWidget(scrollbarh
));
378 gtk_widget_unrealize(PWidget(wPreedit
));
379 gtk_widget_unrealize(PWidget(wPreeditDraw
));
381 if (GTK_WIDGET_CLASS(parentClass
)->unrealize
)
382 GTK_WIDGET_CLASS(parentClass
)->unrealize(widget
);
386 errorStatus
= Status::Failure
;
390 void ScintillaGTK::UnRealize(GtkWidget
*widget
) {
391 ScintillaGTK
*sciThis
= FromWidget(widget
);
392 sciThis
->UnRealizeThis(widget
);
395 void ScintillaGTK::MapThis() {
397 //Platform::DebugPrintf("ScintillaGTK::map this\n");
398 gtk_widget_set_mapped(PWidget(wMain
), TRUE
);
399 MapWidget(PWidget(wText
));
400 MapWidget(PWidget(scrollbarh
));
401 MapWidget(PWidget(scrollbarv
));
402 wMain
.SetCursor(Window::Cursor::arrow
);
403 scrollbarv
.SetCursor(Window::Cursor::arrow
);
404 scrollbarh
.SetCursor(Window::Cursor::arrow
);
405 SetClientRectangle();
407 gdk_window_show(PWindow(wMain
));
409 errorStatus
= Status::Failure
;
413 void ScintillaGTK::Map(GtkWidget
*widget
) {
414 ScintillaGTK
*sciThis
= FromWidget(widget
);
418 void ScintillaGTK::UnMapThis() {
420 //Platform::DebugPrintf("ScintillaGTK::unmap this\n");
421 gtk_widget_set_mapped(PWidget(wMain
), FALSE
);
423 gdk_window_hide(PWindow(wMain
));
424 gtk_widget_unmap(PWidget(wText
));
425 if (PWidget(scrollbarh
))
426 gtk_widget_unmap(PWidget(scrollbarh
));
427 if (PWidget(scrollbarv
))
428 gtk_widget_unmap(PWidget(scrollbarv
));
430 errorStatus
= Status::Failure
;
434 void ScintillaGTK::UnMap(GtkWidget
*widget
) {
435 ScintillaGTK
*sciThis
= FromWidget(widget
);
436 sciThis
->UnMapThis();
439 void ScintillaGTK::ForAll(GtkCallback callback
, gpointer callback_data
) {
441 (*callback
)(PWidget(wText
), callback_data
);
442 if (PWidget(scrollbarv
))
443 (*callback
)(PWidget(scrollbarv
), callback_data
);
444 if (PWidget(scrollbarh
))
445 (*callback
)(PWidget(scrollbarh
), callback_data
);
447 errorStatus
= Status::Failure
;
451 void ScintillaGTK::MainForAll(GtkContainer
*container
, gboolean include_internals
, GtkCallback callback
, gpointer callback_data
) {
452 ScintillaGTK
*sciThis
= FromWidget(GTK_WIDGET(container
));
454 if (callback
&& include_internals
) {
455 sciThis
->ForAll(callback
, callback_data
);
461 class PreEditString
{
465 PangoAttrList
*attrs
;
469 GUnicodeScript pscript
;
471 explicit PreEditString(GtkIMContext
*im_context
) noexcept
{
472 gtk_im_context_get_preedit_string(im_context
, &str
, &attrs
, &cursor_pos
);
473 validUTF8
= g_utf8_validate(str
, strlen(str
), nullptr);
474 uniStr
= g_utf8_to_ucs4_fast(str
, static_cast<glong
>(strlen(str
)), &uniStrLen
);
475 pscript
= g_unichar_get_script(uniStr
[0]);
477 // Deleted so PreEditString objects can not be copied.
478 PreEditString(const PreEditString
&) = delete;
479 PreEditString(PreEditString
&&) = delete;
480 PreEditString
&operator=(const PreEditString
&) = delete;
481 PreEditString
&operator=(PreEditString
&&) = delete;
485 pango_attr_list_unref(attrs
);
491 gint
ScintillaGTK::FocusInThis(GtkWidget
*) {
496 gtk_im_context_focus_in(im_context
.get());
497 PreEditString
pes(im_context
.get());
498 if (PWidget(wPreedit
)) {
499 if (!preeditInitialized
) {
500 GtkWidget
*top
= gtk_widget_get_toplevel(PWidget(wMain
));
501 gtk_window_set_transient_for(GTK_WINDOW(PWidget(wPreedit
)), GTK_WINDOW(top
));
502 preeditInitialized
= true;
505 if (strlen(pes
.str
) > 0) {
506 gtk_widget_show(PWidget(wPreedit
));
508 gtk_widget_hide(PWidget(wPreedit
));
513 errorStatus
= Status::Failure
;
518 gint
ScintillaGTK::FocusIn(GtkWidget
*widget
, GdkEventFocus
* /*event*/) {
519 ScintillaGTK
*sciThis
= FromWidget(widget
);
520 return sciThis
->FocusInThis(widget
);
523 gint
ScintillaGTK::FocusOutThis(GtkWidget
*) {
525 SetFocusState(false);
527 if (PWidget(wPreedit
))
528 gtk_widget_hide(PWidget(wPreedit
));
530 gtk_im_context_focus_out(im_context
.get());
533 errorStatus
= Status::Failure
;
538 gint
ScintillaGTK::FocusOut(GtkWidget
*widget
, GdkEventFocus
* /*event*/) {
539 ScintillaGTK
*sciThis
= FromWidget(widget
);
540 return sciThis
->FocusOutThis(widget
);
543 void ScintillaGTK::SizeRequest(GtkWidget
*widget
, GtkRequisition
*requisition
) {
544 const ScintillaGTK
*sciThis
= FromWidget(widget
);
545 requisition
->width
= 1;
546 requisition
->height
= 1;
547 GtkRequisition child_requisition
;
548 #if GTK_CHECK_VERSION(3,0,0)
549 gtk_widget_get_preferred_size(PWidget(sciThis
->scrollbarh
), nullptr, &child_requisition
);
550 gtk_widget_get_preferred_size(PWidget(sciThis
->scrollbarv
), nullptr, &child_requisition
);
552 gtk_widget_size_request(PWidget(sciThis
->scrollbarh
), &child_requisition
);
553 gtk_widget_size_request(PWidget(sciThis
->scrollbarv
), &child_requisition
);
557 #if GTK_CHECK_VERSION(3,0,0)
559 void ScintillaGTK::GetPreferredWidth(GtkWidget
*widget
, gint
*minimalWidth
, gint
*naturalWidth
) {
560 GtkRequisition requisition
;
561 SizeRequest(widget
, &requisition
);
562 *minimalWidth
= *naturalWidth
= requisition
.width
;
565 void ScintillaGTK::GetPreferredHeight(GtkWidget
*widget
, gint
*minimalHeight
, gint
*naturalHeight
) {
566 GtkRequisition requisition
;
567 SizeRequest(widget
, &requisition
);
568 *minimalHeight
= *naturalHeight
= requisition
.height
;
573 void ScintillaGTK::SizeAllocate(GtkWidget
*widget
, GtkAllocation
*allocation
) {
574 ScintillaGTK
*sciThis
= FromWidget(widget
);
576 gtk_widget_set_allocation(widget
, allocation
);
577 if (IS_WIDGET_REALIZED(widget
))
578 gdk_window_move_resize(WindowFromWidget(widget
),
584 sciThis
->Resize(allocation
->width
, allocation
->height
);
587 sciThis
->errorStatus
= Status::Failure
;
591 void ScintillaGTK::Init() {
592 parentClass
= static_cast<GtkWidgetClass
*>(
593 g_type_class_ref(gtk_container_get_type()));
596 #if defined(GDK_WINDOWING_WAYLAND)
597 GdkDisplay
*pdisplay
= gdk_display_get_default();
598 if (GDK_IS_WAYLAND_DISPLAY(pdisplay
)) {
599 // On Wayland, touch pads only produce smooth scroll events
600 maskSmooth
= GDK_SMOOTH_SCROLL_MASK
;
604 gtk_widget_set_can_focus(PWidget(wMain
), TRUE
);
605 gtk_widget_set_sensitive(PWidget(wMain
), TRUE
);
606 gtk_widget_set_events(PWidget(wMain
),
612 | GDK_KEY_RELEASE_MASK
613 | GDK_FOCUS_CHANGE_MASK
614 | GDK_LEAVE_NOTIFY_MASK
615 | GDK_BUTTON_PRESS_MASK
616 | GDK_BUTTON_RELEASE_MASK
617 | GDK_POINTER_MOTION_MASK
618 | GDK_POINTER_MOTION_HINT_MASK
);
620 wText
= gtk_drawing_area_new();
621 gtk_widget_set_parent(PWidget(wText
), PWidget(wMain
));
622 GtkWidget
*widtxt
= PWidget(wText
); // No code inside the G_OBJECT macro
623 gtk_widget_show(widtxt
);
624 #if GTK_CHECK_VERSION(3,0,0)
625 g_signal_connect(G_OBJECT(widtxt
), "draw",
626 G_CALLBACK(ScintillaGTK::DrawText
), this);
628 g_signal_connect(G_OBJECT(widtxt
), "expose_event",
629 G_CALLBACK(ScintillaGTK::ExposeText
), this);
631 #if GTK_CHECK_VERSION(3,0,0)
632 // we need a runtime check because we don't want double buffering when
633 // running on >= 3.9.2
634 if (gtk_check_version(3, 9, 2) != nullptr /* on < 3.9.2 */)
637 #if !GTK_CHECK_VERSION(3,14,0)
638 // Avoid background drawing flash/missing redraws
639 gtk_widget_set_double_buffered(widtxt
, FALSE
);
642 gtk_widget_set_events(widtxt
, GDK_EXPOSURE_MASK
);
643 gtk_widget_set_size_request(widtxt
, 100, 100);
644 adjustmentv
= GTK_ADJUSTMENT(gtk_adjustment_new(0.0, 0.0, 201.0, 1.0, 20.0, 20.0));
645 #if GTK_CHECK_VERSION(3,0,0)
646 scrollbarv
= gtk_scrollbar_new(GTK_ORIENTATION_VERTICAL
, GTK_ADJUSTMENT(adjustmentv
));
648 scrollbarv
= gtk_vscrollbar_new(GTK_ADJUSTMENT(adjustmentv
));
650 gtk_widget_set_can_focus(PWidget(scrollbarv
), FALSE
);
651 g_signal_connect(G_OBJECT(adjustmentv
), "value_changed",
652 G_CALLBACK(ScrollSignal
), this);
653 gtk_widget_set_parent(PWidget(scrollbarv
), PWidget(wMain
));
654 gtk_widget_show(PWidget(scrollbarv
));
656 adjustmenth
= GTK_ADJUSTMENT(gtk_adjustment_new(0.0, 0.0, 101.0, 1.0, 20.0, 20.0));
657 #if GTK_CHECK_VERSION(3,0,0)
658 scrollbarh
= gtk_scrollbar_new(GTK_ORIENTATION_HORIZONTAL
, GTK_ADJUSTMENT(adjustmenth
));
660 scrollbarh
= gtk_hscrollbar_new(GTK_ADJUSTMENT(adjustmenth
));
662 gtk_widget_set_can_focus(PWidget(scrollbarh
), FALSE
);
663 g_signal_connect(G_OBJECT(adjustmenth
), "value_changed",
664 G_CALLBACK(ScrollHSignal
), this);
665 gtk_widget_set_parent(PWidget(scrollbarh
), PWidget(wMain
));
666 gtk_widget_show(PWidget(scrollbarh
));
668 gtk_widget_grab_focus(PWidget(wMain
));
670 gtk_drag_dest_set(GTK_WIDGET(PWidget(wMain
)),
671 GTK_DEST_DEFAULT_ALL
, clipboardPasteTargets
, nClipboardPasteTargets
,
674 /* create pre-edit window */
675 wPreedit
= gtk_window_new(GTK_WINDOW_POPUP
);
676 gtk_window_set_type_hint(GTK_WINDOW(PWidget(wPreedit
)), GDK_WINDOW_TYPE_HINT_POPUP_MENU
);
677 wPreeditDraw
= gtk_drawing_area_new();
678 GtkWidget
*predrw
= PWidget(wPreeditDraw
); // No code inside the G_OBJECT macro
679 #if GTK_CHECK_VERSION(3,0,0)
680 g_signal_connect(G_OBJECT(predrw
), "draw",
681 G_CALLBACK(DrawPreedit
), this);
683 g_signal_connect(G_OBJECT(predrw
), "expose_event",
684 G_CALLBACK(ExposePreedit
), this);
686 gtk_container_add(GTK_CONTAINER(PWidget(wPreedit
)), predrw
);
687 gtk_widget_show(predrw
);
689 settings
= gtk_settings_get_default();
691 // Set caret period based on GTK settings
692 gboolean blinkOn
= false;
693 SettingGet(settings
, "gtk-cursor-blink", &blinkOn
);
696 if (SettingGet(settings
, "gtk-cursor-blink-time", &value
)) {
697 caret
.period
= static_cast<int>(value
/ 1.75);
703 for (size_t tr
= static_cast<size_t>(TickReason::caret
); tr
<= static_cast<size_t>(TickReason::dwell
); tr
++) {
704 timers
[tr
].reason
= static_cast<TickReason
>(tr
);
705 timers
[tr
].scintilla
= this;
707 vs
.indicators
[SC_INDICATOR_UNKNOWN
] = Indicator(IndicatorStyle::Hidden
, ColourRGBA(0, 0, 0xff));
708 vs
.indicators
[SC_INDICATOR_INPUT
] = Indicator(IndicatorStyle::Dots
, ColourRGBA(0, 0, 0xff));
709 vs
.indicators
[SC_INDICATOR_CONVERTED
] = Indicator(IndicatorStyle::CompositionThick
, ColourRGBA(0, 0, 0xff));
710 vs
.indicators
[SC_INDICATOR_TARGET
] = Indicator(IndicatorStyle::StraightBox
, ColourRGBA(0, 0, 0xff));
712 fontOptionsPrevious
= FontOptions(PWidget(wText
));
715 void ScintillaGTK::Finalise() {
716 for (size_t tr
= static_cast<size_t>(TickReason::caret
); tr
<= static_cast<size_t>(TickReason::dwell
); tr
++) {
717 FineTickerCancel(static_cast<TickReason
>(tr
));
720 gtk_accessible_set_widget(GTK_ACCESSIBLE(accessible
), nullptr);
721 g_object_unref(accessible
);
722 accessible
= nullptr;
725 ScintillaBase::Finalise();
728 bool ScintillaGTK::AbandonPaint() {
729 if ((paintState
== PaintState::painting
) && !paintingAllText
) {
730 repaintFullWindow
= true;
735 void ScintillaGTK::DisplayCursor(Window::Cursor c
) {
736 if (cursorMode
== CursorShape::Normal
)
739 wText
.SetCursor(static_cast<Window::Cursor
>(cursorMode
));
742 bool ScintillaGTK::DragThreshold(Point ptStart
, Point ptNow
) {
743 return gtk_drag_check_threshold(GTK_WIDGET(PWidget(wMain
)),
744 static_cast<gint
>(ptStart
.x
), static_cast<gint
>(ptStart
.y
),
745 static_cast<gint
>(ptNow
.x
), static_cast<gint
>(ptNow
.y
));
748 void ScintillaGTK::StartDrag() {
749 PLATFORM_ASSERT(evbtn
);
750 dragWasDropped
= false;
751 inDragDrop
= DragDrop::dragging
;
752 GtkTargetList
*tl
= gtk_target_list_new(clipboardCopyTargets
, nClipboardCopyTargets
);
753 #if GTK_CHECK_VERSION(3,10,0)
754 gtk_drag_begin_with_coordinates(GTK_WIDGET(PWidget(wMain
)),
761 gtk_drag_begin(GTK_WIDGET(PWidget(wMain
)),
769 namespace Scintilla::Internal
{
771 std::string
ConvertText(const char *s
, size_t len
, const char *charSetDest
,
772 const char *charSetSource
, bool transliterations
, bool silent
) {
773 // s is not const because of different versions of iconv disagreeing about const
774 std::string destForm
;
775 Converter
conv(charSetDest
, charSetSource
, transliterations
);
777 gsize outLeft
= len
*3+1;
778 destForm
= std::string(outLeft
, '\0');
779 // g_iconv does not actually write to its input argument so safe to cast away const
780 char *pin
= const_cast<char *>(s
);
782 char *putf
= &destForm
[0];
784 const gsize conversions
= conv
.Convert(&pin
, &inLeft
, &pout
, &outLeft
);
785 if (conversions
== sizeFailure
) {
788 fprintf(stderr
, "iconv %s->%s failed for %0x '%s'\n",
789 charSetSource
, charSetDest
, static_cast<unsigned char>(*s
), s
);
791 fprintf(stderr
, "iconv %s->%s failed for %s\n",
792 charSetSource
, charSetDest
, s
);
794 destForm
= std::string();
796 destForm
.resize(pout
- putf
);
799 fprintf(stderr
, "Can not iconv %s %s\n", charSetDest
, charSetSource
);
805 // Returns the target converted to UTF8.
806 // Return the length in bytes.
807 Sci::Position
ScintillaGTK::TargetAsUTF8(char *text
) const {
808 const Sci::Position targetLength
= targetRange
.Length();
809 if (IsUnicodeMode()) {
811 pdoc
->GetCharRange(text
, targetRange
.start
.Position(), targetLength
);
815 const char *charSetBuffer
= CharacterSetID();
816 if (*charSetBuffer
) {
817 std::string s
= RangeText(targetRange
.start
.Position(), targetRange
.end
.Position());
818 std::string tmputf
= ConvertText(&s
[0], targetLength
, "UTF-8", charSetBuffer
, false);
820 memcpy(text
, tmputf
.c_str(), tmputf
.length());
822 return tmputf
.length();
825 pdoc
->GetCharRange(text
, targetRange
.start
.Position(), targetLength
);
832 // Translates a nul terminated UTF8 string into the document encoding.
833 // Return the length of the result in bytes.
834 Sci::Position
ScintillaGTK::EncodedFromUTF8(const char *utf8
, char *encoded
) const {
835 const Sci::Position inputLength
= (lengthForEncode
>= 0) ? lengthForEncode
: strlen(utf8
);
836 if (IsUnicodeMode()) {
838 memcpy(encoded
, utf8
, inputLength
);
843 const char *charSetBuffer
= CharacterSetID();
844 if (*charSetBuffer
) {
845 std::string tmpEncoded
= ConvertText(utf8
, inputLength
, charSetBuffer
, "UTF-8", true);
847 memcpy(encoded
, tmpEncoded
.c_str(), tmpEncoded
.length());
849 return tmpEncoded
.length();
852 memcpy(encoded
, utf8
, inputLength
);
861 bool ScintillaGTK::ValidCodePage(int codePage
) const {
863 || codePage
== SC_CP_UTF8
871 std::string
ScintillaGTK::UTF8FromEncoded(std::string_view encoded
) const {
872 if (IsUnicodeMode()) {
873 return std::string(encoded
);
875 const char *charSetBuffer
= CharacterSetID();
876 return ConvertText(encoded
.data(), encoded
.length(), "UTF-8", charSetBuffer
, true);
880 std::string
ScintillaGTK::EncodedFromUTF8(std::string_view utf8
) const {
881 if (IsUnicodeMode()) {
882 return std::string(utf8
);
884 const char *charSetBuffer
= CharacterSetID();
885 return ConvertText(utf8
.data(), utf8
.length(), charSetBuffer
, "UTF-8", true);
889 sptr_t
ScintillaGTK::WndProc(Message iMessage
, uptr_t wParam
, sptr_t lParam
) {
893 case Message::GrabFocus
:
894 gtk_widget_grab_focus(PWidget(wMain
));
897 case Message::GetDirectFunction
:
898 return reinterpret_cast<sptr_t
>(DirectFunction
);
900 case Message::GetDirectStatusFunction
:
901 return reinterpret_cast<sptr_t
>(DirectStatusFunction
);
903 case Message::GetDirectPointer
:
904 return reinterpret_cast<sptr_t
>(this);
906 case Message::TargetAsUTF8
:
907 return TargetAsUTF8(CharPtrFromSPtr(lParam
));
909 case Message::EncodedFromUTF8
:
910 return EncodedFromUTF8(ConstCharPtrFromUPtr(wParam
),
911 CharPtrFromSPtr(lParam
));
913 case Message::SetRectangularSelectionModifier
:
914 rectangularSelectionModifier
= static_cast<int>(wParam
);
917 case Message::GetRectangularSelectionModifier
:
918 return rectangularSelectionModifier
;
920 case Message::SetReadOnly
: {
921 const sptr_t ret
= ScintillaBase::WndProc(iMessage
, wParam
, lParam
);
923 ScintillaGTKAccessible
*sciAccessible
= ScintillaGTKAccessible::FromAccessible(accessible
);
925 sciAccessible
->NotifyReadOnly();
931 case Message::GetAccessibility
:
932 return accessibilityEnabled
;
934 case Message::SetAccessibility
:
935 accessibilityEnabled
= static_cast<int>(wParam
);
937 ScintillaGTKAccessible
*sciAccessible
= ScintillaGTKAccessible::FromAccessible(accessible
);
939 sciAccessible
->SetAccessibility(accessibilityEnabled
);
945 return ScintillaBase::WndProc(iMessage
, wParam
, lParam
);
947 } catch (std::bad_alloc
&) {
948 errorStatus
= Status::BadAlloc
;
950 errorStatus
= Status::Failure
;
955 sptr_t
ScintillaGTK::DefWndProc(Message
, uptr_t
, sptr_t
) {
959 bool ScintillaGTK::FineTickerRunning(TickReason reason
) {
960 return timers
[static_cast<size_t>(reason
)].timer
!= 0;
963 void ScintillaGTK::FineTickerStart(TickReason reason
, int millis
, int /* tolerance */) {
964 FineTickerCancel(reason
);
965 const size_t reasonIndex
= static_cast<size_t>(reason
);
966 timers
[reasonIndex
].timer
= gdk_threads_add_timeout(millis
, TimeOut
, &timers
[reasonIndex
]);
969 void ScintillaGTK::FineTickerCancel(TickReason reason
) {
970 const size_t reasonIndex
= static_cast<size_t>(reason
);
971 if (timers
[reasonIndex
].timer
) {
972 g_source_remove(timers
[reasonIndex
].timer
);
973 timers
[reasonIndex
].timer
= 0;
977 bool ScintillaGTK::SetIdle(bool on
) {
979 // Start idler, if it's not running.
982 idler
.idlerID
= GUINT_TO_POINTER(
983 gdk_threads_add_idle_full(G_PRIORITY_DEFAULT_IDLE
, IdleCallback
, this, nullptr));
986 // Stop idler, if it's running
989 g_source_remove(GPOINTER_TO_UINT(idler
.idlerID
));
995 void ScintillaGTK::SetMouseCapture(bool on
) {
996 if (mouseDownCaptures
) {
998 gtk_grab_add(GTK_WIDGET(PWidget(wMain
)));
1000 gtk_grab_remove(GTK_WIDGET(PWidget(wMain
)));
1006 bool ScintillaGTK::HaveMouseCapture() {
1007 return capturedMouse
;
1010 #if GTK_CHECK_VERSION(3,0,0)
1014 // Is crcTest completely in crcContainer?
1015 bool CRectContains(const cairo_rectangle_t
&crcContainer
, const cairo_rectangle_t
&crcTest
) {
1017 (crcTest
.x
>= crcContainer
.x
) && ((crcTest
.x
+ crcTest
.width
) <= (crcContainer
.x
+ crcContainer
.width
)) &&
1018 (crcTest
.y
>= crcContainer
.y
) && ((crcTest
.y
+ crcTest
.height
) <= (crcContainer
.y
+ crcContainer
.height
));
1021 // Is crcTest completely in crcListContainer?
1022 // May incorrectly return false if complex shape
1023 bool CRectListContains(const cairo_rectangle_list_t
*crcListContainer
, const cairo_rectangle_t
&crcTest
) {
1024 for (int r
=0; r
<crcListContainer
->num_rectangles
; r
++) {
1025 if (CRectContains(crcListContainer
->rectangles
[r
], crcTest
))
1035 bool ScintillaGTK::PaintContains(PRectangle rc
) {
1036 // This allows optimization when a rectangle is completely in the update region.
1037 // It is OK to return false when too difficult to determine as that just performs extra drawing
1038 bool contains
= true;
1039 if (paintState
== PaintState::painting
) {
1040 if (!rcPaint
.Contains(rc
)) {
1042 } else if (rgnUpdate
) {
1043 #if GTK_CHECK_VERSION(3,0,0)
1044 cairo_rectangle_t grc
= {rc
.left
, rc
.top
,
1045 rc
.right
- rc
.left
, rc
.bottom
- rc
.top
1047 contains
= CRectListContains(rgnUpdate
, grc
);
1049 GdkRectangle grc
= {static_cast<gint
>(rc
.left
), static_cast<gint
>(rc
.top
),
1050 static_cast<gint
>(rc
.right
- rc
.left
), static_cast<gint
>(rc
.bottom
- rc
.top
)
1052 if (gdk_region_rect_in(rgnUpdate
, &grc
) != GDK_OVERLAP_RECTANGLE_IN
) {
1061 // Redraw all of text area. This paint will not be abandoned.
1062 void ScintillaGTK::FullPaint() {
1063 wText
.InvalidateAll();
1066 void ScintillaGTK::SetClientRectangle() {
1067 rectangleClient
= wMain
.GetClientPosition();
1070 PRectangle
ScintillaGTK::GetClientRectangle() const {
1071 PRectangle rc
= rectangleClient
;
1072 if (verticalScrollBarVisible
)
1073 rc
.right
-= verticalScrollBarWidth
;
1074 if (horizontalScrollBarVisible
&& !Wrapping())
1075 rc
.bottom
-= horizontalScrollBarHeight
;
1077 rc
.right
-= rc
.left
;
1078 rc
.bottom
-= rc
.top
;
1088 void ScintillaGTK::ScrollText(Sci::Line linesToMove
) {
1091 #if GTK_CHECK_VERSION(3,22,0)
1094 GtkWidget
*wi
= PWidget(wText
);
1095 if (IS_WIDGET_REALIZED(wi
)) {
1096 const Sci::Line diff
= vs
.lineHeight
* -linesToMove
;
1097 gdk_window_scroll(WindowFromWidget(wi
), 0, static_cast<gint
>(-diff
));
1098 gdk_window_process_updates(WindowFromWidget(wi
), FALSE
);
1103 void ScintillaGTK::SetVerticalScrollPos() {
1105 gtk_adjustment_set_value(GTK_ADJUSTMENT(adjustmentv
), static_cast<gdouble
>(topLine
));
1108 void ScintillaGTK::SetHorizontalScrollPos() {
1110 gtk_adjustment_set_value(GTK_ADJUSTMENT(adjustmenth
), xOffset
);
1113 bool ScintillaGTK::ModifyScrollBars(Sci::Line nMax
, Sci::Line nPage
) {
1114 bool modified
= false;
1115 const int pageScroll
= static_cast<int>(LinesToScroll());
1117 if (gtk_adjustment_get_upper(adjustmentv
) != (nMax
+ 1) ||
1118 gtk_adjustment_get_page_size(adjustmentv
) != nPage
||
1119 gtk_adjustment_get_page_increment(adjustmentv
) != pageScroll
) {
1120 gtk_adjustment_set_upper(adjustmentv
, nMax
+ 1.0);
1121 gtk_adjustment_set_page_size(adjustmentv
, static_cast<gdouble
>(nPage
));
1122 gtk_adjustment_set_page_increment(adjustmentv
, pageScroll
);
1123 #if !GTK_CHECK_VERSION(3,18,0)
1124 gtk_adjustment_changed(GTK_ADJUSTMENT(adjustmentv
));
1126 gtk_adjustment_set_value(GTK_ADJUSTMENT(adjustmentv
), static_cast<gdouble
>(topLine
));
1130 const PRectangle rcText
= GetTextRectangle();
1131 int horizEndPreferred
= scrollWidth
;
1132 if (horizEndPreferred
< 0)
1133 horizEndPreferred
= 0;
1134 const unsigned int pageWidth
= static_cast<unsigned int>(rcText
.Width());
1135 const unsigned int pageIncrement
= pageWidth
/ 3;
1136 const unsigned int charWidth
= static_cast<unsigned int>(vs
.styles
[STYLE_DEFAULT
].aveCharWidth
);
1137 if (gtk_adjustment_get_upper(adjustmenth
) != horizEndPreferred
||
1138 gtk_adjustment_get_page_size(adjustmenth
) != pageWidth
||
1139 gtk_adjustment_get_page_increment(adjustmenth
) != pageIncrement
||
1140 gtk_adjustment_get_step_increment(adjustmenth
) != charWidth
) {
1141 gtk_adjustment_set_upper(adjustmenth
, horizEndPreferred
);
1142 gtk_adjustment_set_page_size(adjustmenth
, pageWidth
);
1143 gtk_adjustment_set_page_increment(adjustmenth
, pageIncrement
);
1144 gtk_adjustment_set_step_increment(adjustmenth
, charWidth
);
1145 #if !GTK_CHECK_VERSION(3,18,0)
1146 gtk_adjustment_changed(GTK_ADJUSTMENT(adjustmenth
));
1148 gtk_adjustment_set_value(GTK_ADJUSTMENT(adjustmenth
), xOffset
);
1151 if (modified
&& (paintState
== PaintState::painting
)) {
1152 repaintFullWindow
= true;
1158 void ScintillaGTK::ReconfigureScrollBars() {
1159 const PRectangle rc
= wMain
.GetClientPosition();
1160 Resize(static_cast<int>(rc
.Width()), static_cast<int>(rc
.Height()));
1163 void ScintillaGTK::SetScrollBars() {
1164 if (scrollBarIdleID
) {
1165 // Only allow one scroll bar change to be queued
1168 constexpr gint priorityScrollBar
= GDK_PRIORITY_REDRAW
+ 5;
1169 // On GTK, unlike other platforms, modifying scrollbars inside some events including
1170 // resizes causes problems. Deferring the modification to a lower priority (125) idle
1171 // event avoids the problems. This code did not always work when the priority was
1172 // higher than GTK's resize (GTK_PRIORITY_RESIZE=110) or redraw
1173 // (GDK_PRIORITY_REDRAW=120) idle tasks.
1174 scrollBarIdleID
= gdk_threads_add_idle_full(priorityScrollBar
,
1175 [](gpointer pSci
) -> gboolean
{
1176 ScintillaGTK
*sciThis
= static_cast<ScintillaGTK
*>(pSci
);
1177 sciThis
->ChangeScrollBars();
1178 sciThis
->scrollBarIdleID
= 0;
1184 void ScintillaGTK::NotifyChange() {
1185 g_signal_emit(G_OBJECT(sci
), scintilla_signals
[COMMAND_SIGNAL
], 0,
1186 Platform::LongFromTwoShorts(GetCtrlID(), SCEN_CHANGE
), PWidget(wMain
));
1189 void ScintillaGTK::NotifyFocus(bool focus
) {
1191 g_signal_emit(G_OBJECT(sci
), scintilla_signals
[COMMAND_SIGNAL
], 0,
1192 Platform::LongFromTwoShorts
1193 (GetCtrlID(), focus
? SCEN_SETFOCUS
: SCEN_KILLFOCUS
), PWidget(wMain
));
1194 Editor::NotifyFocus(focus
);
1197 void ScintillaGTK::NotifyParent(NotificationData scn
) {
1198 scn
.nmhdr
.hwndFrom
= PWidget(wMain
);
1199 scn
.nmhdr
.idFrom
= GetCtrlID();
1200 g_signal_emit(G_OBJECT(sci
), scintilla_signals
[NOTIFY_SIGNAL
], 0,
1204 void ScintillaGTK::NotifyKey(Keys key
, KeyMod modifiers
) {
1205 NotificationData scn
= {};
1206 scn
.nmhdr
.code
= Notification::Key
;
1207 scn
.ch
= static_cast<int>(key
);
1208 scn
.modifiers
= modifiers
;
1213 void ScintillaGTK::NotifyURIDropped(const char *list
) {
1214 NotificationData scn
= {};
1215 scn
.nmhdr
.code
= Notification::URIDropped
;
1221 const char *CharacterSetID(CharacterSet characterSet
);
1223 const char *ScintillaGTK::CharacterSetID() const {
1224 return ::CharacterSetID(vs
.styles
[STYLE_DEFAULT
].characterSet
);
1229 class CaseFolderDBCS
: public CaseFolderTable
{
1230 const char *charSet
;
1232 explicit CaseFolderDBCS(const char *charSet_
) noexcept
: charSet(charSet_
) {
1234 size_t Fold(char *folded
, size_t sizeFolded
, const char *mixed
, size_t lenMixed
) override
{
1235 if ((lenMixed
== 1) && (sizeFolded
> 0)) {
1236 folded
[0] = mapping
[static_cast<unsigned char>(mixed
[0])];
1238 } else if (*charSet
) {
1239 std::string sUTF8
= ConvertText(mixed
, lenMixed
,
1240 "UTF-8", charSet
, false);
1241 if (!sUTF8
.empty()) {
1242 UniqueStr
mapped(g_utf8_casefold(sUTF8
.c_str(), sUTF8
.length()));
1243 size_t lenMapped
= strlen(mapped
.get());
1244 if (lenMapped
< sizeFolded
) {
1245 memcpy(folded
, mapped
.get(), lenMapped
);
1253 // Something failed so return a single NUL byte
1261 std::unique_ptr
<CaseFolder
> ScintillaGTK::CaseFolderForEncoding() {
1262 if (pdoc
->dbcsCodePage
== SC_CP_UTF8
) {
1263 return std::make_unique
<CaseFolderUnicode
>();
1265 const char *charSetBuffer
= CharacterSetID();
1266 if (charSetBuffer
) {
1267 if (pdoc
->dbcsCodePage
== 0) {
1268 std::unique_ptr
<CaseFolderTable
> pcf
= std::make_unique
<CaseFolderTable
>();
1269 // Only for single byte encodings
1270 for (int i
=0x80; i
<0x100; i
++) {
1271 char sCharacter
[2] = "A";
1273 // Silent as some bytes have no assigned character
1274 std::string sUTF8
= ConvertText(sCharacter
, 1,
1275 "UTF-8", charSetBuffer
, false, true);
1276 if (!sUTF8
.empty()) {
1277 UniqueStr
mapped(g_utf8_casefold(sUTF8
.c_str(), sUTF8
.length()));
1279 std::string mappedBack
= ConvertText(mapped
.get(), strlen(mapped
.get()),
1280 charSetBuffer
, "UTF-8", false, true);
1281 if ((mappedBack
.length() == 1) && (mappedBack
[0] != sCharacter
[0])) {
1282 pcf
->SetTranslation(sCharacter
[0], mappedBack
[0]);
1289 return std::make_unique
<CaseFolderDBCS
>(charSetBuffer
);
1300 CaseMapper(const std::string
&sUTF8
, bool toUpperCase
) noexcept
{
1302 mapped
.reset(g_utf8_strup(sUTF8
.c_str(), sUTF8
.length()));
1304 mapped
.reset(g_utf8_strdown(sUTF8
.c_str(), sUTF8
.length()));
1311 std::string
ScintillaGTK::CaseMapString(const std::string
&s
, CaseMapping caseMapping
) {
1312 if (s
.empty() || (caseMapping
== CaseMapping::same
))
1315 if (IsUnicodeMode()) {
1316 std::string
retMapped(s
.length() * maxExpansionCaseConversion
, 0);
1317 const size_t lenMapped
= CaseConvertString(&retMapped
[0], retMapped
.length(), s
.c_str(), s
.length(),
1318 (caseMapping
== CaseMapping::upper
) ? CaseConversion::upper
: CaseConversion::lower
);
1319 retMapped
.resize(lenMapped
);
1323 const char *charSetBuffer
= CharacterSetID();
1325 if (!*charSetBuffer
) {
1326 CaseMapper
mapper(s
, caseMapping
== CaseMapping::upper
);
1327 return std::string(mapper
.mapped
.get());
1329 // Change text to UTF-8
1330 std::string sUTF8
= ConvertText(s
.c_str(), s
.length(),
1331 "UTF-8", charSetBuffer
, false);
1332 CaseMapper
mapper(sUTF8
, caseMapping
== CaseMapping::upper
);
1333 return ConvertText(mapper
.mapped
.get(), strlen(mapper
.mapped
.get()), charSetBuffer
, "UTF-8", false);
1337 int ScintillaGTK::KeyDefault(Keys key
, KeyMod modifiers
) {
1338 // Pass up to container in case it is an accelerator
1339 NotifyKey(key
, modifiers
);
1343 void ScintillaGTK::CopyToClipboard(const SelectionText
&selectedText
) {
1344 SelectionText
*clipText
= new SelectionText();
1345 clipText
->Copy(selectedText
);
1346 StoreOnClipboard(clipText
);
1349 void ScintillaGTK::Copy() {
1351 SelectionText
*clipText
= new SelectionText();
1352 CopySelectionRange(clipText
);
1353 StoreOnClipboard(clipText
);
1355 if (sel
.IsRectangular()) {
1356 ::OpenClipboard(NULL
);
1357 ::SetClipboardData(cfColumnSelect
, 0);
1366 // Helper class for the asynchronous paste not to risk calling in a destroyed ScintillaGTK
1368 class SelectionReceiver
: GObjectWatcher
{
1371 void Destroyed() noexcept override
{
1376 SelectionReceiver(ScintillaGTK
*sci_
) :
1377 GObjectWatcher(G_OBJECT(sci_
->MainObject())),
1381 static void ClipboardReceived(GtkClipboard
*clipboard
, GtkSelectionData
*selection_data
, gpointer data
) noexcept
{
1382 SelectionReceiver
*self
= static_cast<SelectionReceiver
*>(data
);
1384 self
->sci
->ReceivedClipboard(clipboard
, selection_data
);
1392 void ScintillaGTK::RequestSelection(GdkAtom atomSelection
) {
1393 atomSought
= atomUTF8
;
1394 GtkClipboard
*clipBoard
=
1395 gtk_widget_get_clipboard(GTK_WIDGET(PWidget(wMain
)), atomSelection
);
1397 gtk_clipboard_request_contents(clipBoard
, atomSought
,
1398 SelectionReceiver::ClipboardReceived
,
1399 new SelectionReceiver(this));
1403 void ScintillaGTK::Paste() {
1404 RequestSelection(GDK_SELECTION_CLIPBOARD
);
1407 void ScintillaGTK::CreateCallTipWindow(PRectangle rc
) {
1408 if (!ct
.wCallTip
.Created()) {
1409 ct
.wCallTip
= gtk_window_new(GTK_WINDOW_POPUP
);
1410 gtk_window_set_type_hint(GTK_WINDOW(PWidget(ct
.wCallTip
)), GDK_WINDOW_TYPE_HINT_TOOLTIP
);
1411 ct
.wDraw
= gtk_drawing_area_new();
1412 GtkWidget
*widcdrw
= PWidget(ct
.wDraw
); // // No code inside the G_OBJECT macro
1413 gtk_container_add(GTK_CONTAINER(PWidget(ct
.wCallTip
)), widcdrw
);
1414 #if GTK_CHECK_VERSION(3,0,0)
1415 g_signal_connect(G_OBJECT(widcdrw
), "draw",
1416 G_CALLBACK(ScintillaGTK::DrawCT
), &ct
);
1418 g_signal_connect(G_OBJECT(widcdrw
), "expose_event",
1419 G_CALLBACK(ScintillaGTK::ExposeCT
), &ct
);
1421 g_signal_connect(G_OBJECT(widcdrw
), "button_press_event",
1422 G_CALLBACK(ScintillaGTK::PressCT
), this);
1423 gtk_widget_set_events(widcdrw
,
1424 GDK_EXPOSURE_MASK
| GDK_BUTTON_PRESS_MASK
);
1425 GtkWidget
*top
= gtk_widget_get_toplevel(PWidget(wMain
));
1426 gtk_window_set_transient_for(GTK_WINDOW(PWidget(ct
.wCallTip
)), GTK_WINDOW(top
));
1428 const int width
= static_cast<int>(rc
.Width());
1429 const int height
= static_cast<int>(rc
.Height());
1430 gtk_widget_set_size_request(PWidget(ct
.wDraw
), width
, height
);
1432 if (PWindow(ct
.wCallTip
)) {
1433 gdk_window_resize(PWindow(ct
.wCallTip
), width
, height
);
1437 void ScintillaGTK::AddToPopUp(const char *label
, int cmd
, bool enabled
) {
1438 GtkWidget
*menuItem
;
1440 menuItem
= gtk_menu_item_new_with_label(label
);
1442 menuItem
= gtk_separator_menu_item_new();
1443 gtk_menu_shell_append(GTK_MENU_SHELL(popup
.GetID()), menuItem
);
1444 g_object_set_data(G_OBJECT(menuItem
), "CmdNum", GINT_TO_POINTER(cmd
));
1445 g_signal_connect(G_OBJECT(menuItem
), "activate", G_CALLBACK(PopUpCB
), this);
1449 gtk_widget_set_sensitive(menuItem
, enabled
);
1453 bool ScintillaGTK::OwnPrimarySelection() {
1454 return primarySelection
;
1457 void ScintillaGTK::ClearPrimarySelection() {
1458 if (primarySelection
) {
1460 // Calls PrimaryClearSelection: primarySelection -> false
1461 gtk_clipboard_clear(gtk_clipboard_get(GDK_SELECTION_PRIMARY
));
1466 void ScintillaGTK::PrimaryGetSelectionThis(GtkClipboard
*clip
, GtkSelectionData
*selection_data
, guint info
) {
1468 if (SelectionOfGSD(selection_data
) == GDK_SELECTION_PRIMARY
) {
1469 if (primary
.Empty()) {
1470 CopySelectionRange(&primary
);
1472 GetSelection(selection_data
, info
, &primary
);
1475 errorStatus
= Status::Failure
;
1479 void ScintillaGTK::PrimaryGetSelection(GtkClipboard
*clip
, GtkSelectionData
*selection_data
, guint info
, gpointer pSci
) {
1480 static_cast<ScintillaGTK
*>(pSci
)->PrimaryGetSelectionThis(clip
, selection_data
, info
);
1483 void ScintillaGTK::PrimaryClearSelectionThis(GtkClipboard
*clip
) {
1485 primarySelection
= false;
1487 if (!inClearSelection
) {
1488 // Called because of another application or window claiming primary selection
1489 // so redraw to show selection in secondary colour.
1493 errorStatus
= Status::Failure
;
1497 void ScintillaGTK::PrimaryClearSelection(GtkClipboard
*clip
, gpointer pSci
) {
1498 static_cast<ScintillaGTK
*>(pSci
)->PrimaryClearSelectionThis(clip
);
1501 void ScintillaGTK::ClaimSelection() {
1502 // X Windows has a 'primary selection' as well as the clipboard.
1503 // Whenever the user selects some text, we become the primary selection
1504 ClearPrimarySelection();
1506 if (gtk_clipboard_set_with_data(
1507 gtk_clipboard_get(GDK_SELECTION_PRIMARY
),
1508 clipboardCopyTargets
, nClipboardCopyTargets
,
1509 PrimaryGetSelection
,
1510 PrimaryClearSelection
,
1512 primarySelection
= true;
1517 bool ScintillaGTK::IsStringAtom(GdkAtom type
) {
1518 return (type
== GDK_TARGET_STRING
) || (type
== atomUTF8
) || (type
== atomUTF8Mime
);
1521 // Detect rectangular text, convert line ends to current mode, convert from or to UTF-8
1522 void ScintillaGTK::GetGtkSelectionText(GtkSelectionData
*selectionData
, SelectionText
&selText
) {
1523 const char *data
= reinterpret_cast<const char *>(DataOfGSD(selectionData
));
1524 int len
= LengthOfGSD(selectionData
);
1525 GdkAtom selectionTypeData
= TypeOfGSD(selectionData
);
1527 // Return empty string if selection is not a string
1528 if (!IsStringAtom(selectionTypeData
)) {
1533 // Check for "\n\0" ending to string indicating that selection is rectangular
1536 isRectangular
= ::IsClipboardFormatAvailable(cfColumnSelect
) != 0;
1538 isRectangular
= ((len
> 2) && (data
[len
- 1] == 0 && data
[len
- 2] == '\n'));
1540 len
--; // Forget the extra '\0'
1544 // Win32 includes an ending '\0' byte in 'len' for clipboard text from
1545 // external applications; ignore it.
1546 if ((len
> 0) && (data
[len
- 1] == '\0'))
1550 std::string
dest(data
, len
);
1551 if (selectionTypeData
== GDK_TARGET_STRING
) {
1552 if (IsUnicodeMode()) {
1553 // Unknown encoding so assume in Latin1
1554 dest
= UTF8FromLatin1(dest
);
1555 selText
.Copy(dest
, CpUtf8
, CharacterSet::Ansi
, isRectangular
, false);
1557 // Assume buffer is in same encoding as selection
1558 selText
.Copy(dest
, pdoc
->dbcsCodePage
,
1559 vs
.styles
[STYLE_DEFAULT
].characterSet
, isRectangular
, false);
1562 const char *charSetBuffer
= CharacterSetID();
1563 if (!IsUnicodeMode() && *charSetBuffer
) {
1564 // Convert to locale
1565 dest
= ConvertText(dest
.c_str(), dest
.length(), charSetBuffer
, "UTF-8", true);
1566 selText
.Copy(dest
, pdoc
->dbcsCodePage
,
1567 vs
.styles
[STYLE_DEFAULT
].characterSet
, isRectangular
, false);
1569 selText
.Copy(dest
, CpUtf8
, CharacterSet::Ansi
, isRectangular
, false);
1574 void ScintillaGTK::InsertSelection(GtkClipboard
*clipBoard
, GtkSelectionData
*selectionData
) {
1575 const gint length
= gtk_selection_data_get_length(selectionData
);
1576 const GdkAtom selection
= gtk_selection_data_get_selection(selectionData
);
1578 SelectionText selText
;
1579 GetGtkSelectionText(selectionData
, selText
);
1582 if (selection
== GDK_SELECTION_CLIPBOARD
) {
1583 ClearSelection(multiPasteMode
== MultiPaste::Each
);
1585 if (selection
== GDK_SELECTION_PRIMARY
) {
1586 SetSelection(posPrimary
, posPrimary
);
1589 InsertPasteShape(selText
.Data(), selText
.Length(),
1590 selText
.rectangular
? PasteShape::rectangular
: PasteShape::stream
);
1591 EnsureCaretVisible();
1593 if (selection
== GDK_SELECTION_PRIMARY
) {
1594 SetSelection(posPrimary
, posPrimary
);
1596 GdkAtom target
= gtk_selection_data_get_target(selectionData
);
1597 if (target
== atomUTF8
) {
1598 // In case data is actually only stored as text/plain;charset=utf-8 not UTF8_STRING
1599 gtk_clipboard_request_contents(clipBoard
, atomUTF8Mime
,
1600 SelectionReceiver::ClipboardReceived
,
1601 new SelectionReceiver(this)
1608 GObject
*ScintillaGTK::MainObject() const noexcept
{
1609 return G_OBJECT(PWidget(wMain
));
1612 void ScintillaGTK::ReceivedClipboard(GtkClipboard
*clipBoard
, GtkSelectionData
*selection_data
) noexcept
{
1614 InsertSelection(clipBoard
, selection_data
);
1616 errorStatus
= Status::Failure
;
1620 void ScintillaGTK::ReceivedSelection(GtkSelectionData
*selection_data
) {
1622 if ((SelectionOfGSD(selection_data
) == GDK_SELECTION_CLIPBOARD
) ||
1623 (SelectionOfGSD(selection_data
) == GDK_SELECTION_PRIMARY
)) {
1624 if ((atomSought
== atomUTF8
) && (LengthOfGSD(selection_data
) <= 0)) {
1625 atomSought
= atomString
;
1626 gtk_selection_convert(GTK_WIDGET(PWidget(wMain
)),
1627 SelectionOfGSD(selection_data
), atomSought
, GDK_CURRENT_TIME
);
1628 } else if ((LengthOfGSD(selection_data
) > 0) && IsStringAtom(TypeOfGSD(selection_data
))) {
1629 GtkClipboard
*clipBoard
= gtk_widget_get_clipboard(GTK_WIDGET(PWidget(wMain
)), SelectionOfGSD(selection_data
));
1630 InsertSelection(clipBoard
, selection_data
);
1633 // else fprintf(stderr, "Target non string %d %d\n", (int)(selection_data->type),
1634 // (int)(atomUTF8));
1636 errorStatus
= Status::Failure
;
1640 void ScintillaGTK::ReceivedDrop(GtkSelectionData
*selection_data
) {
1641 dragWasDropped
= true;
1642 if (TypeOfGSD(selection_data
) == atomUriList
|| TypeOfGSD(selection_data
) == atomDROPFILES_DND
) {
1643 const char *data
= reinterpret_cast<const char *>(DataOfGSD(selection_data
));
1644 std::vector
<char> drop(data
, data
+ LengthOfGSD(selection_data
));
1645 drop
.push_back('\0');
1646 NotifyURIDropped(&drop
[0]);
1647 } else if (IsStringAtom(TypeOfGSD(selection_data
))) {
1648 if (LengthOfGSD(selection_data
) > 0) {
1649 SelectionText selText
;
1650 GetGtkSelectionText(selection_data
, selText
);
1651 DropAt(posDrop
, selText
.Data(), selText
.Length(), false, selText
.rectangular
);
1653 } else if (LengthOfGSD(selection_data
) > 0) {
1654 //~ fprintf(stderr, "ReceivedDrop other %p\n", static_cast<void *>(selection_data->type));
1661 void ScintillaGTK::GetSelection(GtkSelectionData
*selection_data
, guint info
, SelectionText
*text
) {
1663 // GDK on Win32 expands any \n into \r\n, so make a copy of
1664 // the clip text now with newlines converted to \n. Use { } to hide symbols
1666 std::unique_ptr
<SelectionText
> newline_normalized
;
1668 std::string tmpstr
= Document::TransformLineEnds(text
->Data(), text
->Length(), EndOfLine::Lf
);
1669 newline_normalized
= std::make_unique
<SelectionText
>();
1670 newline_normalized
->Copy(tmpstr
, CpUtf8
, CharacterSet::Ansi
, text
->rectangular
, false);
1671 text
= newline_normalized
.get();
1675 // Convert text to utf8 if it isn't already
1676 std::unique_ptr
<SelectionText
> converted
;
1677 if ((text
->codePage
!= SC_CP_UTF8
) && (info
== TARGET_UTF8_STRING
)) {
1678 const char *charSet
= ::CharacterSetID(text
->characterSet
);
1680 std::string tmputf
= ConvertText(text
->Data(), text
->Length(), "UTF-8", charSet
, false);
1681 converted
= std::make_unique
<SelectionText
>();
1682 converted
->Copy(tmputf
, CpUtf8
, CharacterSet::Ansi
, text
->rectangular
, false);
1683 text
= converted
.get();
1687 // Here is a somewhat evil kludge.
1688 // As I can not work out how to store data on the clipboard in multiple formats
1689 // and need some way to mark the clipping as being stream or rectangular,
1690 // the terminating \0 is included in the length for rectangular clippings.
1691 // All other tested applications behave benignly by ignoring the \0.
1692 // The #if is here because on Windows cfColumnSelect clip entry is used
1693 // instead as standard indicator of rectangularness (so no need to kludge)
1694 const char *textData
= text
->Data();
1695 gint len
= static_cast<gint
>(text
->Length());
1696 #if PLAT_GTK_WIN32 == 0
1697 if (text
->rectangular
)
1701 if (info
== TARGET_UTF8_STRING
) {
1702 gtk_selection_data_set_text(selection_data
, textData
, len
);
1704 gtk_selection_data_set(selection_data
,
1705 static_cast<GdkAtom
>(GDK_SELECTION_TYPE_STRING
),
1706 8, reinterpret_cast<const guchar
*>(textData
), len
);
1710 void ScintillaGTK::StoreOnClipboard(SelectionText
*clipText
) {
1711 GtkClipboard
*clipBoard
=
1712 gtk_widget_get_clipboard(GTK_WIDGET(PWidget(wMain
)), GDK_SELECTION_CLIPBOARD
);
1713 if (clipBoard
== nullptr) // Occurs if widget isn't in a toplevel
1716 if (gtk_clipboard_set_with_data(clipBoard
, clipboardCopyTargets
, nClipboardCopyTargets
,
1717 ClipboardGetSelection
, ClipboardClearSelection
, clipText
)) {
1718 gtk_clipboard_set_can_store(clipBoard
, clipboardCopyTargets
, nClipboardCopyTargets
);
1722 void ScintillaGTK::ClipboardGetSelection(GtkClipboard
*, GtkSelectionData
*selection_data
, guint info
, void *data
) {
1723 GetSelection(selection_data
, info
, static_cast<SelectionText
*>(data
));
1726 void ScintillaGTK::ClipboardClearSelection(GtkClipboard
*, void *data
) {
1727 SelectionText
*obj
= static_cast<SelectionText
*>(data
);
1731 void ScintillaGTK::UnclaimSelection(GdkEventSelection
*selection_event
) {
1733 //Platform::DebugPrintf("UnclaimSelection\n");
1734 if (selection_event
->selection
== GDK_SELECTION_PRIMARY
) {
1735 //Platform::DebugPrintf("UnclaimPrimarySelection\n");
1736 if (!OwnPrimarySelection()) {
1738 primarySelection
= false;
1743 errorStatus
= Status::Failure
;
1747 void ScintillaGTK::Resize(int width
, int height
) {
1748 //Platform::DebugPrintf("Resize %d %d\n", width, height);
1749 //printf("Resize %d %d\n", width, height);
1751 // GTK+ 3 warns when we allocate smaller than the minimum allocation,
1752 // so we use these variables to store the minimum scrollbar lengths.
1753 int minVScrollBarHeight
, minHScrollBarWidth
;
1755 // Not always needed, but some themes can have different sizes of scrollbars
1756 #if GTK_CHECK_VERSION(3,0,0)
1757 GtkRequisition minimum
, requisition
;
1758 gtk_widget_get_preferred_size(PWidget(scrollbarv
), &minimum
, &requisition
);
1759 minVScrollBarHeight
= minimum
.height
;
1760 verticalScrollBarWidth
= requisition
.width
;
1761 gtk_widget_get_preferred_size(PWidget(scrollbarh
), &minimum
, &requisition
);
1762 minHScrollBarWidth
= minimum
.width
;
1763 horizontalScrollBarHeight
= requisition
.height
;
1765 minVScrollBarHeight
= minHScrollBarWidth
= 1;
1766 verticalScrollBarWidth
= GTK_WIDGET(PWidget(scrollbarv
))->requisition
.width
;
1767 horizontalScrollBarHeight
= GTK_WIDGET(PWidget(scrollbarh
))->requisition
.height
;
1770 // These allocations should never produce negative sizes as they would wrap around to huge
1771 // unsigned numbers inside GTK+ causing warnings.
1772 const bool showSBHorizontal
= horizontalScrollBarVisible
&& !Wrapping();
1774 GtkAllocation alloc
= {};
1775 if (showSBHorizontal
) {
1776 gtk_widget_show(GTK_WIDGET(PWidget(scrollbarh
)));
1778 alloc
.y
= height
- horizontalScrollBarHeight
;
1779 alloc
.width
= std::max(minHScrollBarWidth
, width
- verticalScrollBarWidth
);
1780 alloc
.height
= horizontalScrollBarHeight
;
1781 gtk_widget_size_allocate(GTK_WIDGET(PWidget(scrollbarh
)), &alloc
);
1783 gtk_widget_hide(GTK_WIDGET(PWidget(scrollbarh
)));
1784 horizontalScrollBarHeight
= 0; // in case horizontalScrollBarVisible is true.
1787 if (verticalScrollBarVisible
) {
1788 gtk_widget_show(GTK_WIDGET(PWidget(scrollbarv
)));
1789 alloc
.x
= width
- verticalScrollBarWidth
;
1791 alloc
.width
= verticalScrollBarWidth
;
1792 alloc
.height
= std::max(minVScrollBarHeight
, height
- horizontalScrollBarHeight
);
1793 gtk_widget_size_allocate(GTK_WIDGET(PWidget(scrollbarv
)), &alloc
);
1795 gtk_widget_hide(GTK_WIDGET(PWidget(scrollbarv
)));
1796 verticalScrollBarWidth
= 0;
1798 SetClientRectangle();
1799 if (IS_WIDGET_MAPPED(PWidget(wMain
))) {
1802 const PRectangle rcTextArea
= GetTextRectangle();
1803 if (wrapWidth
!= rcTextArea
.Width()) {
1804 wrapWidth
= rcTextArea
.Width();
1813 #if GTK_CHECK_VERSION(3, 0, 0)
1814 // please GTK 3.20 and ask wText what size it wants, although we know it doesn't really need
1815 // anything special as it's ours.
1816 gtk_widget_get_preferred_size(PWidget(wText
), &requisition
, nullptr);
1817 alloc
.width
= requisition
.width
;
1818 alloc
.height
= requisition
.height
;
1820 alloc
.width
= std::max(alloc
.width
, width
- verticalScrollBarWidth
);
1821 alloc
.height
= std::max(alloc
.height
, height
- horizontalScrollBarHeight
);
1822 gtk_widget_size_allocate(GTK_WIDGET(PWidget(wText
)), &alloc
);
1827 void SetAdjustmentValue(GtkAdjustment
*object
, int value
) noexcept
{
1828 GtkAdjustment
*adjustment
= GTK_ADJUSTMENT(object
);
1829 const int maxValue
= static_cast<int>(
1830 gtk_adjustment_get_upper(adjustment
) - gtk_adjustment_get_page_size(adjustment
));
1832 if (value
> maxValue
)
1836 gtk_adjustment_set_value(adjustment
, value
);
1839 int modifierTranslated(int sciModifier
) noexcept
{
1840 switch (sciModifier
) {
1842 return GDK_SHIFT_MASK
;
1844 return GDK_CONTROL_MASK
;
1846 return GDK_MOD1_MASK
;
1848 return GDK_MOD4_MASK
;
1854 Point
PointOfEvent(const GdkEventButton
*event
) noexcept
{
1855 // Use floor as want to round in the same direction (-infinity) so
1856 // there is no stickiness crossing 0.0.
1857 return Point(static_cast<XYPOSITION
>(std::floor(event
->x
)), static_cast<XYPOSITION
>(std::floor(event
->y
)));
1862 gint
ScintillaGTK::PressThis(GdkEventButton
*event
) {
1864 //Platform::DebugPrintf("Press %x time=%d state = %x button = %x\n",this,event->time, event->state, event->button);
1865 // Do not use GTK+ double click events as Scintilla has its own double click detection
1866 if (event
->type
!= GDK_BUTTON_PRESS
)
1869 evbtn
.reset(gdk_event_copy(reinterpret_cast<GdkEvent
*>(event
)));
1870 buttonMouse
= event
->button
;
1871 const Point pt
= PointOfEvent(event
);
1872 const PRectangle rcClient
= GetClientRectangle();
1873 //Platform::DebugPrintf("Press %0d,%0d in %0d,%0d %0d,%0d\n",
1874 // pt.x, pt.y, rcClient.left, rcClient.top, rcClient.right, rcClient.bottom);
1875 if ((pt
.x
> rcClient
.right
) || (pt
.y
> rcClient
.bottom
)) {
1876 Platform::DebugPrintf("Bad location\n");
1880 const bool shift
= (event
->state
& GDK_SHIFT_MASK
) != 0;
1881 bool ctrl
= (event
->state
& GDK_CONTROL_MASK
) != 0;
1882 // On X, instead of sending literal modifiers use the user specified
1883 // modifier, defaulting to control instead of alt.
1884 // This is because most X window managers grab alt + click for moving
1885 const bool alt
= (event
->state
& modifierTranslated(rectangularSelectionModifier
)) != 0;
1887 gtk_widget_grab_focus(PWidget(wMain
));
1888 if (event
->button
== 1) {
1890 const bool meta
= ctrl
;
1891 // GDK reports the Command modifier key as GDK_MOD2_MASK for button events,
1892 // not GDK_META_MASK like in key events.
1893 ctrl
= (event
->state
& GDK_MOD2_MASK
) != 0;
1895 const bool meta
= false;
1897 ButtonDownWithModifiers(pt
, event
->time
, ModifierFlags(shift
, ctrl
, alt
, meta
));
1898 } else if (event
->button
== 2) {
1899 // Grab the primary selection if it exists
1900 posPrimary
= SPositionFromLocation(pt
, false, false, UserVirtualSpace());
1901 if (OwnPrimarySelection() && primary
.Empty())
1902 CopySelectionRange(&primary
);
1905 RequestSelection(GDK_SELECTION_PRIMARY
);
1906 } else if (event
->button
== 3) {
1907 if (!PointInSelection(pt
))
1908 SetEmptySelection(PositionFromLocation(pt
));
1909 if (ShouldDisplayPopup(pt
)) {
1911 // Convert to screen
1914 gdk_window_get_origin(PWindow(wMain
), &ox
, &oy
);
1915 ContextMenu(Point(pt
.x
+ ox
, pt
.y
+ oy
));
1918 const bool meta
= ctrl
;
1919 // GDK reports the Command modifier key as GDK_MOD2_MASK for button events,
1920 // not GDK_META_MASK like in key events.
1921 ctrl
= (event
->state
& GDK_MOD2_MASK
) != 0;
1923 const bool meta
= false;
1925 RightButtonDownWithModifiers(pt
, event
->time
, ModifierFlags(shift
, ctrl
, alt
, meta
));
1928 } else if (event
->button
== 4) {
1929 // Wheel scrolling up (only GTK 1.x does it this way)
1931 SetAdjustmentValue(adjustmenth
, xOffset
- 6);
1933 SetAdjustmentValue(adjustmentv
, static_cast<int>(topLine
) - 3);
1934 } else if (event
->button
== 5) {
1935 // Wheel scrolling down (only GTK 1.x does it this way)
1937 SetAdjustmentValue(adjustmenth
, xOffset
+ 6);
1939 SetAdjustmentValue(adjustmentv
, static_cast<int>(topLine
) + 3);
1942 errorStatus
= Status::Failure
;
1947 gint
ScintillaGTK::Press(GtkWidget
*widget
, GdkEventButton
*event
) {
1948 if (event
->window
!= WindowFromWidget(widget
))
1950 ScintillaGTK
*sciThis
= FromWidget(widget
);
1951 return sciThis
->PressThis(event
);
1954 gint
ScintillaGTK::MouseRelease(GtkWidget
*widget
, GdkEventButton
*event
) {
1955 ScintillaGTK
*sciThis
= FromWidget(widget
);
1957 //Platform::DebugPrintf("Release %x %d %d\n",sciThis,event->time,event->state);
1958 if (!sciThis
->HaveMouseCapture())
1960 if (event
->button
== 1) {
1961 Point pt
= PointOfEvent(event
);
1962 //Platform::DebugPrintf("Up %x %x %d %d %d\n",
1963 // sciThis,event->window,event->time, pt.x, pt.y);
1964 if (event
->window
!= PWindow(sciThis
->wMain
))
1965 // If mouse released on scroll bar then the position is relative to the
1966 // scrollbar, not the drawing window so just repeat the most recent point.
1967 pt
= sciThis
->ptMouseLast
;
1968 const KeyMod modifiers
= ModifierFlags(
1969 (event
->state
& GDK_SHIFT_MASK
) != 0,
1970 (event
->state
& GDK_CONTROL_MASK
) != 0,
1971 (event
->state
& modifierTranslated(sciThis
->rectangularSelectionModifier
)) != 0);
1972 sciThis
->ButtonUpWithModifiers(pt
, event
->time
, modifiers
);
1975 sciThis
->errorStatus
= Status::Failure
;
1980 // win32gtk and GTK >= 2 use SCROLL_* events instead of passing the
1981 // button4/5/6/7 events to the GTK app
1982 gint
ScintillaGTK::ScrollEvent(GtkWidget
*widget
, GdkEventScroll
*event
) {
1983 ScintillaGTK
*sciThis
= FromWidget(widget
);
1986 if (widget
== nullptr || event
== nullptr)
1989 #if defined(GDK_WINDOWING_WAYLAND)
1990 if (event
->direction
== GDK_SCROLL_SMOOTH
&& GDK_IS_WAYLAND_WINDOW(event
->window
)) {
1991 const int smoothScrollFactor
= 4;
1992 sciThis
->smoothScrollY
+= event
->delta_y
* smoothScrollFactor
;
1993 sciThis
->smoothScrollX
+= event
->delta_x
* smoothScrollFactor
;;
1994 if (ABS(sciThis
->smoothScrollY
) >= 1.0) {
1995 const int scrollLines
= std::trunc(sciThis
->smoothScrollY
);
1996 sciThis
->ScrollTo(sciThis
->topLine
+ scrollLines
);
1997 sciThis
->smoothScrollY
-= scrollLines
;
1999 if (ABS(sciThis
->smoothScrollX
) >= 1.0) {
2000 const int scrollPixels
= std::trunc(sciThis
->smoothScrollX
);
2001 sciThis
->HorizontalScrollTo(sciThis
->xOffset
+ scrollPixels
);
2002 sciThis
->smoothScrollX
-= scrollPixels
;
2008 // Compute amount and direction to scroll (even tho on win32 there is
2009 // intensity of scrolling info in the native message, gtk doesn't
2010 // support this so we simulate similarly adaptive scrolling)
2011 // Note that this is disabled on macOS (Darwin) with the X11 backend
2012 // where the X11 server already has an adaptive scrolling algorithm
2013 // that fights with this one
2015 #if (defined(__APPLE__) || defined(PLAT_GTK_WIN32)) && !defined(GDK_WINDOWING_QUARTZ)
2016 cLineScroll
= sciThis
->linesPerScroll
;
2017 if (cLineScroll
== 0)
2019 sciThis
->wheelMouseIntensity
= cLineScroll
;
2021 const gint64 curTime
= g_get_monotonic_time();
2022 const gint64 timeDelta
= curTime
- sciThis
->lastWheelMouseTime
;
2023 if ((event
->direction
== sciThis
->lastWheelMouseDirection
) && (timeDelta
< 250000)) {
2024 if (sciThis
->wheelMouseIntensity
< 12)
2025 sciThis
->wheelMouseIntensity
++;
2026 cLineScroll
= sciThis
->wheelMouseIntensity
;
2028 cLineScroll
= sciThis
->linesPerScroll
;
2029 if (cLineScroll
== 0)
2031 sciThis
->wheelMouseIntensity
= cLineScroll
;
2033 sciThis
->lastWheelMouseTime
= curTime
;
2035 if (event
->direction
== GDK_SCROLL_UP
|| event
->direction
== GDK_SCROLL_LEFT
) {
2038 sciThis
->lastWheelMouseDirection
= event
->direction
;
2040 // Note: Unpatched versions of win32gtk don't set the 'state' value so
2041 // only regular scrolling is supported there. Also, unpatched win32gtk
2042 // issues spurious button 2 mouse events during wheeling, which can cause
2043 // problems (a patch for both was submitted by archaeopteryx.com on 13Jun2001)
2045 #if GTK_CHECK_VERSION(3,4,0)
2046 // Smooth scrolling not supported
2047 if (event
->direction
== GDK_SCROLL_SMOOTH
) {
2052 // Horizontal scrolling
2053 if (event
->direction
== GDK_SCROLL_LEFT
|| event
->direction
== GDK_SCROLL_RIGHT
|| event
->state
& GDK_SHIFT_MASK
) {
2054 int hScroll
= gtk_adjustment_get_step_increment(sciThis
->adjustmenth
);
2055 hScroll
*= cLineScroll
; // scroll by this many characters
2056 sciThis
->HorizontalScrollTo(sciThis
->xOffset
+ hScroll
);
2058 // Text font size zoom
2059 } else if (event
->state
& GDK_CONTROL_MASK
) {
2060 if (cLineScroll
< 0) {
2061 sciThis
->KeyCommand(Message::ZoomIn
);
2063 sciThis
->KeyCommand(Message::ZoomOut
);
2066 // Regular scrolling
2068 sciThis
->ScrollTo(sciThis
->topLine
+ cLineScroll
);
2072 sciThis
->errorStatus
= Status::Failure
;
2077 gint
ScintillaGTK::Motion(GtkWidget
*widget
, GdkEventMotion
*event
) {
2078 ScintillaGTK
*sciThis
= FromWidget(widget
);
2080 //Platform::DebugPrintf("Motion %x %d\n",sciThis,event->time);
2081 if (event
->window
!= WindowFromWidget(widget
))
2085 GdkModifierType state
{};
2086 if (event
->is_hint
) {
2087 #if GTK_CHECK_VERSION(3,0,0)
2088 gdk_window_get_device_position(event
->window
,
2089 event
->device
, &x
, &y
, &state
);
2091 gdk_window_get_pointer(event
->window
, &x
, &y
, &state
);
2094 x
= static_cast<int>(event
->x
);
2095 y
= static_cast<int>(event
->y
);
2096 state
= static_cast<GdkModifierType
>(event
->state
);
2098 //Platform::DebugPrintf("Move %x %x %d %c %d %d\n",
2099 // sciThis,event->window,event->time,event->is_hint? 'h' :'.', x, y);
2100 const Point
pt(static_cast<XYPOSITION
>(x
), static_cast<XYPOSITION
>(y
));
2101 const KeyMod modifiers
= ModifierFlags(
2102 (event
->state
& GDK_SHIFT_MASK
) != 0,
2103 (event
->state
& GDK_CONTROL_MASK
) != 0,
2104 (event
->state
& modifierTranslated(sciThis
->rectangularSelectionModifier
)) != 0);
2105 sciThis
->ButtonMoveWithModifiers(pt
, event
->time
, modifiers
);
2107 sciThis
->errorStatus
= Status::Failure
;
2114 // Map the keypad keys to their equivalent functions
2115 int KeyTranslate(int keyIn
) noexcept
{
2117 #if GTK_CHECK_VERSION(3,0,0)
2118 case GDK_KEY_ISO_Left_Tab
:
2120 case GDK_KEY_KP_Down
:
2124 case GDK_KEY_KP_Left
:
2126 case GDK_KEY_KP_Right
:
2128 case GDK_KEY_KP_Home
:
2130 case GDK_KEY_KP_End
:
2132 case GDK_KEY_KP_Page_Up
:
2134 case GDK_KEY_KP_Page_Down
:
2136 case GDK_KEY_KP_Delete
:
2138 case GDK_KEY_KP_Insert
:
2140 case GDK_KEY_KP_Enter
:
2155 case GDK_KEY_Page_Up
:
2157 case GDK_KEY_Page_Down
:
2159 case GDK_KEY_Delete
:
2161 case GDK_KEY_Insert
:
2163 case GDK_KEY_Escape
:
2165 case GDK_KEY_BackSpace
:
2169 case GDK_KEY_Return
:
2171 case GDK_KEY_KP_Add
:
2173 case GDK_KEY_KP_Subtract
:
2174 return SCK_SUBTRACT
;
2175 case GDK_KEY_KP_Divide
:
2177 case GDK_KEY_Super_L
:
2179 case GDK_KEY_Super_R
:
2186 case GDK_ISO_Left_Tab
:
2200 case GDK_KP_Page_Up
:
2202 case GDK_KP_Page_Down
:
2241 case GDK_KP_Subtract
:
2242 return SCK_SUBTRACT
;
2259 gboolean
ScintillaGTK::KeyThis(GdkEventKey
*event
) {
2261 //fprintf(stderr, "SC-key: %d %x [%s]\n",
2262 // event->keyval, event->state, (event->length > 0) ? event->string : "empty");
2263 if (gtk_im_context_filter_keypress(im_context
.get(), event
)) {
2266 if (!event
->keyval
) {
2270 const bool shift
= (event
->state
& GDK_SHIFT_MASK
) != 0;
2271 bool ctrl
= (event
->state
& GDK_CONTROL_MASK
) != 0;
2272 const bool alt
= (event
->state
& GDK_MOD1_MASK
) != 0;
2273 const bool super
= (event
->state
& GDK_MOD4_MASK
) != 0;
2274 guint key
= event
->keyval
;
2275 if ((ctrl
|| alt
) && (key
< 128))
2277 #if GTK_CHECK_VERSION(3,0,0)
2278 else if (!ctrl
&& (key
>= GDK_KEY_KP_Multiply
&& key
<= GDK_KEY_KP_9
))
2280 else if (!ctrl
&& (key
>= GDK_KP_Multiply
&& key
<= GDK_KP_9
))
2283 // Hack for keys over 256 and below command keys but makes Hungarian work.
2284 // This will have to change for Unicode
2285 else if (key
>= 0xFE00)
2286 key
= KeyTranslate(key
);
2288 bool consumed
= false;
2289 #if !(PLAT_GTK_MACOSX)
2290 const bool meta
= false;
2292 const bool meta
= ctrl
;
2293 ctrl
= (event
->state
& GDK_META_MASK
) != 0;
2295 const bool added
= KeyDownWithModifiers(static_cast<Keys
>(key
), ModifierFlags(shift
, ctrl
, alt
, meta
, super
), &consumed
) != 0;
2298 //fprintf(stderr, "SK-key: %d %x %x\n",event->keyval, event->state, consumed);
2299 if (event
->keyval
== 0xffffff && event
->length
> 0) {
2301 const Sci::Position lengthInserted
= pdoc
->InsertString(CurrentPosition(), event
->string
, strlen(event
->string
));
2302 if (lengthInserted
> 0) {
2303 MovePositionTo(CurrentPosition() + lengthInserted
);
2308 errorStatus
= Status::Failure
;
2313 gboolean
ScintillaGTK::KeyPress(GtkWidget
*widget
, GdkEventKey
*event
) {
2314 ScintillaGTK
*sciThis
= FromWidget(widget
);
2315 return sciThis
->KeyThis(event
);
2318 gboolean
ScintillaGTK::KeyRelease(GtkWidget
*widget
, GdkEventKey
*event
) {
2319 //Platform::DebugPrintf("SC-keyrel: %d %x %3s\n",event->keyval, event->state, event->string);
2320 ScintillaGTK
*sciThis
= FromWidget(widget
);
2321 if (gtk_im_context_filter_keypress(sciThis
->im_context
.get(), event
)) {
2327 #if GTK_CHECK_VERSION(3,0,0)
2329 gboolean
ScintillaGTK::DrawPreeditThis(GtkWidget
*, cairo_t
*cr
) {
2331 PreEditString
pes(im_context
.get());
2332 UniquePangoLayout
layout(gtk_widget_create_pango_layout(PWidget(wText
), pes
.str
));
2333 pango_layout_set_attributes(layout
.get(), pes
.attrs
);
2335 cairo_move_to(cr
, 0, 0);
2336 pango_cairo_show_layout(cr
, layout
.get());
2338 errorStatus
= Status::Failure
;
2343 gboolean
ScintillaGTK::DrawPreedit(GtkWidget
*widget
, cairo_t
*cr
, ScintillaGTK
*sciThis
) {
2344 return sciThis
->DrawPreeditThis(widget
, cr
);
2349 gboolean
ScintillaGTK::ExposePreeditThis(GtkWidget
*widget
, GdkEventExpose
*) {
2351 PreEditString
pes(im_context
.get());
2352 UniquePangoLayout
layout(gtk_widget_create_pango_layout(PWidget(wText
), pes
.str
));
2353 pango_layout_set_attributes(layout
.get(), pes
.attrs
);
2355 UniqueCairo
context(gdk_cairo_create(WindowFromWidget(widget
)));
2356 cairo_move_to(context
.get(), 0, 0);
2357 pango_cairo_show_layout(context
.get(), layout
.get());
2359 errorStatus
= Status::Failure
;
2364 gboolean
ScintillaGTK::ExposePreedit(GtkWidget
*widget
, GdkEventExpose
*ose
, ScintillaGTK
*sciThis
) {
2365 return sciThis
->ExposePreeditThis(widget
, ose
);
2370 bool ScintillaGTK::KoreanIME() {
2371 PreEditString
pes(im_context
.get());
2372 if (pes
.pscript
!= G_UNICODE_SCRIPT_COMMON
)
2373 lastNonCommonScript
= pes
.pscript
;
2374 return lastNonCommonScript
== G_UNICODE_SCRIPT_HANGUL
;
2377 void ScintillaGTK::MoveImeCarets(Sci::Position pos
) {
2378 // Move carets relatively by bytes
2379 for (size_t r
=0; r
<sel
.Count(); r
++) {
2380 const Sci::Position positionInsert
= sel
.Range(r
).Start().Position();
2381 sel
.Range(r
).caret
.SetPosition(positionInsert
+ pos
);
2382 sel
.Range(r
).anchor
.SetPosition(positionInsert
+ pos
);
2386 void ScintillaGTK::DrawImeIndicator(int indicator
, Sci::Position len
) {
2387 // Emulate the visual style of IME characters with indicators.
2388 // Draw an indicator on the character before caret by the character bytes of len
2389 // so it should be called after InsertCharacter().
2390 // It does not affect caret positions.
2391 if (indicator
< 8 || indicator
> INDICATOR_MAX
) {
2394 pdoc
->DecorationSetCurrentIndicator(indicator
);
2395 for (size_t r
=0; r
<sel
.Count(); r
++) {
2396 const Sci::Position positionInsert
= sel
.Range(r
).Start().Position();
2397 pdoc
->DecorationFillRange(positionInsert
- len
, 1, len
);
2403 std::vector
<int> MapImeIndicators(PangoAttrList
*attrs
, const char *u8Str
) {
2404 // Map input style to scintilla ime indicator.
2405 // Attrs position points between UTF-8 bytes.
2406 // Indicator index to be returned is character based though.
2407 const glong charactersLen
= g_utf8_strlen(u8Str
, strlen(u8Str
));
2408 std::vector
<int> indicator(charactersLen
, SC_INDICATOR_UNKNOWN
);
2410 PangoAttrIterator
*iterunderline
= pango_attr_list_get_iterator(attrs
);
2411 if (iterunderline
) {
2413 const PangoAttribute
*attrunderline
= pango_attr_iterator_get(iterunderline
, PANGO_ATTR_UNDERLINE
);
2414 if (attrunderline
) {
2415 const glong start
= g_utf8_strlen(u8Str
, attrunderline
->start_index
);
2416 const glong end
= g_utf8_strlen(u8Str
, attrunderline
->end_index
);
2417 const int ulinevalue
= reinterpret_cast<const PangoAttrInt
*>(attrunderline
)->value
;
2418 const PangoUnderline uline
= static_cast<PangoUnderline
>(ulinevalue
);
2419 for (glong i
=start
; i
< end
; ++i
) {
2421 case PANGO_UNDERLINE_NONE
:
2422 indicator
[i
] = SC_INDICATOR_UNKNOWN
;
2424 case PANGO_UNDERLINE_SINGLE
: // normal input
2425 indicator
[i
] = SC_INDICATOR_INPUT
;
2427 case PANGO_UNDERLINE_DOUBLE
:
2428 case PANGO_UNDERLINE_LOW
:
2429 case PANGO_UNDERLINE_ERROR
:
2435 } while (pango_attr_iterator_next(iterunderline
));
2436 pango_attr_iterator_destroy(iterunderline
);
2439 PangoAttrIterator
*itercolor
= pango_attr_list_get_iterator(attrs
);
2442 const PangoAttribute
*backcolor
= pango_attr_iterator_get(itercolor
, PANGO_ATTR_BACKGROUND
);
2444 const glong start
= g_utf8_strlen(u8Str
, backcolor
->start_index
);
2445 const glong end
= g_utf8_strlen(u8Str
, backcolor
->end_index
);
2446 for (glong i
=start
; i
< end
; ++i
) {
2447 indicator
[i
] = SC_INDICATOR_TARGET
; // target converted
2450 } while (pango_attr_iterator_next(itercolor
));
2451 pango_attr_iterator_destroy(itercolor
);
2458 void ScintillaGTK::SetCandidateWindowPos() {
2459 // Composition box accompanies candidate box.
2460 const Point pt
= PointMainCaret();
2461 GdkRectangle imeBox
{};
2462 imeBox
.x
= static_cast<gint
>(pt
.x
);
2463 imeBox
.y
= static_cast<gint
>(pt
.y
+ std::max(4, vs
.lineHeight
/4));
2464 // prevent overlapping with current line
2465 imeBox
.height
= vs
.lineHeight
;
2466 gtk_im_context_set_cursor_location(im_context
.get(), &imeBox
);
2469 void ScintillaGTK::CommitThis(char *commitStr
) {
2471 //~ fprintf(stderr, "Commit '%s'\n", commitStr);
2472 view
.imeCaretBlockOverride
= false;
2474 if (pdoc
->TentativeActive()) {
2475 pdoc
->TentativeUndo();
2478 const char *charSetSource
= CharacterSetID();
2480 glong uniStrLen
= 0;
2481 gunichar
*uniStr
= g_utf8_to_ucs4_fast(commitStr
, static_cast<glong
>(strlen(commitStr
)), &uniStrLen
);
2482 for (glong i
= 0; i
< uniStrLen
; i
++) {
2483 gchar u8Char
[UTF8MaxBytes
+2] = {0};
2484 const gint u8CharLen
= g_unichar_to_utf8(uniStr
[i
], u8Char
);
2485 std::string docChar
= u8Char
;
2486 if (!IsUnicodeMode())
2487 docChar
= ConvertText(u8Char
, u8CharLen
, charSetSource
, "UTF-8", true);
2489 InsertCharacter(docChar
, CharacterSource::DirectInput
);
2492 ShowCaretAtCurrentPosition();
2494 errorStatus
= Status::Failure
;
2498 void ScintillaGTK::Commit(GtkIMContext
*, char *str
, ScintillaGTK
*sciThis
) {
2499 sciThis
->CommitThis(str
);
2502 void ScintillaGTK::PreeditChangedInlineThis() {
2503 // Copy & paste by johnsonj with a lot of helps of Neil
2504 // Great thanks for my foreruners, jiniya and BLUEnLIVE
2506 if (pdoc
->IsReadOnly() || SelectionContainsProtected()) {
2507 gtk_im_context_reset(im_context
.get());
2511 view
.imeCaretBlockOverride
= false; // If backspace.
2513 bool initialCompose
= false;
2514 if (pdoc
->TentativeActive()) {
2515 pdoc
->TentativeUndo();
2517 // No tentative undo means start of this composition so
2518 // fill in any virtual spaces.
2519 initialCompose
= true;
2522 PreEditString
preeditStr(im_context
.get());
2523 const char *charSetSource
= CharacterSetID();
2525 if (!preeditStr
.validUTF8
|| (charSetSource
== nullptr)) {
2526 ShowCaretAtCurrentPosition();
2530 if (preeditStr
.uniStrLen
== 0) {
2531 ShowCaretAtCurrentPosition();
2535 if (initialCompose
) {
2536 ClearBeforeTentativeStart();
2539 SetCandidateWindowPos();
2540 pdoc
->TentativeStart(); // TentativeActive() from now on
2542 std::vector
<int> indicator
= MapImeIndicators(preeditStr
.attrs
, preeditStr
.str
);
2544 for (glong i
= 0; i
< preeditStr
.uniStrLen
; i
++) {
2545 gchar u8Char
[UTF8MaxBytes
+2] = {0};
2546 const gint u8CharLen
= g_unichar_to_utf8(preeditStr
.uniStr
[i
], u8Char
);
2547 std::string docChar
= u8Char
;
2548 if (!IsUnicodeMode())
2549 docChar
= ConvertText(u8Char
, u8CharLen
, charSetSource
, "UTF-8", true);
2551 InsertCharacter(docChar
, CharacterSource::TentativeInput
);
2553 DrawImeIndicator(indicator
[i
], docChar
.size());
2556 // Move caret to ime cursor position.
2557 const int imeEndToImeCaretU32
= preeditStr
.cursor_pos
- preeditStr
.uniStrLen
;
2558 const Sci::Position imeCaretPosDoc
= pdoc
->GetRelativePosition(CurrentPosition(), imeEndToImeCaretU32
);
2560 MoveImeCarets(- CurrentPosition() + imeCaretPosDoc
);
2564 if (preeditStr
.cursor_pos
> 0) {
2565 int oneCharBefore
= pdoc
->GetRelativePosition(CurrentPosition(), -1);
2566 MoveImeCarets(- CurrentPosition() + oneCharBefore
);
2569 view
.imeCaretBlockOverride
= true;
2572 EnsureCaretVisible();
2573 ShowCaretAtCurrentPosition();
2575 errorStatus
= Status::Failure
;
2579 void ScintillaGTK::PreeditChangedWindowedThis() {
2581 PreEditString
pes(im_context
.get());
2582 if (strlen(pes
.str
) > 0) {
2583 SetCandidateWindowPos();
2585 UniquePangoLayout
layout(gtk_widget_create_pango_layout(PWidget(wText
), pes
.str
));
2586 pango_layout_set_attributes(layout
.get(), pes
.attrs
);
2589 pango_layout_get_pixel_size(layout
.get(), &w
, &h
);
2592 gdk_window_get_origin(PWindow(wText
), &x
, &y
);
2594 Point pt
= PointMainCaret();
2600 gtk_window_move(GTK_WINDOW(PWidget(wPreedit
)), x
+ static_cast<gint
>(pt
.x
), y
+ static_cast<gint
>(pt
.y
));
2601 gtk_window_resize(GTK_WINDOW(PWidget(wPreedit
)), w
, h
);
2602 gtk_widget_show(PWidget(wPreedit
));
2603 gtk_widget_queue_draw_area(PWidget(wPreeditDraw
), 0, 0, w
, h
);
2605 gtk_widget_hide(PWidget(wPreedit
));
2608 errorStatus
= Status::Failure
;
2612 void ScintillaGTK::PreeditChanged(GtkIMContext
*, ScintillaGTK
*sciThis
) {
2613 if ((sciThis
->imeInteraction
== IMEInteraction::Inline
) || (sciThis
->KoreanIME())) {
2614 sciThis
->PreeditChangedInlineThis();
2616 sciThis
->PreeditChangedWindowedThis();
2620 bool ScintillaGTK::RetrieveSurroundingThis(GtkIMContext
*context
) {
2622 const Sci::Position pos
= CurrentPosition();
2623 const int line
= pdoc
->LineFromPosition(pos
);
2624 const Sci::Position startByte
= pdoc
->LineStart(line
);
2625 const Sci::Position endByte
= pdoc
->LineEnd(line
);
2627 std::string utf8Text
;
2628 gint cursorIndex
; // index of the cursor inside utf8Text, in bytes
2629 const char *charSetBuffer
;
2631 if (IsUnicodeMode() || ! *(charSetBuffer
= CharacterSetID())) {
2632 utf8Text
= RangeText(startByte
, endByte
);
2633 cursorIndex
= pos
- startByte
;
2636 std::string tmpbuf
= RangeText(startByte
, pos
);
2637 utf8Text
= ConvertText(&tmpbuf
[0], tmpbuf
.length(), "UTF-8", charSetBuffer
, false);
2638 cursorIndex
= utf8Text
.length();
2639 if (endByte
> pos
) {
2640 tmpbuf
= RangeText(pos
, endByte
);
2641 utf8Text
+= ConvertText(&tmpbuf
[0], tmpbuf
.length(), "UTF-8", charSetBuffer
, false);
2645 gtk_im_context_set_surrounding(context
, &utf8Text
[0], utf8Text
.length(), cursorIndex
);
2649 errorStatus
= Status::Failure
;
2654 gboolean
ScintillaGTK::RetrieveSurrounding(GtkIMContext
*context
, ScintillaGTK
*sciThis
) {
2655 return sciThis
->RetrieveSurroundingThis(context
);
2658 bool ScintillaGTK::DeleteSurroundingThis(GtkIMContext
*, gint characterOffset
, gint characterCount
) {
2660 const Sci::Position startByte
= pdoc
->GetRelativePosition(CurrentPosition(), characterOffset
);
2661 if (startByte
== INVALID_POSITION
)
2664 const Sci::Position endByte
= pdoc
->GetRelativePosition(startByte
, characterCount
);
2665 if (endByte
== INVALID_POSITION
)
2668 return pdoc
->DeleteChars(startByte
, endByte
- startByte
);
2670 errorStatus
= Status::Failure
;
2675 gboolean
ScintillaGTK::DeleteSurrounding(GtkIMContext
*context
, gint characterOffset
, gint characterCount
, ScintillaGTK
*sciThis
) {
2676 return sciThis
->DeleteSurroundingThis(context
, characterOffset
, characterCount
);
2679 void ScintillaGTK::StyleSetText(GtkWidget
*widget
, GtkStyle
*, void *) {
2680 RealizeText(widget
, nullptr);
2683 void ScintillaGTK::RealizeText(GtkWidget
*widget
, void *) {
2684 // Set NULL background to avoid automatic clearing so Scintilla responsible for all drawing
2685 if (WindowFromWidget(widget
)) {
2686 #if GTK_CHECK_VERSION(3,22,0)
2687 // Appears unnecessary
2688 #elif GTK_CHECK_VERSION(3,0,0)
2689 gdk_window_set_background_pattern(WindowFromWidget(widget
), nullptr);
2691 gdk_window_set_back_pixmap(WindowFromWidget(widget
), nullptr, FALSE
);
2696 static GObjectClass
*scintilla_class_parent_class
;
2698 void ScintillaGTK::Dispose(GObject
*object
) {
2700 ScintillaObject
*scio
= SCINTILLA(object
);
2701 ScintillaGTK
*sciThis
= static_cast<ScintillaGTK
*>(scio
->pscin
);
2703 if (PWidget(sciThis
->scrollbarv
)) {
2704 gtk_widget_unparent(PWidget(sciThis
->scrollbarv
));
2705 sciThis
->scrollbarv
= nullptr;
2708 if (PWidget(sciThis
->scrollbarh
)) {
2709 gtk_widget_unparent(PWidget(sciThis
->scrollbarh
));
2710 sciThis
->scrollbarh
= nullptr;
2713 scintilla_class_parent_class
->dispose(object
);
2715 // Its dying so nowhere to save the status
2719 void ScintillaGTK::Destroy(GObject
*object
) {
2721 ScintillaObject
*scio
= SCINTILLA(object
);
2723 // This avoids a double destruction
2726 ScintillaGTK
*sciThis
= static_cast<ScintillaGTK
*>(scio
->pscin
);
2727 //Platform::DebugPrintf("Destroying %x %x\n", sciThis, object);
2728 sciThis
->Finalise();
2731 scio
->pscin
= nullptr;
2732 scintilla_class_parent_class
->finalize(object
);
2734 // Its dead so nowhere to save the status
2738 void ScintillaGTK::CheckForFontOptionChange() {
2739 const FontOptions
fontOptionsNow(PWidget(wText
));
2740 if (!(fontOptionsNow
== fontOptionsPrevious
)) {
2741 // Clear position caches
2742 InvalidateStyleData();
2744 fontOptionsPrevious
= fontOptionsNow
;
2747 #if GTK_CHECK_VERSION(3,0,0)
2749 gboolean
ScintillaGTK::DrawTextThis(cairo_t
*cr
) {
2751 CheckForFontOptionChange();
2753 paintState
= PaintState::painting
;
2754 repaintFullWindow
= false;
2756 rcPaint
= GetClientRectangle();
2758 cairo_rectangle_list_t
*oldRgnUpdate
= rgnUpdate
;
2759 rgnUpdate
= cairo_copy_clip_rectangle_list(cr
);
2760 if (rgnUpdate
&& rgnUpdate
->status
!= CAIRO_STATUS_SUCCESS
) {
2761 // If not successful then ignore
2762 fprintf(stderr
, "DrawTextThis failed to copy update region %d [%d]\n", rgnUpdate
->status
, rgnUpdate
->num_rectangles
);
2763 cairo_rectangle_list_destroy(rgnUpdate
);
2764 rgnUpdate
= nullptr;
2767 double x1
, y1
, x2
, y2
;
2768 cairo_clip_extents(cr
, &x1
, &y1
, &x2
, &y2
);
2772 rcPaint
.bottom
= y2
;
2773 PRectangle rcClient
= GetClientRectangle();
2774 paintingAllText
= rcPaint
.Contains(rcClient
);
2775 std::unique_ptr
<Surface
> surfaceWindow(Surface::Allocate(Technology::Default
));
2776 surfaceWindow
->Init(cr
, PWidget(wText
));
2777 Paint(surfaceWindow
.get(), rcPaint
);
2778 surfaceWindow
->Release();
2779 if ((paintState
== PaintState::abandoned
) || repaintFullWindow
) {
2780 // Painting area was insufficient to cover new styling or brace highlight positions
2783 paintState
= PaintState::notPainting
;
2784 repaintFullWindow
= false;
2787 cairo_rectangle_list_destroy(rgnUpdate
);
2789 rgnUpdate
= oldRgnUpdate
;
2790 paintState
= PaintState::notPainting
;
2792 errorStatus
= Status::Failure
;
2798 gboolean
ScintillaGTK::DrawText(GtkWidget
*, cairo_t
*cr
, ScintillaGTK
*sciThis
) {
2799 return sciThis
->DrawTextThis(cr
);
2802 gboolean
ScintillaGTK::DrawThis(cairo_t
*cr
) {
2804 #ifdef GTK_STYLE_CLASS_SCROLLBARS_JUNCTION /* GTK >= 3.4 */
2805 // if both scrollbars are visible, paint the little square on the bottom right corner
2806 if (verticalScrollBarVisible
&& horizontalScrollBarVisible
&& !Wrapping()) {
2807 GtkStyleContext
*styleContext
= gtk_widget_get_style_context(PWidget(wMain
));
2808 PRectangle rc
= GetClientRectangle();
2810 gtk_style_context_save(styleContext
);
2811 gtk_style_context_add_class(styleContext
, GTK_STYLE_CLASS_SCROLLBARS_JUNCTION
);
2813 gtk_render_background(styleContext
, cr
, rc
.right
, rc
.bottom
,
2814 verticalScrollBarWidth
, horizontalScrollBarHeight
);
2815 gtk_render_frame(styleContext
, cr
, rc
.right
, rc
.bottom
,
2816 verticalScrollBarWidth
, horizontalScrollBarHeight
);
2818 gtk_style_context_restore(styleContext
);
2822 gtk_container_propagate_draw(
2823 GTK_CONTAINER(PWidget(wMain
)), PWidget(scrollbarh
), cr
);
2824 gtk_container_propagate_draw(
2825 GTK_CONTAINER(PWidget(wMain
)), PWidget(scrollbarv
), cr
);
2826 // Starting from the following version, the expose event are not propagated
2827 // for double buffered non native windows, so we need to call it ourselves
2828 // or keep the default handler
2829 #if GTK_CHECK_VERSION(3,0,0)
2830 // we want to forward on any >= 3.9.2 runtime
2831 if (gtk_check_version(3, 9, 2) == nullptr) {
2832 gtk_container_propagate_draw(
2833 GTK_CONTAINER(PWidget(wMain
)), PWidget(wText
), cr
);
2837 errorStatus
= Status::Failure
;
2842 gboolean
ScintillaGTK::DrawMain(GtkWidget
*widget
, cairo_t
*cr
) {
2843 ScintillaGTK
*sciThis
= FromWidget(widget
);
2844 return sciThis
->DrawThis(cr
);
2849 gboolean
ScintillaGTK::ExposeTextThis(GtkWidget
* /*widget*/, GdkEventExpose
*ose
) {
2851 CheckForFontOptionChange();
2853 paintState
= PaintState::painting
;
2855 rcPaint
= PRectangle::FromInts(
2858 ose
->area
.x
+ ose
->area
.width
,
2859 ose
->area
.y
+ ose
->area
.height
);
2861 GdkRegion
*oldRgnUpdate
= rgnUpdate
;
2862 rgnUpdate
= gdk_region_copy(ose
->region
);
2863 const PRectangle rcClient
= GetClientRectangle();
2864 paintingAllText
= rcPaint
.Contains(rcClient
);
2866 std::unique_ptr
<Surface
> surfaceWindow(Surface::Allocate(Technology::Default
));
2867 UniqueCairo
cr(gdk_cairo_create(PWindow(wText
)));
2868 surfaceWindow
->Init(cr
.get(), PWidget(wText
));
2869 Paint(surfaceWindow
.get(), rcPaint
);
2871 if ((paintState
== PaintState::abandoned
) || repaintFullWindow
) {
2872 // Painting area was insufficient to cover new styling or brace highlight positions
2875 paintState
= PaintState::notPainting
;
2876 repaintFullWindow
= false;
2879 gdk_region_destroy(rgnUpdate
);
2881 rgnUpdate
= oldRgnUpdate
;
2883 errorStatus
= Status::Failure
;
2889 gboolean
ScintillaGTK::ExposeText(GtkWidget
*widget
, GdkEventExpose
*ose
, ScintillaGTK
*sciThis
) {
2890 return sciThis
->ExposeTextThis(widget
, ose
);
2893 gboolean
ScintillaGTK::ExposeMain(GtkWidget
*widget
, GdkEventExpose
*ose
) {
2894 ScintillaGTK
*sciThis
= FromWidget(widget
);
2895 //Platform::DebugPrintf("Expose Main %0d,%0d %0d,%0d\n",
2896 //ose->area.x, ose->area.y, ose->area.width, ose->area.height);
2897 return sciThis
->Expose(widget
, ose
);
2900 gboolean
ScintillaGTK::Expose(GtkWidget
*, GdkEventExpose
*ose
) {
2902 //fprintf(stderr, "Expose %0d,%0d %0d,%0d\n",
2903 //ose->area.x, ose->area.y, ose->area.width, ose->area.height);
2905 // The text is painted in ExposeText
2906 gtk_container_propagate_expose(
2907 GTK_CONTAINER(PWidget(wMain
)), PWidget(scrollbarh
), ose
);
2908 gtk_container_propagate_expose(
2909 GTK_CONTAINER(PWidget(wMain
)), PWidget(scrollbarv
), ose
);
2912 errorStatus
= Status::Failure
;
2919 void ScintillaGTK::ScrollSignal(GtkAdjustment
*adj
, ScintillaGTK
*sciThis
) {
2921 sciThis
->ScrollTo(static_cast<int>(gtk_adjustment_get_value(adj
)), false);
2923 sciThis
->errorStatus
= Status::Failure
;
2927 void ScintillaGTK::ScrollHSignal(GtkAdjustment
*adj
, ScintillaGTK
*sciThis
) {
2929 sciThis
->HorizontalScrollTo(static_cast<int>(gtk_adjustment_get_value(adj
)));
2931 sciThis
->errorStatus
= Status::Failure
;
2935 void ScintillaGTK::SelectionReceived(GtkWidget
*widget
,
2936 GtkSelectionData
*selection_data
, guint
) {
2937 ScintillaGTK
*sciThis
= FromWidget(widget
);
2938 //Platform::DebugPrintf("Selection received\n");
2939 sciThis
->ReceivedSelection(selection_data
);
2942 void ScintillaGTK::SelectionGet(GtkWidget
*widget
,
2943 GtkSelectionData
*selection_data
, guint info
, guint
) {
2944 ScintillaGTK
*sciThis
= FromWidget(widget
);
2946 //Platform::DebugPrintf("Selection get\n");
2947 if (SelectionOfGSD(selection_data
) == GDK_SELECTION_PRIMARY
) {
2948 if (sciThis
->primary
.Empty()) {
2949 sciThis
->CopySelectionRange(&sciThis
->primary
);
2951 sciThis
->GetSelection(selection_data
, info
, &sciThis
->primary
);
2954 sciThis
->errorStatus
= Status::Failure
;
2958 gint
ScintillaGTK::SelectionClear(GtkWidget
*widget
, GdkEventSelection
*selection_event
) {
2959 ScintillaGTK
*sciThis
= FromWidget(widget
);
2960 //Platform::DebugPrintf("Selection clear\n");
2961 sciThis
->UnclaimSelection(selection_event
);
2962 if (GTK_WIDGET_CLASS(sciThis
->parentClass
)->selection_clear_event
) {
2963 return GTK_WIDGET_CLASS(sciThis
->parentClass
)->selection_clear_event(widget
, selection_event
);
2968 gboolean
ScintillaGTK::DragMotionThis(GdkDragContext
*context
,
2969 gint x
, gint y
, guint dragtime
) {
2971 const Point npt
= Point::FromInts(x
, y
);
2972 SetDragPosition(SPositionFromLocation(npt
, false, false, UserVirtualSpace()));
2973 GdkDragAction preferredAction
= gdk_drag_context_get_suggested_action(context
);
2974 const GdkDragAction actions
= gdk_drag_context_get_actions(context
);
2975 const SelectionPosition pos
= SPositionFromLocation(npt
);
2976 if ((inDragDrop
== DragDrop::dragging
) && (PositionInSelection(pos
.Position()))) {
2977 // Avoid dragging selection onto itself as that produces a move
2978 // with no real effect but which creates undo actions.
2979 preferredAction
= static_cast<GdkDragAction
>(0);
2980 } else if (actions
== actionCopyOrMove
) {
2981 preferredAction
= GDK_ACTION_MOVE
;
2983 gdk_drag_status(context
, preferredAction
, dragtime
);
2985 errorStatus
= Status::Failure
;
2990 gboolean
ScintillaGTK::DragMotion(GtkWidget
*widget
, GdkDragContext
*context
,
2991 gint x
, gint y
, guint dragtime
) {
2992 ScintillaGTK
*sciThis
= FromWidget(widget
);
2993 return sciThis
->DragMotionThis(context
, x
, y
, dragtime
);
2996 void ScintillaGTK::DragLeave(GtkWidget
*widget
, GdkDragContext
* /*context*/, guint
) {
2997 ScintillaGTK
*sciThis
= FromWidget(widget
);
2999 sciThis
->SetDragPosition(SelectionPosition(Sci::invalidPosition
));
3000 //Platform::DebugPrintf("DragLeave %x\n", sciThis);
3002 sciThis
->errorStatus
= Status::Failure
;
3006 void ScintillaGTK::DragEnd(GtkWidget
*widget
, GdkDragContext
* /*context*/) {
3007 ScintillaGTK
*sciThis
= FromWidget(widget
);
3009 // If drag did not result in drop here or elsewhere
3010 if (!sciThis
->dragWasDropped
)
3011 sciThis
->SetEmptySelection(sciThis
->posDrag
);
3012 sciThis
->SetDragPosition(SelectionPosition(Sci::invalidPosition
));
3013 //Platform::DebugPrintf("DragEnd %x %d\n", sciThis, sciThis->dragWasDropped);
3014 sciThis
->inDragDrop
= DragDrop::none
;
3016 sciThis
->errorStatus
= Status::Failure
;
3020 gboolean
ScintillaGTK::Drop(GtkWidget
*widget
, GdkDragContext
* /*context*/,
3021 gint
, gint
, guint
) {
3022 ScintillaGTK
*sciThis
= FromWidget(widget
);
3024 //Platform::DebugPrintf("Drop %x\n", sciThis);
3025 sciThis
->SetDragPosition(SelectionPosition(Sci::invalidPosition
));
3027 sciThis
->errorStatus
= Status::Failure
;
3032 void ScintillaGTK::DragDataReceived(GtkWidget
*widget
, GdkDragContext
* /*context*/,
3033 gint
, gint
, GtkSelectionData
*selection_data
, guint
/*info*/, guint
) {
3034 ScintillaGTK
*sciThis
= FromWidget(widget
);
3036 sciThis
->ReceivedDrop(selection_data
);
3037 sciThis
->SetDragPosition(SelectionPosition(Sci::invalidPosition
));
3039 sciThis
->errorStatus
= Status::Failure
;
3043 void ScintillaGTK::DragDataGet(GtkWidget
*widget
, GdkDragContext
*context
,
3044 GtkSelectionData
*selection_data
, guint info
, guint
) {
3045 ScintillaGTK
*sciThis
= FromWidget(widget
);
3047 sciThis
->dragWasDropped
= true;
3048 if (!sciThis
->sel
.Empty()) {
3049 sciThis
->GetSelection(selection_data
, info
, &sciThis
->drag
);
3051 const GdkDragAction action
= gdk_drag_context_get_selected_action(context
);
3052 if (action
== GDK_ACTION_MOVE
) {
3053 for (size_t r
=0; r
<sciThis
->sel
.Count(); r
++) {
3054 if (sciThis
->posDrop
>= sciThis
->sel
.Range(r
).Start()) {
3055 if (sciThis
->posDrop
> sciThis
->sel
.Range(r
).End()) {
3056 sciThis
->posDrop
.Add(-sciThis
->sel
.Range(r
).Length());
3058 sciThis
->posDrop
.Add(-SelectionRange(sciThis
->posDrop
, sciThis
->sel
.Range(r
).Start()).Length());
3062 sciThis
->ClearSelection();
3064 sciThis
->SetDragPosition(SelectionPosition(Sci::invalidPosition
));
3066 sciThis
->errorStatus
= Status::Failure
;
3070 int ScintillaGTK::TimeOut(gpointer ptt
) {
3071 TimeThunk
*tt
= static_cast<TimeThunk
*>(ptt
);
3072 tt
->scintilla
->TickFor(tt
->reason
);
3076 gboolean
ScintillaGTK::IdleCallback(gpointer pSci
) {
3077 ScintillaGTK
*sciThis
= static_cast<ScintillaGTK
*>(pSci
);
3078 // Idler will be automatically stopped, if there is nothing
3079 // to do while idle.
3080 const bool ret
= sciThis
->Idle();
3082 // FIXME: This will remove the idler from GTK, we don't want to
3083 // remove it as it is removed automatically when this function
3084 // returns false (although, it should be harmless).
3085 sciThis
->SetIdle(false);
3090 gboolean
ScintillaGTK::StyleIdle(gpointer pSci
) {
3091 ScintillaGTK
*sciThis
= static_cast<ScintillaGTK
*>(pSci
);
3092 sciThis
->IdleWork();
3093 // Idler will be automatically stopped
3097 void ScintillaGTK::IdleWork() {
3102 void ScintillaGTK::QueueIdleWork(WorkItems items
, Sci::Position upTo
) {
3103 Editor::QueueIdleWork(items
, upTo
);
3105 // Only allow one style needed to be queued
3106 styleIdleID
= gdk_threads_add_idle_full(G_PRIORITY_HIGH_IDLE
, StyleIdle
, this, nullptr);
3110 void ScintillaGTK::SetDocPointer(Document
*document
) {
3111 Document
*oldDoc
= nullptr;
3112 ScintillaGTKAccessible
*sciAccessible
= nullptr;
3114 sciAccessible
= ScintillaGTKAccessible::FromAccessible(accessible
);
3115 if (sciAccessible
&& pdoc
) {
3121 Editor::SetDocPointer(document
);
3123 if (sciAccessible
) {
3124 // the accessible needs have the old Document, but also the new one active
3125 sciAccessible
->ChangeDocument(oldDoc
, pdoc
);
3132 void ScintillaGTK::PopUpCB(GtkMenuItem
*menuItem
, ScintillaGTK
*sciThis
) {
3133 guint
const action
= GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(menuItem
), "CmdNum"));
3135 sciThis
->Command(action
);
3139 gboolean
ScintillaGTK::PressCT(GtkWidget
*widget
, GdkEventButton
*event
, ScintillaGTK
*sciThis
) {
3141 if (event
->window
!= WindowFromWidget(widget
))
3143 if (event
->type
!= GDK_BUTTON_PRESS
)
3145 const Point pt
= PointOfEvent(event
);
3146 sciThis
->ct
.MouseClick(pt
);
3147 sciThis
->CallTipClick();
3153 #if GTK_CHECK_VERSION(3,0,0)
3155 gboolean
ScintillaGTK::DrawCT(GtkWidget
*widget
, cairo_t
*cr
, CallTip
*ctip
) {
3157 std::unique_ptr
<Surface
> surfaceWindow(Surface::Allocate(Technology::Default
));
3158 surfaceWindow
->Init(cr
, widget
);
3159 surfaceWindow
->SetMode(SurfaceMode(ctip
->codePage
, false));
3160 ctip
->PaintCT(surfaceWindow
.get());
3161 surfaceWindow
->Release();
3163 // No pointer back to Scintilla to save status
3170 gboolean
ScintillaGTK::ExposeCT(GtkWidget
*widget
, GdkEventExpose
* /*ose*/, CallTip
*ctip
) {
3172 std::unique_ptr
<Surface
> surfaceWindow(Surface::Allocate(Technology::Default
));
3173 UniqueCairo
cr(gdk_cairo_create(WindowFromWidget(widget
)));
3174 surfaceWindow
->Init(cr
.get(), widget
);
3175 surfaceWindow
->SetMode(SurfaceMode(ctip
->codePage
, false));
3176 ctip
->PaintCT(surfaceWindow
.get());
3178 // No pointer back to Scintilla to save status
3185 AtkObject
*ScintillaGTK::GetAccessibleThis(GtkWidget
*widget
) {
3186 return ScintillaGTKAccessible::WidgetGetAccessibleImpl(widget
, &accessible
, scintilla_class_parent_class
);
3189 AtkObject
*ScintillaGTK::GetAccessible(GtkWidget
*widget
) {
3190 return FromWidget(widget
)->GetAccessibleThis(widget
);
3193 sptr_t
ScintillaGTK::DirectFunction(
3194 sptr_t ptr
, unsigned int iMessage
, uptr_t wParam
, sptr_t lParam
) {
3195 ScintillaGTK
*sci
= reinterpret_cast<ScintillaGTK
*>(ptr
);
3196 return sci
->WndProc(static_cast<Message
>(iMessage
), wParam
, lParam
);
3199 sptr_t
ScintillaGTK::DirectStatusFunction(
3200 sptr_t ptr
, unsigned int iMessage
, uptr_t wParam
, sptr_t lParam
, int *pStatus
) {
3201 ScintillaGTK
*sci
= reinterpret_cast<ScintillaGTK
*>(ptr
);
3202 const sptr_t returnValue
= sci
->WndProc(static_cast<Message
>(iMessage
), wParam
, lParam
);
3203 *pStatus
= static_cast<int>(sci
->errorStatus
);
3207 /* legacy name for scintilla_object_send_message */
3209 sptr_t
scintilla_send_message(ScintillaObject
*sci
, unsigned int iMessage
, uptr_t wParam
, sptr_t lParam
) {
3210 ScintillaGTK
*psci
= static_cast<ScintillaGTK
*>(sci
->pscin
);
3211 return psci
->WndProc(static_cast<Message
>(iMessage
), wParam
, lParam
);
3215 gintptr
scintilla_object_send_message(ScintillaObject
*sci
, unsigned int iMessage
, uptr_t wParam
, sptr_t lParam
) {
3216 return scintilla_send_message(sci
, iMessage
, wParam
, lParam
);
3219 static void scintilla_class_init(ScintillaClass
*klass
);
3220 static void scintilla_init(ScintillaObject
*sci
);
3222 /* legacy name for scintilla_object_get_type */
3224 GType
scintilla_get_type() {
3225 static GType scintilla_type
= 0;
3228 if (!scintilla_type
) {
3229 scintilla_type
= g_type_from_name("ScintillaObject");
3230 if (!scintilla_type
) {
3231 static GTypeInfo scintilla_info
= {
3232 (guint16
) sizeof(ScintillaObjectClass
),
3233 nullptr, //(GBaseInitFunc)
3234 nullptr, //(GBaseFinalizeFunc)
3235 (GClassInitFunc
) scintilla_class_init
,
3236 nullptr, //(GClassFinalizeFunc)
3237 nullptr, //gconstpointer data
3238 (guint16
) sizeof(ScintillaObject
),
3240 (GInstanceInitFunc
) scintilla_init
,
3241 nullptr //(GTypeValueTable*)
3243 scintilla_type
= g_type_register_static(
3244 GTK_TYPE_CONTAINER
, "ScintillaObject", &scintilla_info
, (GTypeFlags
) 0);
3250 return scintilla_type
;
3254 GType
scintilla_object_get_type() {
3255 return scintilla_get_type();
3258 void ScintillaGTK::ClassInit(OBJECT_CLASS
*object_class
, GtkWidgetClass
*widget_class
, GtkContainerClass
*container_class
) {
3259 Platform_Initialise();
3260 atomUTF8
= gdk_atom_intern("UTF8_STRING", FALSE
);
3261 atomUTF8Mime
= gdk_atom_intern("text/plain;charset=utf-8", FALSE
);
3262 atomString
= GDK_SELECTION_TYPE_STRING
;
3263 atomUriList
= gdk_atom_intern("text/uri-list", FALSE
);
3264 atomDROPFILES_DND
= gdk_atom_intern("DROPFILES_DND", FALSE
);
3266 // Define default signal handlers for the class: Could move more
3267 // of the signal handlers here (those that currently attached to wDraw
3268 // in Init() may require coordinate translation?)
3270 object_class
->dispose
= Dispose
;
3271 object_class
->finalize
= Destroy
;
3272 #if GTK_CHECK_VERSION(3,0,0)
3273 widget_class
->get_preferred_width
= GetPreferredWidth
;
3274 widget_class
->get_preferred_height
= GetPreferredHeight
;
3276 widget_class
->size_request
= SizeRequest
;
3278 widget_class
->size_allocate
= SizeAllocate
;
3279 #if GTK_CHECK_VERSION(3,0,0)
3280 widget_class
->draw
= DrawMain
;
3282 widget_class
->expose_event
= ExposeMain
;
3284 widget_class
->motion_notify_event
= Motion
;
3285 widget_class
->button_press_event
= Press
;
3286 widget_class
->button_release_event
= MouseRelease
;
3287 widget_class
->scroll_event
= ScrollEvent
;
3288 widget_class
->key_press_event
= KeyPress
;
3289 widget_class
->key_release_event
= KeyRelease
;
3290 widget_class
->focus_in_event
= FocusIn
;
3291 widget_class
->focus_out_event
= FocusOut
;
3292 widget_class
->selection_received
= SelectionReceived
;
3293 widget_class
->selection_get
= SelectionGet
;
3294 widget_class
->selection_clear_event
= SelectionClear
;
3296 widget_class
->drag_data_received
= DragDataReceived
;
3297 widget_class
->drag_motion
= DragMotion
;
3298 widget_class
->drag_leave
= DragLeave
;
3299 widget_class
->drag_end
= DragEnd
;
3300 widget_class
->drag_drop
= Drop
;
3301 widget_class
->drag_data_get
= DragDataGet
;
3303 widget_class
->realize
= Realize
;
3304 widget_class
->unrealize
= UnRealize
;
3305 widget_class
->map
= Map
;
3306 widget_class
->unmap
= UnMap
;
3308 widget_class
->get_accessible
= GetAccessible
;
3310 container_class
->forall
= MainForAll
;
3313 static void scintilla_class_init(ScintillaClass
*klass
) {
3315 OBJECT_CLASS
*object_class
= reinterpret_cast<OBJECT_CLASS
*>(klass
);
3316 GtkWidgetClass
*widget_class
= reinterpret_cast<GtkWidgetClass
*>(klass
);
3317 GtkContainerClass
*container_class
= reinterpret_cast<GtkContainerClass
*>(klass
);
3319 const GSignalFlags sigflags
= static_cast<GSignalFlags
>(G_SIGNAL_ACTION
| G_SIGNAL_RUN_LAST
);
3320 scintilla_signals
[COMMAND_SIGNAL
] = g_signal_new(
3322 G_TYPE_FROM_CLASS(object_class
),
3324 G_STRUCT_OFFSET(ScintillaClass
, command
),
3325 nullptr, //(GSignalAccumulator)
3326 nullptr, //(gpointer)
3327 scintilla_marshal_VOID__INT_OBJECT
,
3329 2, G_TYPE_INT
, GTK_TYPE_WIDGET
);
3331 scintilla_signals
[NOTIFY_SIGNAL
] = g_signal_new(
3333 G_TYPE_FROM_CLASS(object_class
),
3335 G_STRUCT_OFFSET(ScintillaClass
, notify
),
3336 nullptr, //(GSignalAccumulator)
3337 nullptr, //(gpointer)
3338 scintilla_marshal_VOID__INT_BOXED
,
3340 2, G_TYPE_INT
, SCINTILLA_TYPE_NOTIFICATION
);
3342 klass
->command
= nullptr;
3343 klass
->notify
= nullptr;
3344 scintilla_class_parent_class
= G_OBJECT_CLASS(g_type_class_peek_parent(klass
));
3345 ScintillaGTK::ClassInit(object_class
, widget_class
, container_class
);
3350 static void scintilla_init(ScintillaObject
*sci
) {
3352 gtk_widget_set_can_focus(GTK_WIDGET(sci
), TRUE
);
3353 sci
->pscin
= new ScintillaGTK(sci
);
3358 /* legacy name for scintilla_object_new */
3360 GtkWidget
*scintilla_new() {
3361 GtkWidget
*widget
= GTK_WIDGET(g_object_new(scintilla_get_type(), nullptr));
3362 gtk_widget_set_direction(widget
, GTK_TEXT_DIR_LTR
);
3368 GtkWidget
*scintilla_object_new() {
3369 return scintilla_new();
3372 void scintilla_set_id(ScintillaObject
*sci
, uptr_t id
) {
3373 ScintillaGTK
*psci
= static_cast<ScintillaGTK
*>(sci
->pscin
);
3374 psci
->ctrlID
= static_cast<int>(id
);
3377 void scintilla_release_resources(void) {
3379 Platform_Finalise();
3384 /* Define a dummy boxed type because g-ir-scanner is unable to
3385 * recognize gpointer-derived types. Note that SCNotificaiton
3386 * is always allocated on stack so copying is not appropriate. */
3387 static void *copy_(void *src
) { return src
; }
3388 static void free_(void *) { }
3391 GType
scnotification_get_type(void) {
3392 static gsize type_id
= 0;
3393 if (g_once_init_enter(&type_id
)) {
3394 const gsize id
= (gsize
) g_boxed_type_register_static(
3395 g_intern_static_string("SCNotification"),
3396 (GBoxedCopyFunc
) copy_
,
3397 (GBoxedFreeFunc
) free_
);
3398 g_once_init_leave(&type_id
, id
);
3400 return (GType
) type_id
;