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