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