Update Scintilla to version 3.7.3
[geany-mirror.git] / scintilla / gtk / ScintillaGTK.cxx
blobc48e24aaa2b1ff86f7a65880fdf738668be50685
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 <stdlib.h>
7 #include <string.h>
8 #include <stdio.h>
9 #include <time.h>
10 #include <math.h>
11 #include <assert.h>
12 #include <ctype.h>
14 #include <stdexcept>
15 #include <new>
16 #include <string>
17 #include <vector>
18 #include <map>
19 #include <algorithm>
21 #include <glib.h>
22 #include <gmodule.h>
23 #include <gdk/gdk.h>
24 #include <gtk/gtk.h>
25 #include <gdk/gdkkeysyms.h>
26 #if defined(GDK_WINDOWING_WAYLAND)
27 #include <gdk/gdkwayland.h>
28 #endif
30 #if defined(__WIN32__) || defined(_MSC_VER)
31 #include <windows.h>
32 #endif
34 #include "Platform.h"
36 #include "ILexer.h"
37 #include "Scintilla.h"
38 #include "ScintillaWidget.h"
39 #ifdef SCI_LEXER
40 #include "SciLexer.h"
41 #endif
42 #include "StringCopy.h"
43 #ifdef SCI_LEXER
44 #include "LexerModule.h"
45 #endif
46 #include "Position.h"
47 #include "SplitVector.h"
48 #include "Partitioning.h"
49 #include "RunStyles.h"
50 #include "ContractionState.h"
51 #include "CellBuffer.h"
52 #include "CallTip.h"
53 #include "KeyMap.h"
54 #include "Indicator.h"
55 #include "XPM.h"
56 #include "LineMarker.h"
57 #include "Style.h"
58 #include "ViewStyle.h"
59 #include "CharClassify.h"
60 #include "Decoration.h"
61 #include "CaseFolder.h"
62 #include "Document.h"
63 #include "CaseConvert.h"
64 #include "UniConversion.h"
65 #include "UnicodeFromUTF8.h"
66 #include "Selection.h"
67 #include "PositionCache.h"
68 #include "EditModel.h"
69 #include "MarginView.h"
70 #include "EditView.h"
71 #include "Editor.h"
72 #include "AutoComplete.h"
73 #include "ScintillaBase.h"
75 #ifdef SCI_LEXER
76 #include "ExternalLexer.h"
77 #endif
79 #include "ScintillaGTK.h"
80 #include "scintilla-marshal.h"
81 #include "ScintillaGTKAccessible.h"
83 #include "Converter.h"
85 #if GTK_CHECK_VERSION(2,20,0)
86 #define IS_WIDGET_REALIZED(w) (gtk_widget_get_realized(GTK_WIDGET(w)))
87 #define IS_WIDGET_MAPPED(w) (gtk_widget_get_mapped(GTK_WIDGET(w)))
88 #else
89 #define IS_WIDGET_REALIZED(w) (GTK_WIDGET_REALIZED(w))
90 #define IS_WIDGET_MAPPED(w) (GTK_WIDGET_MAPPED(w))
91 #endif
93 #define SC_INDICATOR_INPUT INDIC_IME
94 #define SC_INDICATOR_TARGET INDIC_IME+1
95 #define SC_INDICATOR_CONVERTED INDIC_IME+2
96 #define SC_INDICATOR_UNKNOWN INDIC_IME_MAX
98 static GdkWindow *WindowFromWidget(GtkWidget *w) {
99 return gtk_widget_get_window(w);
102 #ifdef _MSC_VER
103 // Constant conditional expressions are because of GTK+ headers
104 #pragma warning(disable: 4127)
105 // Ignore unreferenced local functions in GTK+ headers
106 #pragma warning(disable: 4505)
107 #endif
109 #ifdef SCI_NAMESPACE
110 using namespace Scintilla;
111 #endif
113 static GdkWindow *PWindow(const Window &w) {
114 GtkWidget *widget = static_cast<GtkWidget *>(w.GetID());
115 return gtk_widget_get_window(widget);
118 extern std::string UTF8FromLatin1(const char *s, int len);
120 enum {
121 COMMAND_SIGNAL,
122 NOTIFY_SIGNAL,
123 LAST_SIGNAL
126 static gint scintilla_signals[LAST_SIGNAL] = { 0 };
128 enum {
129 TARGET_STRING,
130 TARGET_TEXT,
131 TARGET_COMPOUND_TEXT,
132 TARGET_UTF8_STRING,
133 TARGET_URI
136 GdkAtom ScintillaGTK::atomClipboard = 0;
137 GdkAtom ScintillaGTK::atomUTF8 = 0;
138 GdkAtom ScintillaGTK::atomString = 0;
139 GdkAtom ScintillaGTK::atomUriList = 0;
140 GdkAtom ScintillaGTK::atomDROPFILES_DND = 0;
142 static const GtkTargetEntry clipboardCopyTargets[] = {
143 { (gchar *) "UTF8_STRING", 0, TARGET_UTF8_STRING },
144 { (gchar *) "STRING", 0, TARGET_STRING },
146 static const gint nClipboardCopyTargets = ELEMENTS(clipboardCopyTargets);
148 static const GtkTargetEntry clipboardPasteTargets[] = {
149 { (gchar *) "text/uri-list", 0, TARGET_URI },
150 { (gchar *) "UTF8_STRING", 0, TARGET_UTF8_STRING },
151 { (gchar *) "STRING", 0, TARGET_STRING },
153 static const gint nClipboardPasteTargets = ELEMENTS(clipboardPasteTargets);
155 static GtkWidget *PWidget(Window &w) {
156 return static_cast<GtkWidget *>(w.GetID());
159 ScintillaGTK *ScintillaGTK::FromWidget(GtkWidget *widget) {
160 ScintillaObject *scio = SCINTILLA(widget);
161 return static_cast<ScintillaGTK *>(scio->pscin);
164 ScintillaGTK::ScintillaGTK(_ScintillaObject *sci_) :
165 adjustmentv(0), adjustmenth(0),
166 verticalScrollBarWidth(30), horizontalScrollBarHeight(30),
167 evbtn(0), capturedMouse(false), dragWasDropped(false),
168 lastKey(0), rectangularSelectionModifier(SCMOD_CTRL), parentClass(0),
169 im_context(NULL), lastNonCommonScript(PANGO_SCRIPT_INVALID_CODE),
170 lastWheelMouseDirection(0),
171 wheelMouseIntensity(0),
172 smoothScrollY(0),
173 smoothScrollX(0),
174 rgnUpdate(0),
175 repaintFullWindow(false),
176 styleIdleID(0),
177 accessible(0) {
178 sci = sci_;
179 wMain = GTK_WIDGET(sci);
181 #if PLAT_GTK_WIN32
182 rectangularSelectionModifier = SCMOD_ALT;
183 #else
184 rectangularSelectionModifier = SCMOD_CTRL;
185 #endif
187 #if PLAT_GTK_WIN32
188 // There does not seem to be a real standard for indicating that the clipboard
189 // contains a rectangular selection, so copy Developer Studio.
190 cfColumnSelect = static_cast<CLIPFORMAT>(
191 ::RegisterClipboardFormat("MSDEVColumnSelect"));
193 // Get intellimouse parameters when running on win32; otherwise use
194 // reasonable default
195 #ifndef SPI_GETWHEELSCROLLLINES
196 #define SPI_GETWHEELSCROLLLINES 104
197 #endif
198 ::SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &linesPerScroll, 0);
199 #else
200 linesPerScroll = 4;
201 #endif
202 lastWheelMouseTime.tv_sec = 0;
203 lastWheelMouseTime.tv_usec = 0;
205 Initialise();
208 ScintillaGTK::~ScintillaGTK() {
209 if (styleIdleID) {
210 g_source_remove(styleIdleID);
211 styleIdleID = 0;
213 if (evbtn) {
214 gdk_event_free(reinterpret_cast<GdkEvent *>(evbtn));
215 evbtn = 0;
217 wPreedit.Destroy();
220 static void UnRefCursor(GdkCursor *cursor) {
221 #if GTK_CHECK_VERSION(3,0,0)
222 g_object_unref(cursor);
223 #else
224 gdk_cursor_unref(cursor);
225 #endif
228 void ScintillaGTK::RealizeThis(GtkWidget *widget) {
229 //Platform::DebugPrintf("ScintillaGTK::realize this\n");
230 #if GTK_CHECK_VERSION(2,20,0)
231 gtk_widget_set_realized(widget, TRUE);
232 #else
233 GTK_WIDGET_SET_FLAGS(widget, GTK_REALIZED);
234 #endif
235 GdkWindowAttr attrs;
236 attrs.window_type = GDK_WINDOW_CHILD;
237 GtkAllocation allocation;
238 gtk_widget_get_allocation(widget, &allocation);
239 attrs.x = allocation.x;
240 attrs.y = allocation.y;
241 attrs.width = allocation.width;
242 attrs.height = allocation.height;
243 attrs.wclass = GDK_INPUT_OUTPUT;
244 attrs.visual = gtk_widget_get_visual(widget);
245 #if !GTK_CHECK_VERSION(3,0,0)
246 attrs.colormap = gtk_widget_get_colormap(widget);
247 #endif
248 attrs.event_mask = gtk_widget_get_events(widget) | GDK_EXPOSURE_MASK;
249 GdkDisplay *pdisplay = gtk_widget_get_display(widget);
250 GdkCursor *cursor = gdk_cursor_new_for_display(pdisplay, GDK_XTERM);
251 attrs.cursor = cursor;
252 #if GTK_CHECK_VERSION(3,0,0)
253 gtk_widget_set_window(widget, gdk_window_new(gtk_widget_get_parent_window(widget), &attrs,
254 GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_CURSOR));
255 #if GTK_CHECK_VERSION(3,8,0)
256 gtk_widget_register_window(widget, gtk_widget_get_window(widget));
257 #else
258 gdk_window_set_user_data(gtk_widget_get_window(widget), widget);
259 #endif
260 #if !GTK_CHECK_VERSION(3,18,0)
261 gtk_style_context_set_background(gtk_widget_get_style_context(widget),
262 gtk_widget_get_window(widget));
263 #endif
264 gdk_window_show(gtk_widget_get_window(widget));
265 UnRefCursor(cursor);
266 #else
267 widget->window = gdk_window_new(gtk_widget_get_parent_window(widget), &attrs,
268 GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP | GDK_WA_CURSOR);
269 gdk_window_set_user_data(widget->window, widget);
270 widget->style = gtk_style_attach(widget->style, widget->window);
271 gdk_window_set_background(widget->window, &widget->style->bg[GTK_STATE_NORMAL]);
272 gdk_window_show(widget->window);
273 UnRefCursor(cursor);
274 #endif
275 gtk_widget_realize(PWidget(wPreedit));
276 gtk_widget_realize(PWidget(wPreeditDraw));
278 im_context = gtk_im_multicontext_new();
279 g_signal_connect(G_OBJECT(im_context), "commit",
280 G_CALLBACK(Commit), this);
281 g_signal_connect(G_OBJECT(im_context), "preedit_changed",
282 G_CALLBACK(PreeditChanged), this);
283 gtk_im_context_set_client_window(im_context, WindowFromWidget(widget));
284 GtkWidget *widtxt = PWidget(wText); // // No code inside the G_OBJECT macro
285 g_signal_connect_after(G_OBJECT(widtxt), "style_set",
286 G_CALLBACK(ScintillaGTK::StyleSetText), NULL);
287 g_signal_connect_after(G_OBJECT(widtxt), "realize",
288 G_CALLBACK(ScintillaGTK::RealizeText), NULL);
289 gtk_widget_realize(widtxt);
290 gtk_widget_realize(PWidget(scrollbarv));
291 gtk_widget_realize(PWidget(scrollbarh));
293 cursor = gdk_cursor_new_for_display(pdisplay, GDK_XTERM);
294 gdk_window_set_cursor(PWindow(wText), cursor);
295 UnRefCursor(cursor);
297 cursor = gdk_cursor_new_for_display(pdisplay, GDK_LEFT_PTR);
298 gdk_window_set_cursor(PWindow(scrollbarv), cursor);
299 UnRefCursor(cursor);
301 cursor = gdk_cursor_new_for_display(pdisplay, GDK_LEFT_PTR);
302 gdk_window_set_cursor(PWindow(scrollbarh), cursor);
303 UnRefCursor(cursor);
305 gtk_selection_add_targets(widget, GDK_SELECTION_PRIMARY,
306 clipboardCopyTargets, nClipboardCopyTargets);
309 void ScintillaGTK::Realize(GtkWidget *widget) {
310 ScintillaGTK *sciThis = FromWidget(widget);
311 sciThis->RealizeThis(widget);
314 void ScintillaGTK::UnRealizeThis(GtkWidget *widget) {
315 try {
316 gtk_selection_clear_targets(widget, GDK_SELECTION_PRIMARY);
318 if (IS_WIDGET_MAPPED(widget)) {
319 gtk_widget_unmap(widget);
321 #if GTK_CHECK_VERSION(2,20,0)
322 gtk_widget_set_realized(widget, FALSE);
323 #else
324 GTK_WIDGET_UNSET_FLAGS(widget, GTK_REALIZED);
325 #endif
326 gtk_widget_unrealize(PWidget(wText));
327 if (PWidget(scrollbarv))
328 gtk_widget_unrealize(PWidget(scrollbarv));
329 if (PWidget(scrollbarh))
330 gtk_widget_unrealize(PWidget(scrollbarh));
331 gtk_widget_unrealize(PWidget(wPreedit));
332 gtk_widget_unrealize(PWidget(wPreeditDraw));
333 g_object_unref(im_context);
334 im_context = NULL;
335 if (GTK_WIDGET_CLASS(parentClass)->unrealize)
336 GTK_WIDGET_CLASS(parentClass)->unrealize(widget);
338 Finalise();
339 } catch (...) {
340 errorStatus = SC_STATUS_FAILURE;
344 void ScintillaGTK::UnRealize(GtkWidget *widget) {
345 ScintillaGTK *sciThis = FromWidget(widget);
346 sciThis->UnRealizeThis(widget);
349 static void MapWidget(GtkWidget *widget) {
350 if (widget &&
351 gtk_widget_get_visible(GTK_WIDGET(widget)) &&
352 !IS_WIDGET_MAPPED(widget)) {
353 gtk_widget_map(widget);
357 void ScintillaGTK::MapThis() {
358 try {
359 //Platform::DebugPrintf("ScintillaGTK::map this\n");
360 #if GTK_CHECK_VERSION(2,20,0)
361 gtk_widget_set_mapped(PWidget(wMain), TRUE);
362 #else
363 GTK_WIDGET_SET_FLAGS(PWidget(wMain), GTK_MAPPED);
364 #endif
365 MapWidget(PWidget(wText));
366 MapWidget(PWidget(scrollbarh));
367 MapWidget(PWidget(scrollbarv));
368 wMain.SetCursor(Window::cursorArrow);
369 scrollbarv.SetCursor(Window::cursorArrow);
370 scrollbarh.SetCursor(Window::cursorArrow);
371 ChangeSize();
372 gdk_window_show(PWindow(wMain));
373 } catch (...) {
374 errorStatus = SC_STATUS_FAILURE;
378 void ScintillaGTK::Map(GtkWidget *widget) {
379 ScintillaGTK *sciThis = FromWidget(widget);
380 sciThis->MapThis();
383 void ScintillaGTK::UnMapThis() {
384 try {
385 //Platform::DebugPrintf("ScintillaGTK::unmap this\n");
386 #if GTK_CHECK_VERSION(2,20,0)
387 gtk_widget_set_mapped(PWidget(wMain), FALSE);
388 #else
389 GTK_WIDGET_UNSET_FLAGS(PWidget(wMain), GTK_MAPPED);
390 #endif
391 DropGraphics(false);
392 gdk_window_hide(PWindow(wMain));
393 gtk_widget_unmap(PWidget(wText));
394 if (PWidget(scrollbarh))
395 gtk_widget_unmap(PWidget(scrollbarh));
396 if (PWidget(scrollbarv))
397 gtk_widget_unmap(PWidget(scrollbarv));
398 } catch (...) {
399 errorStatus = SC_STATUS_FAILURE;
403 void ScintillaGTK::UnMap(GtkWidget *widget) {
404 ScintillaGTK *sciThis = FromWidget(widget);
405 sciThis->UnMapThis();
408 void ScintillaGTK::ForAll(GtkCallback callback, gpointer callback_data) {
409 try {
410 (*callback) (PWidget(wText), callback_data);
411 if (PWidget(scrollbarv))
412 (*callback) (PWidget(scrollbarv), callback_data);
413 if (PWidget(scrollbarh))
414 (*callback) (PWidget(scrollbarh), callback_data);
415 } catch (...) {
416 errorStatus = SC_STATUS_FAILURE;
420 void ScintillaGTK::MainForAll(GtkContainer *container, gboolean include_internals, GtkCallback callback, gpointer callback_data) {
421 ScintillaGTK *sciThis = FromWidget((GtkWidget *)container);
423 if (callback != NULL && include_internals) {
424 sciThis->ForAll(callback, callback_data);
428 namespace {
430 class PreEditString {
431 public:
432 gchar *str;
433 gint cursor_pos;
434 PangoAttrList *attrs;
435 gboolean validUTF8;
436 glong uniStrLen;
437 gunichar *uniStr;
438 PangoScript pscript;
440 explicit PreEditString(GtkIMContext *im_context) {
441 gtk_im_context_get_preedit_string(im_context, &str, &attrs, &cursor_pos);
442 validUTF8 = g_utf8_validate(str, strlen(str), NULL);
443 uniStr = g_utf8_to_ucs4_fast(str, strlen(str), &uniStrLen);
444 pscript = pango_script_for_unichar(uniStr[0]);
446 ~PreEditString() {
447 g_free(str);
448 g_free(uniStr);
449 pango_attr_list_unref(attrs);
455 gint ScintillaGTK::FocusInThis(GtkWidget *) {
456 try {
457 SetFocusState(true);
458 if (im_context != NULL) {
459 PreEditString pes(im_context);
460 if (PWidget(wPreedit) != NULL) {
461 if (strlen(pes.str) > 0) {
462 gtk_widget_show(PWidget(wPreedit));
463 } else {
464 gtk_widget_hide(PWidget(wPreedit));
467 gtk_im_context_focus_in(im_context);
470 } catch (...) {
471 errorStatus = SC_STATUS_FAILURE;
473 return FALSE;
476 gint ScintillaGTK::FocusIn(GtkWidget *widget, GdkEventFocus * /*event*/) {
477 ScintillaGTK *sciThis = FromWidget(widget);
478 return sciThis->FocusInThis(widget);
481 gint ScintillaGTK::FocusOutThis(GtkWidget *) {
482 try {
483 SetFocusState(false);
485 if (PWidget(wPreedit) != NULL)
486 gtk_widget_hide(PWidget(wPreedit));
487 if (im_context != NULL)
488 gtk_im_context_focus_out(im_context);
490 } catch (...) {
491 errorStatus = SC_STATUS_FAILURE;
493 return FALSE;
496 gint ScintillaGTK::FocusOut(GtkWidget *widget, GdkEventFocus * /*event*/) {
497 ScintillaGTK *sciThis = FromWidget(widget);
498 return sciThis->FocusOutThis(widget);
501 void ScintillaGTK::SizeRequest(GtkWidget *widget, GtkRequisition *requisition) {
502 ScintillaGTK *sciThis = FromWidget(widget);
503 requisition->width = 1;
504 requisition->height = 1;
505 GtkRequisition child_requisition;
506 #if GTK_CHECK_VERSION(3,0,0)
507 gtk_widget_get_preferred_size(PWidget(sciThis->scrollbarh), NULL, &child_requisition);
508 gtk_widget_get_preferred_size(PWidget(sciThis->scrollbarv), NULL, &child_requisition);
509 #else
510 gtk_widget_size_request(PWidget(sciThis->scrollbarh), &child_requisition);
511 gtk_widget_size_request(PWidget(sciThis->scrollbarv), &child_requisition);
512 #endif
515 #if GTK_CHECK_VERSION(3,0,0)
517 void ScintillaGTK::GetPreferredWidth(GtkWidget *widget, gint *minimalWidth, gint *naturalWidth) {
518 GtkRequisition requisition;
519 SizeRequest(widget, &requisition);
520 *minimalWidth = *naturalWidth = requisition.width;
523 void ScintillaGTK::GetPreferredHeight(GtkWidget *widget, gint *minimalHeight, gint *naturalHeight) {
524 GtkRequisition requisition;
525 SizeRequest(widget, &requisition);
526 *minimalHeight = *naturalHeight = requisition.height;
529 #endif
531 void ScintillaGTK::SizeAllocate(GtkWidget *widget, GtkAllocation *allocation) {
532 ScintillaGTK *sciThis = FromWidget(widget);
533 try {
534 gtk_widget_set_allocation(widget, allocation);
535 if (IS_WIDGET_REALIZED(widget))
536 gdk_window_move_resize(WindowFromWidget(widget),
537 allocation->x,
538 allocation->y,
539 allocation->width,
540 allocation->height);
542 sciThis->Resize(allocation->width, allocation->height);
544 } catch (...) {
545 sciThis->errorStatus = SC_STATUS_FAILURE;
549 void ScintillaGTK::Initialise() {
550 //Platform::DebugPrintf("ScintillaGTK::Initialise\n");
551 parentClass = reinterpret_cast<GtkWidgetClass *>(
552 g_type_class_ref(gtk_container_get_type()));
554 gint maskSmooth = 0;
555 #if defined(GDK_WINDOWING_WAYLAND)
556 GdkDisplay *pdisplay = gdk_display_get_default();
557 if (GDK_IS_WAYLAND_DISPLAY(pdisplay)) {
558 // On Wayland, touch pads only produce smooth scroll events
559 maskSmooth = GDK_SMOOTH_SCROLL_MASK;
561 #endif
563 gtk_widget_set_can_focus(PWidget(wMain), TRUE);
564 gtk_widget_set_sensitive(PWidget(wMain), TRUE);
565 gtk_widget_set_events(PWidget(wMain),
566 GDK_EXPOSURE_MASK
567 | GDK_SCROLL_MASK
568 | maskSmooth
569 | GDK_STRUCTURE_MASK
570 | GDK_KEY_PRESS_MASK
571 | GDK_KEY_RELEASE_MASK
572 | GDK_FOCUS_CHANGE_MASK
573 | GDK_LEAVE_NOTIFY_MASK
574 | GDK_BUTTON_PRESS_MASK
575 | GDK_BUTTON_RELEASE_MASK
576 | GDK_POINTER_MOTION_MASK
577 | GDK_POINTER_MOTION_HINT_MASK);
579 wText = gtk_drawing_area_new();
580 gtk_widget_set_parent(PWidget(wText), PWidget(wMain));
581 GtkWidget *widtxt = PWidget(wText); // No code inside the G_OBJECT macro
582 gtk_widget_show(widtxt);
583 #if GTK_CHECK_VERSION(3,0,0)
584 g_signal_connect(G_OBJECT(widtxt), "draw",
585 G_CALLBACK(ScintillaGTK::DrawText), this);
586 #else
587 g_signal_connect(G_OBJECT(widtxt), "expose_event",
588 G_CALLBACK(ScintillaGTK::ExposeText), this);
589 #endif
590 #if GTK_CHECK_VERSION(3,0,0)
591 // we need a runtime check because we don't want double buffering when
592 // running on >= 3.9.2
593 if (gtk_check_version(3,9,2) != NULL /* on < 3.9.2 */)
594 #endif
596 #if !GTK_CHECK_VERSION(3,14,0)
597 // Avoid background drawing flash/missing redraws
598 gtk_widget_set_double_buffered(widtxt, FALSE);
599 #endif
601 gtk_widget_set_events(widtxt, GDK_EXPOSURE_MASK);
602 gtk_widget_set_size_request(widtxt, 100, 100);
603 adjustmentv = GTK_ADJUSTMENT(gtk_adjustment_new(0.0, 0.0, 201.0, 1.0, 20.0, 20.0));
604 #if GTK_CHECK_VERSION(3,0,0)
605 scrollbarv = gtk_scrollbar_new(GTK_ORIENTATION_VERTICAL, GTK_ADJUSTMENT(adjustmentv));
606 #else
607 scrollbarv = gtk_vscrollbar_new(GTK_ADJUSTMENT(adjustmentv));
608 #endif
609 gtk_widget_set_can_focus(PWidget(scrollbarv), FALSE);
610 g_signal_connect(G_OBJECT(adjustmentv), "value_changed",
611 G_CALLBACK(ScrollSignal), this);
612 gtk_widget_set_parent(PWidget(scrollbarv), PWidget(wMain));
613 gtk_widget_show(PWidget(scrollbarv));
615 adjustmenth = GTK_ADJUSTMENT(gtk_adjustment_new(0.0, 0.0, 101.0, 1.0, 20.0, 20.0));
616 #if GTK_CHECK_VERSION(3,0,0)
617 scrollbarh = gtk_scrollbar_new(GTK_ORIENTATION_HORIZONTAL, GTK_ADJUSTMENT(adjustmenth));
618 #else
619 scrollbarh = gtk_hscrollbar_new(GTK_ADJUSTMENT(adjustmenth));
620 #endif
621 gtk_widget_set_can_focus(PWidget(scrollbarh), FALSE);
622 g_signal_connect(G_OBJECT(adjustmenth), "value_changed",
623 G_CALLBACK(ScrollHSignal), this);
624 gtk_widget_set_parent(PWidget(scrollbarh), PWidget(wMain));
625 gtk_widget_show(PWidget(scrollbarh));
627 gtk_widget_grab_focus(PWidget(wMain));
629 gtk_drag_dest_set(GTK_WIDGET(PWidget(wMain)),
630 GTK_DEST_DEFAULT_ALL, clipboardPasteTargets, nClipboardPasteTargets,
631 static_cast<GdkDragAction>(GDK_ACTION_COPY | GDK_ACTION_MOVE));
633 /* create pre-edit window */
634 wPreedit = gtk_window_new(GTK_WINDOW_POPUP);
635 wPreeditDraw = gtk_drawing_area_new();
636 GtkWidget *predrw = PWidget(wPreeditDraw); // No code inside the G_OBJECT macro
637 #if GTK_CHECK_VERSION(3,0,0)
638 g_signal_connect(G_OBJECT(predrw), "draw",
639 G_CALLBACK(DrawPreedit), this);
640 #else
641 g_signal_connect(G_OBJECT(predrw), "expose_event",
642 G_CALLBACK(ExposePreedit), this);
643 #endif
644 gtk_container_add(GTK_CONTAINER(PWidget(wPreedit)), predrw);
645 gtk_widget_show(predrw);
647 // Set caret period based on GTK settings
648 gboolean blinkOn = false;
649 if (g_object_class_find_property(G_OBJECT_GET_CLASS(
650 G_OBJECT(gtk_settings_get_default())), "gtk-cursor-blink")) {
651 g_object_get(G_OBJECT(
652 gtk_settings_get_default()), "gtk-cursor-blink", &blinkOn, NULL);
654 if (blinkOn &&
655 g_object_class_find_property(G_OBJECT_GET_CLASS(
656 G_OBJECT(gtk_settings_get_default())), "gtk-cursor-blink-time")) {
657 gint value;
658 g_object_get(G_OBJECT(
659 gtk_settings_get_default()), "gtk-cursor-blink-time", &value, NULL);
660 caret.period = gint(value / 1.75);
661 } else {
662 caret.period = 0;
665 for (TickReason tr = tickCaret; tr <= tickDwell; tr = static_cast<TickReason>(tr + 1)) {
666 timers[tr].reason = tr;
667 timers[tr].scintilla = this;
669 vs.indicators[SC_INDICATOR_UNKNOWN] = Indicator(INDIC_HIDDEN, ColourDesired(0, 0, 0xff));
670 vs.indicators[SC_INDICATOR_INPUT] = Indicator(INDIC_DOTS, ColourDesired(0, 0, 0xff));
671 vs.indicators[SC_INDICATOR_CONVERTED] = Indicator(INDIC_COMPOSITIONTHICK, ColourDesired(0, 0, 0xff));
672 vs.indicators[SC_INDICATOR_TARGET] = Indicator(INDIC_STRAIGHTBOX, ColourDesired(0, 0, 0xff));
675 void ScintillaGTK::Finalise() {
676 for (TickReason tr = tickCaret; tr <= tickDwell; tr = static_cast<TickReason>(tr + 1)) {
677 FineTickerCancel(tr);
679 if (accessible) {
680 gtk_accessible_set_widget(GTK_ACCESSIBLE(accessible), NULL);
681 g_object_unref(accessible);
682 accessible = 0;
685 ScintillaBase::Finalise();
688 bool ScintillaGTK::AbandonPaint() {
689 if ((paintState == painting) && !paintingAllText) {
690 repaintFullWindow = true;
692 return false;
695 void ScintillaGTK::DisplayCursor(Window::Cursor c) {
696 if (cursorMode == SC_CURSORNORMAL)
697 wText.SetCursor(c);
698 else
699 wText.SetCursor(static_cast<Window::Cursor>(cursorMode));
702 bool ScintillaGTK::DragThreshold(Point ptStart, Point ptNow) {
703 return gtk_drag_check_threshold(GTK_WIDGET(PWidget(wMain)),
704 ptStart.x, ptStart.y, ptNow.x, ptNow.y);
707 void ScintillaGTK::StartDrag() {
708 PLATFORM_ASSERT(evbtn != 0);
709 dragWasDropped = false;
710 inDragDrop = ddDragging;
711 GtkTargetList *tl = gtk_target_list_new(clipboardCopyTargets, nClipboardCopyTargets);
712 #if GTK_CHECK_VERSION(3,10,0)
713 gtk_drag_begin_with_coordinates(GTK_WIDGET(PWidget(wMain)),
715 static_cast<GdkDragAction>(GDK_ACTION_COPY | GDK_ACTION_MOVE),
716 evbtn->button,
717 reinterpret_cast<GdkEvent *>(evbtn),
718 -1, -1);
719 #else
720 gtk_drag_begin(GTK_WIDGET(PWidget(wMain)),
722 static_cast<GdkDragAction>(GDK_ACTION_COPY | GDK_ACTION_MOVE),
723 evbtn->button,
724 reinterpret_cast<GdkEvent *>(evbtn));
725 #endif
728 #ifdef SCI_NAMESPACE
729 namespace Scintilla {
730 #endif
731 std::string ConvertText(const char *s, size_t len, const char *charSetDest,
732 const char *charSetSource, bool transliterations, bool silent) {
733 // s is not const because of different versions of iconv disagreeing about const
734 std::string destForm;
735 Converter conv(charSetDest, charSetSource, transliterations);
736 if (conv) {
737 gsize outLeft = len*3+1;
738 destForm = std::string(outLeft, '\0');
739 // g_iconv does not actually write to its input argument so safe to cast away const
740 char *pin = const_cast<char *>(s);
741 gsize inLeft = len;
742 char *putf = &destForm[0];
743 char *pout = putf;
744 gsize conversions = conv.Convert(&pin, &inLeft, &pout, &outLeft);
745 if (conversions == sizeFailure) {
746 if (!silent) {
747 if (len == 1)
748 fprintf(stderr, "iconv %s->%s failed for %0x '%s'\n",
749 charSetSource, charSetDest, (unsigned char)(*s), s);
750 else
751 fprintf(stderr, "iconv %s->%s failed for %s\n",
752 charSetSource, charSetDest, s);
754 destForm = std::string();
755 } else {
756 destForm.resize(pout - putf);
758 } else {
759 fprintf(stderr, "Can not iconv %s %s\n", charSetDest, charSetSource);
761 return destForm;
763 #ifdef SCI_NAMESPACE
765 #endif
767 // Returns the target converted to UTF8.
768 // Return the length in bytes.
769 int ScintillaGTK::TargetAsUTF8(char *text) {
770 int targetLength = targetEnd - targetStart;
771 if (IsUnicodeMode()) {
772 if (text) {
773 pdoc->GetCharRange(text, targetStart, targetLength);
775 } else {
776 // Need to convert
777 const char *charSetBuffer = CharacterSetID();
778 if (*charSetBuffer) {
779 std::string s = RangeText(targetStart, targetEnd);
780 std::string tmputf = ConvertText(&s[0], targetLength, "UTF-8", charSetBuffer, false);
781 if (text) {
782 memcpy(text, tmputf.c_str(), tmputf.length());
784 return tmputf.length();
785 } else {
786 if (text) {
787 pdoc->GetCharRange(text, targetStart, targetLength);
791 return targetLength;
794 // Translates a nul terminated UTF8 string into the document encoding.
795 // Return the length of the result in bytes.
796 int ScintillaGTK::EncodedFromUTF8(char *utf8, char *encoded) const {
797 int inputLength = (lengthForEncode >= 0) ? lengthForEncode : strlen(utf8);
798 if (IsUnicodeMode()) {
799 if (encoded) {
800 memcpy(encoded, utf8, inputLength);
802 return inputLength;
803 } else {
804 // Need to convert
805 const char *charSetBuffer = CharacterSetID();
806 if (*charSetBuffer) {
807 std::string tmpEncoded = ConvertText(utf8, inputLength, charSetBuffer, "UTF-8", true);
808 if (encoded) {
809 memcpy(encoded, tmpEncoded.c_str(), tmpEncoded.length());
811 return tmpEncoded.length();
812 } else {
813 if (encoded) {
814 memcpy(encoded, utf8, inputLength);
816 return inputLength;
819 // Fail
820 return 0;
823 bool ScintillaGTK::ValidCodePage(int codePage) const {
824 return codePage == 0
825 || codePage == SC_CP_UTF8
826 || codePage == 932
827 || codePage == 936
828 || codePage == 949
829 || codePage == 950
830 || codePage == 1361;
833 sptr_t ScintillaGTK::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
834 try {
835 switch (iMessage) {
837 case SCI_GRABFOCUS:
838 gtk_widget_grab_focus(PWidget(wMain));
839 break;
841 case SCI_GETDIRECTFUNCTION:
842 return reinterpret_cast<sptr_t>(DirectFunction);
844 case SCI_GETDIRECTPOINTER:
845 return reinterpret_cast<sptr_t>(this);
847 #ifdef SCI_LEXER
848 case SCI_LOADLEXERLIBRARY:
849 LexerManager::GetInstance()->Load(reinterpret_cast<const char*>(lParam));
850 break;
851 #endif
852 case SCI_TARGETASUTF8:
853 return TargetAsUTF8(reinterpret_cast<char*>(lParam));
855 case SCI_ENCODEDFROMUTF8:
856 return EncodedFromUTF8(reinterpret_cast<char*>(wParam),
857 reinterpret_cast<char*>(lParam));
859 case SCI_SETRECTANGULARSELECTIONMODIFIER:
860 rectangularSelectionModifier = wParam;
861 break;
863 case SCI_GETRECTANGULARSELECTIONMODIFIER:
864 return rectangularSelectionModifier;
866 case SCI_SETREADONLY: {
867 sptr_t ret = ScintillaBase::WndProc(iMessage, wParam, lParam);
868 if (accessible) {
869 ScintillaGTKAccessible *sciAccessible = ScintillaGTKAccessible::FromAccessible(accessible);
870 if (sciAccessible) {
871 sciAccessible->NotifyReadOnly();
874 return ret;
877 default:
878 return ScintillaBase::WndProc(iMessage, wParam, lParam);
880 } catch (std::bad_alloc&) {
881 errorStatus = SC_STATUS_BADALLOC;
882 } catch (...) {
883 errorStatus = SC_STATUS_FAILURE;
885 return 0l;
888 sptr_t ScintillaGTK::DefWndProc(unsigned int, uptr_t, sptr_t) {
889 return 0;
893 * Report that this Editor subclass has a working implementation of FineTickerStart.
895 bool ScintillaGTK::FineTickerAvailable() {
896 return true;
899 bool ScintillaGTK::FineTickerRunning(TickReason reason) {
900 return timers[reason].timer != 0;
903 void ScintillaGTK::FineTickerStart(TickReason reason, int millis, int /* tolerance */) {
904 FineTickerCancel(reason);
905 timers[reason].timer = gdk_threads_add_timeout(millis, TimeOut, &timers[reason]);
908 void ScintillaGTK::FineTickerCancel(TickReason reason) {
909 if (timers[reason].timer) {
910 g_source_remove(timers[reason].timer);
911 timers[reason].timer = 0;
915 bool ScintillaGTK::SetIdle(bool on) {
916 if (on) {
917 // Start idler, if it's not running.
918 if (!idler.state) {
919 idler.state = true;
920 idler.idlerID = reinterpret_cast<IdlerID>(
921 gdk_threads_add_idle_full(G_PRIORITY_DEFAULT_IDLE, IdleCallback, this, NULL));
923 } else {
924 // Stop idler, if it's running
925 if (idler.state) {
926 idler.state = false;
927 g_source_remove(GPOINTER_TO_UINT(idler.idlerID));
930 return true;
933 void ScintillaGTK::SetMouseCapture(bool on) {
934 if (mouseDownCaptures) {
935 if (on) {
936 gtk_grab_add(GTK_WIDGET(PWidget(wMain)));
937 } else {
938 gtk_grab_remove(GTK_WIDGET(PWidget(wMain)));
941 capturedMouse = on;
944 bool ScintillaGTK::HaveMouseCapture() {
945 return capturedMouse;
948 #if GTK_CHECK_VERSION(3,0,0)
950 // Is crcTest completely in crcContainer?
951 static bool CRectContains(const cairo_rectangle_t &crcContainer, const cairo_rectangle_t &crcTest) {
952 return
953 (crcTest.x >= crcContainer.x) && ((crcTest.x + crcTest.width) <= (crcContainer.x + crcContainer.width)) &&
954 (crcTest.y >= crcContainer.y) && ((crcTest.y + crcTest.height) <= (crcContainer.y + crcContainer.height));
957 // Is crcTest completely in crcListContainer?
958 // May incorrectly return false if complex shape
959 static bool CRectListContains(const cairo_rectangle_list_t *crcListContainer, const cairo_rectangle_t &crcTest) {
960 for (int r=0; r<crcListContainer->num_rectangles; r++) {
961 if (CRectContains(crcListContainer->rectangles[r], crcTest))
962 return true;
964 return false;
967 #endif
969 bool ScintillaGTK::PaintContains(PRectangle rc) {
970 // This allows optimization when a rectangle is completely in the update region.
971 // It is OK to return false when too difficult to determine as that just performs extra drawing
972 bool contains = true;
973 if (paintState == painting) {
974 if (!rcPaint.Contains(rc)) {
975 contains = false;
976 } else if (rgnUpdate) {
977 #if GTK_CHECK_VERSION(3,0,0)
978 cairo_rectangle_t grc = {rc.left, rc.top,
979 rc.right - rc.left, rc.bottom - rc.top};
980 contains = CRectListContains(rgnUpdate, grc);
981 #else
982 GdkRectangle grc = {static_cast<gint>(rc.left), static_cast<gint>(rc.top),
983 static_cast<gint>(rc.right - rc.left), static_cast<gint>(rc.bottom - rc.top)};
984 if (gdk_region_rect_in(rgnUpdate, &grc) != GDK_OVERLAP_RECTANGLE_IN) {
985 contains = false;
987 #endif
990 return contains;
993 // Redraw all of text area. This paint will not be abandoned.
994 void ScintillaGTK::FullPaint() {
995 wText.InvalidateAll();
998 PRectangle ScintillaGTK::GetClientRectangle() const {
999 Window win = wMain;
1000 PRectangle rc = win.GetClientPosition();
1001 if (verticalScrollBarVisible)
1002 rc.right -= verticalScrollBarWidth;
1003 if (horizontalScrollBarVisible && !Wrapping())
1004 rc.bottom -= horizontalScrollBarHeight;
1005 // Move to origin
1006 rc.right -= rc.left;
1007 rc.bottom -= rc.top;
1008 if (rc.bottom < 0)
1009 rc.bottom = 0;
1010 if (rc.right < 0)
1011 rc.right = 0;
1012 rc.left = 0;
1013 rc.top = 0;
1014 return rc;
1017 void ScintillaGTK::ScrollText(int linesToMove) {
1018 int diff = vs.lineHeight * -linesToMove;
1019 //Platform::DebugPrintf("ScintillaGTK::ScrollText %d %d %0d,%0d %0d,%0d\n", linesToMove, diff,
1020 // rc.left, rc.top, rc.right, rc.bottom);
1021 GtkWidget *wi = PWidget(wText);
1022 NotifyUpdateUI();
1024 if (IS_WIDGET_REALIZED(wi)) {
1025 gdk_window_scroll(WindowFromWidget(wi), 0, -diff);
1026 gdk_window_process_updates(WindowFromWidget(wi), FALSE);
1030 void ScintillaGTK::SetVerticalScrollPos() {
1031 DwellEnd(true);
1032 gtk_adjustment_set_value(GTK_ADJUSTMENT(adjustmentv), topLine);
1035 void ScintillaGTK::SetHorizontalScrollPos() {
1036 DwellEnd(true);
1037 gtk_adjustment_set_value(GTK_ADJUSTMENT(adjustmenth), xOffset);
1040 bool ScintillaGTK::ModifyScrollBars(int nMax, int nPage) {
1041 bool modified = false;
1042 int pageScroll = LinesToScroll();
1044 if (gtk_adjustment_get_upper(adjustmentv) != (nMax + 1) ||
1045 gtk_adjustment_get_page_size(adjustmentv) != nPage ||
1046 gtk_adjustment_get_page_increment(adjustmentv) != pageScroll) {
1047 gtk_adjustment_set_upper(adjustmentv, nMax + 1);
1048 gtk_adjustment_set_page_size(adjustmentv, nPage);
1049 gtk_adjustment_set_page_increment(adjustmentv, pageScroll);
1050 #if !GTK_CHECK_VERSION(3,18,0)
1051 gtk_adjustment_changed(GTK_ADJUSTMENT(adjustmentv));
1052 #endif
1053 modified = true;
1056 PRectangle rcText = GetTextRectangle();
1057 int horizEndPreferred = scrollWidth;
1058 if (horizEndPreferred < 0)
1059 horizEndPreferred = 0;
1060 unsigned int pageWidth = rcText.Width();
1061 unsigned int pageIncrement = pageWidth / 3;
1062 unsigned int charWidth = vs.styles[STYLE_DEFAULT].aveCharWidth;
1063 if (gtk_adjustment_get_upper(adjustmenth) != horizEndPreferred ||
1064 gtk_adjustment_get_page_size(adjustmenth) != pageWidth ||
1065 gtk_adjustment_get_page_increment(adjustmenth) != pageIncrement ||
1066 gtk_adjustment_get_step_increment(adjustmenth) != charWidth) {
1067 gtk_adjustment_set_upper(adjustmenth, horizEndPreferred);
1068 gtk_adjustment_set_page_size(adjustmenth, pageWidth);
1069 gtk_adjustment_set_page_increment(adjustmenth, pageIncrement);
1070 gtk_adjustment_set_step_increment(adjustmenth, charWidth);
1071 #if !GTK_CHECK_VERSION(3,18,0)
1072 gtk_adjustment_changed(GTK_ADJUSTMENT(adjustmenth));
1073 #endif
1074 modified = true;
1076 if (modified && (paintState == painting)) {
1077 repaintFullWindow = true;
1080 return modified;
1083 void ScintillaGTK::ReconfigureScrollBars() {
1084 PRectangle rc = wMain.GetClientPosition();
1085 Resize(rc.Width(), rc.Height());
1088 void ScintillaGTK::NotifyChange() {
1089 g_signal_emit(G_OBJECT(sci), scintilla_signals[COMMAND_SIGNAL], 0,
1090 Platform::LongFromTwoShorts(GetCtrlID(), SCEN_CHANGE), PWidget(wMain));
1093 void ScintillaGTK::NotifyFocus(bool focus) {
1094 g_signal_emit(G_OBJECT(sci), scintilla_signals[COMMAND_SIGNAL], 0,
1095 Platform::LongFromTwoShorts
1096 (GetCtrlID(), focus ? SCEN_SETFOCUS : SCEN_KILLFOCUS), PWidget(wMain));
1097 Editor::NotifyFocus(focus);
1100 void ScintillaGTK::NotifyParent(SCNotification scn) {
1101 scn.nmhdr.hwndFrom = PWidget(wMain);
1102 scn.nmhdr.idFrom = GetCtrlID();
1103 g_signal_emit(G_OBJECT(sci), scintilla_signals[NOTIFY_SIGNAL], 0,
1104 GetCtrlID(), &scn);
1107 void ScintillaGTK::NotifyKey(int key, int modifiers) {
1108 SCNotification scn = {};
1109 scn.nmhdr.code = SCN_KEY;
1110 scn.ch = key;
1111 scn.modifiers = modifiers;
1113 NotifyParent(scn);
1116 void ScintillaGTK::NotifyURIDropped(const char *list) {
1117 SCNotification scn = {};
1118 scn.nmhdr.code = SCN_URIDROPPED;
1119 scn.text = list;
1121 NotifyParent(scn);
1124 const char *CharacterSetID(int characterSet);
1126 const char *ScintillaGTK::CharacterSetID() const {
1127 return ::CharacterSetID(vs.styles[STYLE_DEFAULT].characterSet);
1130 class CaseFolderDBCS : public CaseFolderTable {
1131 const char *charSet;
1132 public:
1133 explicit CaseFolderDBCS(const char *charSet_) : charSet(charSet_) {
1134 StandardASCII();
1136 virtual size_t Fold(char *folded, size_t sizeFolded, const char *mixed, size_t lenMixed) {
1137 if ((lenMixed == 1) && (sizeFolded > 0)) {
1138 folded[0] = mapping[static_cast<unsigned char>(mixed[0])];
1139 return 1;
1140 } else if (*charSet) {
1141 std::string sUTF8 = ConvertText(mixed, lenMixed,
1142 "UTF-8", charSet, false);
1143 if (!sUTF8.empty()) {
1144 gchar *mapped = g_utf8_casefold(sUTF8.c_str(), sUTF8.length());
1145 size_t lenMapped = strlen(mapped);
1146 if (lenMapped < sizeFolded) {
1147 memcpy(folded, mapped, lenMapped);
1148 } else {
1149 folded[0] = '\0';
1150 lenMapped = 1;
1152 g_free(mapped);
1153 return lenMapped;
1156 // Something failed so return a single NUL byte
1157 folded[0] = '\0';
1158 return 1;
1162 CaseFolder *ScintillaGTK::CaseFolderForEncoding() {
1163 if (pdoc->dbcsCodePage == SC_CP_UTF8) {
1164 return new CaseFolderUnicode();
1165 } else {
1166 const char *charSetBuffer = CharacterSetID();
1167 if (charSetBuffer) {
1168 if (pdoc->dbcsCodePage == 0) {
1169 CaseFolderTable *pcf = new CaseFolderTable();
1170 pcf->StandardASCII();
1171 // Only for single byte encodings
1172 for (int i=0x80; i<0x100; i++) {
1173 char sCharacter[2] = "A";
1174 sCharacter[0] = i;
1175 // Silent as some bytes have no assigned character
1176 std::string sUTF8 = ConvertText(sCharacter, 1,
1177 "UTF-8", charSetBuffer, false, true);
1178 if (!sUTF8.empty()) {
1179 gchar *mapped = g_utf8_casefold(sUTF8.c_str(), sUTF8.length());
1180 if (mapped) {
1181 std::string mappedBack = ConvertText(mapped, strlen(mapped),
1182 charSetBuffer, "UTF-8", false, true);
1183 if ((mappedBack.length() == 1) && (mappedBack[0] != sCharacter[0])) {
1184 pcf->SetTranslation(sCharacter[0], mappedBack[0]);
1186 g_free(mapped);
1190 return pcf;
1191 } else {
1192 return new CaseFolderDBCS(charSetBuffer);
1195 return 0;
1199 namespace {
1201 struct CaseMapper {
1202 gchar *mapped; // Must be freed with g_free
1203 CaseMapper(const std::string &sUTF8, bool toUpperCase) {
1204 if (toUpperCase) {
1205 mapped = g_utf8_strup(sUTF8.c_str(), sUTF8.length());
1206 } else {
1207 mapped = g_utf8_strdown(sUTF8.c_str(), sUTF8.length());
1210 ~CaseMapper() {
1211 g_free(mapped);
1217 std::string ScintillaGTK::CaseMapString(const std::string &s, int caseMapping) {
1218 if ((s.size() == 0) || (caseMapping == cmSame))
1219 return s;
1221 if (IsUnicodeMode()) {
1222 std::string retMapped(s.length() * maxExpansionCaseConversion, 0);
1223 size_t lenMapped = CaseConvertString(&retMapped[0], retMapped.length(), s.c_str(), s.length(),
1224 (caseMapping == cmUpper) ? CaseConversionUpper : CaseConversionLower);
1225 retMapped.resize(lenMapped);
1226 return retMapped;
1229 const char *charSetBuffer = CharacterSetID();
1231 if (!*charSetBuffer) {
1232 CaseMapper mapper(s, caseMapping == cmUpper);
1233 return std::string(mapper.mapped, strlen(mapper.mapped));
1234 } else {
1235 // Change text to UTF-8
1236 std::string sUTF8 = ConvertText(s.c_str(), s.length(),
1237 "UTF-8", charSetBuffer, false);
1238 CaseMapper mapper(sUTF8, caseMapping == cmUpper);
1239 return ConvertText(mapper.mapped, strlen(mapper.mapped), charSetBuffer, "UTF-8", false);
1243 int ScintillaGTK::KeyDefault(int key, int modifiers) {
1244 // Pass up to container in case it is an accelerator
1245 NotifyKey(key, modifiers);
1246 return 0;
1249 void ScintillaGTK::CopyToClipboard(const SelectionText &selectedText) {
1250 SelectionText *clipText = new SelectionText();
1251 clipText->Copy(selectedText);
1252 StoreOnClipboard(clipText);
1255 void ScintillaGTK::Copy() {
1256 if (!sel.Empty()) {
1257 SelectionText *clipText = new SelectionText();
1258 CopySelectionRange(clipText);
1259 StoreOnClipboard(clipText);
1260 #if PLAT_GTK_WIN32
1261 if (sel.IsRectangular()) {
1262 ::OpenClipboard(NULL);
1263 ::SetClipboardData(cfColumnSelect, 0);
1264 ::CloseClipboard();
1266 #endif
1270 void ScintillaGTK::Paste() {
1271 atomSought = atomUTF8;
1272 GtkClipboard *clipBoard =
1273 gtk_widget_get_clipboard(GTK_WIDGET(PWidget(wMain)), atomClipboard);
1274 if (clipBoard == NULL)
1275 return;
1277 // helper class for the asynchronous paste not to risk calling in a destroyed ScintillaGTK
1278 class Helper : GObjectWatcher {
1279 ScintillaGTK *sci;
1281 virtual void Destroyed() {
1282 sci = 0;
1285 public:
1286 Helper(ScintillaGTK *sci_) :
1287 GObjectWatcher(G_OBJECT(PWidget(sci_->wMain))),
1288 sci(sci_) {
1291 static void ClipboardReceived(GtkClipboard *, GtkSelectionData *selection_data, gpointer data) {
1292 Helper *self = static_cast<Helper*>(data);
1293 if (self->sci != 0) {
1294 self->sci->ReceivedSelection(selection_data);
1296 delete self;
1300 gtk_clipboard_request_contents(clipBoard, atomSought, Helper::ClipboardReceived, new Helper(this));
1303 void ScintillaGTK::CreateCallTipWindow(PRectangle rc) {
1304 if (!ct.wCallTip.Created()) {
1305 ct.wCallTip = gtk_window_new(GTK_WINDOW_POPUP);
1306 ct.wDraw = gtk_drawing_area_new();
1307 GtkWidget *widcdrw = PWidget(ct.wDraw); // // No code inside the G_OBJECT macro
1308 gtk_container_add(GTK_CONTAINER(PWidget(ct.wCallTip)), widcdrw);
1309 #if GTK_CHECK_VERSION(3,0,0)
1310 g_signal_connect(G_OBJECT(widcdrw), "draw",
1311 G_CALLBACK(ScintillaGTK::DrawCT), &ct);
1312 #else
1313 g_signal_connect(G_OBJECT(widcdrw), "expose_event",
1314 G_CALLBACK(ScintillaGTK::ExposeCT), &ct);
1315 #endif
1316 g_signal_connect(G_OBJECT(widcdrw), "button_press_event",
1317 G_CALLBACK(ScintillaGTK::PressCT), static_cast<void *>(this));
1318 gtk_widget_set_events(widcdrw,
1319 GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK);
1320 GtkWidget *top = gtk_widget_get_toplevel(static_cast<GtkWidget *>(wMain.GetID()));
1321 gtk_window_set_transient_for(GTK_WINDOW(static_cast<GtkWidget *>(PWidget(ct.wCallTip))),
1322 GTK_WINDOW(top));
1324 gtk_widget_set_size_request(PWidget(ct.wDraw), rc.Width(), rc.Height());
1325 ct.wDraw.Show();
1326 if (PWindow(ct.wCallTip)) {
1327 gdk_window_resize(PWindow(ct.wCallTip), rc.Width(), rc.Height());
1331 void ScintillaGTK::AddToPopUp(const char *label, int cmd, bool enabled) {
1332 GtkWidget *menuItem;
1333 if (label[0])
1334 menuItem = gtk_menu_item_new_with_label(label);
1335 else
1336 menuItem = gtk_separator_menu_item_new();
1337 gtk_menu_shell_append(GTK_MENU_SHELL(popup.GetID()), menuItem);
1338 g_object_set_data(G_OBJECT(menuItem), "CmdNum", GINT_TO_POINTER(cmd));
1339 g_signal_connect(G_OBJECT(menuItem),"activate", G_CALLBACK(PopUpCB), this);
1341 if (cmd) {
1342 if (menuItem)
1343 gtk_widget_set_sensitive(menuItem, enabled);
1347 bool ScintillaGTK::OwnPrimarySelection() {
1348 return ((gdk_selection_owner_get(GDK_SELECTION_PRIMARY)
1349 == PWindow(wMain)) &&
1350 (PWindow(wMain) != NULL));
1353 void ScintillaGTK::ClaimSelection() {
1354 // X Windows has a 'primary selection' as well as the clipboard.
1355 // Whenever the user selects some text, we become the primary selection
1356 if (!sel.Empty() && IS_WIDGET_REALIZED(GTK_WIDGET(PWidget(wMain)))) {
1357 primarySelection = true;
1358 gtk_selection_owner_set(GTK_WIDGET(PWidget(wMain)),
1359 GDK_SELECTION_PRIMARY, GDK_CURRENT_TIME);
1360 primary.Clear();
1361 } else if (OwnPrimarySelection()) {
1362 primarySelection = true;
1363 if (primary.Empty())
1364 gtk_selection_owner_set(NULL, GDK_SELECTION_PRIMARY, GDK_CURRENT_TIME);
1365 } else {
1366 primarySelection = false;
1367 primary.Clear();
1371 static const guchar *DataOfGSD(GtkSelectionData *sd) { return gtk_selection_data_get_data(sd); }
1372 static gint LengthOfGSD(GtkSelectionData *sd) { return gtk_selection_data_get_length(sd); }
1373 static GdkAtom TypeOfGSD(GtkSelectionData *sd) { return gtk_selection_data_get_data_type(sd); }
1374 static GdkAtom SelectionOfGSD(GtkSelectionData *sd) { return gtk_selection_data_get_selection(sd); }
1376 // Detect rectangular text, convert line ends to current mode, convert from or to UTF-8
1377 void ScintillaGTK::GetGtkSelectionText(GtkSelectionData *selectionData, SelectionText &selText) {
1378 const char *data = reinterpret_cast<const char *>(DataOfGSD(selectionData));
1379 int len = LengthOfGSD(selectionData);
1380 GdkAtom selectionTypeData = TypeOfGSD(selectionData);
1382 // Return empty string if selection is not a string
1383 if ((selectionTypeData != GDK_TARGET_STRING) && (selectionTypeData != atomUTF8)) {
1384 selText.Clear();
1385 return;
1388 // Check for "\n\0" ending to string indicating that selection is rectangular
1389 bool isRectangular;
1390 #if PLAT_GTK_WIN32
1391 isRectangular = ::IsClipboardFormatAvailable(cfColumnSelect) != 0;
1392 #else
1393 isRectangular = ((len > 2) && (data[len - 1] == 0 && data[len - 2] == '\n'));
1394 if (isRectangular)
1395 len--; // Forget the extra '\0'
1396 #endif
1398 #if PLAT_GTK_WIN32
1399 // Win32 includes an ending '\0' byte in 'len' for clipboard text from
1400 // external applications; ignore it.
1401 if ((len > 0) && (data[len - 1] == '\0'))
1402 len--;
1403 #endif
1405 std::string dest(data, len);
1406 if (selectionTypeData == GDK_TARGET_STRING) {
1407 if (IsUnicodeMode()) {
1408 // Unknown encoding so assume in Latin1
1409 dest = UTF8FromLatin1(dest.c_str(), dest.length());
1410 selText.Copy(dest, SC_CP_UTF8, 0, isRectangular, false);
1411 } else {
1412 // Assume buffer is in same encoding as selection
1413 selText.Copy(dest, pdoc->dbcsCodePage,
1414 vs.styles[STYLE_DEFAULT].characterSet, isRectangular, false);
1416 } else { // UTF-8
1417 const char *charSetBuffer = CharacterSetID();
1418 if (!IsUnicodeMode() && *charSetBuffer) {
1419 // Convert to locale
1420 dest = ConvertText(dest.c_str(), dest.length(), charSetBuffer, "UTF-8", true);
1421 selText.Copy(dest, pdoc->dbcsCodePage,
1422 vs.styles[STYLE_DEFAULT].characterSet, isRectangular, false);
1423 } else {
1424 selText.Copy(dest, SC_CP_UTF8, 0, isRectangular, false);
1429 void ScintillaGTK::ReceivedSelection(GtkSelectionData *selection_data) {
1430 try {
1431 if ((SelectionOfGSD(selection_data) == atomClipboard) ||
1432 (SelectionOfGSD(selection_data) == GDK_SELECTION_PRIMARY)) {
1433 if ((atomSought == atomUTF8) && (LengthOfGSD(selection_data) <= 0)) {
1434 atomSought = atomString;
1435 gtk_selection_convert(GTK_WIDGET(PWidget(wMain)),
1436 SelectionOfGSD(selection_data), atomSought, GDK_CURRENT_TIME);
1437 } else if ((LengthOfGSD(selection_data) > 0) &&
1438 ((TypeOfGSD(selection_data) == GDK_TARGET_STRING) || (TypeOfGSD(selection_data) == atomUTF8))) {
1439 SelectionText selText;
1440 GetGtkSelectionText(selection_data, selText);
1442 UndoGroup ug(pdoc);
1443 if (SelectionOfGSD(selection_data) != GDK_SELECTION_PRIMARY) {
1444 ClearSelection(multiPasteMode == SC_MULTIPASTE_EACH);
1447 InsertPasteShape(selText.Data(), selText.Length(),
1448 selText.rectangular ? pasteRectangular : pasteStream);
1449 EnsureCaretVisible();
1452 // else fprintf(stderr, "Target non string %d %d\n", (int)(selection_data->type),
1453 // (int)(atomUTF8));
1454 Redraw();
1455 } catch (...) {
1456 errorStatus = SC_STATUS_FAILURE;
1460 void ScintillaGTK::ReceivedDrop(GtkSelectionData *selection_data) {
1461 dragWasDropped = true;
1462 if (TypeOfGSD(selection_data) == atomUriList || TypeOfGSD(selection_data) == atomDROPFILES_DND) {
1463 const char *data = reinterpret_cast<const char *>(DataOfGSD(selection_data));
1464 std::vector<char> drop(data, data + LengthOfGSD(selection_data));
1465 drop.push_back('\0');
1466 NotifyURIDropped(&drop[0]);
1467 } else if ((TypeOfGSD(selection_data) == GDK_TARGET_STRING) || (TypeOfGSD(selection_data) == atomUTF8)) {
1468 if (LengthOfGSD(selection_data) > 0) {
1469 SelectionText selText;
1470 GetGtkSelectionText(selection_data, selText);
1471 DropAt(posDrop, selText.Data(), selText.Length(), false, selText.rectangular);
1473 } else if (LengthOfGSD(selection_data) > 0) {
1474 //~ fprintf(stderr, "ReceivedDrop other %p\n", static_cast<void *>(selection_data->type));
1476 Redraw();
1481 void ScintillaGTK::GetSelection(GtkSelectionData *selection_data, guint info, SelectionText *text) {
1482 #if PLAT_GTK_WIN32
1483 // GDK on Win32 expands any \n into \r\n, so make a copy of
1484 // the clip text now with newlines converted to \n. Use { } to hide symbols
1485 // from code below
1486 SelectionText *newline_normalized = NULL;
1488 std::string tmpstr = Document::TransformLineEnds(text->Data(), text->Length(), SC_EOL_LF);
1489 newline_normalized = new SelectionText();
1490 newline_normalized->Copy(tmpstr, SC_CP_UTF8, 0, text->rectangular, false);
1491 text = newline_normalized;
1493 #endif
1495 // Convert text to utf8 if it isn't already
1496 SelectionText *converted = 0;
1497 if ((text->codePage != SC_CP_UTF8) && (info == TARGET_UTF8_STRING)) {
1498 const char *charSet = ::CharacterSetID(text->characterSet);
1499 if (*charSet) {
1500 std::string tmputf = ConvertText(text->Data(), text->Length(), "UTF-8", charSet, false);
1501 converted = new SelectionText();
1502 converted->Copy(tmputf, SC_CP_UTF8, 0, text->rectangular, false);
1503 text = converted;
1507 // Here is a somewhat evil kludge.
1508 // As I can not work out how to store data on the clipboard in multiple formats
1509 // and need some way to mark the clipping as being stream or rectangular,
1510 // the terminating \0 is included in the length for rectangular clippings.
1511 // All other tested aplications behave benignly by ignoring the \0.
1512 // The #if is here because on Windows cfColumnSelect clip entry is used
1513 // instead as standard indicator of rectangularness (so no need to kludge)
1514 const char *textData = text->Data();
1515 int len = text->Length();
1516 #if PLAT_GTK_WIN32 == 0
1517 if (text->rectangular)
1518 len++;
1519 #endif
1521 if (info == TARGET_UTF8_STRING) {
1522 gtk_selection_data_set_text(selection_data, textData, len);
1523 } else {
1524 gtk_selection_data_set(selection_data,
1525 static_cast<GdkAtom>(GDK_SELECTION_TYPE_STRING),
1526 8, reinterpret_cast<const unsigned char *>(textData), len);
1528 delete converted;
1530 #if PLAT_GTK_WIN32
1531 delete newline_normalized;
1532 #endif
1535 void ScintillaGTK::StoreOnClipboard(SelectionText *clipText) {
1536 GtkClipboard *clipBoard =
1537 gtk_widget_get_clipboard(GTK_WIDGET(PWidget(wMain)), atomClipboard);
1538 if (clipBoard == NULL) // Occurs if widget isn't in a toplevel
1539 return;
1541 if (gtk_clipboard_set_with_data(clipBoard, clipboardCopyTargets, nClipboardCopyTargets,
1542 ClipboardGetSelection, ClipboardClearSelection, clipText)) {
1543 gtk_clipboard_set_can_store(clipBoard, clipboardCopyTargets, nClipboardCopyTargets);
1547 void ScintillaGTK::ClipboardGetSelection(GtkClipboard *, GtkSelectionData *selection_data, guint info, void *data) {
1548 GetSelection(selection_data, info, static_cast<SelectionText*>(data));
1551 void ScintillaGTK::ClipboardClearSelection(GtkClipboard *, void *data) {
1552 SelectionText *obj = static_cast<SelectionText*>(data);
1553 delete obj;
1556 void ScintillaGTK::UnclaimSelection(GdkEventSelection *selection_event) {
1557 try {
1558 //Platform::DebugPrintf("UnclaimSelection\n");
1559 if (selection_event->selection == GDK_SELECTION_PRIMARY) {
1560 //Platform::DebugPrintf("UnclaimPrimarySelection\n");
1561 if (!OwnPrimarySelection()) {
1562 primary.Clear();
1563 primarySelection = false;
1564 FullPaint();
1567 } catch (...) {
1568 errorStatus = SC_STATUS_FAILURE;
1572 void ScintillaGTK::Resize(int width, int height) {
1573 //Platform::DebugPrintf("Resize %d %d\n", width, height);
1574 //printf("Resize %d %d\n", width, height);
1576 // GTK+ 3 warns when we allocate smaller than the minimum allocation,
1577 // so we use these variables to store the minimum scrollbar lengths.
1578 int minVScrollBarHeight, minHScrollBarWidth;
1580 // Not always needed, but some themes can have different sizes of scrollbars
1581 #if GTK_CHECK_VERSION(3,0,0)
1582 GtkRequisition minimum, requisition;
1583 gtk_widget_get_preferred_size(PWidget(scrollbarv), &minimum, &requisition);
1584 minVScrollBarHeight = minimum.height;
1585 verticalScrollBarWidth = requisition.width;
1586 gtk_widget_get_preferred_size(PWidget(scrollbarh), &minimum, &requisition);
1587 minHScrollBarWidth = minimum.width;
1588 horizontalScrollBarHeight = requisition.height;
1589 #else
1590 minVScrollBarHeight = minHScrollBarWidth = 1;
1591 verticalScrollBarWidth = GTK_WIDGET(PWidget(scrollbarv))->requisition.width;
1592 horizontalScrollBarHeight = GTK_WIDGET(PWidget(scrollbarh))->requisition.height;
1593 #endif
1595 // These allocations should never produce negative sizes as they would wrap around to huge
1596 // unsigned numbers inside GTK+ causing warnings.
1597 bool showSBHorizontal = horizontalScrollBarVisible && !Wrapping();
1599 GtkAllocation alloc;
1600 if (showSBHorizontal) {
1601 gtk_widget_show(GTK_WIDGET(PWidget(scrollbarh)));
1602 alloc.x = 0;
1603 alloc.y = height - horizontalScrollBarHeight;
1604 alloc.width = Platform::Maximum(minHScrollBarWidth, width - verticalScrollBarWidth);
1605 alloc.height = horizontalScrollBarHeight;
1606 gtk_widget_size_allocate(GTK_WIDGET(PWidget(scrollbarh)), &alloc);
1607 } else {
1608 gtk_widget_hide(GTK_WIDGET(PWidget(scrollbarh)));
1609 horizontalScrollBarHeight = 0; // in case horizontalScrollBarVisible is true.
1612 if (verticalScrollBarVisible) {
1613 gtk_widget_show(GTK_WIDGET(PWidget(scrollbarv)));
1614 alloc.x = width - verticalScrollBarWidth;
1615 alloc.y = 0;
1616 alloc.width = verticalScrollBarWidth;
1617 alloc.height = Platform::Maximum(minVScrollBarHeight, height - horizontalScrollBarHeight);
1618 gtk_widget_size_allocate(GTK_WIDGET(PWidget(scrollbarv)), &alloc);
1619 } else {
1620 gtk_widget_hide(GTK_WIDGET(PWidget(scrollbarv)));
1621 verticalScrollBarWidth = 0;
1623 if (IS_WIDGET_MAPPED(PWidget(wMain))) {
1624 ChangeSize();
1627 alloc.x = 0;
1628 alloc.y = 0;
1629 alloc.width = 1;
1630 alloc.height = 1;
1631 #if GTK_CHECK_VERSION(3, 0, 0)
1632 // please GTK 3.20 and ask wText what size it wants, although we know it doesn't really need
1633 // anything special as it's ours.
1634 gtk_widget_get_preferred_size(PWidget(wText), &requisition, NULL);
1635 alloc.width = requisition.width;
1636 alloc.height = requisition.height;
1637 #endif
1638 alloc.width = Platform::Maximum(alloc.width, width - verticalScrollBarWidth);
1639 alloc.height = Platform::Maximum(alloc.height, height - horizontalScrollBarHeight);
1640 gtk_widget_size_allocate(GTK_WIDGET(PWidget(wText)), &alloc);
1643 static void SetAdjustmentValue(GtkAdjustment *object, int value) {
1644 GtkAdjustment *adjustment = GTK_ADJUSTMENT(object);
1645 int maxValue = static_cast<int>(
1646 gtk_adjustment_get_upper(adjustment) - gtk_adjustment_get_page_size(adjustment));
1648 if (value > maxValue)
1649 value = maxValue;
1650 if (value < 0)
1651 value = 0;
1652 gtk_adjustment_set_value(adjustment, value);
1655 static int modifierTranslated(int sciModifier) {
1656 switch (sciModifier) {
1657 case SCMOD_SHIFT:
1658 return GDK_SHIFT_MASK;
1659 case SCMOD_CTRL:
1660 return GDK_CONTROL_MASK;
1661 case SCMOD_ALT:
1662 return GDK_MOD1_MASK;
1663 case SCMOD_SUPER:
1664 return GDK_MOD4_MASK;
1665 default:
1666 return 0;
1670 gint ScintillaGTK::PressThis(GdkEventButton *event) {
1671 try {
1672 //Platform::DebugPrintf("Press %x time=%d state = %x button = %x\n",this,event->time, event->state, event->button);
1673 // Do not use GTK+ double click events as Scintilla has its own double click detection
1674 if (event->type != GDK_BUTTON_PRESS)
1675 return FALSE;
1677 if (evbtn) {
1678 gdk_event_free(reinterpret_cast<GdkEvent *>(evbtn));
1679 evbtn = 0;
1681 evbtn = reinterpret_cast<GdkEventButton *>(gdk_event_copy(reinterpret_cast<GdkEvent *>(event)));
1682 Point pt;
1683 pt.x = int(event->x);
1684 pt.y = int(event->y);
1685 PRectangle rcClient = GetClientRectangle();
1686 //Platform::DebugPrintf("Press %0d,%0d in %0d,%0d %0d,%0d\n",
1687 // pt.x, pt.y, rcClient.left, rcClient.top, rcClient.right, rcClient.bottom);
1688 if ((pt.x > rcClient.right) || (pt.y > rcClient.bottom)) {
1689 Platform::DebugPrintf("Bad location\n");
1690 return FALSE;
1693 bool shift = (event->state & GDK_SHIFT_MASK) != 0;
1694 bool ctrl = (event->state & GDK_CONTROL_MASK) != 0;
1695 // On X, instead of sending literal modifiers use the user specified
1696 // modifier, defaulting to control instead of alt.
1697 // This is because most X window managers grab alt + click for moving
1698 bool alt = (event->state & modifierTranslated(rectangularSelectionModifier)) != 0;
1700 gtk_widget_grab_focus(PWidget(wMain));
1701 if (event->button == 1) {
1702 #if PLAT_GTK_MACOSX
1703 bool meta = ctrl;
1704 // GDK reports the Command modifer key as GDK_MOD2_MASK for button events,
1705 // not GDK_META_MASK like in key events.
1706 ctrl = (event->state & GDK_MOD2_MASK) != 0;
1707 #else
1708 bool meta = false;
1709 #endif
1710 ButtonDownWithModifiers(pt, event->time, ModifierFlags(shift, ctrl, alt, meta));
1711 } else if (event->button == 2) {
1712 // Grab the primary selection if it exists
1713 SelectionPosition pos = SPositionFromLocation(pt, false, false, UserVirtualSpace());
1714 if (OwnPrimarySelection() && primary.Empty())
1715 CopySelectionRange(&primary);
1717 sel.Clear();
1718 SetSelection(pos, pos);
1719 atomSought = atomUTF8;
1720 gtk_selection_convert(GTK_WIDGET(PWidget(wMain)), GDK_SELECTION_PRIMARY,
1721 atomSought, event->time);
1722 } else if (event->button == 3) {
1723 if (!PointInSelection(pt))
1724 SetEmptySelection(PositionFromLocation(pt));
1725 if (ShouldDisplayPopup(pt)) {
1726 // PopUp menu
1727 // Convert to screen
1728 int ox = 0;
1729 int oy = 0;
1730 gdk_window_get_origin(PWindow(wMain), &ox, &oy);
1731 ContextMenu(Point(pt.x + ox, pt.y + oy));
1732 } else {
1733 #if PLAT_GTK_MACOSX
1734 bool meta = ctrl;
1735 // GDK reports the Command modifer key as GDK_MOD2_MASK for button events,
1736 // not GDK_META_MASK like in key events.
1737 ctrl = (event->state & GDK_MOD2_MASK) != 0;
1738 #else
1739 bool meta = false;
1740 #endif
1741 RightButtonDownWithModifiers(pt, event->time, ModifierFlags(shift, ctrl, alt, meta));
1742 return FALSE;
1744 } else if (event->button == 4) {
1745 // Wheel scrolling up (only GTK 1.x does it this way)
1746 if (ctrl)
1747 SetAdjustmentValue(adjustmenth, xOffset - 6);
1748 else
1749 SetAdjustmentValue(adjustmentv, topLine - 3);
1750 } else if (event->button == 5) {
1751 // Wheel scrolling down (only GTK 1.x does it this way)
1752 if (ctrl)
1753 SetAdjustmentValue(adjustmenth, xOffset + 6);
1754 else
1755 SetAdjustmentValue(adjustmentv, topLine + 3);
1757 } catch (...) {
1758 errorStatus = SC_STATUS_FAILURE;
1760 return TRUE;
1763 gint ScintillaGTK::Press(GtkWidget *widget, GdkEventButton *event) {
1764 if (event->window != WindowFromWidget(widget))
1765 return FALSE;
1766 ScintillaGTK *sciThis = FromWidget(widget);
1767 return sciThis->PressThis(event);
1770 gint ScintillaGTK::MouseRelease(GtkWidget *widget, GdkEventButton *event) {
1771 ScintillaGTK *sciThis = FromWidget(widget);
1772 try {
1773 //Platform::DebugPrintf("Release %x %d %d\n",sciThis,event->time,event->state);
1774 if (!sciThis->HaveMouseCapture())
1775 return FALSE;
1776 if (event->button == 1) {
1777 Point pt;
1778 pt.x = int(event->x);
1779 pt.y = int(event->y);
1780 //Platform::DebugPrintf("Up %x %x %d %d %d\n",
1781 // sciThis,event->window,event->time, pt.x, pt.y);
1782 if (event->window != PWindow(sciThis->wMain))
1783 // If mouse released on scroll bar then the position is relative to the
1784 // scrollbar, not the drawing window so just repeat the most recent point.
1785 pt = sciThis->ptMouseLast;
1786 sciThis->ButtonUp(pt, event->time, (event->state & GDK_CONTROL_MASK) != 0);
1788 } catch (...) {
1789 sciThis->errorStatus = SC_STATUS_FAILURE;
1791 return FALSE;
1794 // win32gtk and GTK >= 2 use SCROLL_* events instead of passing the
1795 // button4/5/6/7 events to the GTK app
1796 gint ScintillaGTK::ScrollEvent(GtkWidget *widget, GdkEventScroll *event) {
1797 ScintillaGTK *sciThis = FromWidget(widget);
1798 try {
1800 if (widget == NULL || event == NULL)
1801 return FALSE;
1803 #if defined(GDK_WINDOWING_WAYLAND)
1804 if (event->direction == GDK_SCROLL_SMOOTH) {
1805 const int smoothScrollFactor = 4;
1806 sciThis->smoothScrollY += event->delta_y * smoothScrollFactor;
1807 sciThis->smoothScrollX += event->delta_x * smoothScrollFactor;;
1808 if (ABS(sciThis->smoothScrollY) >= 1.0) {
1809 const int scrollLines = trunc(sciThis->smoothScrollY);
1810 sciThis->ScrollTo(sciThis->topLine + scrollLines);
1811 sciThis->smoothScrollY -= scrollLines;
1813 if (ABS(sciThis->smoothScrollX) >= 1.0) {
1814 const int scrollPixels = trunc(sciThis->smoothScrollX);
1815 sciThis->HorizontalScrollTo(sciThis->xOffset + scrollPixels);
1816 sciThis->smoothScrollX -= scrollPixels;
1818 return TRUE;
1820 #endif
1822 // Compute amount and direction to scroll (even tho on win32 there is
1823 // intensity of scrolling info in the native message, gtk doesn't
1824 // support this so we simulate similarly adaptive scrolling)
1825 // Note that this is disabled on OS X (Darwin) with the X11 backend
1826 // where the X11 server already has an adaptive scrolling algorithm
1827 // that fights with this one
1828 int cLineScroll;
1829 #if defined(__APPLE__) && !defined(GDK_WINDOWING_QUARTZ)
1830 cLineScroll = sciThis->linesPerScroll;
1831 if (cLineScroll == 0)
1832 cLineScroll = 4;
1833 sciThis->wheelMouseIntensity = cLineScroll;
1834 #else
1835 int timeDelta = 1000000;
1836 GTimeVal curTime;
1837 g_get_current_time(&curTime);
1838 if (curTime.tv_sec == sciThis->lastWheelMouseTime.tv_sec)
1839 timeDelta = curTime.tv_usec - sciThis->lastWheelMouseTime.tv_usec;
1840 else if (curTime.tv_sec == sciThis->lastWheelMouseTime.tv_sec + 1)
1841 timeDelta = 1000000 + (curTime.tv_usec - sciThis->lastWheelMouseTime.tv_usec);
1842 if ((event->direction == sciThis->lastWheelMouseDirection) && (timeDelta < 250000)) {
1843 if (sciThis->wheelMouseIntensity < 12)
1844 sciThis->wheelMouseIntensity++;
1845 cLineScroll = sciThis->wheelMouseIntensity;
1846 } else {
1847 cLineScroll = sciThis->linesPerScroll;
1848 if (cLineScroll == 0)
1849 cLineScroll = 4;
1850 sciThis->wheelMouseIntensity = cLineScroll;
1852 #endif
1853 if (event->direction == GDK_SCROLL_UP || event->direction == GDK_SCROLL_LEFT) {
1854 cLineScroll *= -1;
1856 g_get_current_time(&sciThis->lastWheelMouseTime);
1857 sciThis->lastWheelMouseDirection = event->direction;
1859 // Note: Unpatched versions of win32gtk don't set the 'state' value so
1860 // only regular scrolling is supported there. Also, unpatched win32gtk
1861 // issues spurious button 2 mouse events during wheeling, which can cause
1862 // problems (a patch for both was submitted by archaeopteryx.com on 13Jun2001)
1864 // Data zoom not supported
1865 if (event->state & GDK_SHIFT_MASK) {
1866 return FALSE;
1869 #if GTK_CHECK_VERSION(3,4,0)
1870 // Smooth scrolling not supported
1871 if (event->direction == GDK_SCROLL_SMOOTH) {
1872 return FALSE;
1874 #endif
1876 // Horizontal scrolling
1877 if (event->direction == GDK_SCROLL_LEFT || event->direction == GDK_SCROLL_RIGHT) {
1878 sciThis->HorizontalScrollTo(sciThis->xOffset + cLineScroll);
1880 // Text font size zoom
1881 } else if (event->state & GDK_CONTROL_MASK) {
1882 if (cLineScroll < 0) {
1883 sciThis->KeyCommand(SCI_ZOOMIN);
1884 } else {
1885 sciThis->KeyCommand(SCI_ZOOMOUT);
1888 // Regular scrolling
1889 } else {
1890 sciThis->ScrollTo(sciThis->topLine + cLineScroll);
1892 return TRUE;
1893 } catch (...) {
1894 sciThis->errorStatus = SC_STATUS_FAILURE;
1896 return FALSE;
1899 gint ScintillaGTK::Motion(GtkWidget *widget, GdkEventMotion *event) {
1900 ScintillaGTK *sciThis = FromWidget(widget);
1901 try {
1902 //Platform::DebugPrintf("Motion %x %d\n",sciThis,event->time);
1903 if (event->window != WindowFromWidget(widget))
1904 return FALSE;
1905 int x = 0;
1906 int y = 0;
1907 GdkModifierType state;
1908 if (event->is_hint) {
1909 #if GTK_CHECK_VERSION(3,0,0)
1910 gdk_window_get_device_position(event->window,
1911 event->device, &x, &y, &state);
1912 #else
1913 gdk_window_get_pointer(event->window, &x, &y, &state);
1914 #endif
1915 } else {
1916 x = static_cast<int>(event->x);
1917 y = static_cast<int>(event->y);
1918 state = static_cast<GdkModifierType>(event->state);
1920 //Platform::DebugPrintf("Move %x %x %d %c %d %d\n",
1921 // sciThis,event->window,event->time,event->is_hint? 'h' :'.', x, y);
1922 Point pt(x, y);
1923 int modifiers = ((event->state & GDK_SHIFT_MASK) != 0 ? SCI_SHIFT : 0) |
1924 ((event->state & GDK_CONTROL_MASK) != 0 ? SCI_CTRL : 0) |
1925 ((event->state & modifierTranslated(sciThis->rectangularSelectionModifier)) != 0 ? SCI_ALT : 0);
1926 sciThis->ButtonMoveWithModifiers(pt, modifiers);
1927 } catch (...) {
1928 sciThis->errorStatus = SC_STATUS_FAILURE;
1930 return FALSE;
1933 // Map the keypad keys to their equivalent functions
1934 static int KeyTranslate(int keyIn) {
1935 switch (keyIn) {
1936 #if GTK_CHECK_VERSION(3,0,0)
1937 case GDK_KEY_ISO_Left_Tab:
1938 return SCK_TAB;
1939 case GDK_KEY_KP_Down:
1940 return SCK_DOWN;
1941 case GDK_KEY_KP_Up:
1942 return SCK_UP;
1943 case GDK_KEY_KP_Left:
1944 return SCK_LEFT;
1945 case GDK_KEY_KP_Right:
1946 return SCK_RIGHT;
1947 case GDK_KEY_KP_Home:
1948 return SCK_HOME;
1949 case GDK_KEY_KP_End:
1950 return SCK_END;
1951 case GDK_KEY_KP_Page_Up:
1952 return SCK_PRIOR;
1953 case GDK_KEY_KP_Page_Down:
1954 return SCK_NEXT;
1955 case GDK_KEY_KP_Delete:
1956 return SCK_DELETE;
1957 case GDK_KEY_KP_Insert:
1958 return SCK_INSERT;
1959 case GDK_KEY_KP_Enter:
1960 return SCK_RETURN;
1962 case GDK_KEY_Down:
1963 return SCK_DOWN;
1964 case GDK_KEY_Up:
1965 return SCK_UP;
1966 case GDK_KEY_Left:
1967 return SCK_LEFT;
1968 case GDK_KEY_Right:
1969 return SCK_RIGHT;
1970 case GDK_KEY_Home:
1971 return SCK_HOME;
1972 case GDK_KEY_End:
1973 return SCK_END;
1974 case GDK_KEY_Page_Up:
1975 return SCK_PRIOR;
1976 case GDK_KEY_Page_Down:
1977 return SCK_NEXT;
1978 case GDK_KEY_Delete:
1979 return SCK_DELETE;
1980 case GDK_KEY_Insert:
1981 return SCK_INSERT;
1982 case GDK_KEY_Escape:
1983 return SCK_ESCAPE;
1984 case GDK_KEY_BackSpace:
1985 return SCK_BACK;
1986 case GDK_KEY_Tab:
1987 return SCK_TAB;
1988 case GDK_KEY_Return:
1989 return SCK_RETURN;
1990 case GDK_KEY_KP_Add:
1991 return SCK_ADD;
1992 case GDK_KEY_KP_Subtract:
1993 return SCK_SUBTRACT;
1994 case GDK_KEY_KP_Divide:
1995 return SCK_DIVIDE;
1996 case GDK_KEY_Super_L:
1997 return SCK_WIN;
1998 case GDK_KEY_Super_R:
1999 return SCK_RWIN;
2000 case GDK_KEY_Menu:
2001 return SCK_MENU;
2003 #else
2005 case GDK_ISO_Left_Tab:
2006 return SCK_TAB;
2007 case GDK_KP_Down:
2008 return SCK_DOWN;
2009 case GDK_KP_Up:
2010 return SCK_UP;
2011 case GDK_KP_Left:
2012 return SCK_LEFT;
2013 case GDK_KP_Right:
2014 return SCK_RIGHT;
2015 case GDK_KP_Home:
2016 return SCK_HOME;
2017 case GDK_KP_End:
2018 return SCK_END;
2019 case GDK_KP_Page_Up:
2020 return SCK_PRIOR;
2021 case GDK_KP_Page_Down:
2022 return SCK_NEXT;
2023 case GDK_KP_Delete:
2024 return SCK_DELETE;
2025 case GDK_KP_Insert:
2026 return SCK_INSERT;
2027 case GDK_KP_Enter:
2028 return SCK_RETURN;
2030 case GDK_Down:
2031 return SCK_DOWN;
2032 case GDK_Up:
2033 return SCK_UP;
2034 case GDK_Left:
2035 return SCK_LEFT;
2036 case GDK_Right:
2037 return SCK_RIGHT;
2038 case GDK_Home:
2039 return SCK_HOME;
2040 case GDK_End:
2041 return SCK_END;
2042 case GDK_Page_Up:
2043 return SCK_PRIOR;
2044 case GDK_Page_Down:
2045 return SCK_NEXT;
2046 case GDK_Delete:
2047 return SCK_DELETE;
2048 case GDK_Insert:
2049 return SCK_INSERT;
2050 case GDK_Escape:
2051 return SCK_ESCAPE;
2052 case GDK_BackSpace:
2053 return SCK_BACK;
2054 case GDK_Tab:
2055 return SCK_TAB;
2056 case GDK_Return:
2057 return SCK_RETURN;
2058 case GDK_KP_Add:
2059 return SCK_ADD;
2060 case GDK_KP_Subtract:
2061 return SCK_SUBTRACT;
2062 case GDK_KP_Divide:
2063 return SCK_DIVIDE;
2064 case GDK_Super_L:
2065 return SCK_WIN;
2066 case GDK_Super_R:
2067 return SCK_RWIN;
2068 case GDK_Menu:
2069 return SCK_MENU;
2070 #endif
2071 default:
2072 return keyIn;
2076 gboolean ScintillaGTK::KeyThis(GdkEventKey *event) {
2077 try {
2078 //fprintf(stderr, "SC-key: %d %x [%s]\n",
2079 // event->keyval, event->state, (event->length > 0) ? event->string : "empty");
2080 if (gtk_im_context_filter_keypress(im_context, event)) {
2081 return 1;
2083 if (!event->keyval) {
2084 return true;
2087 bool shift = (event->state & GDK_SHIFT_MASK) != 0;
2088 bool ctrl = (event->state & GDK_CONTROL_MASK) != 0;
2089 bool alt = (event->state & GDK_MOD1_MASK) != 0;
2090 bool super = (event->state & GDK_MOD4_MASK) != 0;
2091 guint key = event->keyval;
2092 if ((ctrl || alt) && (key < 128))
2093 key = toupper(key);
2094 #if GTK_CHECK_VERSION(3,0,0)
2095 else if (!ctrl && (key >= GDK_KEY_KP_Multiply && key <= GDK_KEY_KP_9))
2096 #else
2097 else if (!ctrl && (key >= GDK_KP_Multiply && key <= GDK_KP_9))
2098 #endif
2099 key &= 0x7F;
2100 // Hack for keys over 256 and below command keys but makes Hungarian work.
2101 // This will have to change for Unicode
2102 else if (key >= 0xFE00)
2103 key = KeyTranslate(key);
2105 bool consumed = false;
2106 #if !(PLAT_GTK_MACOSX)
2107 bool meta = false;
2108 #else
2109 bool meta = ctrl;
2110 ctrl = (event->state & GDK_META_MASK) != 0;
2111 #endif
2112 bool added = KeyDownWithModifiers(key, ModifierFlags(shift, ctrl, alt, meta, super), &consumed) != 0;
2113 if (!consumed)
2114 consumed = added;
2115 //fprintf(stderr, "SK-key: %d %x %x\n",event->keyval, event->state, consumed);
2116 if (event->keyval == 0xffffff && event->length > 0) {
2117 ClearSelection();
2118 const int lengthInserted = pdoc->InsertString(CurrentPosition(), event->string, strlen(event->string));
2119 if (lengthInserted > 0) {
2120 MovePositionTo(CurrentPosition() + lengthInserted);
2123 return consumed;
2124 } catch (...) {
2125 errorStatus = SC_STATUS_FAILURE;
2127 return FALSE;
2130 gboolean ScintillaGTK::KeyPress(GtkWidget *widget, GdkEventKey *event) {
2131 ScintillaGTK *sciThis = FromWidget(widget);
2132 return sciThis->KeyThis(event);
2135 gboolean ScintillaGTK::KeyRelease(GtkWidget *widget, GdkEventKey *event) {
2136 //Platform::DebugPrintf("SC-keyrel: %d %x %3s\n",event->keyval, event->state, event->string);
2137 ScintillaGTK *sciThis = FromWidget(widget);
2138 if (gtk_im_context_filter_keypress(sciThis->im_context, event)) {
2139 return TRUE;
2141 return FALSE;
2144 #if GTK_CHECK_VERSION(3,0,0)
2146 gboolean ScintillaGTK::DrawPreeditThis(GtkWidget *, cairo_t *cr) {
2147 try {
2148 PreEditString pes(im_context);
2149 PangoLayout *layout = gtk_widget_create_pango_layout(PWidget(wText), pes.str);
2150 pango_layout_set_attributes(layout, pes.attrs);
2152 cairo_move_to(cr, 0, 0);
2153 pango_cairo_show_layout(cr, layout);
2155 g_object_unref(layout);
2156 } catch (...) {
2157 errorStatus = SC_STATUS_FAILURE;
2159 return TRUE;
2162 gboolean ScintillaGTK::DrawPreedit(GtkWidget *widget, cairo_t *cr, ScintillaGTK *sciThis) {
2163 return sciThis->DrawPreeditThis(widget, cr);
2166 #else
2168 gboolean ScintillaGTK::ExposePreeditThis(GtkWidget *widget, GdkEventExpose *) {
2169 try {
2170 PreEditString pes(im_context);
2171 PangoLayout *layout = gtk_widget_create_pango_layout(PWidget(wText), pes.str);
2172 pango_layout_set_attributes(layout, pes.attrs);
2174 cairo_t *context = gdk_cairo_create(reinterpret_cast<GdkDrawable *>(WindowFromWidget(widget)));
2175 cairo_move_to(context, 0, 0);
2176 pango_cairo_show_layout(context, layout);
2177 cairo_destroy(context);
2178 g_object_unref(layout);
2179 } catch (...) {
2180 errorStatus = SC_STATUS_FAILURE;
2182 return TRUE;
2185 gboolean ScintillaGTK::ExposePreedit(GtkWidget *widget, GdkEventExpose *ose, ScintillaGTK *sciThis) {
2186 return sciThis->ExposePreeditThis(widget, ose);
2189 #endif
2191 bool ScintillaGTK::KoreanIME() {
2192 PreEditString pes(im_context);
2193 if (pes.pscript != PANGO_SCRIPT_COMMON)
2194 lastNonCommonScript = pes.pscript;
2195 return lastNonCommonScript == PANGO_SCRIPT_HANGUL;
2198 void ScintillaGTK::MoveImeCarets(int pos) {
2199 // Move carets relatively by bytes
2200 for (size_t r=0; r<sel.Count(); r++) {
2201 int positionInsert = sel.Range(r).Start().Position();
2202 sel.Range(r).caret.SetPosition(positionInsert + pos);
2203 sel.Range(r).anchor.SetPosition(positionInsert + pos);
2207 void ScintillaGTK::DrawImeIndicator(int indicator, int len) {
2208 // Emulate the visual style of IME characters with indicators.
2209 // Draw an indicator on the character before caret by the character bytes of len
2210 // so it should be called after addCharUTF().
2211 // It does not affect caret positions.
2212 if (indicator < 8 || indicator > INDIC_MAX) {
2213 return;
2215 pdoc->decorations.SetCurrentIndicator(indicator);
2216 for (size_t r=0; r<sel.Count(); r++) {
2217 int positionInsert = sel.Range(r).Start().Position();
2218 pdoc->DecorationFillRange(positionInsert - len, 1, len);
2222 static std::vector<int> MapImeIndicators(PangoAttrList *attrs, const char *u8Str) {
2223 // Map input style to scintilla ime indicator.
2224 // Attrs position points between UTF-8 bytes.
2225 // Indicator index to be returned is character based though.
2226 glong charactersLen = g_utf8_strlen(u8Str, strlen(u8Str));
2227 std::vector<int> indicator(charactersLen, SC_INDICATOR_UNKNOWN);
2229 PangoAttrIterator *iterunderline = pango_attr_list_get_iterator(attrs);
2230 if (iterunderline) {
2231 do {
2232 PangoAttribute *attrunderline = pango_attr_iterator_get(iterunderline, PANGO_ATTR_UNDERLINE);
2233 if (attrunderline) {
2234 glong start = g_utf8_strlen(u8Str, attrunderline->start_index);
2235 glong end = g_utf8_strlen(u8Str, attrunderline->end_index);
2236 PangoUnderline uline = (PangoUnderline)((PangoAttrInt *)attrunderline)->value;
2237 for (glong i=start; i < end; ++i) {
2238 switch (uline) {
2239 case PANGO_UNDERLINE_NONE:
2240 indicator[i] = SC_INDICATOR_UNKNOWN;
2241 break;
2242 case PANGO_UNDERLINE_SINGLE: // normal input
2243 indicator[i] = SC_INDICATOR_INPUT;
2244 break;
2245 case PANGO_UNDERLINE_DOUBLE:
2246 case PANGO_UNDERLINE_LOW:
2247 case PANGO_UNDERLINE_ERROR:
2248 break;
2252 } while (pango_attr_iterator_next(iterunderline));
2253 pango_attr_iterator_destroy(iterunderline);
2256 PangoAttrIterator *itercolor = pango_attr_list_get_iterator(attrs);
2257 if (itercolor) {
2258 do {
2259 PangoAttribute *backcolor = pango_attr_iterator_get(itercolor, PANGO_ATTR_BACKGROUND);
2260 if (backcolor) {
2261 glong start = g_utf8_strlen(u8Str, backcolor->start_index);
2262 glong end = g_utf8_strlen(u8Str, backcolor->end_index);
2263 for (glong i=start; i < end; ++i) {
2264 indicator[i] = SC_INDICATOR_TARGET; // target converted
2267 } while (pango_attr_iterator_next(itercolor));
2268 pango_attr_iterator_destroy(itercolor);
2270 return indicator;
2273 void ScintillaGTK::SetCandidateWindowPos() {
2274 // Composition box accompanies candidate box.
2275 Point pt = PointMainCaret();
2276 GdkRectangle imeBox = {0}; // No need to set width
2277 imeBox.x = pt.x; // Only need positiion
2278 imeBox.y = pt.y + vs.lineHeight; // underneath the first charater
2279 gtk_im_context_set_cursor_location(im_context, &imeBox);
2282 void ScintillaGTK::CommitThis(char *commitStr) {
2283 try {
2284 //~ fprintf(stderr, "Commit '%s'\n", commitStr);
2285 view.imeCaretBlockOverride = false;
2287 if (pdoc->TentativeActive()) {
2288 pdoc->TentativeUndo();
2291 const char *charSetSource = CharacterSetID();
2293 glong uniStrLen = 0;
2294 gunichar *uniStr = g_utf8_to_ucs4_fast(commitStr, strlen(commitStr), &uniStrLen);
2295 for (glong i = 0; i < uniStrLen; i++) {
2296 gchar u8Char[UTF8MaxBytes+2] = {0};
2297 gint u8CharLen = g_unichar_to_utf8(uniStr[i], u8Char);
2298 std::string docChar = u8Char;
2299 if (!IsUnicodeMode())
2300 docChar = ConvertText(u8Char, u8CharLen, charSetSource, "UTF-8", true);
2302 AddCharUTF(docChar.c_str(), docChar.size());
2304 g_free(uniStr);
2305 ShowCaretAtCurrentPosition();
2306 } catch (...) {
2307 errorStatus = SC_STATUS_FAILURE;
2311 void ScintillaGTK::Commit(GtkIMContext *, char *str, ScintillaGTK *sciThis) {
2312 sciThis->CommitThis(str);
2315 void ScintillaGTK::PreeditChangedInlineThis() {
2316 // Copy & paste by johnsonj with a lot of helps of Neil
2317 // Great thanks for my foreruners, jiniya and BLUEnLIVE
2318 try {
2319 if (pdoc->IsReadOnly() || SelectionContainsProtected()) {
2320 gtk_im_context_reset(im_context);
2321 return;
2324 view.imeCaretBlockOverride = false; // If backspace.
2326 if (pdoc->TentativeActive()) {
2327 pdoc->TentativeUndo();
2328 } else {
2329 // No tentative undo means start of this composition so
2330 // fill in any virtual spaces.
2331 ClearBeforeTentativeStart();
2334 PreEditString preeditStr(im_context);
2335 const char *charSetSource = CharacterSetID();
2337 if (!preeditStr.validUTF8 || (charSetSource == NULL)) {
2338 ShowCaretAtCurrentPosition();
2339 return;
2342 if (preeditStr.uniStrLen == 0 || preeditStr.uniStrLen > maxLenInputIME) {
2343 //fprintf(stderr, "Do not allow over 200 chars: %i\n", preeditStr.uniStrLen);
2344 ShowCaretAtCurrentPosition();
2345 return;
2348 pdoc->TentativeStart(); // TentativeActive() from now on
2350 std::vector<int> indicator = MapImeIndicators(preeditStr.attrs, preeditStr.str);
2352 bool tmpRecordingMacro = recordingMacro;
2353 recordingMacro = false;
2354 for (glong i = 0; i < preeditStr.uniStrLen; i++) {
2355 gchar u8Char[UTF8MaxBytes+2] = {0};
2356 gint u8CharLen = g_unichar_to_utf8(preeditStr.uniStr[i], u8Char);
2357 std::string docChar = u8Char;
2358 if (!IsUnicodeMode())
2359 docChar = ConvertText(u8Char, u8CharLen, charSetSource, "UTF-8", true);
2361 AddCharUTF(docChar.c_str(), docChar.size());
2363 DrawImeIndicator(indicator[i], docChar.size());
2365 recordingMacro = tmpRecordingMacro;
2367 // Move caret to ime cursor position.
2368 int imeEndToImeCaretU32 = preeditStr.cursor_pos - preeditStr.uniStrLen;
2369 int imeCaretPosDoc = pdoc->GetRelativePosition(CurrentPosition(), imeEndToImeCaretU32);
2371 MoveImeCarets(- CurrentPosition() + imeCaretPosDoc);
2373 if (KoreanIME()) {
2374 #if !PLAT_GTK_WIN32
2375 if (preeditStr.cursor_pos > 0) {
2376 int oneCharBefore = pdoc->GetRelativePosition(CurrentPosition(), -1);
2377 MoveImeCarets(- CurrentPosition() + oneCharBefore);
2379 #endif
2380 view.imeCaretBlockOverride = true;
2383 EnsureCaretVisible();
2384 SetCandidateWindowPos();
2385 ShowCaretAtCurrentPosition();
2386 } catch (...) {
2387 errorStatus = SC_STATUS_FAILURE;
2391 void ScintillaGTK::PreeditChangedWindowedThis() {
2392 try {
2393 PreEditString pes(im_context);
2394 if (strlen(pes.str) > 0) {
2395 PangoLayout *layout = gtk_widget_create_pango_layout(PWidget(wText), pes.str);
2396 pango_layout_set_attributes(layout, pes.attrs);
2398 gint w, h;
2399 pango_layout_get_pixel_size(layout, &w, &h);
2400 g_object_unref(layout);
2402 gint x, y;
2403 gdk_window_get_origin(PWindow(wText), &x, &y);
2405 Point pt = PointMainCaret();
2406 if (pt.x < 0)
2407 pt.x = 0;
2408 if (pt.y < 0)
2409 pt.y = 0;
2411 gtk_window_move(GTK_WINDOW(PWidget(wPreedit)), x + pt.x, y + pt.y);
2412 gtk_window_resize(GTK_WINDOW(PWidget(wPreedit)), w, h);
2413 gtk_widget_show(PWidget(wPreedit));
2414 gtk_widget_queue_draw_area(PWidget(wPreeditDraw), 0, 0, w, h);
2415 } else {
2416 gtk_widget_hide(PWidget(wPreedit));
2418 } catch (...) {
2419 errorStatus = SC_STATUS_FAILURE;
2423 void ScintillaGTK::PreeditChanged(GtkIMContext *, ScintillaGTK *sciThis) {
2424 if ((sciThis->imeInteraction == imeInline) || (sciThis->KoreanIME())) {
2425 sciThis->PreeditChangedInlineThis();
2426 } else {
2427 sciThis->PreeditChangedWindowedThis();
2431 void ScintillaGTK::StyleSetText(GtkWidget *widget, GtkStyle *, void*) {
2432 RealizeText(widget, NULL);
2435 void ScintillaGTK::RealizeText(GtkWidget *widget, void*) {
2436 // Set NULL background to avoid automatic clearing so Scintilla responsible for all drawing
2437 if (WindowFromWidget(widget)) {
2438 #if GTK_CHECK_VERSION(3,22,0)
2439 // Appears unnecessary
2440 #elif GTK_CHECK_VERSION(3,0,0)
2441 gdk_window_set_background_pattern(WindowFromWidget(widget), NULL);
2442 #else
2443 gdk_window_set_back_pixmap(WindowFromWidget(widget), NULL, FALSE);
2444 #endif
2448 static GObjectClass *scintilla_class_parent_class;
2450 void ScintillaGTK::Dispose(GObject *object) {
2451 try {
2452 ScintillaObject *scio = reinterpret_cast<ScintillaObject *>(object);
2453 ScintillaGTK *sciThis = reinterpret_cast<ScintillaGTK *>(scio->pscin);
2455 if (PWidget(sciThis->scrollbarv)) {
2456 gtk_widget_unparent(PWidget(sciThis->scrollbarv));
2457 sciThis->scrollbarv = NULL;
2460 if (PWidget(sciThis->scrollbarh)) {
2461 gtk_widget_unparent(PWidget(sciThis->scrollbarh));
2462 sciThis->scrollbarh = NULL;
2465 scintilla_class_parent_class->dispose(object);
2466 } catch (...) {
2467 // Its dying so nowhere to save the status
2471 void ScintillaGTK::Destroy(GObject *object) {
2472 try {
2473 ScintillaObject *scio = SCINTILLA(object);
2475 // This avoids a double destruction
2476 if (!scio->pscin)
2477 return;
2478 ScintillaGTK *sciThis = static_cast<ScintillaGTK *>(scio->pscin);
2479 //Platform::DebugPrintf("Destroying %x %x\n", sciThis, object);
2480 sciThis->Finalise();
2482 delete sciThis;
2483 scio->pscin = 0;
2484 scintilla_class_parent_class->finalize(object);
2485 } catch (...) {
2486 // Its dead so nowhere to save the status
2490 #if GTK_CHECK_VERSION(3,0,0)
2492 gboolean ScintillaGTK::DrawTextThis(cairo_t *cr) {
2493 try {
2494 paintState = painting;
2495 repaintFullWindow = false;
2497 rcPaint = GetClientRectangle();
2499 PLATFORM_ASSERT(rgnUpdate == NULL);
2500 rgnUpdate = cairo_copy_clip_rectangle_list(cr);
2501 if (rgnUpdate && rgnUpdate->status != CAIRO_STATUS_SUCCESS) {
2502 // If not successful then ignore
2503 fprintf(stderr, "DrawTextThis failed to copy update region %d [%d]\n", rgnUpdate->status, rgnUpdate->num_rectangles);
2504 cairo_rectangle_list_destroy(rgnUpdate);
2505 rgnUpdate = 0;
2508 double x1, y1, x2, y2;
2509 cairo_clip_extents(cr, &x1, &y1, &x2, &y2);
2510 rcPaint.left = x1;
2511 rcPaint.top = y1;
2512 rcPaint.right = x2;
2513 rcPaint.bottom = y2;
2514 PRectangle rcClient = GetClientRectangle();
2515 paintingAllText = rcPaint.Contains(rcClient);
2516 Surface *surfaceWindow = Surface::Allocate(SC_TECHNOLOGY_DEFAULT);
2517 if (surfaceWindow) {
2518 surfaceWindow->Init(cr, PWidget(wText));
2519 Paint(surfaceWindow, rcPaint);
2520 surfaceWindow->Release();
2521 delete surfaceWindow;
2523 if ((paintState == paintAbandoned) || repaintFullWindow) {
2524 // Painting area was insufficient to cover new styling or brace highlight positions
2525 FullPaint();
2527 paintState = notPainting;
2528 repaintFullWindow = false;
2530 if (rgnUpdate) {
2531 cairo_rectangle_list_destroy(rgnUpdate);
2533 rgnUpdate = 0;
2534 paintState = notPainting;
2535 } catch (...) {
2536 errorStatus = SC_STATUS_FAILURE;
2539 return FALSE;
2542 gboolean ScintillaGTK::DrawText(GtkWidget *, cairo_t *cr, ScintillaGTK *sciThis) {
2543 return sciThis->DrawTextThis(cr);
2546 gboolean ScintillaGTK::DrawThis(cairo_t *cr) {
2547 try {
2548 #ifdef GTK_STYLE_CLASS_SCROLLBARS_JUNCTION /* GTK >= 3.4 */
2549 // if both scrollbars are visible, paint the little square on the bottom right corner
2550 if (verticalScrollBarVisible && horizontalScrollBarVisible && !Wrapping()) {
2551 GtkStyleContext *styleContext = gtk_widget_get_style_context(PWidget(wMain));
2552 PRectangle rc = GetClientRectangle();
2554 gtk_style_context_save(styleContext);
2555 gtk_style_context_add_class(styleContext, GTK_STYLE_CLASS_SCROLLBARS_JUNCTION);
2557 gtk_render_background(styleContext, cr, rc.right, rc.bottom,
2558 verticalScrollBarWidth, horizontalScrollBarHeight);
2559 gtk_render_frame(styleContext, cr, rc.right, rc.bottom,
2560 verticalScrollBarWidth, horizontalScrollBarHeight);
2562 gtk_style_context_restore(styleContext);
2564 #endif
2566 gtk_container_propagate_draw(
2567 GTK_CONTAINER(PWidget(wMain)), PWidget(scrollbarh), cr);
2568 gtk_container_propagate_draw(
2569 GTK_CONTAINER(PWidget(wMain)), PWidget(scrollbarv), cr);
2570 // Starting from the following version, the expose event are not propagated
2571 // for double buffered non native windows, so we need to call it ourselves
2572 // or keep the default handler
2573 #if GTK_CHECK_VERSION(3,0,0)
2574 // we want to forward on any >= 3.9.2 runtime
2575 if (gtk_check_version(3,9,2) == NULL) {
2576 gtk_container_propagate_draw(
2577 GTK_CONTAINER(PWidget(wMain)), PWidget(wText), cr);
2579 #endif
2580 } catch (...) {
2581 errorStatus = SC_STATUS_FAILURE;
2583 return FALSE;
2586 gboolean ScintillaGTK::DrawMain(GtkWidget *widget, cairo_t *cr) {
2587 ScintillaGTK *sciThis = FromWidget(widget);
2588 return sciThis->DrawThis(cr);
2591 #else
2593 gboolean ScintillaGTK::ExposeTextThis(GtkWidget * /*widget*/, GdkEventExpose *ose) {
2594 try {
2595 paintState = painting;
2597 rcPaint.left = ose->area.x;
2598 rcPaint.top = ose->area.y;
2599 rcPaint.right = ose->area.x + ose->area.width;
2600 rcPaint.bottom = ose->area.y + ose->area.height;
2602 PLATFORM_ASSERT(rgnUpdate == NULL);
2603 rgnUpdate = gdk_region_copy(ose->region);
2604 PRectangle rcClient = GetClientRectangle();
2605 paintingAllText = rcPaint.Contains(rcClient);
2606 Surface *surfaceWindow = Surface::Allocate(SC_TECHNOLOGY_DEFAULT);
2607 if (surfaceWindow) {
2608 cairo_t *cr = gdk_cairo_create(PWindow(wText));
2609 surfaceWindow->Init(cr, PWidget(wText));
2610 Paint(surfaceWindow, rcPaint);
2611 surfaceWindow->Release();
2612 delete surfaceWindow;
2613 cairo_destroy(cr);
2615 if (paintState == paintAbandoned) {
2616 // Painting area was insufficient to cover new styling or brace highlight positions
2617 FullPaint();
2619 paintState = notPainting;
2621 if (rgnUpdate) {
2622 gdk_region_destroy(rgnUpdate);
2624 rgnUpdate = 0;
2625 } catch (...) {
2626 errorStatus = SC_STATUS_FAILURE;
2629 return FALSE;
2632 gboolean ScintillaGTK::ExposeText(GtkWidget *widget, GdkEventExpose *ose, ScintillaGTK *sciThis) {
2633 return sciThis->ExposeTextThis(widget, ose);
2636 gboolean ScintillaGTK::ExposeMain(GtkWidget *widget, GdkEventExpose *ose) {
2637 ScintillaGTK *sciThis = FromWidget(widget);
2638 //Platform::DebugPrintf("Expose Main %0d,%0d %0d,%0d\n",
2639 //ose->area.x, ose->area.y, ose->area.width, ose->area.height);
2640 return sciThis->Expose(widget, ose);
2643 gboolean ScintillaGTK::Expose(GtkWidget *, GdkEventExpose *ose) {
2644 try {
2645 //fprintf(stderr, "Expose %0d,%0d %0d,%0d\n",
2646 //ose->area.x, ose->area.y, ose->area.width, ose->area.height);
2648 // The text is painted in ExposeText
2649 gtk_container_propagate_expose(
2650 GTK_CONTAINER(PWidget(wMain)), PWidget(scrollbarh), ose);
2651 gtk_container_propagate_expose(
2652 GTK_CONTAINER(PWidget(wMain)), PWidget(scrollbarv), ose);
2654 } catch (...) {
2655 errorStatus = SC_STATUS_FAILURE;
2657 return FALSE;
2660 #endif
2662 void ScintillaGTK::ScrollSignal(GtkAdjustment *adj, ScintillaGTK *sciThis) {
2663 try {
2664 sciThis->ScrollTo(static_cast<int>(gtk_adjustment_get_value(adj)), false);
2665 } catch (...) {
2666 sciThis->errorStatus = SC_STATUS_FAILURE;
2670 void ScintillaGTK::ScrollHSignal(GtkAdjustment *adj, ScintillaGTK *sciThis) {
2671 try {
2672 sciThis->HorizontalScrollTo(static_cast<int>(gtk_adjustment_get_value(adj)));
2673 } catch (...) {
2674 sciThis->errorStatus = SC_STATUS_FAILURE;
2678 void ScintillaGTK::SelectionReceived(GtkWidget *widget,
2679 GtkSelectionData *selection_data, guint) {
2680 ScintillaGTK *sciThis = FromWidget(widget);
2681 //Platform::DebugPrintf("Selection received\n");
2682 sciThis->ReceivedSelection(selection_data);
2685 void ScintillaGTK::SelectionGet(GtkWidget *widget,
2686 GtkSelectionData *selection_data, guint info, guint) {
2687 ScintillaGTK *sciThis = FromWidget(widget);
2688 try {
2689 //Platform::DebugPrintf("Selection get\n");
2690 if (SelectionOfGSD(selection_data) == GDK_SELECTION_PRIMARY) {
2691 if (sciThis->primary.Empty()) {
2692 sciThis->CopySelectionRange(&sciThis->primary);
2694 sciThis->GetSelection(selection_data, info, &sciThis->primary);
2696 } catch (...) {
2697 sciThis->errorStatus = SC_STATUS_FAILURE;
2701 gint ScintillaGTK::SelectionClear(GtkWidget *widget, GdkEventSelection *selection_event) {
2702 ScintillaGTK *sciThis = FromWidget(widget);
2703 //Platform::DebugPrintf("Selection clear\n");
2704 sciThis->UnclaimSelection(selection_event);
2705 if (GTK_WIDGET_CLASS(sciThis->parentClass)->selection_clear_event) {
2706 return GTK_WIDGET_CLASS(sciThis->parentClass)->selection_clear_event(widget, selection_event);
2708 return TRUE;
2711 gboolean ScintillaGTK::DragMotionThis(GdkDragContext *context,
2712 gint x, gint y, guint dragtime) {
2713 try {
2714 Point npt(x, y);
2715 SetDragPosition(SPositionFromLocation(npt, false, false, UserVirtualSpace()));
2716 #if GTK_CHECK_VERSION(2,22,0)
2717 GdkDragAction preferredAction = gdk_drag_context_get_suggested_action(context);
2718 GdkDragAction actions = gdk_drag_context_get_actions(context);
2719 #else
2720 GdkDragAction preferredAction = context->suggested_action;
2721 GdkDragAction actions = context->actions;
2722 #endif
2723 SelectionPosition pos = SPositionFromLocation(npt);
2724 if ((inDragDrop == ddDragging) && (PositionInSelection(pos.Position()))) {
2725 // Avoid dragging selection onto itself as that produces a move
2726 // with no real effect but which creates undo actions.
2727 preferredAction = static_cast<GdkDragAction>(0);
2728 } else if (actions == static_cast<GdkDragAction>
2729 (GDK_ACTION_COPY | GDK_ACTION_MOVE)) {
2730 preferredAction = GDK_ACTION_MOVE;
2732 gdk_drag_status(context, preferredAction, dragtime);
2733 } catch (...) {
2734 errorStatus = SC_STATUS_FAILURE;
2736 return FALSE;
2739 gboolean ScintillaGTK::DragMotion(GtkWidget *widget, GdkDragContext *context,
2740 gint x, gint y, guint dragtime) {
2741 ScintillaGTK *sciThis = FromWidget(widget);
2742 return sciThis->DragMotionThis(context, x, y, dragtime);
2745 void ScintillaGTK::DragLeave(GtkWidget *widget, GdkDragContext * /*context*/, guint) {
2746 ScintillaGTK *sciThis = FromWidget(widget);
2747 try {
2748 sciThis->SetDragPosition(SelectionPosition(invalidPosition));
2749 //Platform::DebugPrintf("DragLeave %x\n", sciThis);
2750 } catch (...) {
2751 sciThis->errorStatus = SC_STATUS_FAILURE;
2755 void ScintillaGTK::DragEnd(GtkWidget *widget, GdkDragContext * /*context*/) {
2756 ScintillaGTK *sciThis = FromWidget(widget);
2757 try {
2758 // If drag did not result in drop here or elsewhere
2759 if (!sciThis->dragWasDropped)
2760 sciThis->SetEmptySelection(sciThis->posDrag);
2761 sciThis->SetDragPosition(SelectionPosition(invalidPosition));
2762 //Platform::DebugPrintf("DragEnd %x %d\n", sciThis, sciThis->dragWasDropped);
2763 sciThis->inDragDrop = ddNone;
2764 } catch (...) {
2765 sciThis->errorStatus = SC_STATUS_FAILURE;
2769 gboolean ScintillaGTK::Drop(GtkWidget *widget, GdkDragContext * /*context*/,
2770 gint, gint, guint) {
2771 ScintillaGTK *sciThis = FromWidget(widget);
2772 try {
2773 //Platform::DebugPrintf("Drop %x\n", sciThis);
2774 sciThis->SetDragPosition(SelectionPosition(invalidPosition));
2775 } catch (...) {
2776 sciThis->errorStatus = SC_STATUS_FAILURE;
2778 return FALSE;
2781 void ScintillaGTK::DragDataReceived(GtkWidget *widget, GdkDragContext * /*context*/,
2782 gint, gint, GtkSelectionData *selection_data, guint /*info*/, guint) {
2783 ScintillaGTK *sciThis = FromWidget(widget);
2784 try {
2785 sciThis->ReceivedDrop(selection_data);
2786 sciThis->SetDragPosition(SelectionPosition(invalidPosition));
2787 } catch (...) {
2788 sciThis->errorStatus = SC_STATUS_FAILURE;
2792 void ScintillaGTK::DragDataGet(GtkWidget *widget, GdkDragContext *context,
2793 GtkSelectionData *selection_data, guint info, guint) {
2794 ScintillaGTK *sciThis = FromWidget(widget);
2795 try {
2796 sciThis->dragWasDropped = true;
2797 if (!sciThis->sel.Empty()) {
2798 sciThis->GetSelection(selection_data, info, &sciThis->drag);
2800 #if GTK_CHECK_VERSION(2,22,0)
2801 GdkDragAction action = gdk_drag_context_get_selected_action(context);
2802 #else
2803 GdkDragAction action = context->action;
2804 #endif
2805 if (action == GDK_ACTION_MOVE) {
2806 for (size_t r=0; r<sciThis->sel.Count(); r++) {
2807 if (sciThis->posDrop >= sciThis->sel.Range(r).Start()) {
2808 if (sciThis->posDrop > sciThis->sel.Range(r).End()) {
2809 sciThis->posDrop.Add(-sciThis->sel.Range(r).Length());
2810 } else {
2811 sciThis->posDrop.Add(-SelectionRange(sciThis->posDrop, sciThis->sel.Range(r).Start()).Length());
2815 sciThis->ClearSelection();
2817 sciThis->SetDragPosition(SelectionPosition(invalidPosition));
2818 } catch (...) {
2819 sciThis->errorStatus = SC_STATUS_FAILURE;
2823 int ScintillaGTK::TimeOut(gpointer ptt) {
2824 TimeThunk *tt = static_cast<TimeThunk *>(ptt);
2825 tt->scintilla->TickFor(tt->reason);
2826 return 1;
2829 gboolean ScintillaGTK::IdleCallback(gpointer pSci) {
2830 ScintillaGTK *sciThis = static_cast<ScintillaGTK *>(pSci);
2831 // Idler will be automatically stopped, if there is nothing
2832 // to do while idle.
2833 bool ret = sciThis->Idle();
2834 if (ret == false) {
2835 // FIXME: This will remove the idler from GTK, we don't want to
2836 // remove it as it is removed automatically when this function
2837 // returns false (although, it should be harmless).
2838 sciThis->SetIdle(false);
2840 return ret;
2843 gboolean ScintillaGTK::StyleIdle(gpointer pSci) {
2844 ScintillaGTK *sciThis = static_cast<ScintillaGTK *>(pSci);
2845 sciThis->IdleWork();
2846 // Idler will be automatically stopped
2847 return FALSE;
2850 void ScintillaGTK::IdleWork() {
2851 Editor::IdleWork();
2852 styleIdleID = 0;
2855 void ScintillaGTK::QueueIdleWork(WorkNeeded::workItems items, int upTo) {
2856 Editor::QueueIdleWork(items, upTo);
2857 if (!styleIdleID) {
2858 // Only allow one style needed to be queued
2859 styleIdleID = gdk_threads_add_idle_full(G_PRIORITY_HIGH_IDLE, StyleIdle, this, NULL);
2863 void ScintillaGTK::SetDocPointer(Document *document) {
2864 Document *oldDoc = 0;
2865 ScintillaGTKAccessible *sciAccessible = 0;
2866 if (accessible) {
2867 sciAccessible = ScintillaGTKAccessible::FromAccessible(accessible);
2868 if (sciAccessible && pdoc) {
2869 oldDoc = pdoc;
2870 oldDoc->AddRef();
2874 Editor::SetDocPointer(document);
2876 if (sciAccessible) {
2877 // the accessible needs have the old Document, but also the new one active
2878 sciAccessible->ChangeDocument(oldDoc, pdoc);
2880 if (oldDoc) {
2881 oldDoc->Release();
2885 void ScintillaGTK::PopUpCB(GtkMenuItem *menuItem, ScintillaGTK *sciThis) {
2886 guint action = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(menuItem), "CmdNum"));
2887 if (action) {
2888 sciThis->Command(action);
2892 gboolean ScintillaGTK::PressCT(GtkWidget *widget, GdkEventButton *event, ScintillaGTK *sciThis) {
2893 try {
2894 if (event->window != WindowFromWidget(widget))
2895 return FALSE;
2896 if (event->type != GDK_BUTTON_PRESS)
2897 return FALSE;
2898 Point pt;
2899 pt.x = int(event->x);
2900 pt.y = int(event->y);
2901 sciThis->ct.MouseClick(pt);
2902 sciThis->CallTipClick();
2903 } catch (...) {
2905 return TRUE;
2908 #if GTK_CHECK_VERSION(3,0,0)
2910 gboolean ScintillaGTK::DrawCT(GtkWidget *widget, cairo_t *cr, CallTip *ctip) {
2911 try {
2912 Surface *surfaceWindow = Surface::Allocate(SC_TECHNOLOGY_DEFAULT);
2913 if (surfaceWindow) {
2914 surfaceWindow->Init(cr, widget);
2915 surfaceWindow->SetUnicodeMode(SC_CP_UTF8 == ctip->codePage);
2916 surfaceWindow->SetDBCSMode(ctip->codePage);
2917 ctip->PaintCT(surfaceWindow);
2918 surfaceWindow->Release();
2919 delete surfaceWindow;
2921 } catch (...) {
2922 // No pointer back to Scintilla to save status
2924 return TRUE;
2927 #else
2929 gboolean ScintillaGTK::ExposeCT(GtkWidget *widget, GdkEventExpose * /*ose*/, CallTip *ctip) {
2930 try {
2931 Surface *surfaceWindow = Surface::Allocate(SC_TECHNOLOGY_DEFAULT);
2932 if (surfaceWindow) {
2933 cairo_t *cr = gdk_cairo_create(WindowFromWidget(widget));
2934 surfaceWindow->Init(cr, widget);
2935 surfaceWindow->SetUnicodeMode(SC_CP_UTF8 == ctip->codePage);
2936 surfaceWindow->SetDBCSMode(ctip->codePage);
2937 ctip->PaintCT(surfaceWindow);
2938 surfaceWindow->Release();
2939 delete surfaceWindow;
2940 cairo_destroy(cr);
2942 } catch (...) {
2943 // No pointer back to Scintilla to save status
2945 return TRUE;
2948 #endif
2950 AtkObject* ScintillaGTK::GetAccessibleThis(GtkWidget *widget) {
2951 return ScintillaGTKAccessible::WidgetGetAccessibleImpl(widget, &accessible, scintilla_class_parent_class);
2954 AtkObject* ScintillaGTK::GetAccessible(GtkWidget *widget) {
2955 return FromWidget(widget)->GetAccessibleThis(widget);
2958 sptr_t ScintillaGTK::DirectFunction(
2959 sptr_t ptr, unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
2960 return reinterpret_cast<ScintillaGTK *>(ptr)->WndProc(iMessage, wParam, lParam);
2963 /* legacy name for scintilla_object_send_message */
2964 GEANY_API_SYMBOL
2965 sptr_t scintilla_send_message(ScintillaObject *sci, unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
2966 ScintillaGTK *psci = static_cast<ScintillaGTK *>(sci->pscin);
2967 return psci->WndProc(iMessage, wParam, lParam);
2970 GEANY_API_SYMBOL
2971 gintptr scintilla_object_send_message(ScintillaObject *sci, unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
2972 return scintilla_send_message(sci, iMessage, wParam, lParam);
2975 static void scintilla_class_init(ScintillaClass *klass);
2976 static void scintilla_init(ScintillaObject *sci);
2978 extern void Platform_Initialise();
2979 extern void Platform_Finalise();
2981 /* legacy name for scintilla_object_get_type */
2982 GEANY_API_SYMBOL
2983 GType scintilla_get_type() {
2984 static GType scintilla_type = 0;
2985 try {
2987 if (!scintilla_type) {
2988 scintilla_type = g_type_from_name("ScintillaObject");
2989 if (!scintilla_type) {
2990 static GTypeInfo scintilla_info = {
2991 (guint16) sizeof (ScintillaObjectClass),
2992 NULL, //(GBaseInitFunc)
2993 NULL, //(GBaseFinalizeFunc)
2994 (GClassInitFunc) scintilla_class_init,
2995 NULL, //(GClassFinalizeFunc)
2996 NULL, //gconstpointer data
2997 (guint16) sizeof (ScintillaObject),
2998 0, //n_preallocs
2999 (GInstanceInitFunc) scintilla_init,
3000 NULL //(GTypeValueTable*)
3002 scintilla_type = g_type_register_static(
3003 GTK_TYPE_CONTAINER, "ScintillaObject", &scintilla_info, (GTypeFlags) 0);
3007 } catch (...) {
3009 return scintilla_type;
3012 GEANY_API_SYMBOL
3013 GType scintilla_object_get_type() {
3014 return scintilla_get_type();
3017 void ScintillaGTK::ClassInit(OBJECT_CLASS* object_class, GtkWidgetClass *widget_class, GtkContainerClass *container_class) {
3018 Platform_Initialise();
3019 #ifdef SCI_LEXER
3020 Scintilla_LinkLexers();
3021 #endif
3022 atomClipboard = gdk_atom_intern("CLIPBOARD", FALSE);
3023 atomUTF8 = gdk_atom_intern("UTF8_STRING", FALSE);
3024 atomString = GDK_SELECTION_TYPE_STRING;
3025 atomUriList = gdk_atom_intern("text/uri-list", FALSE);
3026 atomDROPFILES_DND = gdk_atom_intern("DROPFILES_DND", FALSE);
3028 // Define default signal handlers for the class: Could move more
3029 // of the signal handlers here (those that currently attached to wDraw
3030 // in Initialise() may require coordinate translation?)
3032 object_class->dispose = Dispose;
3033 object_class->finalize = Destroy;
3034 #if GTK_CHECK_VERSION(3,0,0)
3035 widget_class->get_preferred_width = GetPreferredWidth;
3036 widget_class->get_preferred_height = GetPreferredHeight;
3037 #else
3038 widget_class->size_request = SizeRequest;
3039 #endif
3040 widget_class->size_allocate = SizeAllocate;
3041 #if GTK_CHECK_VERSION(3,0,0)
3042 widget_class->draw = DrawMain;
3043 #else
3044 widget_class->expose_event = ExposeMain;
3045 #endif
3046 widget_class->motion_notify_event = Motion;
3047 widget_class->button_press_event = Press;
3048 widget_class->button_release_event = MouseRelease;
3049 widget_class->scroll_event = ScrollEvent;
3050 widget_class->key_press_event = KeyPress;
3051 widget_class->key_release_event = KeyRelease;
3052 widget_class->focus_in_event = FocusIn;
3053 widget_class->focus_out_event = FocusOut;
3054 widget_class->selection_received = SelectionReceived;
3055 widget_class->selection_get = SelectionGet;
3056 widget_class->selection_clear_event = SelectionClear;
3058 widget_class->drag_data_received = DragDataReceived;
3059 widget_class->drag_motion = DragMotion;
3060 widget_class->drag_leave = DragLeave;
3061 widget_class->drag_end = DragEnd;
3062 widget_class->drag_drop = Drop;
3063 widget_class->drag_data_get = DragDataGet;
3065 widget_class->realize = Realize;
3066 widget_class->unrealize = UnRealize;
3067 widget_class->map = Map;
3068 widget_class->unmap = UnMap;
3070 widget_class->get_accessible = GetAccessible;
3072 container_class->forall = MainForAll;
3075 static void scintilla_class_init(ScintillaClass *klass) {
3076 try {
3077 OBJECT_CLASS *object_class = (OBJECT_CLASS*) klass;
3078 GtkWidgetClass *widget_class = (GtkWidgetClass*) klass;
3079 GtkContainerClass *container_class = (GtkContainerClass*) klass;
3081 GSignalFlags sigflags = GSignalFlags(G_SIGNAL_ACTION | G_SIGNAL_RUN_LAST);
3082 scintilla_signals[COMMAND_SIGNAL] = g_signal_new(
3083 "command",
3084 G_TYPE_FROM_CLASS(object_class),
3085 sigflags,
3086 G_STRUCT_OFFSET(ScintillaClass, command),
3087 NULL, //(GSignalAccumulator)
3088 NULL, //(gpointer)
3089 scintilla_marshal_VOID__INT_OBJECT,
3090 G_TYPE_NONE,
3091 2, G_TYPE_INT, GTK_TYPE_WIDGET);
3093 scintilla_signals[NOTIFY_SIGNAL] = g_signal_new(
3094 SCINTILLA_NOTIFY,
3095 G_TYPE_FROM_CLASS(object_class),
3096 sigflags,
3097 G_STRUCT_OFFSET(ScintillaClass, notify),
3098 NULL, //(GSignalAccumulator)
3099 NULL, //(gpointer)
3100 scintilla_marshal_VOID__INT_BOXED,
3101 G_TYPE_NONE,
3102 2, G_TYPE_INT, SCINTILLA_TYPE_NOTIFICATION);
3104 klass->command = NULL;
3105 klass->notify = NULL;
3106 scintilla_class_parent_class = G_OBJECT_CLASS(g_type_class_peek_parent(klass));
3107 ScintillaGTK::ClassInit(object_class, widget_class, container_class);
3108 } catch (...) {
3112 static void scintilla_init(ScintillaObject *sci) {
3113 try {
3114 gtk_widget_set_can_focus(GTK_WIDGET(sci), TRUE);
3115 sci->pscin = new ScintillaGTK(sci);
3116 } catch (...) {
3120 /* legacy name for scintilla_object_new */
3121 GEANY_API_SYMBOL
3122 GtkWidget* scintilla_new() {
3123 GtkWidget *widget = GTK_WIDGET(g_object_new(scintilla_get_type(), NULL));
3124 gtk_widget_set_direction(widget, GTK_TEXT_DIR_LTR);
3126 return widget;
3129 GEANY_API_SYMBOL
3130 GtkWidget *scintilla_object_new() {
3131 return scintilla_new();
3134 void scintilla_set_id(ScintillaObject *sci, uptr_t id) {
3135 ScintillaGTK *psci = static_cast<ScintillaGTK *>(sci->pscin);
3136 psci->ctrlID = id;
3139 void scintilla_release_resources(void) {
3140 try {
3141 Platform_Finalise();
3142 } catch (...) {
3146 /* Define a dummy boxed type because g-ir-scanner is unable to
3147 * recognize gpointer-derived types. Note that SCNotificaiton
3148 * is always allocated on stack so copying is not appropriate. */
3149 static void *copy_(void *src) { return src; }
3150 static void free_(void *) { }
3152 GEANY_API_SYMBOL
3153 GType scnotification_get_type(void) {
3154 static gsize type_id = 0;
3155 if (g_once_init_enter(&type_id)) {
3156 gsize id = (gsize) g_boxed_type_register_static(
3157 g_intern_static_string("SCNotification"),
3158 (GBoxedCopyFunc) copy_,
3159 (GBoxedFreeFunc) free_);
3160 g_once_init_leave(&type_id, id);
3162 return (GType) type_id;