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