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.
5 
6 #include <stdlib.h>
7 #include <string.h>
8 #include <stdio.h>
9 #include <time.h>
10 #include <assert.h>
11 #include <ctype.h>
12 
13 #include <new>
14 #include <string>
15 #include <vector>
16 #include <map>
17 #include <algorithm>
18 
19 #include <glib.h>
20 #include <gmodule.h>
21 #include <gtk/gtk.h>
22 #include <gdk/gdkkeysyms.h>
23 
24 #if defined(__WIN32__) || defined(_MSC_VER)
25 #include <windows.h>
26 #endif
27 
28 #include "Platform.h"
29 
30 #include "ILexer.h"
31 #include "Scintilla.h"
32 #include "ScintillaWidget.h"
33 #ifdef SCI_LEXER
34 #include "SciLexer.h"
35 #endif
36 #include "StringCopy.h"
37 #ifdef SCI_LEXER
38 #include "LexerModule.h"
39 #endif
40 #include "SplitVector.h"
41 #include "Partitioning.h"
42 #include "RunStyles.h"
43 #include "ContractionState.h"
44 #include "CellBuffer.h"
45 #include "CallTip.h"
46 #include "KeyMap.h"
47 #include "Indicator.h"
48 #include "XPM.h"
49 #include "LineMarker.h"
50 #include "Style.h"
51 #include "ViewStyle.h"
52 #include "CharClassify.h"
53 #include "Decoration.h"
54 #include "CaseFolder.h"
55 #include "Document.h"
56 #include "CaseConvert.h"
57 #include "UniConversion.h"
58 #include "Selection.h"
59 #include "PositionCache.h"
60 #include "EditModel.h"
61 #include "MarginView.h"
62 #include "EditView.h"
63 #include "Editor.h"
64 #include "AutoComplete.h"
65 #include "ScintillaBase.h"
66 #include "UnicodeFromUTF8.h"
67 
68 #ifdef SCI_LEXER
69 #include "ExternalLexer.h"
70 #endif
71 
72 #include "scintilla-marshal.h"
73 
74 #include "Converter.h"
75 
76 #if defined(__clang__)
77 // Clang 3.0 incorrectly displays  sentinel warnings. Fixed by clang 3.1.
78 #pragma GCC diagnostic ignored "-Wsentinel"
79 #endif
80 
81 #if GTK_CHECK_VERSION(2,20,0)
82 #define IS_WIDGET_REALIZED(w) (gtk_widget_get_realized(GTK_WIDGET(w)))
83 #define IS_WIDGET_MAPPED(w) (gtk_widget_get_mapped(GTK_WIDGET(w)))
84 #define IS_WIDGET_VISIBLE(w) (gtk_widget_get_visible(GTK_WIDGET(w)))
85 #else
86 #define IS_WIDGET_REALIZED(w) (GTK_WIDGET_REALIZED(w))
87 #define IS_WIDGET_MAPPED(w) (GTK_WIDGET_MAPPED(w))
88 #define IS_WIDGET_VISIBLE(w) (GTK_WIDGET_VISIBLE(w))
89 #endif
90 
WindowFromWidget(GtkWidget * w)91 static GdkWindow *WindowFromWidget(GtkWidget *w) {
92 #if GTK_CHECK_VERSION(3,0,0)
93 	return gtk_widget_get_window(w);
94 #else
95 	return w->window;
96 #endif
97 }
98 
99 #ifdef _MSC_VER
100 // Constant conditional expressions are because of GTK+ headers
101 #pragma warning(disable: 4127)
102 // Ignore unreferenced local functions in GTK+ headers
103 #pragma warning(disable: 4505)
104 #endif
105 
106 #define OBJECT_CLASS GObjectClass
107 
108 #ifdef SCI_NAMESPACE
109 using namespace Scintilla;
110 #endif
111 
PWindow(const Window & w)112 static GdkWindow *PWindow(const Window &w) {
113 	GtkWidget *widget = reinterpret_cast<GtkWidget *>(w.GetID());
114 #if GTK_CHECK_VERSION(3,0,0)
115 	return gtk_widget_get_window(widget);
116 #else
117 	return widget->window;
118 #endif
119 }
120 
121 extern std::string UTF8FromLatin1(const char *s, int len);
122 
123 class ScintillaGTK : public ScintillaBase {
124 	_ScintillaObject *sci;
125 	Window wText;
126 	Window scrollbarv;
127 	Window scrollbarh;
128 	GtkAdjustment *adjustmentv;
129 	GtkAdjustment *adjustmenth;
130 	int verticalScrollBarWidth;
131 	int horizontalScrollBarHeight;
132 
133 	SelectionText primary;
134 
135 	GdkEventButton *evbtn;
136 	bool capturedMouse;
137 	bool dragWasDropped;
138 	int lastKey;
139 	int rectangularSelectionModifier;
140 
141 	GtkWidgetClass *parentClass;
142 
143 	static GdkAtom atomClipboard;
144 	static GdkAtom atomUTF8;
145 	static GdkAtom atomString;
146 	static GdkAtom atomUriList;
147 	static GdkAtom atomDROPFILES_DND;
148 	GdkAtom atomSought;
149 
150 #if PLAT_GTK_WIN32
151 	CLIPFORMAT cfColumnSelect;
152 #endif
153 
154 	Window wPreedit;
155 	Window wPreeditDraw;
156 	GtkIMContext *im_context;
157 
158 	// Wheel mouse support
159 	unsigned int linesPerScroll;
160 	GTimeVal lastWheelMouseTime;
161 	gint lastWheelMouseDirection;
162 	gint wheelMouseIntensity;
163 
164 #if GTK_CHECK_VERSION(3,0,0)
165 	cairo_rectangle_list_t *rgnUpdate;
166 #else
167 	GdkRegion *rgnUpdate;
168 #endif
169 	bool repaintFullWindow;
170 
171 	// Private so ScintillaGTK objects can not be copied
172 	ScintillaGTK(const ScintillaGTK &);
173 	ScintillaGTK &operator=(const ScintillaGTK &);
174 
175 public:
176 	explicit ScintillaGTK(_ScintillaObject *sci_);
177 	virtual ~ScintillaGTK();
178 	static void ClassInit(OBJECT_CLASS* object_class, GtkWidgetClass *widget_class, GtkContainerClass *container_class);
179 private:
180 	virtual void Initialise();
181 	virtual void Finalise();
182 	virtual bool AbandonPaint();
183 	virtual void DisplayCursor(Window::Cursor c);
184 	virtual bool DragThreshold(Point ptStart, Point ptNow);
185 	virtual void StartDrag();
186 	int TargetAsUTF8(char *text);
187 	int EncodedFromUTF8(char *utf8, char *encoded) const;
188 	virtual bool ValidCodePage(int codePage) const;
189 public: 	// Public for scintilla_send_message
190 	virtual sptr_t WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam);
191 private:
192 	virtual sptr_t DefWndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam);
193 	struct TimeThunk {
194 		TickReason reason;
195 		ScintillaGTK *scintilla;
196 		guint timer;
TimeThunkScintillaGTK::TimeThunk197 		TimeThunk() : reason(tickCaret), scintilla(NULL), timer(0) {}
198 	};
199 	TimeThunk timers[tickDwell+1];
200 	virtual bool FineTickerAvailable();
201 	virtual bool FineTickerRunning(TickReason reason);
202 	virtual void FineTickerStart(TickReason reason, int millis, int tolerance);
203 	virtual void FineTickerCancel(TickReason reason);
204 	virtual bool SetIdle(bool on);
205 	virtual void SetMouseCapture(bool on);
206 	virtual bool HaveMouseCapture();
207 	virtual bool PaintContains(PRectangle rc);
208 	void FullPaint();
209 	virtual PRectangle GetClientRectangle() const;
210 	virtual void ScrollText(int linesToMove);
211 	virtual void SetVerticalScrollPos();
212 	virtual void SetHorizontalScrollPos();
213 	virtual bool ModifyScrollBars(int nMax, int nPage);
214 	void ReconfigureScrollBars();
215 	virtual void NotifyChange();
216 	virtual void NotifyFocus(bool focus);
217 	virtual void NotifyParent(SCNotification scn);
218 	void NotifyKey(int key, int modifiers);
219 	void NotifyURIDropped(const char *list);
220 	const char *CharacterSetID() const;
221 	virtual CaseFolder *CaseFolderForEncoding();
222 	virtual std::string CaseMapString(const std::string &s, int caseMapping);
223 	virtual int KeyDefault(int key, int modifiers);
224 	virtual void CopyToClipboard(const SelectionText &selectedText);
225 	virtual void Copy();
226 	virtual void Paste();
227 	virtual void CreateCallTipWindow(PRectangle rc);
228 	virtual void AddToPopUp(const char *label, int cmd = 0, bool enabled = true);
229 	bool OwnPrimarySelection();
230 	virtual void ClaimSelection();
231 	void GetGtkSelectionText(GtkSelectionData *selectionData, SelectionText &selText);
232 	void ReceivedSelection(GtkSelectionData *selection_data);
233 	void ReceivedDrop(GtkSelectionData *selection_data);
234 	static void GetSelection(GtkSelectionData *selection_data, guint info, SelectionText *selected);
235 	void StoreOnClipboard(SelectionText *clipText);
236 	static void ClipboardGetSelection(GtkClipboard* clip, GtkSelectionData *selection_data, guint info, void *data);
237 	static void ClipboardClearSelection(GtkClipboard* clip, void *data);
238 
239 	void UnclaimSelection(GdkEventSelection *selection_event);
240 	void Resize(int width, int height);
241 
242 	// Callback functions
243 	void RealizeThis(GtkWidget *widget);
244 	static void Realize(GtkWidget *widget);
245 	void UnRealizeThis(GtkWidget *widget);
246 	static void UnRealize(GtkWidget *widget);
247 	void MapThis();
248 	static void Map(GtkWidget *widget);
249 	void UnMapThis();
250 	static void UnMap(GtkWidget *widget);
251 	gint FocusInThis(GtkWidget *widget);
252 	static gint FocusIn(GtkWidget *widget, GdkEventFocus *event);
253 	gint FocusOutThis(GtkWidget *widget);
254 	static gint FocusOut(GtkWidget *widget, GdkEventFocus *event);
255 	static void SizeRequest(GtkWidget *widget, GtkRequisition *requisition);
256 #if GTK_CHECK_VERSION(3,0,0)
257 	static void GetPreferredWidth(GtkWidget *widget, gint *minimalWidth, gint *naturalWidth);
258 	static void GetPreferredHeight(GtkWidget *widget, gint *minimalHeight, gint *naturalHeight);
259 #endif
260 	static void SizeAllocate(GtkWidget *widget, GtkAllocation *allocation);
261 #if GTK_CHECK_VERSION(3,0,0)
262 	gboolean DrawTextThis(cairo_t *cr);
263 	static gboolean DrawText(GtkWidget *widget, cairo_t *cr, ScintillaGTK *sciThis);
264 	gboolean DrawThis(cairo_t *cr);
265 	static gboolean DrawMain(GtkWidget *widget, cairo_t *cr);
266 #else
267 	gboolean ExposeTextThis(GtkWidget *widget, GdkEventExpose *ose);
268 	static gboolean ExposeText(GtkWidget *widget, GdkEventExpose *ose, ScintillaGTK *sciThis);
269 	gboolean Expose(GtkWidget *widget, GdkEventExpose *ose);
270 	static gboolean ExposeMain(GtkWidget *widget, GdkEventExpose *ose);
271 #endif
272 	static void Draw(GtkWidget *widget, GdkRectangle *area);
273 	void ForAll(GtkCallback callback, gpointer callback_data);
274 	static void MainForAll(GtkContainer *container, gboolean include_internals, GtkCallback callback, gpointer callback_data);
275 
276 	static void ScrollSignal(GtkAdjustment *adj, ScintillaGTK *sciThis);
277 	static void ScrollHSignal(GtkAdjustment *adj, ScintillaGTK *sciThis);
278 	gint PressThis(GdkEventButton *event);
279 	static gint Press(GtkWidget *widget, GdkEventButton *event);
280 	static gint MouseRelease(GtkWidget *widget, GdkEventButton *event);
281 	static gint ScrollEvent(GtkWidget *widget, GdkEventScroll *event);
282 	static gint Motion(GtkWidget *widget, GdkEventMotion *event);
283 	gboolean KeyThis(GdkEventKey *event);
284 	static gboolean KeyPress(GtkWidget *widget, GdkEventKey *event);
285 	static gboolean KeyRelease(GtkWidget *widget, GdkEventKey *event);
286 #if GTK_CHECK_VERSION(3,0,0)
287 	gboolean DrawPreeditThis(GtkWidget *widget, cairo_t *cr);
288 	static gboolean DrawPreedit(GtkWidget *widget, cairo_t *cr, ScintillaGTK *sciThis);
289 #else
290 	gboolean ExposePreeditThis(GtkWidget *widget, GdkEventExpose *ose);
291 	static gboolean ExposePreedit(GtkWidget *widget, GdkEventExpose *ose, ScintillaGTK *sciThis);
292 #endif
293 	void CommitThis(char *str);
294 	static void Commit(GtkIMContext *context, char *str, ScintillaGTK *sciThis);
295 	void PreeditChangedThis();
296 	static void PreeditChanged(GtkIMContext *context, ScintillaGTK *sciThis);
297 
298 	bool KoreanIME();
299 
300 	static void StyleSetText(GtkWidget *widget, GtkStyle *previous, void*);
301 	static void RealizeText(GtkWidget *widget, void*);
302 	static void Destroy(GObject *object);
303 	static void SelectionReceived(GtkWidget *widget, GtkSelectionData *selection_data,
304 	                              guint time);
305 	static void SelectionGet(GtkWidget *widget, GtkSelectionData *selection_data,
306 	                         guint info, guint time);
307 	static gint SelectionClear(GtkWidget *widget, GdkEventSelection *selection_event);
308 	gboolean DragMotionThis(GdkDragContext *context, gint x, gint y, guint dragtime);
309 	static gboolean DragMotion(GtkWidget *widget, GdkDragContext *context,
310 	                           gint x, gint y, guint dragtime);
311 	static void DragLeave(GtkWidget *widget, GdkDragContext *context,
312 	                      guint time);
313 	static void DragEnd(GtkWidget *widget, GdkDragContext *context);
314 	static gboolean Drop(GtkWidget *widget, GdkDragContext *context,
315 	                     gint x, gint y, guint time);
316 	static void DragDataReceived(GtkWidget *widget, GdkDragContext *context,
317 	                             gint x, gint y, GtkSelectionData *selection_data, guint info, guint time);
318 	static void DragDataGet(GtkWidget *widget, GdkDragContext *context,
319 	                        GtkSelectionData *selection_data, guint info, guint time);
320 	static gboolean TimeOut(TimeThunk *tt);
321 	static gboolean IdleCallback(ScintillaGTK *sciThis);
322 	static gboolean StyleIdle(ScintillaGTK *sciThis);
323 	virtual void QueueIdleWork(WorkNeeded::workItems items, int upTo);
324 	static void PopUpCB(GtkMenuItem *menuItem, ScintillaGTK *sciThis);
325 
326 #if GTK_CHECK_VERSION(3,0,0)
327 	static gboolean DrawCT(GtkWidget *widget, cairo_t *cr, CallTip *ctip);
328 #else
329 	static gboolean ExposeCT(GtkWidget *widget, GdkEventExpose *ose, CallTip *ct);
330 #endif
331 	static gboolean PressCT(GtkWidget *widget, GdkEventButton *event, ScintillaGTK *sciThis);
332 
333 	static sptr_t DirectFunction(sptr_t ptr,
334 	                             unsigned int iMessage, uptr_t wParam, sptr_t lParam);
335 };
336 
337 enum {
338     COMMAND_SIGNAL,
339     NOTIFY_SIGNAL,
340     LAST_SIGNAL
341 };
342 
343 static gint scintilla_signals[LAST_SIGNAL] = { 0 };
344 
345 enum {
346     TARGET_STRING,
347     TARGET_TEXT,
348     TARGET_COMPOUND_TEXT,
349     TARGET_UTF8_STRING,
350     TARGET_URI
351 };
352 
353 GdkAtom ScintillaGTK::atomClipboard = 0;
354 GdkAtom ScintillaGTK::atomUTF8 = 0;
355 GdkAtom ScintillaGTK::atomString = 0;
356 GdkAtom ScintillaGTK::atomUriList = 0;
357 GdkAtom ScintillaGTK::atomDROPFILES_DND = 0;
358 
359 static const GtkTargetEntry clipboardCopyTargets[] = {
360 	{ (gchar *) "UTF8_STRING", 0, TARGET_UTF8_STRING },
361 	{ (gchar *) "STRING", 0, TARGET_STRING },
362 };
363 static const gint nClipboardCopyTargets = ELEMENTS(clipboardCopyTargets);
364 
365 static const GtkTargetEntry clipboardPasteTargets[] = {
366 	{ (gchar *) "text/uri-list", 0, TARGET_URI },
367 	{ (gchar *) "UTF8_STRING", 0, TARGET_UTF8_STRING },
368 	{ (gchar *) "STRING", 0, TARGET_STRING },
369 };
370 static const gint nClipboardPasteTargets = ELEMENTS(clipboardPasteTargets);
371 
PWidget(Window & w)372 static GtkWidget *PWidget(Window &w) {
373 	return reinterpret_cast<GtkWidget *>(w.GetID());
374 }
375 
ScintillaFromWidget(GtkWidget * widget)376 static ScintillaGTK *ScintillaFromWidget(GtkWidget *widget) {
377 	ScintillaObject *scio = reinterpret_cast<ScintillaObject *>(widget);
378 	return reinterpret_cast<ScintillaGTK *>(scio->pscin);
379 }
380 
ScintillaGTK(_ScintillaObject * sci_)381 ScintillaGTK::ScintillaGTK(_ScintillaObject *sci_) :
382 		adjustmentv(0), adjustmenth(0),
383 		verticalScrollBarWidth(30), horizontalScrollBarHeight(30),
384 		evbtn(0), capturedMouse(false), dragWasDropped(false),
385 		lastKey(0), rectangularSelectionModifier(SCMOD_CTRL), parentClass(0),
386 		im_context(NULL),
387 		lastWheelMouseDirection(0),
388 		wheelMouseIntensity(0),
389 		rgnUpdate(0),
390 		repaintFullWindow(false) {
391 	sci = sci_;
392 	wMain = GTK_WIDGET(sci);
393 
394 #if PLAT_GTK_WIN32
395 	rectangularSelectionModifier = SCMOD_ALT;
396 #else
397 	rectangularSelectionModifier = SCMOD_CTRL;
398 #endif
399 
400 #if PLAT_GTK_WIN32
401 	// There does not seem to be a real standard for indicating that the clipboard
402 	// contains a rectangular selection, so copy Developer Studio.
403 	cfColumnSelect = static_cast<CLIPFORMAT>(
404 		::RegisterClipboardFormat("MSDEVColumnSelect"));
405 
406 	// Get intellimouse parameters when running on win32; otherwise use
407 	// reasonable default
408 #ifndef SPI_GETWHEELSCROLLLINES
409 #define SPI_GETWHEELSCROLLLINES   104
410 #endif
411 	::SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &linesPerScroll, 0);
412 #else
413 	linesPerScroll = 4;
414 #endif
415 	lastWheelMouseTime.tv_sec = 0;
416 	lastWheelMouseTime.tv_usec = 0;
417 
418 	Initialise();
419 }
420 
~ScintillaGTK()421 ScintillaGTK::~ScintillaGTK() {
422 	g_idle_remove_by_data(this);
423 	if (evbtn) {
424 		gdk_event_free(reinterpret_cast<GdkEvent *>(evbtn));
425 		evbtn = 0;
426 	}
427 }
428 
UnRefCursor(GdkCursor * cursor)429 static void UnRefCursor(GdkCursor *cursor) {
430 #if GTK_CHECK_VERSION(3,0,0)
431 	g_object_unref(cursor);
432 #else
433 	gdk_cursor_unref(cursor);
434 #endif
435 }
436 
RealizeThis(GtkWidget * widget)437 void ScintillaGTK::RealizeThis(GtkWidget *widget) {
438 	//Platform::DebugPrintf("ScintillaGTK::realize this\n");
439 #if GTK_CHECK_VERSION(2,20,0)
440 	gtk_widget_set_realized(widget, TRUE);
441 #else
442 	GTK_WIDGET_SET_FLAGS(widget, GTK_REALIZED);
443 #endif
444 	GdkWindowAttr attrs;
445 	attrs.window_type = GDK_WINDOW_CHILD;
446 	GtkAllocation allocation;
447 #if GTK_CHECK_VERSION(3,0,0)
448 	gtk_widget_get_allocation(widget, &allocation);
449 #else
450 	allocation = widget->allocation;
451 #endif
452 	attrs.x = allocation.x;
453 	attrs.y = allocation.y;
454 	attrs.width = allocation.width;
455 	attrs.height = allocation.height;
456 	attrs.wclass = GDK_INPUT_OUTPUT;
457 	attrs.visual = gtk_widget_get_visual(widget);
458 #if !GTK_CHECK_VERSION(3,0,0)
459 	attrs.colormap = gtk_widget_get_colormap(widget);
460 #endif
461 	attrs.event_mask = gtk_widget_get_events(widget) | GDK_EXPOSURE_MASK;
462 	GdkCursor *cursor = gdk_cursor_new(GDK_XTERM);
463 	attrs.cursor = cursor;
464 #if GTK_CHECK_VERSION(3,0,0)
465 	gtk_widget_set_window(widget, gdk_window_new(gtk_widget_get_parent_window(widget), &attrs,
466 		GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_CURSOR));
467 	gdk_window_set_user_data(gtk_widget_get_window(widget), widget);
468 	GtkStyleContext *styleContext = gtk_widget_get_style_context(widget);
469 	if (styleContext) {
470 		GdkRGBA colourBackWidget;
471 		gtk_style_context_get_background_color(styleContext, GTK_STATE_FLAG_NORMAL, &colourBackWidget);
472 		gdk_window_set_background_rgba(gtk_widget_get_window(widget), &colourBackWidget);
473 	}
474 	gdk_window_show(gtk_widget_get_window(widget));
475 	UnRefCursor(cursor);
476 #else
477 	widget->window = gdk_window_new(gtk_widget_get_parent_window(widget), &attrs,
478 		GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP | GDK_WA_CURSOR);
479 	gdk_window_set_user_data(widget->window, widget);
480 	widget->style = gtk_style_attach(widget->style, widget->window);
481 	gdk_window_set_background(widget->window, &widget->style->bg[GTK_STATE_NORMAL]);
482 	gdk_window_show(widget->window);
483 	UnRefCursor(cursor);
484 #endif
485 	wPreedit = gtk_window_new(GTK_WINDOW_POPUP);
486 	wPreeditDraw = gtk_drawing_area_new();
487 	GtkWidget *predrw = PWidget(wPreeditDraw);	// No code inside the G_OBJECT macro
488 #if GTK_CHECK_VERSION(3,0,0)
489 	g_signal_connect(G_OBJECT(predrw), "draw",
490 		G_CALLBACK(DrawPreedit), this);
491 #else
492 	g_signal_connect(G_OBJECT(predrw), "expose_event",
493 		G_CALLBACK(ExposePreedit), this);
494 #endif
495 	gtk_container_add(GTK_CONTAINER(PWidget(wPreedit)), predrw);
496 	gtk_widget_realize(PWidget(wPreedit));
497 	gtk_widget_realize(predrw);
498 	gtk_widget_show(predrw);
499 
500 	im_context = gtk_im_multicontext_new();
501 	g_signal_connect(G_OBJECT(im_context), "commit",
502 		G_CALLBACK(Commit), this);
503 	g_signal_connect(G_OBJECT(im_context), "preedit_changed",
504 		G_CALLBACK(PreeditChanged), this);
505 	gtk_im_context_set_client_window(im_context, WindowFromWidget(widget));
506 	GtkWidget *widtxt = PWidget(wText);	//	// No code inside the G_OBJECT macro
507 	g_signal_connect_after(G_OBJECT(widtxt), "style_set",
508 		G_CALLBACK(ScintillaGTK::StyleSetText), NULL);
509 	g_signal_connect_after(G_OBJECT(widtxt), "realize",
510 		G_CALLBACK(ScintillaGTK::RealizeText), NULL);
511 	gtk_widget_realize(widtxt);
512 	gtk_widget_realize(PWidget(scrollbarv));
513 	gtk_widget_realize(PWidget(scrollbarh));
514 
515 	cursor = gdk_cursor_new(GDK_XTERM);
516 	gdk_window_set_cursor(PWindow(wText), cursor);
517 	UnRefCursor(cursor);
518 
519 	cursor = gdk_cursor_new(GDK_LEFT_PTR);
520 	gdk_window_set_cursor(PWindow(scrollbarv), cursor);
521 	UnRefCursor(cursor);
522 
523 	cursor = gdk_cursor_new(GDK_LEFT_PTR);
524 	gdk_window_set_cursor(PWindow(scrollbarh), cursor);
525 	UnRefCursor(cursor);
526 
527 	gtk_selection_add_targets(widget, GDK_SELECTION_PRIMARY,
528 	                          clipboardCopyTargets, nClipboardCopyTargets);
529 }
530 
Realize(GtkWidget * widget)531 void ScintillaGTK::Realize(GtkWidget *widget) {
532 	ScintillaGTK *sciThis = ScintillaFromWidget(widget);
533 	sciThis->RealizeThis(widget);
534 }
535 
UnRealizeThis(GtkWidget * widget)536 void ScintillaGTK::UnRealizeThis(GtkWidget *widget) {
537 	try {
538 		gtk_selection_clear_targets(widget, GDK_SELECTION_PRIMARY);
539 
540 		if (IS_WIDGET_MAPPED(widget)) {
541 			gtk_widget_unmap(widget);
542 		}
543 #if GTK_CHECK_VERSION(2,20,0)
544 		gtk_widget_set_realized(widget, FALSE);
545 #else
546 		GTK_WIDGET_UNSET_FLAGS(widget, GTK_REALIZED);
547 #endif
548 		gtk_widget_unrealize(PWidget(wText));
549 		gtk_widget_unrealize(PWidget(scrollbarv));
550 		gtk_widget_unrealize(PWidget(scrollbarh));
551 		gtk_widget_unrealize(PWidget(wPreedit));
552 		gtk_widget_unrealize(PWidget(wPreeditDraw));
553 		g_object_unref(im_context);
554 		im_context = NULL;
555 		if (GTK_WIDGET_CLASS(parentClass)->unrealize)
556 			GTK_WIDGET_CLASS(parentClass)->unrealize(widget);
557 
558 		Finalise();
559 	} catch (...) {
560 		errorStatus = SC_STATUS_FAILURE;
561 	}
562 }
563 
UnRealize(GtkWidget * widget)564 void ScintillaGTK::UnRealize(GtkWidget *widget) {
565 	ScintillaGTK *sciThis = ScintillaFromWidget(widget);
566 	sciThis->UnRealizeThis(widget);
567 }
568 
MapWidget(GtkWidget * widget)569 static void MapWidget(GtkWidget *widget) {
570 	if (widget &&
571 	        IS_WIDGET_VISIBLE(widget) &&
572 	        !IS_WIDGET_MAPPED(widget)) {
573 		gtk_widget_map(widget);
574 	}
575 }
576 
MapThis()577 void ScintillaGTK::MapThis() {
578 	try {
579 		//Platform::DebugPrintf("ScintillaGTK::map this\n");
580 #if GTK_CHECK_VERSION(2,20,0)
581 		gtk_widget_set_mapped(PWidget(wMain), TRUE);
582 #else
583 		GTK_WIDGET_SET_FLAGS(PWidget(wMain), GTK_MAPPED);
584 #endif
585 		MapWidget(PWidget(wText));
586 		MapWidget(PWidget(scrollbarh));
587 		MapWidget(PWidget(scrollbarv));
588 		wMain.SetCursor(Window::cursorArrow);
589 		scrollbarv.SetCursor(Window::cursorArrow);
590 		scrollbarh.SetCursor(Window::cursorArrow);
591 		ChangeSize();
592 		gdk_window_show(PWindow(wMain));
593 	} catch (...) {
594 		errorStatus = SC_STATUS_FAILURE;
595 	}
596 }
597 
Map(GtkWidget * widget)598 void ScintillaGTK::Map(GtkWidget *widget) {
599 	ScintillaGTK *sciThis = ScintillaFromWidget(widget);
600 	sciThis->MapThis();
601 }
602 
UnMapThis()603 void ScintillaGTK::UnMapThis() {
604 	try {
605 		//Platform::DebugPrintf("ScintillaGTK::unmap this\n");
606 #if GTK_CHECK_VERSION(2,20,0)
607 		gtk_widget_set_mapped(PWidget(wMain), FALSE);
608 #else
609 		GTK_WIDGET_UNSET_FLAGS(PWidget(wMain), GTK_MAPPED);
610 #endif
611 		DropGraphics(false);
612 		gdk_window_hide(PWindow(wMain));
613 		gtk_widget_unmap(PWidget(wText));
614 		gtk_widget_unmap(PWidget(scrollbarh));
615 		gtk_widget_unmap(PWidget(scrollbarv));
616 	} catch (...) {
617 		errorStatus = SC_STATUS_FAILURE;
618 	}
619 }
620 
UnMap(GtkWidget * widget)621 void ScintillaGTK::UnMap(GtkWidget *widget) {
622 	ScintillaGTK *sciThis = ScintillaFromWidget(widget);
623 	sciThis->UnMapThis();
624 }
625 
ForAll(GtkCallback callback,gpointer callback_data)626 void ScintillaGTK::ForAll(GtkCallback callback, gpointer callback_data) {
627 	try {
628 		(*callback) (PWidget(wText), callback_data);
629 		(*callback) (PWidget(scrollbarv), callback_data);
630 		(*callback) (PWidget(scrollbarh), callback_data);
631 	} catch (...) {
632 		errorStatus = SC_STATUS_FAILURE;
633 	}
634 }
635 
MainForAll(GtkContainer * container,gboolean include_internals,GtkCallback callback,gpointer callback_data)636 void ScintillaGTK::MainForAll(GtkContainer *container, gboolean include_internals, GtkCallback callback, gpointer callback_data) {
637 	ScintillaGTK *sciThis = ScintillaFromWidget((GtkWidget *)container);
638 
639 	if (callback != NULL && include_internals) {
640 		sciThis->ForAll(callback, callback_data);
641 	}
642 }
643 
644 namespace {
645 
646 class PreEditString {
647 public:
648 	gchar *str;
649 	gint cursor_pos;
650 	PangoAttrList *attrs;
651 
PreEditString(GtkIMContext * im_context)652 	PreEditString(GtkIMContext *im_context) {
653 		gtk_im_context_get_preedit_string(im_context, &str, &attrs, &cursor_pos);
654 	}
~PreEditString()655 	~PreEditString() {
656 		g_free(str);
657 		pango_attr_list_unref(attrs);
658 	}
659 };
660 
661 }
662 
FocusInThis(GtkWidget * widget)663 gint ScintillaGTK::FocusInThis(GtkWidget *widget) {
664 	try {
665 		SetFocusState(true);
666 		if (im_context != NULL) {
667 			PreEditString pes(im_context);
668 			if (PWidget(wPreedit) != NULL) {
669 				if (strlen(pes.str) > 0) {
670 					gtk_widget_show(PWidget(wPreedit));
671 				} else {
672 					gtk_widget_hide(PWidget(wPreedit));
673 				}
674 			}
675 			gtk_im_context_focus_in(im_context);
676 		}
677 
678 	} catch (...) {
679 		errorStatus = SC_STATUS_FAILURE;
680 	}
681 	return FALSE;
682 }
683 
FocusIn(GtkWidget * widget,GdkEventFocus *)684 gint ScintillaGTK::FocusIn(GtkWidget *widget, GdkEventFocus * /*event*/) {
685 	ScintillaGTK *sciThis = ScintillaFromWidget(widget);
686 	return sciThis->FocusInThis(widget);
687 }
688 
FocusOutThis(GtkWidget * widget)689 gint ScintillaGTK::FocusOutThis(GtkWidget *widget) {
690 	try {
691 		SetFocusState(false);
692 
693 		if (PWidget(wPreedit) != NULL)
694 			gtk_widget_hide(PWidget(wPreedit));
695 		if (im_context != NULL)
696 			gtk_im_context_focus_out(im_context);
697 
698 	} catch (...) {
699 		errorStatus = SC_STATUS_FAILURE;
700 	}
701 	return FALSE;
702 }
703 
FocusOut(GtkWidget * widget,GdkEventFocus *)704 gint ScintillaGTK::FocusOut(GtkWidget *widget, GdkEventFocus * /*event*/) {
705 	ScintillaGTK *sciThis = ScintillaFromWidget(widget);
706 	return sciThis->FocusOutThis(widget);
707 }
708 
SizeRequest(GtkWidget * widget,GtkRequisition * requisition)709 void ScintillaGTK::SizeRequest(GtkWidget *widget, GtkRequisition *requisition) {
710 	ScintillaGTK *sciThis = ScintillaFromWidget(widget);
711 	requisition->width = 1;
712 	requisition->height = 1;
713 	GtkRequisition child_requisition;
714 #if GTK_CHECK_VERSION(3,0,0)
715 	gtk_widget_get_preferred_size(PWidget(sciThis->scrollbarh), NULL, &child_requisition);
716 	gtk_widget_get_preferred_size(PWidget(sciThis->scrollbarv), NULL, &child_requisition);
717 #else
718 	gtk_widget_size_request(PWidget(sciThis->scrollbarh), &child_requisition);
719 	gtk_widget_size_request(PWidget(sciThis->scrollbarv), &child_requisition);
720 #endif
721 }
722 
723 #if GTK_CHECK_VERSION(3,0,0)
724 
GetPreferredWidth(GtkWidget * widget,gint * minimalWidth,gint * naturalWidth)725 void ScintillaGTK::GetPreferredWidth(GtkWidget *widget, gint *minimalWidth, gint *naturalWidth) {
726 	GtkRequisition requisition;
727 	SizeRequest(widget, &requisition);
728 	*minimalWidth = *naturalWidth = requisition.width;
729 }
730 
GetPreferredHeight(GtkWidget * widget,gint * minimalHeight,gint * naturalHeight)731 void ScintillaGTK::GetPreferredHeight(GtkWidget *widget, gint *minimalHeight, gint *naturalHeight) {
732 	GtkRequisition requisition;
733 	SizeRequest(widget, &requisition);
734 	*minimalHeight = *naturalHeight = requisition.height;
735 }
736 
737 #endif
738 
SizeAllocate(GtkWidget * widget,GtkAllocation * allocation)739 void ScintillaGTK::SizeAllocate(GtkWidget *widget, GtkAllocation *allocation) {
740 	ScintillaGTK *sciThis = ScintillaFromWidget(widget);
741 	try {
742 #if GTK_CHECK_VERSION(2,20,0)
743 		gtk_widget_set_allocation(widget, allocation);
744 #else
745 		widget->allocation = *allocation;
746 #endif
747 		if (IS_WIDGET_REALIZED(widget))
748 			gdk_window_move_resize(WindowFromWidget(widget),
749 			        allocation->x,
750 			        allocation->y,
751 			        allocation->width,
752 			        allocation->height);
753 
754 		sciThis->Resize(allocation->width, allocation->height);
755 
756 	} catch (...) {
757 		sciThis->errorStatus = SC_STATUS_FAILURE;
758 	}
759 }
760 
Initialise()761 void ScintillaGTK::Initialise() {
762 	//Platform::DebugPrintf("ScintillaGTK::Initialise\n");
763 	parentClass = reinterpret_cast<GtkWidgetClass *>(
764 	                  g_type_class_ref(gtk_container_get_type()));
765 
766 #if GTK_CHECK_VERSION(2,20,0)
767 	gtk_widget_set_can_focus(PWidget(wMain), TRUE);
768 	gtk_widget_set_sensitive(PWidget(wMain), TRUE);
769 #else
770 	GTK_WIDGET_SET_FLAGS(PWidget(wMain), GTK_CAN_FOCUS);
771 	GTK_WIDGET_SET_FLAGS(GTK_WIDGET(PWidget(wMain)), GTK_SENSITIVE);
772 #endif
773 	gtk_widget_set_events(PWidget(wMain),
774 	                      GDK_EXPOSURE_MASK
775 	                      | GDK_SCROLL_MASK
776 	                      | GDK_STRUCTURE_MASK
777 	                      | GDK_KEY_PRESS_MASK
778 	                      | GDK_KEY_RELEASE_MASK
779 	                      | GDK_FOCUS_CHANGE_MASK
780 	                      | GDK_LEAVE_NOTIFY_MASK
781 	                      | GDK_BUTTON_PRESS_MASK
782 	                      | GDK_BUTTON_RELEASE_MASK
783 	                      | GDK_POINTER_MOTION_MASK
784 	                      | GDK_POINTER_MOTION_HINT_MASK);
785 
786 	wText = gtk_drawing_area_new();
787 	gtk_widget_set_parent(PWidget(wText), PWidget(wMain));
788 	GtkWidget *widtxt = PWidget(wText);	// No code inside the G_OBJECT macro
789 	gtk_widget_show(widtxt);
790 #if GTK_CHECK_VERSION(3,0,0)
791 	g_signal_connect(G_OBJECT(widtxt), "draw",
792 			   G_CALLBACK(ScintillaGTK::DrawText), this);
793 #else
794 	g_signal_connect(G_OBJECT(widtxt), "expose_event",
795 			   G_CALLBACK(ScintillaGTK::ExposeText), this);
796 #endif
797 #if GTK_CHECK_VERSION(3,0,0)
798 	// we need a runtime check because we don't want double buffering when
799 	// running on >= 3.9.2
800 	if (gtk_check_version(3,9,2) != NULL /* on < 3.9.2 */)
801 #endif
802 	{
803 		// Avoid background drawing flash/missing redraws
804 		gtk_widget_set_double_buffered(widtxt, FALSE);
805 	}
806 	gtk_widget_set_events(widtxt, GDK_EXPOSURE_MASK);
807 	gtk_widget_set_size_request(widtxt, 100, 100);
808 	adjustmentv = GTK_ADJUSTMENT(gtk_adjustment_new(0.0, 0.0, 201.0, 1.0, 20.0, 20.0));
809 #if GTK_CHECK_VERSION(3,0,0)
810 	scrollbarv = gtk_scrollbar_new(GTK_ORIENTATION_VERTICAL, GTK_ADJUSTMENT(adjustmentv));
811 #else
812 	scrollbarv = gtk_vscrollbar_new(GTK_ADJUSTMENT(adjustmentv));
813 #endif
814 #if GTK_CHECK_VERSION(2,20,0)
815 	gtk_widget_set_can_focus(PWidget(scrollbarv), FALSE);
816 #else
817 	GTK_WIDGET_UNSET_FLAGS(PWidget(scrollbarv), GTK_CAN_FOCUS);
818 #endif
819 	g_signal_connect(G_OBJECT(adjustmentv), "value_changed",
820 			   G_CALLBACK(ScrollSignal), this);
821 	gtk_widget_set_parent(PWidget(scrollbarv), PWidget(wMain));
822 	gtk_widget_show(PWidget(scrollbarv));
823 
824 	adjustmenth = GTK_ADJUSTMENT(gtk_adjustment_new(0.0, 0.0, 101.0, 1.0, 20.0, 20.0));
825 #if GTK_CHECK_VERSION(3,0,0)
826 	scrollbarh = gtk_scrollbar_new(GTK_ORIENTATION_HORIZONTAL, GTK_ADJUSTMENT(adjustmenth));
827 #else
828 	scrollbarh = gtk_hscrollbar_new(GTK_ADJUSTMENT(adjustmenth));
829 #endif
830 #if GTK_CHECK_VERSION(2,20,0)
831 	gtk_widget_set_can_focus(PWidget(scrollbarh), FALSE);
832 #else
833 	GTK_WIDGET_UNSET_FLAGS(PWidget(scrollbarh), GTK_CAN_FOCUS);
834 #endif
835 	g_signal_connect(G_OBJECT(adjustmenth), "value_changed",
836 			   G_CALLBACK(ScrollHSignal), this);
837 	gtk_widget_set_parent(PWidget(scrollbarh), PWidget(wMain));
838 	gtk_widget_show(PWidget(scrollbarh));
839 
840 	gtk_widget_grab_focus(PWidget(wMain));
841 
842 	gtk_drag_dest_set(GTK_WIDGET(PWidget(wMain)),
843 	                  GTK_DEST_DEFAULT_ALL, clipboardPasteTargets, nClipboardPasteTargets,
844 	                  static_cast<GdkDragAction>(GDK_ACTION_COPY | GDK_ACTION_MOVE));
845 
846 	// Set caret period based on GTK settings
847 	gboolean blinkOn = false;
848 	if (g_object_class_find_property(G_OBJECT_GET_CLASS(
849 			G_OBJECT(gtk_settings_get_default())), "gtk-cursor-blink")) {
850 		g_object_get(G_OBJECT(
851 			gtk_settings_get_default()), "gtk-cursor-blink", &blinkOn, NULL);
852 	}
853 	if (blinkOn &&
854 		g_object_class_find_property(G_OBJECT_GET_CLASS(
855 			G_OBJECT(gtk_settings_get_default())), "gtk-cursor-blink-time")) {
856 		gint value;
857 		g_object_get(G_OBJECT(
858 			gtk_settings_get_default()), "gtk-cursor-blink-time", &value, NULL);
859 		caret.period = gint(value / 1.75);
860 	} else {
861 		caret.period = 0;
862 	}
863 
864 	for (TickReason tr = tickCaret; tr <= tickDwell; tr = static_cast<TickReason>(tr + 1)) {
865 		timers[tr].reason = tr;
866 		timers[tr].scintilla = this;
867 	}
868 }
869 
Finalise()870 void ScintillaGTK::Finalise() {
871 	for (TickReason tr = tickCaret; tr <= tickDwell; tr = static_cast<TickReason>(tr + 1)) {
872 		FineTickerCancel(tr);
873 	}
874 	ScintillaBase::Finalise();
875 }
876 
AbandonPaint()877 bool ScintillaGTK::AbandonPaint() {
878 	if ((paintState == painting) && !paintingAllText) {
879 		repaintFullWindow = true;
880 	}
881 	return false;
882 }
883 
DisplayCursor(Window::Cursor c)884 void ScintillaGTK::DisplayCursor(Window::Cursor c) {
885 	if (cursorMode == SC_CURSORNORMAL)
886 		wText.SetCursor(c);
887 	else
888 		wText.SetCursor(static_cast<Window::Cursor>(cursorMode));
889 }
890 
DragThreshold(Point ptStart,Point ptNow)891 bool ScintillaGTK::DragThreshold(Point ptStart, Point ptNow) {
892 	return gtk_drag_check_threshold(GTK_WIDGET(PWidget(wMain)),
893 		ptStart.x, ptStart.y, ptNow.x, ptNow.y);
894 }
895 
StartDrag()896 void ScintillaGTK::StartDrag() {
897 	PLATFORM_ASSERT(evbtn != 0);
898 	dragWasDropped = false;
899 	inDragDrop = ddDragging;
900 	GtkTargetList *tl = gtk_target_list_new(clipboardCopyTargets, nClipboardCopyTargets);
901 #if GTK_CHECK_VERSION(3,10,0)
902 	gtk_drag_begin_with_coordinates(GTK_WIDGET(PWidget(wMain)),
903 	               tl,
904 	               static_cast<GdkDragAction>(GDK_ACTION_COPY | GDK_ACTION_MOVE),
905 	               evbtn->button,
906 	               reinterpret_cast<GdkEvent *>(evbtn),
907 			-1, -1);
908 #else
909 	gtk_drag_begin(GTK_WIDGET(PWidget(wMain)),
910 	               tl,
911 	               static_cast<GdkDragAction>(GDK_ACTION_COPY | GDK_ACTION_MOVE),
912 	               evbtn->button,
913 	               reinterpret_cast<GdkEvent *>(evbtn));
914 #endif
915 }
916 
ConvertText(const char * s,size_t len,const char * charSetDest,const char * charSetSource,bool transliterations,bool silent=false)917 static std::string ConvertText(const char *s, size_t len, const char *charSetDest,
918 	const char *charSetSource, bool transliterations, bool silent=false) {
919 	// s is not const because of different versions of iconv disagreeing about const
920 	std::string destForm;
921 	Converter conv(charSetDest, charSetSource, transliterations);
922 	if (conv) {
923 		size_t outLeft = len*3+1;
924 		destForm = std::string(outLeft, '\0');
925 		// g_iconv does not actually write to its input argument so safe to cast away const
926 		char *pin = const_cast<char *>(s);
927 		size_t inLeft = len;
928 		char *putf = &destForm[0];
929 		char *pout = putf;
930 		size_t conversions = conv.Convert(&pin, &inLeft, &pout, &outLeft);
931 		if (conversions == ((size_t)(-1))) {
932 			if (!silent) {
933 				if (len == 1)
934 					fprintf(stderr, "iconv %s->%s failed for %0x '%s'\n",
935 						charSetSource, charSetDest, (unsigned char)(*s), s);
936 				else
937 					fprintf(stderr, "iconv %s->%s failed for %s\n",
938 						charSetSource, charSetDest, s);
939 			}
940 			destForm = std::string();
941 		} else {
942 			destForm.resize(pout - putf);
943 		}
944 	} else {
945 		fprintf(stderr, "Can not iconv %s %s\n", charSetDest, charSetSource);
946 	}
947 	return destForm;
948 }
949 
950 // Returns the target converted to UTF8.
951 // Return the length in bytes.
TargetAsUTF8(char * text)952 int ScintillaGTK::TargetAsUTF8(char *text) {
953 	int targetLength = targetEnd - targetStart;
954 	if (IsUnicodeMode()) {
955 		if (text) {
956 			pdoc->GetCharRange(text, targetStart, targetLength);
957 		}
958 	} else {
959 		// Need to convert
960 		const char *charSetBuffer = CharacterSetID();
961 		if (*charSetBuffer) {
962 			std::string s = RangeText(targetStart, targetEnd);
963 			std::string tmputf = ConvertText(&s[0], targetLength, "UTF-8", charSetBuffer, false);
964 			if (text) {
965 				memcpy(text, tmputf.c_str(), tmputf.length());
966 			}
967 			return tmputf.length();
968 		} else {
969 			if (text) {
970 				pdoc->GetCharRange(text, targetStart, targetLength);
971 			}
972 		}
973 	}
974 	return targetLength;
975 }
976 
977 // Translates a nul terminated UTF8 string into the document encoding.
978 // Return the length of the result in bytes.
EncodedFromUTF8(char * utf8,char * encoded) const979 int ScintillaGTK::EncodedFromUTF8(char *utf8, char *encoded) const {
980 	int inputLength = (lengthForEncode >= 0) ? lengthForEncode : strlen(utf8);
981 	if (IsUnicodeMode()) {
982 		if (encoded) {
983 			memcpy(encoded, utf8, inputLength);
984 		}
985 		return inputLength;
986 	} else {
987 		// Need to convert
988 		const char *charSetBuffer = CharacterSetID();
989 		if (*charSetBuffer) {
990 			std::string tmpEncoded = ConvertText(utf8, inputLength, charSetBuffer, "UTF-8", true);
991 			if (encoded) {
992 				memcpy(encoded, tmpEncoded.c_str(), tmpEncoded.length());
993 			}
994 			return tmpEncoded.length();
995 		} else {
996 			if (encoded) {
997 				memcpy(encoded, utf8, inputLength);
998 			}
999 			return inputLength;
1000 		}
1001 	}
1002 	// Fail
1003 	return 0;
1004 }
1005 
ValidCodePage(int codePage) const1006 bool ScintillaGTK::ValidCodePage(int codePage) const {
1007 	return codePage == 0
1008 	|| codePage == SC_CP_UTF8
1009 	|| codePage == 932
1010 	|| codePage == 936
1011 	|| codePage == 949
1012 	|| codePage == 950
1013 	|| codePage == 1361;
1014 }
1015 
WndProc(unsigned int iMessage,uptr_t wParam,sptr_t lParam)1016 sptr_t ScintillaGTK::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
1017 	try {
1018 		switch (iMessage) {
1019 
1020 		case SCI_GRABFOCUS:
1021 			gtk_widget_grab_focus(PWidget(wMain));
1022 			break;
1023 
1024 		case SCI_GETDIRECTFUNCTION:
1025 			return reinterpret_cast<sptr_t>(DirectFunction);
1026 
1027 		case SCI_GETDIRECTPOINTER:
1028 			return reinterpret_cast<sptr_t>(this);
1029 
1030 #ifdef SCI_LEXER
1031 		case SCI_LOADLEXERLIBRARY:
1032 			LexerManager::GetInstance()->Load(reinterpret_cast<const char*>(lParam));
1033 			break;
1034 #endif
1035 		case SCI_TARGETASUTF8:
1036 			return TargetAsUTF8(reinterpret_cast<char*>(lParam));
1037 
1038 		case SCI_ENCODEDFROMUTF8:
1039 			return EncodedFromUTF8(reinterpret_cast<char*>(wParam),
1040 			        reinterpret_cast<char*>(lParam));
1041 
1042 		case SCI_SETRECTANGULARSELECTIONMODIFIER:
1043 			rectangularSelectionModifier = wParam;
1044 			break;
1045 
1046 		case SCI_GETRECTANGULARSELECTIONMODIFIER:
1047 			return rectangularSelectionModifier;
1048 
1049 		default:
1050 			return ScintillaBase::WndProc(iMessage, wParam, lParam);
1051 		}
1052 	} catch (std::bad_alloc&) {
1053 		errorStatus = SC_STATUS_BADALLOC;
1054 	} catch (...) {
1055 		errorStatus = SC_STATUS_FAILURE;
1056 	}
1057 	return 0l;
1058 }
1059 
DefWndProc(unsigned int,uptr_t,sptr_t)1060 sptr_t ScintillaGTK::DefWndProc(unsigned int, uptr_t, sptr_t) {
1061 	return 0;
1062 }
1063 
1064 /**
1065 * Report that this Editor subclass has a working implementation of FineTickerStart.
1066 */
FineTickerAvailable()1067 bool ScintillaGTK::FineTickerAvailable() {
1068 	return true;
1069 }
1070 
FineTickerRunning(TickReason reason)1071 bool ScintillaGTK::FineTickerRunning(TickReason reason) {
1072 	return timers[reason].timer != 0;
1073 }
1074 
FineTickerStart(TickReason reason,int millis,int)1075 void ScintillaGTK::FineTickerStart(TickReason reason, int millis, int /* tolerance */) {
1076 	FineTickerCancel(reason);
1077 	timers[reason].timer = g_timeout_add(millis, reinterpret_cast<GSourceFunc>(TimeOut), &timers[reason]);
1078 }
1079 
FineTickerCancel(TickReason reason)1080 void ScintillaGTK::FineTickerCancel(TickReason reason) {
1081 	if (timers[reason].timer) {
1082 		g_source_remove(timers[reason].timer);
1083 		timers[reason].timer = 0;
1084 	}
1085 }
1086 
SetIdle(bool on)1087 bool ScintillaGTK::SetIdle(bool on) {
1088 	if (on) {
1089 		// Start idler, if it's not running.
1090 		if (!idler.state) {
1091 			idler.state = true;
1092 			idler.idlerID = reinterpret_cast<IdlerID>(
1093 				g_idle_add_full(G_PRIORITY_DEFAULT_IDLE,
1094 					reinterpret_cast<GSourceFunc>(IdleCallback), this, NULL));
1095 		}
1096 	} else {
1097 		// Stop idler, if it's running
1098 		if (idler.state) {
1099 			idler.state = false;
1100 			g_source_remove(GPOINTER_TO_UINT(idler.idlerID));
1101 		}
1102 	}
1103 	return true;
1104 }
1105 
SetMouseCapture(bool on)1106 void ScintillaGTK::SetMouseCapture(bool on) {
1107 	if (mouseDownCaptures) {
1108 		if (on) {
1109 			gtk_grab_add(GTK_WIDGET(PWidget(wMain)));
1110 		} else {
1111 			gtk_grab_remove(GTK_WIDGET(PWidget(wMain)));
1112 		}
1113 	}
1114 	capturedMouse = on;
1115 }
1116 
HaveMouseCapture()1117 bool ScintillaGTK::HaveMouseCapture() {
1118 	return capturedMouse;
1119 }
1120 
1121 #if GTK_CHECK_VERSION(3,0,0)
1122 
1123 // Is crcTest completely in crcContainer?
CRectContains(const cairo_rectangle_t & crcContainer,const cairo_rectangle_t & crcTest)1124 static bool CRectContains(const cairo_rectangle_t &crcContainer, const cairo_rectangle_t &crcTest) {
1125 	return
1126 		(crcTest.x >= crcContainer.x) && ((crcTest.x + crcTest.width) <= (crcContainer.x + crcContainer.width)) &&
1127 		(crcTest.y >= crcContainer.y) && ((crcTest.y + crcTest.height) <= (crcContainer.y + crcContainer.height));
1128 }
1129 
1130 // Is crcTest completely in crcListContainer?
1131 // May incorrectly return false if complex shape
CRectListContains(const cairo_rectangle_list_t * crcListContainer,const cairo_rectangle_t & crcTest)1132 static bool CRectListContains(const cairo_rectangle_list_t *crcListContainer, const cairo_rectangle_t &crcTest) {
1133 	for (int r=0; r<crcListContainer->num_rectangles; r++) {
1134 		if (CRectContains(crcListContainer->rectangles[r], crcTest))
1135 			return true;
1136 	}
1137 	return false;
1138 }
1139 
1140 #endif
1141 
PaintContains(PRectangle rc)1142 bool ScintillaGTK::PaintContains(PRectangle rc) {
1143 	// This allows optimization when a rectangle is completely in the update region.
1144 	// It is OK to return false when too difficult to determine as that just performs extra drawing
1145 	bool contains = true;
1146 	if (paintState == painting) {
1147 		if (!rcPaint.Contains(rc)) {
1148 			contains = false;
1149 		} else if (rgnUpdate) {
1150 #if GTK_CHECK_VERSION(3,0,0)
1151 			cairo_rectangle_t grc = {rc.left, rc.top,
1152 				rc.right - rc.left, rc.bottom - rc.top};
1153 			contains = CRectListContains(rgnUpdate, grc);
1154 #else
1155 			GdkRectangle grc = {static_cast<gint>(rc.left), static_cast<gint>(rc.top),
1156 				static_cast<gint>(rc.right - rc.left), static_cast<gint>(rc.bottom - rc.top)};
1157 			if (gdk_region_rect_in(rgnUpdate, &grc) != GDK_OVERLAP_RECTANGLE_IN) {
1158 				contains = false;
1159 			}
1160 #endif
1161 		}
1162 	}
1163 	return contains;
1164 }
1165 
1166 // Redraw all of text area. This paint will not be abandoned.
FullPaint()1167 void ScintillaGTK::FullPaint() {
1168 	wText.InvalidateAll();
1169 }
1170 
GetClientRectangle() const1171 PRectangle ScintillaGTK::GetClientRectangle() const {
1172 	Window &win = const_cast<Window &>(wMain);
1173 	PRectangle rc = win.GetClientPosition();
1174 	if (verticalScrollBarVisible)
1175 		rc.right -= verticalScrollBarWidth;
1176 	if (horizontalScrollBarVisible && !Wrapping())
1177 		rc.bottom -= horizontalScrollBarHeight;
1178 	// Move to origin
1179 	rc.right -= rc.left;
1180 	rc.bottom -= rc.top;
1181 	rc.left = 0;
1182 	rc.top = 0;
1183 	return rc;
1184 }
1185 
ScrollText(int linesToMove)1186 void ScintillaGTK::ScrollText(int linesToMove) {
1187 	int diff = vs.lineHeight * -linesToMove;
1188 	//Platform::DebugPrintf("ScintillaGTK::ScrollText %d %d %0d,%0d %0d,%0d\n", linesToMove, diff,
1189 	//	rc.left, rc.top, rc.right, rc.bottom);
1190 	GtkWidget *wi = PWidget(wText);
1191 	NotifyUpdateUI();
1192 
1193 	if (IS_WIDGET_REALIZED(wi)) {
1194 		gdk_window_scroll(WindowFromWidget(wi), 0, -diff);
1195 		gdk_window_process_updates(WindowFromWidget(wi), FALSE);
1196 	}
1197 }
1198 
SetVerticalScrollPos()1199 void ScintillaGTK::SetVerticalScrollPos() {
1200 	DwellEnd(true);
1201 	gtk_adjustment_set_value(GTK_ADJUSTMENT(adjustmentv), topLine);
1202 }
1203 
SetHorizontalScrollPos()1204 void ScintillaGTK::SetHorizontalScrollPos() {
1205 	DwellEnd(true);
1206 	gtk_adjustment_set_value(GTK_ADJUSTMENT(adjustmenth), xOffset);
1207 }
1208 
ModifyScrollBars(int nMax,int nPage)1209 bool ScintillaGTK::ModifyScrollBars(int nMax, int nPage) {
1210 	bool modified = false;
1211 	int pageScroll = LinesToScroll();
1212 
1213 #if GTK_CHECK_VERSION(3,0,0)
1214 	if (gtk_adjustment_get_upper(adjustmentv) != (nMax + 1) ||
1215 	        gtk_adjustment_get_page_size(adjustmentv) != nPage ||
1216 	        gtk_adjustment_get_page_increment(adjustmentv) != pageScroll) {
1217 		gtk_adjustment_set_upper(adjustmentv, nMax + 1);
1218 	        gtk_adjustment_set_page_size(adjustmentv, nPage);
1219 	        gtk_adjustment_set_page_increment(adjustmentv, pageScroll);
1220 		gtk_adjustment_changed(GTK_ADJUSTMENT(adjustmentv));
1221 		modified = true;
1222 	}
1223 #else
1224 	if (GTK_ADJUSTMENT(adjustmentv)->upper != (nMax + 1) ||
1225 	        GTK_ADJUSTMENT(adjustmentv)->page_size != nPage ||
1226 	        GTK_ADJUSTMENT(adjustmentv)->page_increment != pageScroll) {
1227 		GTK_ADJUSTMENT(adjustmentv)->upper = nMax + 1;
1228 		GTK_ADJUSTMENT(adjustmentv)->page_size = nPage;
1229 		GTK_ADJUSTMENT(adjustmentv)->page_increment = pageScroll;
1230 		gtk_adjustment_changed(GTK_ADJUSTMENT(adjustmentv));
1231 		modified = true;
1232 	}
1233 #endif
1234 
1235 	PRectangle rcText = GetTextRectangle();
1236 	int horizEndPreferred = scrollWidth;
1237 	if (horizEndPreferred < 0)
1238 		horizEndPreferred = 0;
1239 	unsigned int pageWidth = rcText.Width();
1240 	unsigned int pageIncrement = pageWidth / 3;
1241 	unsigned int charWidth = vs.styles[STYLE_DEFAULT].aveCharWidth;
1242 #if GTK_CHECK_VERSION(3,0,0)
1243 	if (gtk_adjustment_get_upper(adjustmenth) != horizEndPreferred ||
1244 	        gtk_adjustment_get_page_size(adjustmenth) != pageWidth ||
1245 	        gtk_adjustment_get_page_increment(adjustmenth) != pageIncrement ||
1246 	        gtk_adjustment_get_step_increment(adjustmenth) != charWidth) {
1247 		gtk_adjustment_set_upper(adjustmenth, horizEndPreferred);
1248 	        gtk_adjustment_set_page_size(adjustmenth, pageWidth);
1249 	        gtk_adjustment_set_page_increment(adjustmenth, pageIncrement);
1250 	        gtk_adjustment_set_step_increment(adjustmenth, charWidth);
1251 		gtk_adjustment_changed(GTK_ADJUSTMENT(adjustmenth));
1252 		modified = true;
1253 	}
1254 #else
1255 	if (GTK_ADJUSTMENT(adjustmenth)->upper != horizEndPreferred ||
1256 	        GTK_ADJUSTMENT(adjustmenth)->page_size != pageWidth ||
1257 	        GTK_ADJUSTMENT(adjustmenth)->page_increment != pageIncrement ||
1258 	        GTK_ADJUSTMENT(adjustmenth)->step_increment != charWidth) {
1259 		GTK_ADJUSTMENT(adjustmenth)->upper = horizEndPreferred;
1260 		GTK_ADJUSTMENT(adjustmenth)->step_increment = charWidth;
1261 		GTK_ADJUSTMENT(adjustmenth)->page_size = pageWidth;
1262 		GTK_ADJUSTMENT(adjustmenth)->page_increment = pageIncrement;
1263 		gtk_adjustment_changed(GTK_ADJUSTMENT(adjustmenth));
1264 		modified = true;
1265 	}
1266 #endif
1267 	if (modified && (paintState == painting)) {
1268 		repaintFullWindow = true;
1269 	}
1270 
1271 	return modified;
1272 }
1273 
ReconfigureScrollBars()1274 void ScintillaGTK::ReconfigureScrollBars() {
1275 	PRectangle rc = wMain.GetClientPosition();
1276 	Resize(rc.Width(), rc.Height());
1277 }
1278 
NotifyChange()1279 void ScintillaGTK::NotifyChange() {
1280 	g_signal_emit(G_OBJECT(sci), scintilla_signals[COMMAND_SIGNAL], 0,
1281 	                Platform::LongFromTwoShorts(GetCtrlID(), SCEN_CHANGE), PWidget(wMain));
1282 }
1283 
NotifyFocus(bool focus)1284 void ScintillaGTK::NotifyFocus(bool focus) {
1285 	g_signal_emit(G_OBJECT(sci), scintilla_signals[COMMAND_SIGNAL], 0,
1286 	                Platform::LongFromTwoShorts
1287 					(GetCtrlID(), focus ? SCEN_SETFOCUS : SCEN_KILLFOCUS), PWidget(wMain));
1288 	Editor::NotifyFocus(focus);
1289 }
1290 
NotifyParent(SCNotification scn)1291 void ScintillaGTK::NotifyParent(SCNotification scn) {
1292 	scn.nmhdr.hwndFrom = PWidget(wMain);
1293 	scn.nmhdr.idFrom = GetCtrlID();
1294 	g_signal_emit(G_OBJECT(sci), scintilla_signals[NOTIFY_SIGNAL], 0,
1295 	                GetCtrlID(), &scn);
1296 }
1297 
NotifyKey(int key,int modifiers)1298 void ScintillaGTK::NotifyKey(int key, int modifiers) {
1299 	SCNotification scn = {};
1300 	scn.nmhdr.code = SCN_KEY;
1301 	scn.ch = key;
1302 	scn.modifiers = modifiers;
1303 
1304 	NotifyParent(scn);
1305 }
1306 
NotifyURIDropped(const char * list)1307 void ScintillaGTK::NotifyURIDropped(const char *list) {
1308 	SCNotification scn = {};
1309 	scn.nmhdr.code = SCN_URIDROPPED;
1310 	scn.text = list;
1311 
1312 	NotifyParent(scn);
1313 }
1314 
1315 const char *CharacterSetID(int characterSet);
1316 
CharacterSetID() const1317 const char *ScintillaGTK::CharacterSetID() const {
1318 	return ::CharacterSetID(vs.styles[STYLE_DEFAULT].characterSet);
1319 }
1320 
1321 class CaseFolderDBCS : public CaseFolderTable {
1322 	const char *charSet;
1323 public:
CaseFolderDBCS(const char * charSet_)1324 	explicit CaseFolderDBCS(const char *charSet_) : charSet(charSet_) {
1325 		StandardASCII();
1326 	}
Fold(char * folded,size_t sizeFolded,const char * mixed,size_t lenMixed)1327 	virtual size_t Fold(char *folded, size_t sizeFolded, const char *mixed, size_t lenMixed) {
1328 		if ((lenMixed == 1) && (sizeFolded > 0)) {
1329 			folded[0] = mapping[static_cast<unsigned char>(mixed[0])];
1330 			return 1;
1331 		} else if (*charSet) {
1332 			std::string sUTF8 = ConvertText(mixed, lenMixed,
1333 				"UTF-8", charSet, false);
1334 			if (!sUTF8.empty()) {
1335 				gchar *mapped = g_utf8_casefold(sUTF8.c_str(), sUTF8.length());
1336 				size_t lenMapped = strlen(mapped);
1337 				if (lenMapped < sizeFolded) {
1338 					memcpy(folded, mapped,  lenMapped);
1339 				} else {
1340 					folded[0] = '\0';
1341 					lenMapped = 1;
1342 				}
1343 				g_free(mapped);
1344 				return lenMapped;
1345 			}
1346 		}
1347 		// Something failed so return a single NUL byte
1348 		folded[0] = '\0';
1349 		return 1;
1350 	}
1351 };
1352 
CaseFolderForEncoding()1353 CaseFolder *ScintillaGTK::CaseFolderForEncoding() {
1354 	if (pdoc->dbcsCodePage == SC_CP_UTF8) {
1355 		return new CaseFolderUnicode();
1356 	} else {
1357 		const char *charSetBuffer = CharacterSetID();
1358 		if (charSetBuffer) {
1359 			if (pdoc->dbcsCodePage == 0) {
1360 				CaseFolderTable *pcf = new CaseFolderTable();
1361 				pcf->StandardASCII();
1362 				// Only for single byte encodings
1363 				for (int i=0x80; i<0x100; i++) {
1364 					char sCharacter[2] = "A";
1365 					sCharacter[0] = i;
1366 					// Silent as some bytes have no assigned character
1367 					std::string sUTF8 = ConvertText(sCharacter, 1,
1368 						"UTF-8", charSetBuffer, false, true);
1369 					if (!sUTF8.empty()) {
1370 						gchar *mapped = g_utf8_casefold(sUTF8.c_str(), sUTF8.length());
1371 						if (mapped) {
1372 							std::string mappedBack = ConvertText(mapped, strlen(mapped),
1373 								charSetBuffer, "UTF-8", false, true);
1374 							if ((mappedBack.length() == 1) && (mappedBack[0] != sCharacter[0])) {
1375 								pcf->SetTranslation(sCharacter[0], mappedBack[0]);
1376 							}
1377 							g_free(mapped);
1378 						}
1379 					}
1380 				}
1381 				return pcf;
1382 			} else {
1383 				return new CaseFolderDBCS(charSetBuffer);
1384 			}
1385 		}
1386 		return 0;
1387 	}
1388 }
1389 
1390 namespace {
1391 
1392 struct CaseMapper {
1393 	gchar *mapped;	// Must be freed with g_free
CaseMapper__anon6e3060980411::CaseMapper1394 	CaseMapper(const std::string &sUTF8, bool toUpperCase) {
1395 		if (toUpperCase) {
1396 			mapped = g_utf8_strup(sUTF8.c_str(), sUTF8.length());
1397 		} else {
1398 			mapped = g_utf8_strdown(sUTF8.c_str(), sUTF8.length());
1399 		}
1400 	}
~CaseMapper__anon6e3060980411::CaseMapper1401 	~CaseMapper() {
1402 		g_free(mapped);
1403 	}
1404 };
1405 
1406 }
1407 
CaseMapString(const std::string & s,int caseMapping)1408 std::string ScintillaGTK::CaseMapString(const std::string &s, int caseMapping) {
1409 	if ((s.size() == 0) || (caseMapping == cmSame))
1410 		return s;
1411 
1412 	if (IsUnicodeMode()) {
1413 		std::string retMapped(s.length() * maxExpansionCaseConversion, 0);
1414 		size_t lenMapped = CaseConvertString(&retMapped[0], retMapped.length(), s.c_str(), s.length(),
1415 			(caseMapping == cmUpper) ? CaseConversionUpper : CaseConversionLower);
1416 		retMapped.resize(lenMapped);
1417 		return retMapped;
1418 	}
1419 
1420 	const char *charSetBuffer = CharacterSetID();
1421 
1422 	if (!*charSetBuffer) {
1423 		CaseMapper mapper(s, caseMapping == cmUpper);
1424 		return std::string(mapper.mapped, strlen(mapper.mapped));
1425 	} else {
1426 		// Change text to UTF-8
1427 		std::string sUTF8 = ConvertText(s.c_str(), s.length(),
1428 			"UTF-8", charSetBuffer, false);
1429 		CaseMapper mapper(sUTF8, caseMapping == cmUpper);
1430 		return ConvertText(mapper.mapped, strlen(mapper.mapped), charSetBuffer, "UTF-8", false);
1431 	}
1432 }
1433 
KeyDefault(int key,int modifiers)1434 int ScintillaGTK::KeyDefault(int key, int modifiers) {
1435 	// Pass up to container in case it is an accelerator
1436 	NotifyKey(key, modifiers);
1437 	return 0;
1438 }
1439 
CopyToClipboard(const SelectionText & selectedText)1440 void ScintillaGTK::CopyToClipboard(const SelectionText &selectedText) {
1441 	SelectionText *clipText = new SelectionText();
1442 	clipText->Copy(selectedText);
1443 	StoreOnClipboard(clipText);
1444 }
1445 
Copy()1446 void ScintillaGTK::Copy() {
1447 	if (!sel.Empty()) {
1448 		SelectionText *clipText = new SelectionText();
1449 		CopySelectionRange(clipText);
1450 		StoreOnClipboard(clipText);
1451 #if PLAT_GTK_WIN32
1452 		if (sel.IsRectangular()) {
1453 			::OpenClipboard(NULL);
1454 			::SetClipboardData(cfColumnSelect, 0);
1455 			::CloseClipboard();
1456 		}
1457 #endif
1458 	}
1459 }
1460 
Paste()1461 void ScintillaGTK::Paste() {
1462 	atomSought = atomUTF8;
1463 	gtk_selection_convert(GTK_WIDGET(PWidget(wMain)),
1464 	                      atomClipboard, atomSought, GDK_CURRENT_TIME);
1465 }
1466 
CreateCallTipWindow(PRectangle rc)1467 void ScintillaGTK::CreateCallTipWindow(PRectangle rc) {
1468 	if (!ct.wCallTip.Created()) {
1469 		ct.wCallTip = gtk_window_new(GTK_WINDOW_POPUP);
1470 		ct.wDraw = gtk_drawing_area_new();
1471 		GtkWidget *widcdrw = PWidget(ct.wDraw);	//	// No code inside the G_OBJECT macro
1472 		gtk_container_add(GTK_CONTAINER(PWidget(ct.wCallTip)), widcdrw);
1473 #if GTK_CHECK_VERSION(3,0,0)
1474 		g_signal_connect(G_OBJECT(widcdrw), "draw",
1475 				   G_CALLBACK(ScintillaGTK::DrawCT), &ct);
1476 #else
1477 		g_signal_connect(G_OBJECT(widcdrw), "expose_event",
1478 				   G_CALLBACK(ScintillaGTK::ExposeCT), &ct);
1479 #endif
1480 		g_signal_connect(G_OBJECT(widcdrw), "button_press_event",
1481 				   G_CALLBACK(ScintillaGTK::PressCT), static_cast<void *>(this));
1482 		gtk_widget_set_events(widcdrw,
1483 			GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK);
1484 	}
1485 	gtk_widget_set_size_request(PWidget(ct.wDraw), rc.Width(), rc.Height());
1486 	ct.wDraw.Show();
1487 	if (PWindow(ct.wCallTip)) {
1488 		gdk_window_resize(PWindow(ct.wCallTip), rc.Width(), rc.Height());
1489 	}
1490 }
1491 
AddToPopUp(const char * label,int cmd,bool enabled)1492 void ScintillaGTK::AddToPopUp(const char *label, int cmd, bool enabled) {
1493 	GtkWidget *menuItem;
1494 	if (label[0])
1495 		menuItem = gtk_menu_item_new_with_label(label);
1496 	else
1497 		menuItem = gtk_separator_menu_item_new();
1498 	gtk_menu_shell_append(GTK_MENU_SHELL(popup.GetID()), menuItem);
1499 	g_object_set_data(G_OBJECT(menuItem), "CmdNum", reinterpret_cast<void *>(cmd));
1500 	g_signal_connect(G_OBJECT(menuItem),"activate", G_CALLBACK(PopUpCB), this);
1501 
1502 	if (cmd) {
1503 		if (menuItem)
1504 			gtk_widget_set_sensitive(menuItem, enabled);
1505 	}
1506 }
1507 
OwnPrimarySelection()1508 bool ScintillaGTK::OwnPrimarySelection() {
1509 	return ((gdk_selection_owner_get(GDK_SELECTION_PRIMARY)
1510 		== PWindow(wMain)) &&
1511 			(PWindow(wMain) != NULL));
1512 }
1513 
ClaimSelection()1514 void ScintillaGTK::ClaimSelection() {
1515 	// X Windows has a 'primary selection' as well as the clipboard.
1516 	// Whenever the user selects some text, we become the primary selection
1517 	if (!sel.Empty() && IS_WIDGET_REALIZED(GTK_WIDGET(PWidget(wMain)))) {
1518 		primarySelection = true;
1519 		gtk_selection_owner_set(GTK_WIDGET(PWidget(wMain)),
1520 		                        GDK_SELECTION_PRIMARY, GDK_CURRENT_TIME);
1521 		primary.Clear();
1522 	} else if (OwnPrimarySelection()) {
1523 		primarySelection = true;
1524 		if (primary.Empty())
1525 			gtk_selection_owner_set(NULL, GDK_SELECTION_PRIMARY, GDK_CURRENT_TIME);
1526 	} else {
1527 		primarySelection = false;
1528 		primary.Clear();
1529 	}
1530 }
1531 
1532 #if GTK_CHECK_VERSION(3,0,0)
DataOfGSD(GtkSelectionData * sd)1533 static const guchar *DataOfGSD(GtkSelectionData *sd) { return gtk_selection_data_get_data(sd); }
LengthOfGSD(GtkSelectionData * sd)1534 static gint LengthOfGSD(GtkSelectionData *sd) { return gtk_selection_data_get_length(sd); }
TypeOfGSD(GtkSelectionData * sd)1535 static GdkAtom TypeOfGSD(GtkSelectionData *sd) { return gtk_selection_data_get_data_type(sd); }
SelectionOfGSD(GtkSelectionData * sd)1536 static GdkAtom SelectionOfGSD(GtkSelectionData *sd) { return gtk_selection_data_get_selection(sd); }
1537 #else
DataOfGSD(GtkSelectionData * sd)1538 static const guchar *DataOfGSD(GtkSelectionData *sd) { return sd->data; }
LengthOfGSD(GtkSelectionData * sd)1539 static gint LengthOfGSD(GtkSelectionData *sd) { return sd->length; }
TypeOfGSD(GtkSelectionData * sd)1540 static GdkAtom TypeOfGSD(GtkSelectionData *sd) { return sd->type; }
SelectionOfGSD(GtkSelectionData * sd)1541 static GdkAtom SelectionOfGSD(GtkSelectionData *sd) { return sd->selection; }
1542 #endif
1543 
1544 // Detect rectangular text, convert line ends to current mode, convert from or to UTF-8
GetGtkSelectionText(GtkSelectionData * selectionData,SelectionText & selText)1545 void ScintillaGTK::GetGtkSelectionText(GtkSelectionData *selectionData, SelectionText &selText) {
1546 	const char *data = reinterpret_cast<const char *>(DataOfGSD(selectionData));
1547 	int len = LengthOfGSD(selectionData);
1548 	GdkAtom selectionTypeData = TypeOfGSD(selectionData);
1549 
1550 	// Return empty string if selection is not a string
1551 	if ((selectionTypeData != GDK_TARGET_STRING) && (selectionTypeData != atomUTF8)) {
1552 		selText.Clear();
1553 		return;
1554 	}
1555 
1556 	// Check for "\n\0" ending to string indicating that selection is rectangular
1557 	bool isRectangular;
1558 #if PLAT_GTK_WIN32
1559 	isRectangular = ::IsClipboardFormatAvailable(cfColumnSelect) != 0;
1560 #else
1561 	isRectangular = ((len > 2) && (data[len - 1] == 0 && data[len - 2] == '\n'));
1562 	if (isRectangular)
1563 		len--;	// Forget the extra '\0'
1564 #endif
1565 
1566 	std::string dest(data, len);
1567 	if (selectionTypeData == GDK_TARGET_STRING) {
1568 		if (IsUnicodeMode()) {
1569 			// Unknown encoding so assume in Latin1
1570 			dest = UTF8FromLatin1(dest.c_str(), dest.length());
1571 			selText.Copy(dest, SC_CP_UTF8, 0, isRectangular, false);
1572 		} else {
1573 			// Assume buffer is in same encoding as selection
1574 			selText.Copy(dest, pdoc->dbcsCodePage,
1575 				vs.styles[STYLE_DEFAULT].characterSet, isRectangular, false);
1576 		}
1577 	} else {	// UTF-8
1578 		const char *charSetBuffer = CharacterSetID();
1579 		if (!IsUnicodeMode() && *charSetBuffer) {
1580 			// Convert to locale
1581 			dest = ConvertText(dest.c_str(), dest.length(), charSetBuffer, "UTF-8", true);
1582 			selText.Copy(dest, pdoc->dbcsCodePage,
1583 				vs.styles[STYLE_DEFAULT].characterSet, isRectangular, false);
1584 		} else {
1585 			selText.Copy(dest, SC_CP_UTF8, 0, isRectangular, false);
1586 		}
1587 	}
1588 }
1589 
ReceivedSelection(GtkSelectionData * selection_data)1590 void ScintillaGTK::ReceivedSelection(GtkSelectionData *selection_data) {
1591 	try {
1592 		if ((SelectionOfGSD(selection_data) == atomClipboard) ||
1593 		        (SelectionOfGSD(selection_data) == GDK_SELECTION_PRIMARY)) {
1594 			if ((atomSought == atomUTF8) && (LengthOfGSD(selection_data) <= 0)) {
1595 				atomSought = atomString;
1596 				gtk_selection_convert(GTK_WIDGET(PWidget(wMain)),
1597 				        SelectionOfGSD(selection_data), atomSought, GDK_CURRENT_TIME);
1598 			} else if ((LengthOfGSD(selection_data) > 0) &&
1599 			        ((TypeOfGSD(selection_data) == GDK_TARGET_STRING) || (TypeOfGSD(selection_data) == atomUTF8))) {
1600 				SelectionText selText;
1601 				GetGtkSelectionText(selection_data, selText);
1602 
1603 				UndoGroup ug(pdoc);
1604 				if (SelectionOfGSD(selection_data) != GDK_SELECTION_PRIMARY) {
1605 					ClearSelection(multiPasteMode == SC_MULTIPASTE_EACH);
1606 				}
1607 
1608 				InsertPasteShape(selText.Data(), selText.Length(),
1609 					selText.rectangular ? pasteRectangular : pasteStream);
1610 				EnsureCaretVisible();
1611 			}
1612 		}
1613 //	else fprintf(stderr, "Target non string %d %d\n", (int)(selection_data->type),
1614 //		(int)(atomUTF8));
1615 		Redraw();
1616 	} catch (...) {
1617 		errorStatus = SC_STATUS_FAILURE;
1618 	}
1619 }
1620 
ReceivedDrop(GtkSelectionData * selection_data)1621 void ScintillaGTK::ReceivedDrop(GtkSelectionData *selection_data) {
1622 	dragWasDropped = true;
1623 	if (TypeOfGSD(selection_data) == atomUriList || TypeOfGSD(selection_data) == atomDROPFILES_DND) {
1624 		const char *data = reinterpret_cast<const char *>(DataOfGSD(selection_data));
1625 		std::vector<char> drop(data, data + LengthOfGSD(selection_data));
1626 		drop.push_back('\0');
1627 		NotifyURIDropped(&drop[0]);
1628 	} else if ((TypeOfGSD(selection_data) == GDK_TARGET_STRING) || (TypeOfGSD(selection_data) == atomUTF8)) {
1629 		if (TypeOfGSD(selection_data) != NULL) {
1630 			SelectionText selText;
1631 			GetGtkSelectionText(selection_data, selText);
1632 			DropAt(posDrop, selText.Data(), selText.Length(), false, selText.rectangular);
1633 		}
1634 	} else if (LengthOfGSD(selection_data) > 0) {
1635 		//~ fprintf(stderr, "ReceivedDrop other %p\n", static_cast<void *>(selection_data->type));
1636 	}
1637 	Redraw();
1638 }
1639 
1640 
1641 
GetSelection(GtkSelectionData * selection_data,guint info,SelectionText * text)1642 void ScintillaGTK::GetSelection(GtkSelectionData *selection_data, guint info, SelectionText *text) {
1643 #if PLAT_GTK_WIN32
1644 	// GDK on Win32 expands any \n into \r\n, so make a copy of
1645 	// the clip text now with newlines converted to \n.  Use { } to hide symbols
1646 	// from code below
1647 	SelectionText *newline_normalized = NULL;
1648 	{
1649 		std::string tmpstr = Document::TransformLineEnds(text->Data(), text->Length(), SC_EOL_LF);
1650 		newline_normalized = new SelectionText();
1651 		newline_normalized->Copy(tmpstr, SC_CP_UTF8, 0, text->rectangular, false);
1652 		text = newline_normalized;
1653 	}
1654 #endif
1655 
1656 	// Convert text to utf8 if it isn't already
1657 	SelectionText *converted = 0;
1658 	if ((text->codePage != SC_CP_UTF8) && (info == TARGET_UTF8_STRING)) {
1659 		const char *charSet = ::CharacterSetID(text->characterSet);
1660 		if (*charSet) {
1661 			std::string tmputf = ConvertText(text->Data(), text->Length(), "UTF-8", charSet, false);
1662 			converted = new SelectionText();
1663 			converted->Copy(tmputf, SC_CP_UTF8, 0, text->rectangular, false);
1664 			text = converted;
1665 		}
1666 	}
1667 
1668 	// Here is a somewhat evil kludge.
1669 	// As I can not work out how to store data on the clipboard in multiple formats
1670 	// and need some way to mark the clipping as being stream or rectangular,
1671 	// the terminating \0 is included in the length for rectangular clippings.
1672 	// All other tested aplications behave benignly by ignoring the \0.
1673 	// The #if is here because on Windows cfColumnSelect clip entry is used
1674 	// instead as standard indicator of rectangularness (so no need to kludge)
1675 	const char *textData = text->Data();
1676 	int len = text->Length();
1677 #if PLAT_GTK_WIN32 == 0
1678 	if (text->rectangular)
1679 		len++;
1680 #endif
1681 
1682 	if (info == TARGET_UTF8_STRING) {
1683 		gtk_selection_data_set_text(selection_data, textData, len);
1684 	} else {
1685 		gtk_selection_data_set(selection_data,
1686 			static_cast<GdkAtom>(GDK_SELECTION_TYPE_STRING),
1687 			8, reinterpret_cast<const unsigned char *>(textData), len);
1688 	}
1689 	delete converted;
1690 
1691 #if PLAT_GTK_WIN32
1692 	delete newline_normalized;
1693 #endif
1694 }
1695 
StoreOnClipboard(SelectionText * clipText)1696 void ScintillaGTK::StoreOnClipboard(SelectionText *clipText) {
1697 	GtkClipboard *clipBoard =
1698 		gtk_widget_get_clipboard(GTK_WIDGET(PWidget(wMain)), atomClipboard);
1699 	if (clipBoard == NULL) // Occurs if widget isn't in a toplevel
1700 		return;
1701 
1702 	if (gtk_clipboard_set_with_data(clipBoard, clipboardCopyTargets, nClipboardCopyTargets,
1703 				    ClipboardGetSelection, ClipboardClearSelection, clipText)) {
1704 		gtk_clipboard_set_can_store(clipBoard, clipboardCopyTargets, nClipboardCopyTargets);
1705 	}
1706 }
1707 
ClipboardGetSelection(GtkClipboard *,GtkSelectionData * selection_data,guint info,void * data)1708 void ScintillaGTK::ClipboardGetSelection(GtkClipboard *, GtkSelectionData *selection_data, guint info, void *data) {
1709 	GetSelection(selection_data, info, static_cast<SelectionText*>(data));
1710 }
1711 
ClipboardClearSelection(GtkClipboard *,void * data)1712 void ScintillaGTK::ClipboardClearSelection(GtkClipboard *, void *data) {
1713 	SelectionText *obj = static_cast<SelectionText*>(data);
1714 	delete obj;
1715 }
1716 
UnclaimSelection(GdkEventSelection * selection_event)1717 void ScintillaGTK::UnclaimSelection(GdkEventSelection *selection_event) {
1718 	try {
1719 		//Platform::DebugPrintf("UnclaimSelection\n");
1720 		if (selection_event->selection == GDK_SELECTION_PRIMARY) {
1721 			//Platform::DebugPrintf("UnclaimPrimarySelection\n");
1722 			if (!OwnPrimarySelection()) {
1723 				primary.Clear();
1724 				primarySelection = false;
1725 				FullPaint();
1726 			}
1727 		}
1728 	} catch (...) {
1729 		errorStatus = SC_STATUS_FAILURE;
1730 	}
1731 }
1732 
Resize(int width,int height)1733 void ScintillaGTK::Resize(int width, int height) {
1734 	//Platform::DebugPrintf("Resize %d %d\n", width, height);
1735 	//printf("Resize %d %d\n", width, height);
1736 
1737 	// Not always needed, but some themes can have different sizes of scrollbars
1738 #if GTK_CHECK_VERSION(3,0,0)
1739 	GtkRequisition requisition;
1740 	gtk_widget_get_preferred_size(PWidget(scrollbarv), NULL, &requisition);
1741 	verticalScrollBarWidth = requisition.width;
1742 	gtk_widget_get_preferred_size(PWidget(scrollbarh), NULL, &requisition);
1743 	horizontalScrollBarHeight = requisition.height;
1744 #else
1745 	verticalScrollBarWidth = GTK_WIDGET(PWidget(scrollbarv))->requisition.width;
1746 	horizontalScrollBarHeight = GTK_WIDGET(PWidget(scrollbarh))->requisition.height;
1747 #endif
1748 
1749 	// These allocations should never produce negative sizes as they would wrap around to huge
1750 	// unsigned numbers inside GTK+ causing warnings.
1751 	bool showSBHorizontal = horizontalScrollBarVisible && !Wrapping();
1752 
1753 	GtkAllocation alloc;
1754 	if (showSBHorizontal) {
1755 		gtk_widget_show(GTK_WIDGET(PWidget(scrollbarh)));
1756 		alloc.x = 0;
1757 		alloc.y = height - horizontalScrollBarHeight;
1758 		alloc.width = Platform::Maximum(1, width - verticalScrollBarWidth);
1759 		alloc.height = horizontalScrollBarHeight;
1760 		gtk_widget_size_allocate(GTK_WIDGET(PWidget(scrollbarh)), &alloc);
1761 	} else {
1762 		gtk_widget_hide(GTK_WIDGET(PWidget(scrollbarh)));
1763 		horizontalScrollBarHeight = 0; // in case horizontalScrollBarVisible is true.
1764 	}
1765 
1766 	if (verticalScrollBarVisible) {
1767 		gtk_widget_show(GTK_WIDGET(PWidget(scrollbarv)));
1768 		alloc.x = width - verticalScrollBarWidth;
1769 		alloc.y = 0;
1770 		alloc.width = verticalScrollBarWidth;
1771 		alloc.height = Platform::Maximum(1, height - horizontalScrollBarHeight);
1772 		gtk_widget_size_allocate(GTK_WIDGET(PWidget(scrollbarv)), &alloc);
1773 	} else {
1774 		gtk_widget_hide(GTK_WIDGET(PWidget(scrollbarv)));
1775 		verticalScrollBarWidth = 0;
1776 	}
1777 	if (IS_WIDGET_MAPPED(PWidget(wMain))) {
1778 		ChangeSize();
1779 	}
1780 
1781 	alloc.x = 0;
1782 	alloc.y = 0;
1783 	alloc.width = Platform::Maximum(1, width - verticalScrollBarWidth);
1784 	alloc.height = Platform::Maximum(1, height - horizontalScrollBarHeight);
1785 	gtk_widget_size_allocate(GTK_WIDGET(PWidget(wText)), &alloc);
1786 }
1787 
SetAdjustmentValue(GtkAdjustment * object,int value)1788 static void SetAdjustmentValue(GtkAdjustment *object, int value) {
1789 	GtkAdjustment *adjustment = GTK_ADJUSTMENT(object);
1790 #if GTK_CHECK_VERSION(3,0,0)
1791 	int maxValue = static_cast<int>(
1792 		gtk_adjustment_get_upper(adjustment) - gtk_adjustment_get_page_size(adjustment));
1793 #else
1794 	int maxValue = static_cast<int>(
1795 		adjustment->upper - adjustment->page_size);
1796 #endif
1797 
1798 	if (value > maxValue)
1799 		value = maxValue;
1800 	if (value < 0)
1801 		value = 0;
1802 	gtk_adjustment_set_value(adjustment, value);
1803 }
1804 
modifierTranslated(int sciModifier)1805 static int modifierTranslated(int sciModifier) {
1806 	switch (sciModifier) {
1807 		case SCMOD_SHIFT:
1808 			return GDK_SHIFT_MASK;
1809 		case SCMOD_CTRL:
1810 			return GDK_CONTROL_MASK;
1811 		case SCMOD_ALT:
1812 			return GDK_MOD1_MASK;
1813 		case SCMOD_SUPER:
1814 			return GDK_MOD4_MASK;
1815 		default:
1816 			return 0;
1817 	}
1818 }
1819 
PressThis(GdkEventButton * event)1820 gint ScintillaGTK::PressThis(GdkEventButton *event) {
1821 	try {
1822 		//Platform::DebugPrintf("Press %x time=%d state = %x button = %x\n",this,event->time, event->state, event->button);
1823 		// Do not use GTK+ double click events as Scintilla has its own double click detection
1824 		if (event->type != GDK_BUTTON_PRESS)
1825 			return FALSE;
1826 
1827 		if (evbtn) {
1828 			gdk_event_free(reinterpret_cast<GdkEvent *>(evbtn));
1829 			evbtn = 0;
1830 		}
1831 		evbtn = reinterpret_cast<GdkEventButton *>(gdk_event_copy(reinterpret_cast<GdkEvent *>(event)));
1832 		Point pt;
1833 		pt.x = int(event->x);
1834 		pt.y = int(event->y);
1835 		PRectangle rcClient = GetClientRectangle();
1836 		//Platform::DebugPrintf("Press %0d,%0d in %0d,%0d %0d,%0d\n",
1837 		//	pt.x, pt.y, rcClient.left, rcClient.top, rcClient.right, rcClient.bottom);
1838 		if ((pt.x > rcClient.right) || (pt.y > rcClient.bottom)) {
1839 			Platform::DebugPrintf("Bad location\n");
1840 			return FALSE;
1841 		}
1842 
1843 		bool shift = (event->state & GDK_SHIFT_MASK) != 0;
1844 		bool ctrl = (event->state & GDK_CONTROL_MASK) != 0;
1845 		// On X, instead of sending literal modifiers use the user specified
1846 		// modifier, defaulting to control instead of alt.
1847 		// This is because most X window managers grab alt + click for moving
1848 		bool alt = (event->state & modifierTranslated(rectangularSelectionModifier)) != 0;
1849 
1850 		gtk_widget_grab_focus(PWidget(wMain));
1851 		if (event->button == 1) {
1852 #if PLAT_GTK_MACOSX
1853 			bool meta = ctrl;
1854 			// GDK reports the Command modifer key as GDK_MOD2_MASK for button events,
1855 			// not GDK_META_MASK like in key events.
1856 			ctrl = (event->state & GDK_MOD2_MASK) != 0;
1857 #else
1858 			bool meta = false;
1859 #endif
1860 			ButtonDownWithModifiers(pt, event->time, ModifierFlags(shift, ctrl, alt, meta));
1861 		} else if (event->button == 2) {
1862 			// Grab the primary selection if it exists
1863 			SelectionPosition pos = SPositionFromLocation(pt, false, false, UserVirtualSpace());
1864 			if (OwnPrimarySelection() && primary.Empty())
1865 				CopySelectionRange(&primary);
1866 
1867 			sel.Clear();
1868 			SetSelection(pos, pos);
1869 			atomSought = atomUTF8;
1870 			gtk_selection_convert(GTK_WIDGET(PWidget(wMain)), GDK_SELECTION_PRIMARY,
1871 			        atomSought, event->time);
1872 		} else if (event->button == 3) {
1873 			if (!PointInSelection(pt))
1874 				SetEmptySelection(PositionFromLocation(pt));
1875 			if (displayPopupMenu) {
1876 				// PopUp menu
1877 				// Convert to screen
1878 				int ox = 0;
1879 				int oy = 0;
1880 				gdk_window_get_origin(PWindow(wMain), &ox, &oy);
1881 				ContextMenu(Point(pt.x + ox, pt.y + oy));
1882 			} else {
1883 				return FALSE;
1884 			}
1885 		} else if (event->button == 4) {
1886 			// Wheel scrolling up (only GTK 1.x does it this way)
1887 			if (ctrl)
1888 				SetAdjustmentValue(adjustmenth, xOffset - 6);
1889 			else
1890 				SetAdjustmentValue(adjustmentv, topLine - 3);
1891 		} else if (event->button == 5) {
1892 			// Wheel scrolling down (only GTK 1.x does it this way)
1893 			if (ctrl)
1894 				SetAdjustmentValue(adjustmenth, xOffset + 6);
1895 			else
1896 				SetAdjustmentValue(adjustmentv, topLine + 3);
1897 		}
1898 	} catch (...) {
1899 		errorStatus = SC_STATUS_FAILURE;
1900 	}
1901 	return TRUE;
1902 }
1903 
Press(GtkWidget * widget,GdkEventButton * event)1904 gint ScintillaGTK::Press(GtkWidget *widget, GdkEventButton *event) {
1905 	if (event->window != WindowFromWidget(widget))
1906 		return FALSE;
1907 	ScintillaGTK *sciThis = ScintillaFromWidget(widget);
1908 	return sciThis->PressThis(event);
1909 }
1910 
MouseRelease(GtkWidget * widget,GdkEventButton * event)1911 gint ScintillaGTK::MouseRelease(GtkWidget *widget, GdkEventButton *event) {
1912 	ScintillaGTK *sciThis = ScintillaFromWidget(widget);
1913 	try {
1914 		//Platform::DebugPrintf("Release %x %d %d\n",sciThis,event->time,event->state);
1915 		if (!sciThis->HaveMouseCapture())
1916 			return FALSE;
1917 		if (event->button == 1) {
1918 			Point pt;
1919 			pt.x = int(event->x);
1920 			pt.y = int(event->y);
1921 			//Platform::DebugPrintf("Up %x %x %d %d %d\n",
1922 			//	sciThis,event->window,event->time, pt.x, pt.y);
1923 			if (event->window != PWindow(sciThis->wMain))
1924 				// If mouse released on scroll bar then the position is relative to the
1925 				// scrollbar, not the drawing window so just repeat the most recent point.
1926 				pt = sciThis->ptMouseLast;
1927 			sciThis->ButtonUp(pt, event->time, (event->state & GDK_CONTROL_MASK) != 0);
1928 		}
1929 	} catch (...) {
1930 		sciThis->errorStatus = SC_STATUS_FAILURE;
1931 	}
1932 	return FALSE;
1933 }
1934 
1935 // win32gtk and GTK >= 2 use SCROLL_* events instead of passing the
1936 // button4/5/6/7 events to the GTK app
ScrollEvent(GtkWidget * widget,GdkEventScroll * event)1937 gint ScintillaGTK::ScrollEvent(GtkWidget *widget, GdkEventScroll *event) {
1938 	ScintillaGTK *sciThis = ScintillaFromWidget(widget);
1939 	try {
1940 
1941 		if (widget == NULL || event == NULL)
1942 			return FALSE;
1943 
1944 		// Compute amount and direction to scroll (even tho on win32 there is
1945 		// intensity of scrolling info in the native message, gtk doesn't
1946 		// support this so we simulate similarly adaptive scrolling)
1947 		// Note that this is disabled on OS X (Darwin) where the X11 server already has
1948 		// and adaptive scrolling algorithm that fights with this one
1949 		int cLineScroll;
1950 #if defined(__MWERKS__) || defined(__APPLE_CPP__) || defined(__APPLE_CC__)
1951 		cLineScroll = sciThis->linesPerScroll;
1952 		if (cLineScroll == 0)
1953 			cLineScroll = 4;
1954 		sciThis->wheelMouseIntensity = cLineScroll;
1955 #else
1956 		int timeDelta = 1000000;
1957 		GTimeVal curTime;
1958 		g_get_current_time(&curTime);
1959 		if (curTime.tv_sec == sciThis->lastWheelMouseTime.tv_sec)
1960 			timeDelta = curTime.tv_usec - sciThis->lastWheelMouseTime.tv_usec;
1961 		else if (curTime.tv_sec == sciThis->lastWheelMouseTime.tv_sec + 1)
1962 			timeDelta = 1000000 + (curTime.tv_usec - sciThis->lastWheelMouseTime.tv_usec);
1963 		if ((event->direction == sciThis->lastWheelMouseDirection) && (timeDelta < 250000)) {
1964 			if (sciThis->wheelMouseIntensity < 12)
1965 				sciThis->wheelMouseIntensity++;
1966 			cLineScroll = sciThis->wheelMouseIntensity;
1967 		} else {
1968 			cLineScroll = sciThis->linesPerScroll;
1969 			if (cLineScroll == 0)
1970 				cLineScroll = 4;
1971 			sciThis->wheelMouseIntensity = cLineScroll;
1972 		}
1973 #endif
1974 		if (event->direction == GDK_SCROLL_UP || event->direction == GDK_SCROLL_LEFT) {
1975 			cLineScroll *= -1;
1976 		}
1977 		g_get_current_time(&sciThis->lastWheelMouseTime);
1978 		sciThis->lastWheelMouseDirection = event->direction;
1979 
1980 		// Note:  Unpatched versions of win32gtk don't set the 'state' value so
1981 		// only regular scrolling is supported there.  Also, unpatched win32gtk
1982 		// issues spurious button 2 mouse events during wheeling, which can cause
1983 		// problems (a patch for both was submitted by archaeopteryx.com on 13Jun2001)
1984 
1985 		// Data zoom not supported
1986 		if (event->state & GDK_SHIFT_MASK) {
1987 			return FALSE;
1988 		}
1989 
1990 #if GTK_CHECK_VERSION(3,4,0)
1991 		// Smooth scrolling not supported
1992 		if (event->direction == GDK_SCROLL_SMOOTH) {
1993 			return FALSE;
1994 		}
1995 #endif
1996 
1997 		// Horizontal scrolling
1998 		if (event->direction == GDK_SCROLL_LEFT || event->direction == GDK_SCROLL_RIGHT) {
1999 			sciThis->HorizontalScrollTo(sciThis->xOffset + cLineScroll);
2000 
2001 			// Text font size zoom
2002 		} else if (event->state & GDK_CONTROL_MASK) {
2003 			if (cLineScroll < 0) {
2004 				sciThis->KeyCommand(SCI_ZOOMIN);
2005 			} else {
2006 				sciThis->KeyCommand(SCI_ZOOMOUT);
2007 			}
2008 
2009 			// Regular scrolling
2010 		} else {
2011 			sciThis->ScrollTo(sciThis->topLine + cLineScroll);
2012 		}
2013 		return TRUE;
2014 	} catch (...) {
2015 		sciThis->errorStatus = SC_STATUS_FAILURE;
2016 	}
2017 	return FALSE;
2018 }
2019 
Motion(GtkWidget * widget,GdkEventMotion * event)2020 gint ScintillaGTK::Motion(GtkWidget *widget, GdkEventMotion *event) {
2021 	ScintillaGTK *sciThis = ScintillaFromWidget(widget);
2022 	try {
2023 		//Platform::DebugPrintf("Motion %x %d\n",sciThis,event->time);
2024 		if (event->window != WindowFromWidget(widget))
2025 			return FALSE;
2026 		int x = 0;
2027 		int y = 0;
2028 		GdkModifierType state;
2029 		if (event->is_hint) {
2030 #if GTK_CHECK_VERSION(3,0,0)
2031 			gdk_window_get_device_position(event->window,
2032 				event->device, &x, &y, &state);
2033 #else
2034 			gdk_window_get_pointer(event->window, &x, &y, &state);
2035 #endif
2036 		} else {
2037 			x = static_cast<int>(event->x);
2038 			y = static_cast<int>(event->y);
2039 			state = static_cast<GdkModifierType>(event->state);
2040 		}
2041 		//Platform::DebugPrintf("Move %x %x %d %c %d %d\n",
2042 		//	sciThis,event->window,event->time,event->is_hint? 'h' :'.', x, y);
2043 		Point pt(x, y);
2044 		int modifiers = ((event->state & GDK_SHIFT_MASK) != 0 ? SCI_SHIFT : 0) |
2045 		                ((event->state & GDK_CONTROL_MASK) != 0 ? SCI_CTRL : 0) |
2046 		                ((event->state & modifierTranslated(sciThis->rectangularSelectionModifier)) != 0 ? SCI_ALT : 0);
2047 		sciThis->ButtonMoveWithModifiers(pt, modifiers);
2048 	} catch (...) {
2049 		sciThis->errorStatus = SC_STATUS_FAILURE;
2050 	}
2051 	return FALSE;
2052 }
2053 
2054 // Map the keypad keys to their equivalent functions
KeyTranslate(int keyIn)2055 static int KeyTranslate(int keyIn) {
2056 	switch (keyIn) {
2057 #if GTK_CHECK_VERSION(3,0,0)
2058 	case GDK_KEY_ISO_Left_Tab:
2059 		return SCK_TAB;
2060 	case GDK_KEY_KP_Down:
2061 		return SCK_DOWN;
2062 	case GDK_KEY_KP_Up:
2063 		return SCK_UP;
2064 	case GDK_KEY_KP_Left:
2065 		return SCK_LEFT;
2066 	case GDK_KEY_KP_Right:
2067 		return SCK_RIGHT;
2068 	case GDK_KEY_KP_Home:
2069 		return SCK_HOME;
2070 	case GDK_KEY_KP_End:
2071 		return SCK_END;
2072 	case GDK_KEY_KP_Page_Up:
2073 		return SCK_PRIOR;
2074 	case GDK_KEY_KP_Page_Down:
2075 		return SCK_NEXT;
2076 	case GDK_KEY_KP_Delete:
2077 		return SCK_DELETE;
2078 	case GDK_KEY_KP_Insert:
2079 		return SCK_INSERT;
2080 	case GDK_KEY_KP_Enter:
2081 		return SCK_RETURN;
2082 
2083 	case GDK_KEY_Down:
2084 		return SCK_DOWN;
2085 	case GDK_KEY_Up:
2086 		return SCK_UP;
2087 	case GDK_KEY_Left:
2088 		return SCK_LEFT;
2089 	case GDK_KEY_Right:
2090 		return SCK_RIGHT;
2091 	case GDK_KEY_Home:
2092 		return SCK_HOME;
2093 	case GDK_KEY_End:
2094 		return SCK_END;
2095 	case GDK_KEY_Page_Up:
2096 		return SCK_PRIOR;
2097 	case GDK_KEY_Page_Down:
2098 		return SCK_NEXT;
2099 	case GDK_KEY_Delete:
2100 		return SCK_DELETE;
2101 	case GDK_KEY_Insert:
2102 		return SCK_INSERT;
2103 	case GDK_KEY_Escape:
2104 		return SCK_ESCAPE;
2105 	case GDK_KEY_BackSpace:
2106 		return SCK_BACK;
2107 	case GDK_KEY_Tab:
2108 		return SCK_TAB;
2109 	case GDK_KEY_Return:
2110 		return SCK_RETURN;
2111 	case GDK_KEY_KP_Add:
2112 		return SCK_ADD;
2113 	case GDK_KEY_KP_Subtract:
2114 		return SCK_SUBTRACT;
2115 	case GDK_KEY_KP_Divide:
2116 		return SCK_DIVIDE;
2117 	case GDK_KEY_Super_L:
2118 		return SCK_WIN;
2119 	case GDK_KEY_Super_R:
2120 		return SCK_RWIN;
2121 	case GDK_KEY_Menu:
2122 		return SCK_MENU;
2123 
2124 #else
2125 
2126 	case GDK_ISO_Left_Tab:
2127 		return SCK_TAB;
2128 	case GDK_KP_Down:
2129 		return SCK_DOWN;
2130 	case GDK_KP_Up:
2131 		return SCK_UP;
2132 	case GDK_KP_Left:
2133 		return SCK_LEFT;
2134 	case GDK_KP_Right:
2135 		return SCK_RIGHT;
2136 	case GDK_KP_Home:
2137 		return SCK_HOME;
2138 	case GDK_KP_End:
2139 		return SCK_END;
2140 	case GDK_KP_Page_Up:
2141 		return SCK_PRIOR;
2142 	case GDK_KP_Page_Down:
2143 		return SCK_NEXT;
2144 	case GDK_KP_Delete:
2145 		return SCK_DELETE;
2146 	case GDK_KP_Insert:
2147 		return SCK_INSERT;
2148 	case GDK_KP_Enter:
2149 		return SCK_RETURN;
2150 
2151 	case GDK_Down:
2152 		return SCK_DOWN;
2153 	case GDK_Up:
2154 		return SCK_UP;
2155 	case GDK_Left:
2156 		return SCK_LEFT;
2157 	case GDK_Right:
2158 		return SCK_RIGHT;
2159 	case GDK_Home:
2160 		return SCK_HOME;
2161 	case GDK_End:
2162 		return SCK_END;
2163 	case GDK_Page_Up:
2164 		return SCK_PRIOR;
2165 	case GDK_Page_Down:
2166 		return SCK_NEXT;
2167 	case GDK_Delete:
2168 		return SCK_DELETE;
2169 	case GDK_Insert:
2170 		return SCK_INSERT;
2171 	case GDK_Escape:
2172 		return SCK_ESCAPE;
2173 	case GDK_BackSpace:
2174 		return SCK_BACK;
2175 	case GDK_Tab:
2176 		return SCK_TAB;
2177 	case GDK_Return:
2178 		return SCK_RETURN;
2179 	case GDK_KP_Add:
2180 		return SCK_ADD;
2181 	case GDK_KP_Subtract:
2182 		return SCK_SUBTRACT;
2183 	case GDK_KP_Divide:
2184 		return SCK_DIVIDE;
2185 	case GDK_Super_L:
2186 		return SCK_WIN;
2187 	case GDK_Super_R:
2188 		return SCK_RWIN;
2189 	case GDK_Menu:
2190 		return SCK_MENU;
2191 #endif
2192 	default:
2193 		return keyIn;
2194 	}
2195 }
2196 
KeyThis(GdkEventKey * event)2197 gboolean ScintillaGTK::KeyThis(GdkEventKey *event) {
2198 	try {
2199 		//fprintf(stderr, "SC-key: %d %x [%s]\n",
2200 		//	event->keyval, event->state, (event->length > 0) ? event->string : "empty");
2201 		if (gtk_im_context_filter_keypress(im_context, event)) {
2202 			return 1;
2203 		}
2204 		if (!event->keyval) {
2205 			return true;
2206 		}
2207 
2208 		bool shift = (event->state & GDK_SHIFT_MASK) != 0;
2209 		bool ctrl = (event->state & GDK_CONTROL_MASK) != 0;
2210 		bool alt = (event->state & GDK_MOD1_MASK) != 0;
2211 		guint key = event->keyval;
2212 		if ((ctrl || alt) && (key < 128))
2213 			key = toupper(key);
2214 #if GTK_CHECK_VERSION(3,0,0)
2215 		else if (!ctrl && (key >= GDK_KEY_KP_Multiply && key <= GDK_KEY_KP_9))
2216 #else
2217 		else if (!ctrl && (key >= GDK_KP_Multiply && key <= GDK_KP_9))
2218 #endif
2219 			key &= 0x7F;
2220 		// Hack for keys over 256 and below command keys but makes Hungarian work.
2221 		// This will have to change for Unicode
2222 		else if (key >= 0xFE00)
2223 			key = KeyTranslate(key);
2224 
2225 		bool consumed = false;
2226 #if !(PLAT_GTK_MACOSX)
2227 		bool added = KeyDown(key, shift, ctrl, alt, &consumed) != 0;
2228 #else
2229 		bool meta = ctrl;
2230 		ctrl = (event->state & GDK_META_MASK) != 0;
2231 		bool added = KeyDownWithModifiers(key, (shift ? SCI_SHIFT : 0) |
2232 		                                       (ctrl ? SCI_CTRL : 0) |
2233 		                                       (alt ? SCI_ALT : 0) |
2234 		                                       (meta ? SCI_META : 0), &consumed) != 0;
2235 #endif
2236 		if (!consumed)
2237 			consumed = added;
2238 		//fprintf(stderr, "SK-key: %d %x %x\n",event->keyval, event->state, consumed);
2239 		if (event->keyval == 0xffffff && event->length > 0) {
2240 			ClearSelection();
2241 			const int lengthInserted = pdoc->InsertString(CurrentPosition(), event->string, strlen(event->string));
2242 			if (lengthInserted > 0) {
2243 				MovePositionTo(CurrentPosition() + lengthInserted);
2244 			}
2245 		}
2246 		return consumed;
2247 	} catch (...) {
2248 		errorStatus = SC_STATUS_FAILURE;
2249 	}
2250 	return FALSE;
2251 }
2252 
KeyPress(GtkWidget * widget,GdkEventKey * event)2253 gboolean ScintillaGTK::KeyPress(GtkWidget *widget, GdkEventKey *event) {
2254 	ScintillaGTK *sciThis = ScintillaFromWidget(widget);
2255 	return sciThis->KeyThis(event);
2256 }
2257 
KeyRelease(GtkWidget * widget,GdkEventKey * event)2258 gboolean ScintillaGTK::KeyRelease(GtkWidget *widget, GdkEventKey *event) {
2259 	//Platform::DebugPrintf("SC-keyrel: %d %x %3s\n",event->keyval, event->state, event->string);
2260 	ScintillaGTK *sciThis = ScintillaFromWidget(widget);
2261 	if (gtk_im_context_filter_keypress(sciThis->im_context, event)) {
2262 		return TRUE;
2263 	}
2264 	return FALSE;
2265 }
2266 
2267 #if GTK_CHECK_VERSION(3,0,0)
2268 
DrawPreeditThis(GtkWidget * widget,cairo_t * cr)2269 gboolean ScintillaGTK::DrawPreeditThis(GtkWidget *widget, cairo_t *cr) {
2270 	try {
2271 		PreEditString pes(im_context);
2272 		PangoLayout *layout = gtk_widget_create_pango_layout(PWidget(wText), pes.str);
2273 		pango_layout_set_attributes(layout, pes.attrs);
2274 
2275 		cairo_move_to(cr, 0, 0);
2276 		pango_cairo_show_layout(cr, layout);
2277 
2278 		g_object_unref(layout);
2279 	} catch (...) {
2280 		errorStatus = SC_STATUS_FAILURE;
2281 	}
2282 	return TRUE;
2283 }
2284 
DrawPreedit(GtkWidget * widget,cairo_t * cr,ScintillaGTK * sciThis)2285 gboolean ScintillaGTK::DrawPreedit(GtkWidget *widget, cairo_t *cr, ScintillaGTK *sciThis) {
2286 	return sciThis->DrawPreeditThis(widget, cr);
2287 }
2288 
2289 #else
2290 
ExposePreeditThis(GtkWidget * widget,GdkEventExpose * ose)2291 gboolean ScintillaGTK::ExposePreeditThis(GtkWidget *widget, GdkEventExpose *ose) {
2292 	try {
2293 		PreEditString pes(im_context);
2294 		PangoLayout *layout = gtk_widget_create_pango_layout(PWidget(wText), pes.str);
2295 		pango_layout_set_attributes(layout, pes.attrs);
2296 
2297 		cairo_t *context = gdk_cairo_create(reinterpret_cast<GdkDrawable *>(WindowFromWidget(widget)));
2298 		cairo_move_to(context, 0, 0);
2299 		pango_cairo_show_layout(context, layout);
2300 		cairo_destroy(context);
2301 		g_object_unref(layout);
2302 	} catch (...) {
2303 		errorStatus = SC_STATUS_FAILURE;
2304 	}
2305 	return TRUE;
2306 }
2307 
ExposePreedit(GtkWidget * widget,GdkEventExpose * ose,ScintillaGTK * sciThis)2308 gboolean ScintillaGTK::ExposePreedit(GtkWidget *widget, GdkEventExpose *ose, ScintillaGTK *sciThis) {
2309 	return sciThis->ExposePreeditThis(widget, ose);
2310 }
2311 
2312 #endif
2313 
KoreanIME()2314 bool ScintillaGTK::KoreanIME() {
2315 	// Warn : for KoreanIME use only.
2316 	if (pdoc->TentativeActive()) {
2317 		return true;
2318 	}
2319 
2320 	bool koreanIME = false;
2321 	PreEditString utfval(im_context);
2322 
2323 	// Only need to check if the first preedit char is Korean.
2324 	// The rest will be handled in TentativeActive()
2325 	// which can handle backspace and CJK commons and so forth.
2326 
2327 	if (strlen(utfval.str) == 3) {  // One hangul char has 3byte.
2328 		int unicode = UnicodeFromUTF8(reinterpret_cast<unsigned char *>(utfval.str));
2329 		// Korean character ranges which are used for the first preedit chars.
2330 		// http://www.programminginkorean.com/programming/hangul-in-unicode/
2331 		bool HangulJamo = (0x1100 <= unicode && unicode <= 0x11FF);
2332 		bool HangulCompatibleJamo = (0x3130 <= unicode && unicode <= 0x318F);
2333 		bool HangulJamoExtendedA = (0xA960 <= unicode && unicode <= 0xA97F);
2334 		bool HangulJamoExtendedB = (0xD7B0 <= unicode && unicode <= 0xD7FF);
2335 		bool HangulSyllable = (0xAC00 <= unicode && unicode <= 0xD7A3);
2336 		koreanIME = (HangulJamo | HangulCompatibleJamo  | HangulSyllable
2337 					| HangulJamoExtendedA | HangulJamoExtendedB);
2338 	}
2339 	return koreanIME;
2340 }
2341 
CommitThis(char * utfVal)2342 void ScintillaGTK::CommitThis(char *utfVal) {
2343 	try {
2344 		//~ fprintf(stderr, "Commit '%s'\n", utfVal);
2345 		if (pdoc->TentativeActive()) {
2346 			pdoc->TentativeUndo();
2347 		}
2348 
2349 		view.imeCaretBlockOverride = false;
2350 
2351 		if (IsUnicodeMode()) {
2352 			AddCharUTF(utfVal, strlen(utfVal));
2353 		} else {
2354 			const char *source = CharacterSetID();
2355 			if (*source) {
2356 				Converter conv(source, "UTF-8", true);
2357 				if (conv) {
2358 					char localeVal[maxLenInputIME * 2];
2359 					char *pin = utfVal;
2360 					size_t inLeft = strlen(utfVal);
2361 					char *pout = localeVal;
2362 					size_t outLeft = sizeof(localeVal);
2363 					size_t conversions = conv.Convert(&pin, &inLeft, &pout, &outLeft);
2364 					if (conversions != ((size_t)(-1))) {
2365 						*pout = '\0';
2366 						AddCharUTF(localeVal, strlen(localeVal));
2367 					} else {
2368 						fprintf(stderr, "Conversion failed '%s'\n", utfVal);
2369 					}
2370 				}
2371 			}
2372 		}
2373 		ShowCaretAtCurrentPosition();
2374 	} catch (...) {
2375 		errorStatus = SC_STATUS_FAILURE;
2376 	}
2377 }
2378 
Commit(GtkIMContext *,char * str,ScintillaGTK * sciThis)2379 void ScintillaGTK::Commit(GtkIMContext *, char  *str, ScintillaGTK *sciThis) {
2380 	sciThis->CommitThis(str);
2381 }
2382 
PreeditChangedThis()2383 void ScintillaGTK::PreeditChangedThis() {
2384 	try {
2385 		if (KoreanIME()) {
2386 			// Copy & paste by johnsonj.
2387 			// Great thanks to
2388 			// jiniya from http://www.jiniya.net/tt/494  for DBCS input with AddCharUTF().
2389 			// BLUEnLIVE from http://zockr.tistory.com/1118 for UNDO and inOverstrike.
2390 			view.imeCaretBlockOverride = false; // If backspace.
2391 
2392 			if (pdoc->TentativeActive()) {
2393 				pdoc->TentativeUndo();
2394 			} else {
2395 				// No tentative undo means start of this composition so
2396 				// fill in any virtual spaces.
2397 				bool tmpOverstrike = inOverstrike;
2398 				inOverstrike = false;   // Not allowed to be deleted twice.
2399 				AddCharUTF("", 0);
2400 				inOverstrike = tmpOverstrike;
2401 			}
2402 
2403 			PreEditString utfval(im_context);
2404 
2405 			if ((strlen(utfval.str) == 0) || strlen(utfval.str) > maxLenInputIME * 3) {
2406 				return; // Do not allow over 200 chars.
2407 			}
2408 
2409 			char localeVal[maxLenInputIME * 2];
2410 			char *hanval = (char *)"";
2411 
2412 			if (IsUnicodeMode()) {
2413 				hanval = utfval.str;
2414 			} else {
2415 				const char *source = CharacterSetID();
2416 				if (*source) {
2417 					Converter conv(source, "UTF-8", true);
2418 					if (conv) {
2419 						char *pin = utfval.str;
2420 						size_t inLeft = strlen(utfval.str);
2421 						char *pout = localeVal;
2422 						size_t outLeft = sizeof(localeVal);
2423 						size_t conversions = conv.Convert(&pin, &inLeft, &pout, &outLeft);
2424 						if (conversions != ((size_t)(-1))) {
2425 							*pout = '\0';
2426 							hanval = localeVal;
2427 						} else {
2428 							fprintf(stderr, "Conversion failed '%s'\n", utfval.str);
2429 						}
2430 					}
2431 				}
2432 			}
2433 
2434 			if (!pdoc->TentativeActive()) {
2435 				pdoc->TentativeStart();
2436 			}
2437 
2438 			bool tmpRecordingMacro = recordingMacro;
2439 			recordingMacro = false;
2440 			int hanlen = strlen(hanval);
2441 			AddCharUTF(hanval, hanlen);
2442 			recordingMacro = tmpRecordingMacro;
2443 
2444 			// For block caret which means KoreanIME is in composition.
2445 			view.imeCaretBlockOverride = true;
2446 			for (size_t r = 0; r < sel.Count(); r++) {
2447 				int positionInsert = sel.Range(r).Start().Position();
2448 				sel.Range(r).caret.SetPosition(positionInsert - hanlen);
2449 				sel.Range(r).anchor.SetPosition(positionInsert - hanlen);
2450 			}
2451 		} else { // Original code follows  for other IMEs.
2452 			PreEditString pes(im_context);
2453 			if (strlen(pes.str) > 0) {
2454 				PangoLayout *layout = gtk_widget_create_pango_layout(PWidget(wText), pes.str);
2455 				pango_layout_set_attributes(layout, pes.attrs);
2456 
2457 				gint w, h;
2458 				pango_layout_get_pixel_size(layout, &w, &h);
2459 				g_object_unref(layout);
2460 
2461 				gint x, y;
2462 				gdk_window_get_origin(PWindow(wText), &x, &y);
2463 
2464 				Point pt = PointMainCaret();
2465 				if (pt.x < 0)
2466 					pt.x = 0;
2467 				if (pt.y < 0)
2468 					pt.y = 0;
2469 
2470 				gtk_window_move(GTK_WINDOW(PWidget(wPreedit)), x + pt.x, y + pt.y);
2471 				gtk_window_resize(GTK_WINDOW(PWidget(wPreedit)), w, h);
2472 				gtk_widget_show(PWidget(wPreedit));
2473 				gtk_widget_queue_draw_area(PWidget(wPreeditDraw), 0, 0, w, h);
2474 			} else {
2475 				gtk_widget_hide(PWidget(wPreedit));
2476 			}
2477 		}
2478 	} catch (...) {
2479 		errorStatus = SC_STATUS_FAILURE;
2480 	}
2481 }
2482 
PreeditChanged(GtkIMContext *,ScintillaGTK * sciThis)2483 void ScintillaGTK::PreeditChanged(GtkIMContext *, ScintillaGTK *sciThis) {
2484 	sciThis->PreeditChangedThis();
2485 }
2486 
StyleSetText(GtkWidget * widget,GtkStyle *,void *)2487 void ScintillaGTK::StyleSetText(GtkWidget *widget, GtkStyle *, void*) {
2488 	RealizeText(widget, NULL);
2489 }
2490 
RealizeText(GtkWidget * widget,void *)2491 void ScintillaGTK::RealizeText(GtkWidget *widget, void*) {
2492 	// Set NULL background to avoid automatic clearing so Scintilla responsible for all drawing
2493 	if (WindowFromWidget(widget)) {
2494 #if GTK_CHECK_VERSION(3,0,0)
2495 		gdk_window_set_background_pattern(WindowFromWidget(widget), NULL);
2496 #else
2497 		gdk_window_set_back_pixmap(WindowFromWidget(widget), NULL, FALSE);
2498 #endif
2499 	}
2500 }
2501 
2502 static GObjectClass *scintilla_class_parent_class;
2503 
Destroy(GObject * object)2504 void ScintillaGTK::Destroy(GObject *object) {
2505 	try {
2506 		ScintillaObject *scio = reinterpret_cast<ScintillaObject *>(object);
2507 
2508 		// This avoids a double destruction
2509 		if (!scio->pscin)
2510 			return;
2511 		ScintillaGTK *sciThis = reinterpret_cast<ScintillaGTK *>(scio->pscin);
2512 		//Platform::DebugPrintf("Destroying %x %x\n", sciThis, object);
2513 		sciThis->Finalise();
2514 
2515 		delete sciThis;
2516 		scio->pscin = 0;
2517 		scintilla_class_parent_class->finalize(object);
2518 	} catch (...) {
2519 		// Its dead so nowhere to save the status
2520 	}
2521 }
2522 
2523 #if GTK_CHECK_VERSION(3,0,0)
2524 
DrawTextThis(cairo_t * cr)2525 gboolean ScintillaGTK::DrawTextThis(cairo_t *cr) {
2526 	try {
2527 		paintState = painting;
2528 		repaintFullWindow = false;
2529 
2530 		rcPaint = GetClientRectangle();
2531 
2532 		PLATFORM_ASSERT(rgnUpdate == NULL);
2533 		rgnUpdate = cairo_copy_clip_rectangle_list(cr);
2534 		if (rgnUpdate && rgnUpdate->status != CAIRO_STATUS_SUCCESS) {
2535 			// If not successful then ignore
2536 			fprintf(stderr, "DrawTextThis failed to copy update region %d [%d]\n", rgnUpdate->status, rgnUpdate->num_rectangles);
2537 			cairo_rectangle_list_destroy(rgnUpdate);
2538 			rgnUpdate = 0;
2539 		}
2540 
2541 		double x1, y1, x2, y2;
2542 		cairo_clip_extents(cr, &x1, &y1, &x2, &y2);
2543 		rcPaint.left = x1;
2544 		rcPaint.top = y1;
2545 		rcPaint.right = x2;
2546 		rcPaint.bottom = y2;
2547 		PRectangle rcClient = GetClientRectangle();
2548 		paintingAllText = rcPaint.Contains(rcClient);
2549 		Surface *surfaceWindow = Surface::Allocate(SC_TECHNOLOGY_DEFAULT);
2550 		if (surfaceWindow) {
2551 			surfaceWindow->Init(cr, PWidget(wText));
2552 			Paint(surfaceWindow, rcPaint);
2553 			surfaceWindow->Release();
2554 			delete surfaceWindow;
2555 		}
2556 		if ((paintState == paintAbandoned) || repaintFullWindow) {
2557 			// Painting area was insufficient to cover new styling or brace highlight positions
2558 			FullPaint();
2559 		}
2560 		paintState = notPainting;
2561 		repaintFullWindow = false;
2562 
2563 		if (rgnUpdate) {
2564 			cairo_rectangle_list_destroy(rgnUpdate);
2565 		}
2566 		rgnUpdate = 0;
2567 		paintState = notPainting;
2568 	} catch (...) {
2569 		errorStatus = SC_STATUS_FAILURE;
2570 	}
2571 
2572 	return FALSE;
2573 }
2574 
DrawText(GtkWidget *,cairo_t * cr,ScintillaGTK * sciThis)2575 gboolean ScintillaGTK::DrawText(GtkWidget *, cairo_t *cr, ScintillaGTK *sciThis) {
2576 	return sciThis->DrawTextThis(cr);
2577 }
2578 
DrawThis(cairo_t * cr)2579 gboolean ScintillaGTK::DrawThis(cairo_t *cr) {
2580 	try {
2581 		gtk_container_propagate_draw(
2582 		    GTK_CONTAINER(PWidget(wMain)), PWidget(scrollbarh), cr);
2583 		gtk_container_propagate_draw(
2584 		    GTK_CONTAINER(PWidget(wMain)), PWidget(scrollbarv), cr);
2585 // Starting from the following version, the expose event are not propagated
2586 // for double buffered non native windows, so we need to call it ourselves
2587 // or keep the default handler
2588 #if GTK_CHECK_VERSION(3,0,0)
2589 		// we want to forward on any >= 3.9.2 runtime
2590 		if (gtk_check_version(3,9,2) == NULL) {
2591 			gtk_container_propagate_draw(
2592 					GTK_CONTAINER(PWidget(wMain)), PWidget(wText), cr);
2593 		}
2594 #endif
2595 	} catch (...) {
2596 		errorStatus = SC_STATUS_FAILURE;
2597 	}
2598 	return FALSE;
2599 }
2600 
DrawMain(GtkWidget * widget,cairo_t * cr)2601 gboolean ScintillaGTK::DrawMain(GtkWidget *widget, cairo_t *cr) {
2602 	ScintillaGTK *sciThis = ScintillaFromWidget(widget);
2603 	return sciThis->DrawThis(cr);
2604 }
2605 
2606 #else
2607 
ExposeTextThis(GtkWidget *,GdkEventExpose * ose)2608 gboolean ScintillaGTK::ExposeTextThis(GtkWidget * /*widget*/, GdkEventExpose *ose) {
2609 	try {
2610 		paintState = painting;
2611 
2612 		rcPaint.left = ose->area.x;
2613 		rcPaint.top = ose->area.y;
2614 		rcPaint.right = ose->area.x + ose->area.width;
2615 		rcPaint.bottom = ose->area.y + ose->area.height;
2616 
2617 		PLATFORM_ASSERT(rgnUpdate == NULL);
2618 		rgnUpdate = gdk_region_copy(ose->region);
2619 		PRectangle rcClient = GetClientRectangle();
2620 		paintingAllText = rcPaint.Contains(rcClient);
2621 		Surface *surfaceWindow = Surface::Allocate(SC_TECHNOLOGY_DEFAULT);
2622 		if (surfaceWindow) {
2623 			cairo_t *cr = gdk_cairo_create(PWindow(wText));
2624 			surfaceWindow->Init(cr, PWidget(wText));
2625 			Paint(surfaceWindow, rcPaint);
2626 			surfaceWindow->Release();
2627 			delete surfaceWindow;
2628 			cairo_destroy(cr);
2629 		}
2630 		if (paintState == paintAbandoned) {
2631 			// Painting area was insufficient to cover new styling or brace highlight positions
2632 			FullPaint();
2633 		}
2634 		paintState = notPainting;
2635 
2636 		if (rgnUpdate) {
2637 			gdk_region_destroy(rgnUpdate);
2638 		}
2639 		rgnUpdate = 0;
2640 	} catch (...) {
2641 		errorStatus = SC_STATUS_FAILURE;
2642 	}
2643 
2644 	return FALSE;
2645 }
2646 
ExposeText(GtkWidget * widget,GdkEventExpose * ose,ScintillaGTK * sciThis)2647 gboolean ScintillaGTK::ExposeText(GtkWidget *widget, GdkEventExpose *ose, ScintillaGTK *sciThis) {
2648 	return sciThis->ExposeTextThis(widget, ose);
2649 }
2650 
ExposeMain(GtkWidget * widget,GdkEventExpose * ose)2651 gboolean ScintillaGTK::ExposeMain(GtkWidget *widget, GdkEventExpose *ose) {
2652 	ScintillaGTK *sciThis = ScintillaFromWidget(widget);
2653 	//Platform::DebugPrintf("Expose Main %0d,%0d %0d,%0d\n",
2654 	//ose->area.x, ose->area.y, ose->area.width, ose->area.height);
2655 	return sciThis->Expose(widget, ose);
2656 }
2657 
Expose(GtkWidget *,GdkEventExpose * ose)2658 gboolean ScintillaGTK::Expose(GtkWidget *, GdkEventExpose *ose) {
2659 	try {
2660 		//fprintf(stderr, "Expose %0d,%0d %0d,%0d\n",
2661 		//ose->area.x, ose->area.y, ose->area.width, ose->area.height);
2662 
2663 		// The text is painted in ExposeText
2664 		gtk_container_propagate_expose(
2665 		    GTK_CONTAINER(PWidget(wMain)), PWidget(scrollbarh), ose);
2666 		gtk_container_propagate_expose(
2667 		    GTK_CONTAINER(PWidget(wMain)), PWidget(scrollbarv), ose);
2668 
2669 	} catch (...) {
2670 		errorStatus = SC_STATUS_FAILURE;
2671 	}
2672 	return FALSE;
2673 }
2674 
2675 #endif
2676 
ScrollSignal(GtkAdjustment * adj,ScintillaGTK * sciThis)2677 void ScintillaGTK::ScrollSignal(GtkAdjustment *adj, ScintillaGTK *sciThis) {
2678 	try {
2679 #if GTK_CHECK_VERSION(3,0,0)
2680 		sciThis->ScrollTo(static_cast<int>(gtk_adjustment_get_value(adj)), false);
2681 #else
2682 		sciThis->ScrollTo(static_cast<int>(adj->value), false);
2683 #endif
2684 	} catch (...) {
2685 		sciThis->errorStatus = SC_STATUS_FAILURE;
2686 	}
2687 }
2688 
ScrollHSignal(GtkAdjustment * adj,ScintillaGTK * sciThis)2689 void ScintillaGTK::ScrollHSignal(GtkAdjustment *adj, ScintillaGTK *sciThis) {
2690 	try {
2691 #if GTK_CHECK_VERSION(3,0,0)
2692 		sciThis->HorizontalScrollTo(static_cast<int>(gtk_adjustment_get_value(adj)));
2693 #else
2694 		sciThis->HorizontalScrollTo(static_cast<int>(adj->value));
2695 #endif
2696 	} catch (...) {
2697 		sciThis->errorStatus = SC_STATUS_FAILURE;
2698 	}
2699 }
2700 
SelectionReceived(GtkWidget * widget,GtkSelectionData * selection_data,guint)2701 void ScintillaGTK::SelectionReceived(GtkWidget *widget,
2702                                      GtkSelectionData *selection_data, guint) {
2703 	ScintillaGTK *sciThis = ScintillaFromWidget(widget);
2704 	//Platform::DebugPrintf("Selection received\n");
2705 	sciThis->ReceivedSelection(selection_data);
2706 }
2707 
SelectionGet(GtkWidget * widget,GtkSelectionData * selection_data,guint info,guint)2708 void ScintillaGTK::SelectionGet(GtkWidget *widget,
2709                                 GtkSelectionData *selection_data, guint info, guint) {
2710 	ScintillaGTK *sciThis = ScintillaFromWidget(widget);
2711 	try {
2712 		//Platform::DebugPrintf("Selection get\n");
2713 		if (SelectionOfGSD(selection_data) == GDK_SELECTION_PRIMARY) {
2714 			if (sciThis->primary.Empty()) {
2715 				sciThis->CopySelectionRange(&sciThis->primary);
2716 			}
2717 			sciThis->GetSelection(selection_data, info, &sciThis->primary);
2718 		}
2719 	} catch (...) {
2720 		sciThis->errorStatus = SC_STATUS_FAILURE;
2721 	}
2722 }
2723 
SelectionClear(GtkWidget * widget,GdkEventSelection * selection_event)2724 gint ScintillaGTK::SelectionClear(GtkWidget *widget, GdkEventSelection *selection_event) {
2725 	ScintillaGTK *sciThis = ScintillaFromWidget(widget);
2726 	//Platform::DebugPrintf("Selection clear\n");
2727 	sciThis->UnclaimSelection(selection_event);
2728 	if (GTK_WIDGET_CLASS(sciThis->parentClass)->selection_clear_event) {
2729 		return GTK_WIDGET_CLASS(sciThis->parentClass)->selection_clear_event(widget, selection_event);
2730 	}
2731 	return TRUE;
2732 }
2733 
DragMotionThis(GdkDragContext * context,gint x,gint y,guint dragtime)2734 gboolean ScintillaGTK::DragMotionThis(GdkDragContext *context,
2735                                  gint x, gint y, guint dragtime) {
2736 	try {
2737 		Point npt(x, y);
2738 		SetDragPosition(SPositionFromLocation(npt, false, false, UserVirtualSpace()));
2739 #if GTK_CHECK_VERSION(3,0,0)
2740 		GdkDragAction preferredAction = gdk_drag_context_get_suggested_action(context);
2741 		GdkDragAction actions = gdk_drag_context_get_actions(context);
2742 #else
2743 		GdkDragAction preferredAction = context->suggested_action;
2744 		GdkDragAction actions = context->actions;
2745 #endif
2746 		SelectionPosition pos = SPositionFromLocation(npt);
2747 		if ((inDragDrop == ddDragging) && (PositionInSelection(pos.Position()))) {
2748 			// Avoid dragging selection onto itself as that produces a move
2749 			// with no real effect but which creates undo actions.
2750 			preferredAction = static_cast<GdkDragAction>(0);
2751 		} else if (actions == static_cast<GdkDragAction>
2752 		        (GDK_ACTION_COPY | GDK_ACTION_MOVE)) {
2753 			preferredAction = GDK_ACTION_MOVE;
2754 		}
2755 		gdk_drag_status(context, preferredAction, dragtime);
2756 	} catch (...) {
2757 		errorStatus = SC_STATUS_FAILURE;
2758 	}
2759 	return FALSE;
2760 }
2761 
DragMotion(GtkWidget * widget,GdkDragContext * context,gint x,gint y,guint dragtime)2762 gboolean ScintillaGTK::DragMotion(GtkWidget *widget, GdkDragContext *context,
2763                                  gint x, gint y, guint dragtime) {
2764 	ScintillaGTK *sciThis = ScintillaFromWidget(widget);
2765 	return sciThis->DragMotionThis(context, x, y, dragtime);
2766 }
2767 
DragLeave(GtkWidget * widget,GdkDragContext *,guint)2768 void ScintillaGTK::DragLeave(GtkWidget *widget, GdkDragContext * /*context*/, guint) {
2769 	ScintillaGTK *sciThis = ScintillaFromWidget(widget);
2770 	try {
2771 		sciThis->SetDragPosition(SelectionPosition(invalidPosition));
2772 		//Platform::DebugPrintf("DragLeave %x\n", sciThis);
2773 	} catch (...) {
2774 		sciThis->errorStatus = SC_STATUS_FAILURE;
2775 	}
2776 }
2777 
DragEnd(GtkWidget * widget,GdkDragContext *)2778 void ScintillaGTK::DragEnd(GtkWidget *widget, GdkDragContext * /*context*/) {
2779 	ScintillaGTK *sciThis = ScintillaFromWidget(widget);
2780 	try {
2781 		// If drag did not result in drop here or elsewhere
2782 		if (!sciThis->dragWasDropped)
2783 			sciThis->SetEmptySelection(sciThis->posDrag);
2784 		sciThis->SetDragPosition(SelectionPosition(invalidPosition));
2785 		//Platform::DebugPrintf("DragEnd %x %d\n", sciThis, sciThis->dragWasDropped);
2786 		sciThis->inDragDrop = ddNone;
2787 	} catch (...) {
2788 		sciThis->errorStatus = SC_STATUS_FAILURE;
2789 	}
2790 }
2791 
Drop(GtkWidget * widget,GdkDragContext *,gint,gint,guint)2792 gboolean ScintillaGTK::Drop(GtkWidget *widget, GdkDragContext * /*context*/,
2793                             gint, gint, guint) {
2794 	ScintillaGTK *sciThis = ScintillaFromWidget(widget);
2795 	try {
2796 		//Platform::DebugPrintf("Drop %x\n", sciThis);
2797 		sciThis->SetDragPosition(SelectionPosition(invalidPosition));
2798 	} catch (...) {
2799 		sciThis->errorStatus = SC_STATUS_FAILURE;
2800 	}
2801 	return FALSE;
2802 }
2803 
DragDataReceived(GtkWidget * widget,GdkDragContext *,gint,gint,GtkSelectionData * selection_data,guint,guint)2804 void ScintillaGTK::DragDataReceived(GtkWidget *widget, GdkDragContext * /*context*/,
2805                                     gint, gint, GtkSelectionData *selection_data, guint /*info*/, guint) {
2806 	ScintillaGTK *sciThis = ScintillaFromWidget(widget);
2807 	try {
2808 		sciThis->ReceivedDrop(selection_data);
2809 		sciThis->SetDragPosition(SelectionPosition(invalidPosition));
2810 	} catch (...) {
2811 		sciThis->errorStatus = SC_STATUS_FAILURE;
2812 	}
2813 }
2814 
DragDataGet(GtkWidget * widget,GdkDragContext * context,GtkSelectionData * selection_data,guint info,guint)2815 void ScintillaGTK::DragDataGet(GtkWidget *widget, GdkDragContext *context,
2816                                GtkSelectionData *selection_data, guint info, guint) {
2817 	ScintillaGTK *sciThis = ScintillaFromWidget(widget);
2818 	try {
2819 		sciThis->dragWasDropped = true;
2820 		if (!sciThis->sel.Empty()) {
2821 			sciThis->GetSelection(selection_data, info, &sciThis->drag);
2822 		}
2823 #if GTK_CHECK_VERSION(3,0,0)
2824 		GdkDragAction action = gdk_drag_context_get_selected_action(context);
2825 #else
2826 		GdkDragAction action = context->action;
2827 #endif
2828 		if (action == GDK_ACTION_MOVE) {
2829 			for (size_t r=0; r<sciThis->sel.Count(); r++) {
2830 				if (sciThis->posDrop >= sciThis->sel.Range(r).Start()) {
2831 					if (sciThis->posDrop > sciThis->sel.Range(r).End()) {
2832 						sciThis->posDrop.Add(-sciThis->sel.Range(r).Length());
2833 					} else {
2834 						sciThis->posDrop.Add(-SelectionRange(sciThis->posDrop, sciThis->sel.Range(r).Start()).Length());
2835 					}
2836 				}
2837 			}
2838 			sciThis->ClearSelection();
2839 		}
2840 		sciThis->SetDragPosition(SelectionPosition(invalidPosition));
2841 	} catch (...) {
2842 		sciThis->errorStatus = SC_STATUS_FAILURE;
2843 	}
2844 }
2845 
TimeOut(TimeThunk * tt)2846 int ScintillaGTK::TimeOut(TimeThunk *tt) {
2847 	tt->scintilla->TickFor(tt->reason);
2848 	return 1;
2849 }
2850 
IdleCallback(ScintillaGTK * sciThis)2851 gboolean ScintillaGTK::IdleCallback(ScintillaGTK *sciThis) {
2852 	// Idler will be automatically stopped, if there is nothing
2853 	// to do while idle.
2854 #ifndef GDK_VERSION_3_6
2855 	gdk_threads_enter();
2856 #endif
2857 	bool ret = sciThis->Idle();
2858 	if (ret == false) {
2859 		// FIXME: This will remove the idler from GTK, we don't want to
2860 		// remove it as it is removed automatically when this function
2861 		// returns false (although, it should be harmless).
2862 		sciThis->SetIdle(false);
2863 	}
2864 #ifndef GDK_VERSION_3_6
2865 	gdk_threads_leave();
2866 #endif
2867 	return ret;
2868 }
2869 
StyleIdle(ScintillaGTK * sciThis)2870 gboolean ScintillaGTK::StyleIdle(ScintillaGTK *sciThis) {
2871 #ifndef GDK_VERSION_3_6
2872 	gdk_threads_enter();
2873 #endif
2874 	sciThis->IdleWork();
2875 #ifndef GDK_VERSION_3_6
2876 	gdk_threads_leave();
2877 #endif
2878 	// Idler will be automatically stopped
2879 	return FALSE;
2880 }
2881 
QueueIdleWork(WorkNeeded::workItems items,int upTo)2882 void ScintillaGTK::QueueIdleWork(WorkNeeded::workItems items, int upTo) {
2883 	Editor::QueueIdleWork(items, upTo);
2884 	if (!workNeeded.active) {
2885 		// Only allow one style needed to be queued
2886 		workNeeded.active = true;
2887 		g_idle_add_full(G_PRIORITY_HIGH_IDLE,
2888 			reinterpret_cast<GSourceFunc>(StyleIdle), this, NULL);
2889 	}
2890 }
2891 
PopUpCB(GtkMenuItem * menuItem,ScintillaGTK * sciThis)2892 void ScintillaGTK::PopUpCB(GtkMenuItem *menuItem, ScintillaGTK *sciThis) {
2893 	guint action = (sptr_t)(g_object_get_data(G_OBJECT(menuItem), "CmdNum"));
2894 	if (action) {
2895 		sciThis->Command(action);
2896 	}
2897 }
2898 
PressCT(GtkWidget * widget,GdkEventButton * event,ScintillaGTK * sciThis)2899 gboolean ScintillaGTK::PressCT(GtkWidget *widget, GdkEventButton *event, ScintillaGTK *sciThis) {
2900 	try {
2901 		if (event->window != WindowFromWidget(widget))
2902 			return FALSE;
2903 		if (event->type != GDK_BUTTON_PRESS)
2904 			return FALSE;
2905 		Point pt;
2906 		pt.x = int(event->x);
2907 		pt.y = int(event->y);
2908 		sciThis->ct.MouseClick(pt);
2909 		sciThis->CallTipClick();
2910 	} catch (...) {
2911 	}
2912 	return TRUE;
2913 }
2914 
2915 #if GTK_CHECK_VERSION(3,0,0)
2916 
DrawCT(GtkWidget * widget,cairo_t * cr,CallTip * ctip)2917 gboolean ScintillaGTK::DrawCT(GtkWidget *widget, cairo_t *cr, CallTip *ctip) {
2918 	try {
2919 		Surface *surfaceWindow = Surface::Allocate(SC_TECHNOLOGY_DEFAULT);
2920 		if (surfaceWindow) {
2921 			surfaceWindow->Init(cr, widget);
2922 			surfaceWindow->SetUnicodeMode(SC_CP_UTF8 == ctip->codePage);
2923 			surfaceWindow->SetDBCSMode(ctip->codePage);
2924 			ctip->PaintCT(surfaceWindow);
2925 			surfaceWindow->Release();
2926 			delete surfaceWindow;
2927 		}
2928 	} catch (...) {
2929 		// No pointer back to Scintilla to save status
2930 	}
2931 	return TRUE;
2932 }
2933 
2934 #else
2935 
ExposeCT(GtkWidget * widget,GdkEventExpose *,CallTip * ctip)2936 gboolean ScintillaGTK::ExposeCT(GtkWidget *widget, GdkEventExpose * /*ose*/, CallTip *ctip) {
2937 	try {
2938 		Surface *surfaceWindow = Surface::Allocate(SC_TECHNOLOGY_DEFAULT);
2939 		if (surfaceWindow) {
2940 			cairo_t *cr = gdk_cairo_create(WindowFromWidget(widget));
2941 			surfaceWindow->Init(cr, widget);
2942 			surfaceWindow->SetUnicodeMode(SC_CP_UTF8 == ctip->codePage);
2943 			surfaceWindow->SetDBCSMode(ctip->codePage);
2944 			ctip->PaintCT(surfaceWindow);
2945 			surfaceWindow->Release();
2946 			delete surfaceWindow;
2947 			cairo_destroy(cr);
2948 		}
2949 	} catch (...) {
2950 		// No pointer back to Scintilla to save status
2951 	}
2952 	return TRUE;
2953 }
2954 
2955 #endif
2956 
DirectFunction(sptr_t ptr,unsigned int iMessage,uptr_t wParam,sptr_t lParam)2957 sptr_t ScintillaGTK::DirectFunction(
2958     sptr_t ptr, unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
2959 	return reinterpret_cast<ScintillaGTK *>(ptr)->WndProc(iMessage, wParam, lParam);
2960 }
2961 
scintilla_send_message(ScintillaObject * sci,unsigned int iMessage,uptr_t wParam,sptr_t lParam)2962 sptr_t scintilla_send_message(ScintillaObject *sci, unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
2963 	ScintillaGTK *psci = reinterpret_cast<ScintillaGTK *>(sci->pscin);
2964 	return psci->WndProc(iMessage, wParam, lParam);
2965 }
2966 
2967 static void scintilla_class_init(ScintillaClass *klass);
2968 static void scintilla_init(ScintillaObject *sci);
2969 
2970 extern void Platform_Initialise();
2971 extern void Platform_Finalise();
2972 
scintilla_get_type()2973 GType scintilla_get_type() {
2974 	static GType scintilla_type = 0;
2975 	try {
2976 
2977 		if (!scintilla_type) {
2978 			scintilla_type = g_type_from_name("Scintilla");
2979 			if (!scintilla_type) {
2980 				static GTypeInfo scintilla_info = {
2981 					(guint16) sizeof (ScintillaClass),
2982 					NULL, //(GBaseInitFunc)
2983 					NULL, //(GBaseFinalizeFunc)
2984 					(GClassInitFunc) scintilla_class_init,
2985 					NULL, //(GClassFinalizeFunc)
2986 					NULL, //gconstpointer data
2987 					(guint16) sizeof (ScintillaObject),
2988 					0, //n_preallocs
2989 					(GInstanceInitFunc) scintilla_init,
2990 					NULL //(GTypeValueTable*)
2991 				};
2992 
2993 				scintilla_type = g_type_register_static(
2994 				            GTK_TYPE_CONTAINER, "Scintilla", &scintilla_info, (GTypeFlags) 0);
2995 			}
2996 		}
2997 
2998 	} catch (...) {
2999 	}
3000 	return scintilla_type;
3001 }
3002 
ClassInit(OBJECT_CLASS * object_class,GtkWidgetClass * widget_class,GtkContainerClass * container_class)3003 void ScintillaGTK::ClassInit(OBJECT_CLASS* object_class, GtkWidgetClass *widget_class, GtkContainerClass *container_class) {
3004 	Platform_Initialise();
3005 #ifdef SCI_LEXER
3006 	Scintilla_LinkLexers();
3007 #endif
3008 	atomClipboard = gdk_atom_intern("CLIPBOARD", FALSE);
3009 	atomUTF8 = gdk_atom_intern("UTF8_STRING", FALSE);
3010 	atomString = GDK_SELECTION_TYPE_STRING;
3011 	atomUriList = gdk_atom_intern("text/uri-list", FALSE);
3012 	atomDROPFILES_DND = gdk_atom_intern("DROPFILES_DND", FALSE);
3013 
3014 	// Define default signal handlers for the class:  Could move more
3015 	// of the signal handlers here (those that currently attached to wDraw
3016 	// in Initialise() may require coordinate translation?)
3017 
3018 	object_class->finalize = Destroy;
3019 #if GTK_CHECK_VERSION(3,0,0)
3020 	widget_class->get_preferred_width = GetPreferredWidth;
3021 	widget_class->get_preferred_height = GetPreferredHeight;
3022 #else
3023 	widget_class->size_request = SizeRequest;
3024 #endif
3025 	widget_class->size_allocate = SizeAllocate;
3026 #if GTK_CHECK_VERSION(3,0,0)
3027 	widget_class->draw = DrawMain;
3028 #else
3029 	widget_class->expose_event = ExposeMain;
3030 #endif
3031 	widget_class->motion_notify_event = Motion;
3032 	widget_class->button_press_event = Press;
3033 	widget_class->button_release_event = MouseRelease;
3034 	widget_class->scroll_event = ScrollEvent;
3035 	widget_class->key_press_event = KeyPress;
3036 	widget_class->key_release_event = KeyRelease;
3037 	widget_class->focus_in_event = FocusIn;
3038 	widget_class->focus_out_event = FocusOut;
3039 	widget_class->selection_received = SelectionReceived;
3040 	widget_class->selection_get = SelectionGet;
3041 	widget_class->selection_clear_event = SelectionClear;
3042 
3043 	widget_class->drag_data_received = DragDataReceived;
3044 	widget_class->drag_motion = DragMotion;
3045 	widget_class->drag_leave = DragLeave;
3046 	widget_class->drag_end = DragEnd;
3047 	widget_class->drag_drop = Drop;
3048 	widget_class->drag_data_get = DragDataGet;
3049 
3050 	widget_class->realize = Realize;
3051 	widget_class->unrealize = UnRealize;
3052 	widget_class->map = Map;
3053 	widget_class->unmap = UnMap;
3054 
3055 	container_class->forall = MainForAll;
3056 }
3057 
3058 #define SIG_MARSHAL scintilla_marshal_NONE__INT_POINTER
3059 #define MARSHAL_ARGUMENTS G_TYPE_INT, G_TYPE_POINTER
3060 
scintilla_class_init(ScintillaClass * klass)3061 static void scintilla_class_init(ScintillaClass *klass) {
3062 	try {
3063 		OBJECT_CLASS *object_class = (OBJECT_CLASS*) klass;
3064 		GtkWidgetClass *widget_class = (GtkWidgetClass*) klass;
3065 		GtkContainerClass *container_class = (GtkContainerClass*) klass;
3066 
3067 		GSignalFlags sigflags = GSignalFlags(G_SIGNAL_ACTION | G_SIGNAL_RUN_LAST);
3068 		scintilla_signals[COMMAND_SIGNAL] = g_signal_new(
3069 		            "command",
3070 		            G_TYPE_FROM_CLASS(object_class),
3071 		            sigflags,
3072 		            G_STRUCT_OFFSET(ScintillaClass, command),
3073 		            NULL, //(GSignalAccumulator)
3074 		            NULL, //(gpointer)
3075 		            SIG_MARSHAL,
3076 		            G_TYPE_NONE,
3077 		            2, MARSHAL_ARGUMENTS);
3078 
3079 		scintilla_signals[NOTIFY_SIGNAL] = g_signal_new(
3080 		            SCINTILLA_NOTIFY,
3081 		            G_TYPE_FROM_CLASS(object_class),
3082 		            sigflags,
3083 		            G_STRUCT_OFFSET(ScintillaClass, notify),
3084 		            NULL,
3085 		            NULL,
3086 		            SIG_MARSHAL,
3087 		            G_TYPE_NONE,
3088 		            2, MARSHAL_ARGUMENTS);
3089 
3090 		klass->command = NULL;
3091 		klass->notify = NULL;
3092 		scintilla_class_parent_class = G_OBJECT_CLASS(g_type_class_peek_parent(klass));
3093 		ScintillaGTK::ClassInit(object_class, widget_class, container_class);
3094 	} catch (...) {
3095 	}
3096 }
3097 
scintilla_init(ScintillaObject * sci)3098 static void scintilla_init(ScintillaObject *sci) {
3099 	try {
3100 #if GTK_CHECK_VERSION(2,20,0)
3101 		gtk_widget_set_can_focus(GTK_WIDGET(sci), TRUE);
3102 #else
3103 		GTK_WIDGET_SET_FLAGS(sci, GTK_CAN_FOCUS);
3104 #endif
3105 		sci->pscin = new ScintillaGTK(sci);
3106 	} catch (...) {
3107 	}
3108 }
3109 
scintilla_new()3110 GtkWidget* scintilla_new() {
3111 	GtkWidget *widget = GTK_WIDGET(g_object_new(scintilla_get_type(), NULL));
3112 	gtk_widget_set_direction(widget, GTK_TEXT_DIR_LTR);
3113 
3114 	return widget;
3115 }
3116 
scintilla_set_id(ScintillaObject * sci,uptr_t id)3117 void scintilla_set_id(ScintillaObject *sci, uptr_t id) {
3118 	ScintillaGTK *psci = reinterpret_cast<ScintillaGTK *>(sci->pscin);
3119 	psci->ctrlID = id;
3120 }
3121 
scintilla_release_resources(void)3122 void scintilla_release_resources(void) {
3123 	try {
3124 		Platform_Finalise();
3125 	} catch (...) {
3126 	}
3127 }
3128