Perform typename re-colourisation only when typename list changes
[geany-mirror.git] / scintilla / gtk / ScintillaGTK.cxx
blob728e6743bed6c976eb237a13e5dd9f24e2da650e
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 <stdexcept>
14 #include <new>
15 #include <string>
16 #include <vector>
17 #include <map>
18 #include <algorithm>
20 #include <glib.h>
21 #include <gmodule.h>
22 #include <gdk/gdk.h>
23 #include <gtk/gtk.h>
24 #include <gdk/gdkkeysyms.h>
26 #if defined(__WIN32__) || defined(_MSC_VER)
27 #include <windows.h>
28 #endif
30 #include "Platform.h"
32 #include "ILexer.h"
33 #include "Scintilla.h"
34 #include "ScintillaWidget.h"
35 #ifdef SCI_LEXER
36 #include "SciLexer.h"
37 #endif
38 #include "StringCopy.h"
39 #ifdef SCI_LEXER
40 #include "LexerModule.h"
41 #endif
42 #include "Position.h"
43 #include "SplitVector.h"
44 #include "Partitioning.h"
45 #include "RunStyles.h"
46 #include "ContractionState.h"
47 #include "CellBuffer.h"
48 #include "CallTip.h"
49 #include "KeyMap.h"
50 #include "Indicator.h"
51 #include "XPM.h"
52 #include "LineMarker.h"
53 #include "Style.h"
54 #include "ViewStyle.h"
55 #include "CharClassify.h"
56 #include "Decoration.h"
57 #include "CaseFolder.h"
58 #include "Document.h"
59 #include "CaseConvert.h"
60 #include "UniConversion.h"
61 #include "UnicodeFromUTF8.h"
62 #include "Selection.h"
63 #include "PositionCache.h"
64 #include "EditModel.h"
65 #include "MarginView.h"
66 #include "EditView.h"
67 #include "Editor.h"
68 #include "AutoComplete.h"
69 #include "ScintillaBase.h"
71 #ifdef SCI_LEXER
72 #include "ExternalLexer.h"
73 #endif
75 #include "scintilla-marshal.h"
77 #include "Converter.h"
79 #if defined(__clang__)
80 // Clang 3.0 incorrectly displays sentinel warnings. Fixed by clang 3.1.
81 #pragma GCC diagnostic ignored "-Wsentinel"
82 #endif
84 #if GTK_CHECK_VERSION(2,20,0)
85 #define IS_WIDGET_REALIZED(w) (gtk_widget_get_realized(GTK_WIDGET(w)))
86 #define IS_WIDGET_MAPPED(w) (gtk_widget_get_mapped(GTK_WIDGET(w)))
87 #else
88 #define IS_WIDGET_REALIZED(w) (GTK_WIDGET_REALIZED(w))
89 #define IS_WIDGET_MAPPED(w) (GTK_WIDGET_MAPPED(w))
90 #endif
92 #define SC_INDICATOR_INPUT INDIC_IME
93 #define SC_INDICATOR_TARGET INDIC_IME+1
94 #define SC_INDICATOR_CONVERTED INDIC_IME+2
95 #define SC_INDICATOR_UNKNOWN INDIC_IME_MAX
97 static GdkWindow *WindowFromWidget(GtkWidget *w) {
98 return gtk_widget_get_window(w);
101 #ifdef _MSC_VER
102 // Constant conditional expressions are because of GTK+ headers
103 #pragma warning(disable: 4127)
104 // Ignore unreferenced local functions in GTK+ headers
105 #pragma warning(disable: 4505)
106 #endif
108 #define OBJECT_CLASS GObjectClass
110 #ifdef SCI_NAMESPACE
111 using namespace Scintilla;
112 #endif
114 static GdkWindow *PWindow(const Window &w) {
115 GtkWidget *widget = static_cast<GtkWidget *>(w.GetID());
116 return gtk_widget_get_window(widget);
119 extern std::string UTF8FromLatin1(const char *s, int len);
121 class ScintillaGTK : public ScintillaBase {
122 _ScintillaObject *sci;
123 Window wText;
124 Window scrollbarv;
125 Window scrollbarh;
126 GtkAdjustment *adjustmentv;
127 GtkAdjustment *adjustmenth;
128 int verticalScrollBarWidth;
129 int horizontalScrollBarHeight;
131 SelectionText primary;
133 GdkEventButton *evbtn;
134 bool capturedMouse;
135 bool dragWasDropped;
136 int lastKey;
137 int rectangularSelectionModifier;
139 GtkWidgetClass *parentClass;
141 static GdkAtom atomClipboard;
142 static GdkAtom atomUTF8;
143 static GdkAtom atomString;
144 static GdkAtom atomUriList;
145 static GdkAtom atomDROPFILES_DND;
146 GdkAtom atomSought;
148 #if PLAT_GTK_WIN32
149 CLIPFORMAT cfColumnSelect;
150 #endif
152 Window wPreedit;
153 Window wPreeditDraw;
154 GtkIMContext *im_context;
155 PangoScript lastNonCommonScript;
157 // Wheel mouse support
158 unsigned int linesPerScroll;
159 GTimeVal lastWheelMouseTime;
160 gint lastWheelMouseDirection;
161 gint wheelMouseIntensity;
163 #if GTK_CHECK_VERSION(3,0,0)
164 cairo_rectangle_list_t *rgnUpdate;
165 #else
166 GdkRegion *rgnUpdate;
167 #endif
168 bool repaintFullWindow;
170 // Private so ScintillaGTK objects can not be copied
171 ScintillaGTK(const ScintillaGTK &);
172 ScintillaGTK &operator=(const ScintillaGTK &);
174 public:
175 explicit ScintillaGTK(_ScintillaObject *sci_);
176 virtual ~ScintillaGTK();
177 static void ClassInit(OBJECT_CLASS* object_class, GtkWidgetClass *widget_class, GtkContainerClass *container_class);
178 private:
179 virtual void Initialise();
180 virtual void Finalise();
181 virtual bool AbandonPaint();
182 virtual void DisplayCursor(Window::Cursor c);
183 virtual bool DragThreshold(Point ptStart, Point ptNow);
184 virtual void StartDrag();
185 int TargetAsUTF8(char *text);
186 int EncodedFromUTF8(char *utf8, char *encoded) const;
187 virtual bool ValidCodePage(int codePage) const;
188 public: // Public for scintilla_send_message
189 virtual sptr_t WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam);
190 private:
191 virtual sptr_t DefWndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam);
192 struct TimeThunk {
193 TickReason reason;
194 ScintillaGTK *scintilla;
195 guint timer;
196 TimeThunk() : reason(tickCaret), scintilla(NULL), timer(0) {}
198 TimeThunk timers[tickDwell+1];
199 virtual bool FineTickerAvailable();
200 virtual bool FineTickerRunning(TickReason reason);
201 virtual void FineTickerStart(TickReason reason, int millis, int tolerance);
202 virtual void FineTickerCancel(TickReason reason);
203 virtual bool SetIdle(bool on);
204 virtual void SetMouseCapture(bool on);
205 virtual bool HaveMouseCapture();
206 virtual bool PaintContains(PRectangle rc);
207 void FullPaint();
208 virtual PRectangle GetClientRectangle() const;
209 virtual void ScrollText(int linesToMove);
210 virtual void SetVerticalScrollPos();
211 virtual void SetHorizontalScrollPos();
212 virtual bool ModifyScrollBars(int nMax, int nPage);
213 void ReconfigureScrollBars();
214 virtual void NotifyChange();
215 virtual void NotifyFocus(bool focus);
216 virtual void NotifyParent(SCNotification scn);
217 void NotifyKey(int key, int modifiers);
218 void NotifyURIDropped(const char *list);
219 const char *CharacterSetID() const;
220 virtual CaseFolder *CaseFolderForEncoding();
221 virtual std::string CaseMapString(const std::string &s, int caseMapping);
222 virtual int KeyDefault(int key, int modifiers);
223 virtual void CopyToClipboard(const SelectionText &selectedText);
224 virtual void Copy();
225 virtual void Paste();
226 virtual void CreateCallTipWindow(PRectangle rc);
227 virtual void AddToPopUp(const char *label, int cmd = 0, bool enabled = true);
228 bool OwnPrimarySelection();
229 virtual void ClaimSelection();
230 void GetGtkSelectionText(GtkSelectionData *selectionData, SelectionText &selText);
231 void ReceivedSelection(GtkSelectionData *selection_data);
232 void ReceivedDrop(GtkSelectionData *selection_data);
233 static void GetSelection(GtkSelectionData *selection_data, guint info, SelectionText *selected);
234 void StoreOnClipboard(SelectionText *clipText);
235 static void ClipboardGetSelection(GtkClipboard* clip, GtkSelectionData *selection_data, guint info, void *data);
236 static void ClipboardClearSelection(GtkClipboard* clip, void *data);
238 void UnclaimSelection(GdkEventSelection *selection_event);
239 void Resize(int width, int height);
241 // Callback functions
242 void RealizeThis(GtkWidget *widget);
243 static void Realize(GtkWidget *widget);
244 void UnRealizeThis(GtkWidget *widget);
245 static void UnRealize(GtkWidget *widget);
246 void MapThis();
247 static void Map(GtkWidget *widget);
248 void UnMapThis();
249 static void UnMap(GtkWidget *widget);
250 gint FocusInThis(GtkWidget *widget);
251 static gint FocusIn(GtkWidget *widget, GdkEventFocus *event);
252 gint FocusOutThis(GtkWidget *widget);
253 static gint FocusOut(GtkWidget *widget, GdkEventFocus *event);
254 static void SizeRequest(GtkWidget *widget, GtkRequisition *requisition);
255 #if GTK_CHECK_VERSION(3,0,0)
256 static void GetPreferredWidth(GtkWidget *widget, gint *minimalWidth, gint *naturalWidth);
257 static void GetPreferredHeight(GtkWidget *widget, gint *minimalHeight, gint *naturalHeight);
258 #endif
259 static void SizeAllocate(GtkWidget *widget, GtkAllocation *allocation);
260 #if GTK_CHECK_VERSION(3,0,0)
261 gboolean DrawTextThis(cairo_t *cr);
262 static gboolean DrawText(GtkWidget *widget, cairo_t *cr, ScintillaGTK *sciThis);
263 gboolean DrawThis(cairo_t *cr);
264 static gboolean DrawMain(GtkWidget *widget, cairo_t *cr);
265 #else
266 gboolean ExposeTextThis(GtkWidget *widget, GdkEventExpose *ose);
267 static gboolean ExposeText(GtkWidget *widget, GdkEventExpose *ose, ScintillaGTK *sciThis);
268 gboolean Expose(GtkWidget *widget, GdkEventExpose *ose);
269 static gboolean ExposeMain(GtkWidget *widget, GdkEventExpose *ose);
270 #endif
271 void ForAll(GtkCallback callback, gpointer callback_data);
272 static void MainForAll(GtkContainer *container, gboolean include_internals, GtkCallback callback, gpointer callback_data);
274 static void ScrollSignal(GtkAdjustment *adj, ScintillaGTK *sciThis);
275 static void ScrollHSignal(GtkAdjustment *adj, ScintillaGTK *sciThis);
276 gint PressThis(GdkEventButton *event);
277 static gint Press(GtkWidget *widget, GdkEventButton *event);
278 static gint MouseRelease(GtkWidget *widget, GdkEventButton *event);
279 static gint ScrollEvent(GtkWidget *widget, GdkEventScroll *event);
280 static gint Motion(GtkWidget *widget, GdkEventMotion *event);
281 gboolean KeyThis(GdkEventKey *event);
282 static gboolean KeyPress(GtkWidget *widget, GdkEventKey *event);
283 static gboolean KeyRelease(GtkWidget *widget, GdkEventKey *event);
284 #if GTK_CHECK_VERSION(3,0,0)
285 gboolean DrawPreeditThis(GtkWidget *widget, cairo_t *cr);
286 static gboolean DrawPreedit(GtkWidget *widget, cairo_t *cr, ScintillaGTK *sciThis);
287 #else
288 gboolean ExposePreeditThis(GtkWidget *widget, GdkEventExpose *ose);
289 static gboolean ExposePreedit(GtkWidget *widget, GdkEventExpose *ose, ScintillaGTK *sciThis);
290 #endif
292 bool KoreanIME();
293 void CommitThis(char *str);
294 static void Commit(GtkIMContext *context, char *str, ScintillaGTK *sciThis);
295 void PreeditChangedInlineThis();
296 void PreeditChangedWindowedThis();
297 static void PreeditChanged(GtkIMContext *context, ScintillaGTK *sciThis);
298 void MoveImeCarets(int pos);
299 void DrawImeIndicator(int indicator, int len);
300 void SetCandidateWindowPos();
302 static void StyleSetText(GtkWidget *widget, GtkStyle *previous, void*);
303 static void RealizeText(GtkWidget *widget, void*);
304 static void Dispose(GObject *object);
305 static void Destroy(GObject *object);
306 static void SelectionReceived(GtkWidget *widget, GtkSelectionData *selection_data,
307 guint time);
308 static void ClipboardReceived(GtkClipboard *clipboard, GtkSelectionData *selection_data,
309 gpointer data);
310 static void SelectionGet(GtkWidget *widget, GtkSelectionData *selection_data,
311 guint info, guint time);
312 static gint SelectionClear(GtkWidget *widget, GdkEventSelection *selection_event);
313 gboolean DragMotionThis(GdkDragContext *context, gint x, gint y, guint dragtime);
314 static gboolean DragMotion(GtkWidget *widget, GdkDragContext *context,
315 gint x, gint y, guint dragtime);
316 static void DragLeave(GtkWidget *widget, GdkDragContext *context,
317 guint time);
318 static void DragEnd(GtkWidget *widget, GdkDragContext *context);
319 static gboolean Drop(GtkWidget *widget, GdkDragContext *context,
320 gint x, gint y, guint time);
321 static void DragDataReceived(GtkWidget *widget, GdkDragContext *context,
322 gint x, gint y, GtkSelectionData *selection_data, guint info, guint time);
323 static void DragDataGet(GtkWidget *widget, GdkDragContext *context,
324 GtkSelectionData *selection_data, guint info, guint time);
325 static gboolean TimeOut(gpointer ptt);
326 static gboolean IdleCallback(gpointer pSci);
327 static gboolean StyleIdle(gpointer pSci);
328 virtual void QueueIdleWork(WorkNeeded::workItems items, int upTo);
329 static void PopUpCB(GtkMenuItem *menuItem, ScintillaGTK *sciThis);
331 #if GTK_CHECK_VERSION(3,0,0)
332 static gboolean DrawCT(GtkWidget *widget, cairo_t *cr, CallTip *ctip);
333 #else
334 static gboolean ExposeCT(GtkWidget *widget, GdkEventExpose *ose, CallTip *ct);
335 #endif
336 static gboolean PressCT(GtkWidget *widget, GdkEventButton *event, ScintillaGTK *sciThis);
338 static sptr_t DirectFunction(sptr_t ptr,
339 unsigned int iMessage, uptr_t wParam, sptr_t lParam);
342 enum {
343 COMMAND_SIGNAL,
344 NOTIFY_SIGNAL,
345 LAST_SIGNAL
348 static gint scintilla_signals[LAST_SIGNAL] = { 0 };
350 enum {
351 TARGET_STRING,
352 TARGET_TEXT,
353 TARGET_COMPOUND_TEXT,
354 TARGET_UTF8_STRING,
355 TARGET_URI
358 GdkAtom ScintillaGTK::atomClipboard = 0;
359 GdkAtom ScintillaGTK::atomUTF8 = 0;
360 GdkAtom ScintillaGTK::atomString = 0;
361 GdkAtom ScintillaGTK::atomUriList = 0;
362 GdkAtom ScintillaGTK::atomDROPFILES_DND = 0;
364 static const GtkTargetEntry clipboardCopyTargets[] = {
365 { (gchar *) "UTF8_STRING", 0, TARGET_UTF8_STRING },
366 { (gchar *) "STRING", 0, TARGET_STRING },
368 static const gint nClipboardCopyTargets = ELEMENTS(clipboardCopyTargets);
370 static const GtkTargetEntry clipboardPasteTargets[] = {
371 { (gchar *) "text/uri-list", 0, TARGET_URI },
372 { (gchar *) "UTF8_STRING", 0, TARGET_UTF8_STRING },
373 { (gchar *) "STRING", 0, TARGET_STRING },
375 static const gint nClipboardPasteTargets = ELEMENTS(clipboardPasteTargets);
377 static GtkWidget *PWidget(Window &w) {
378 return static_cast<GtkWidget *>(w.GetID());
381 static ScintillaGTK *ScintillaFromWidget(GtkWidget *widget) {
382 ScintillaObject *scio = SCINTILLA(widget);
383 return static_cast<ScintillaGTK *>(scio->pscin);
386 ScintillaGTK::ScintillaGTK(_ScintillaObject *sci_) :
387 adjustmentv(0), adjustmenth(0),
388 verticalScrollBarWidth(30), horizontalScrollBarHeight(30),
389 evbtn(0), capturedMouse(false), dragWasDropped(false),
390 lastKey(0), rectangularSelectionModifier(SCMOD_CTRL), parentClass(0),
391 im_context(NULL), lastNonCommonScript(PANGO_SCRIPT_INVALID_CODE),
392 lastWheelMouseDirection(0),
393 wheelMouseIntensity(0),
394 rgnUpdate(0),
395 repaintFullWindow(false) {
396 sci = sci_;
397 wMain = GTK_WIDGET(sci);
399 #if PLAT_GTK_WIN32
400 rectangularSelectionModifier = SCMOD_ALT;
401 #else
402 rectangularSelectionModifier = SCMOD_CTRL;
403 #endif
405 #if PLAT_GTK_WIN32
406 // There does not seem to be a real standard for indicating that the clipboard
407 // contains a rectangular selection, so copy Developer Studio.
408 cfColumnSelect = static_cast<CLIPFORMAT>(
409 ::RegisterClipboardFormat("MSDEVColumnSelect"));
411 // Get intellimouse parameters when running on win32; otherwise use
412 // reasonable default
413 #ifndef SPI_GETWHEELSCROLLLINES
414 #define SPI_GETWHEELSCROLLLINES 104
415 #endif
416 ::SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &linesPerScroll, 0);
417 #else
418 linesPerScroll = 4;
419 #endif
420 lastWheelMouseTime.tv_sec = 0;
421 lastWheelMouseTime.tv_usec = 0;
423 Initialise();
426 ScintillaGTK::~ScintillaGTK() {
427 g_source_remove_by_user_data(this);
428 if (evbtn) {
429 gdk_event_free(reinterpret_cast<GdkEvent *>(evbtn));
430 evbtn = 0;
432 wPreedit.Destroy();
435 static void UnRefCursor(GdkCursor *cursor) {
436 #if GTK_CHECK_VERSION(3,0,0)
437 g_object_unref(cursor);
438 #else
439 gdk_cursor_unref(cursor);
440 #endif
443 void ScintillaGTK::RealizeThis(GtkWidget *widget) {
444 //Platform::DebugPrintf("ScintillaGTK::realize this\n");
445 #if GTK_CHECK_VERSION(2,20,0)
446 gtk_widget_set_realized(widget, TRUE);
447 #else
448 GTK_WIDGET_SET_FLAGS(widget, GTK_REALIZED);
449 #endif
450 GdkWindowAttr attrs;
451 attrs.window_type = GDK_WINDOW_CHILD;
452 GtkAllocation allocation;
453 gtk_widget_get_allocation(widget, &allocation);
454 attrs.x = allocation.x;
455 attrs.y = allocation.y;
456 attrs.width = allocation.width;
457 attrs.height = allocation.height;
458 attrs.wclass = GDK_INPUT_OUTPUT;
459 attrs.visual = gtk_widget_get_visual(widget);
460 #if !GTK_CHECK_VERSION(3,0,0)
461 attrs.colormap = gtk_widget_get_colormap(widget);
462 #endif
463 attrs.event_mask = gtk_widget_get_events(widget) | GDK_EXPOSURE_MASK;
464 GdkDisplay *pdisplay = gtk_widget_get_display(widget);
465 GdkCursor *cursor = gdk_cursor_new_for_display(pdisplay, GDK_XTERM);
466 attrs.cursor = cursor;
467 #if GTK_CHECK_VERSION(3,0,0)
468 gtk_widget_set_window(widget, gdk_window_new(gtk_widget_get_parent_window(widget), &attrs,
469 GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_CURSOR));
470 #if GTK_CHECK_VERSION(3,8,0)
471 gtk_widget_register_window(widget, gtk_widget_get_window(widget));
472 #else
473 gdk_window_set_user_data(gtk_widget_get_window(widget), widget);
474 #endif
475 #if !GTK_CHECK_VERSION(3,18,0)
476 gtk_style_context_set_background(gtk_widget_get_style_context(widget),
477 gtk_widget_get_window(widget));
478 #endif
479 gdk_window_show(gtk_widget_get_window(widget));
480 UnRefCursor(cursor);
481 #else
482 widget->window = gdk_window_new(gtk_widget_get_parent_window(widget), &attrs,
483 GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP | GDK_WA_CURSOR);
484 gdk_window_set_user_data(widget->window, widget);
485 widget->style = gtk_style_attach(widget->style, widget->window);
486 gdk_window_set_background(widget->window, &widget->style->bg[GTK_STATE_NORMAL]);
487 gdk_window_show(widget->window);
488 UnRefCursor(cursor);
489 #endif
490 gtk_widget_realize(PWidget(wPreedit));
491 gtk_widget_realize(PWidget(wPreeditDraw));
493 im_context = gtk_im_multicontext_new();
494 g_signal_connect(G_OBJECT(im_context), "commit",
495 G_CALLBACK(Commit), this);
496 g_signal_connect(G_OBJECT(im_context), "preedit_changed",
497 G_CALLBACK(PreeditChanged), this);
498 gtk_im_context_set_client_window(im_context, WindowFromWidget(widget));
499 GtkWidget *widtxt = PWidget(wText); // // No code inside the G_OBJECT macro
500 g_signal_connect_after(G_OBJECT(widtxt), "style_set",
501 G_CALLBACK(ScintillaGTK::StyleSetText), NULL);
502 g_signal_connect_after(G_OBJECT(widtxt), "realize",
503 G_CALLBACK(ScintillaGTK::RealizeText), NULL);
504 gtk_widget_realize(widtxt);
505 gtk_widget_realize(PWidget(scrollbarv));
506 gtk_widget_realize(PWidget(scrollbarh));
508 cursor = gdk_cursor_new_for_display(pdisplay, GDK_XTERM);
509 gdk_window_set_cursor(PWindow(wText), cursor);
510 UnRefCursor(cursor);
512 cursor = gdk_cursor_new_for_display(pdisplay, GDK_LEFT_PTR);
513 gdk_window_set_cursor(PWindow(scrollbarv), cursor);
514 UnRefCursor(cursor);
516 cursor = gdk_cursor_new_for_display(pdisplay, GDK_LEFT_PTR);
517 gdk_window_set_cursor(PWindow(scrollbarh), cursor);
518 UnRefCursor(cursor);
520 gtk_selection_add_targets(widget, GDK_SELECTION_PRIMARY,
521 clipboardCopyTargets, nClipboardCopyTargets);
524 void ScintillaGTK::Realize(GtkWidget *widget) {
525 ScintillaGTK *sciThis = ScintillaFromWidget(widget);
526 sciThis->RealizeThis(widget);
529 void ScintillaGTK::UnRealizeThis(GtkWidget *widget) {
530 try {
531 gtk_selection_clear_targets(widget, GDK_SELECTION_PRIMARY);
533 if (IS_WIDGET_MAPPED(widget)) {
534 gtk_widget_unmap(widget);
536 #if GTK_CHECK_VERSION(2,20,0)
537 gtk_widget_set_realized(widget, FALSE);
538 #else
539 GTK_WIDGET_UNSET_FLAGS(widget, GTK_REALIZED);
540 #endif
541 gtk_widget_unrealize(PWidget(wText));
542 gtk_widget_unrealize(PWidget(scrollbarv));
543 gtk_widget_unrealize(PWidget(scrollbarh));
544 gtk_widget_unrealize(PWidget(wPreedit));
545 gtk_widget_unrealize(PWidget(wPreeditDraw));
546 g_object_unref(im_context);
547 im_context = NULL;
548 if (GTK_WIDGET_CLASS(parentClass)->unrealize)
549 GTK_WIDGET_CLASS(parentClass)->unrealize(widget);
551 Finalise();
552 } catch (...) {
553 errorStatus = SC_STATUS_FAILURE;
557 void ScintillaGTK::UnRealize(GtkWidget *widget) {
558 ScintillaGTK *sciThis = ScintillaFromWidget(widget);
559 sciThis->UnRealizeThis(widget);
562 static void MapWidget(GtkWidget *widget) {
563 if (widget &&
564 gtk_widget_get_visible(GTK_WIDGET(widget)) &&
565 !IS_WIDGET_MAPPED(widget)) {
566 gtk_widget_map(widget);
570 void ScintillaGTK::MapThis() {
571 try {
572 //Platform::DebugPrintf("ScintillaGTK::map this\n");
573 #if GTK_CHECK_VERSION(2,20,0)
574 gtk_widget_set_mapped(PWidget(wMain), TRUE);
575 #else
576 GTK_WIDGET_SET_FLAGS(PWidget(wMain), GTK_MAPPED);
577 #endif
578 MapWidget(PWidget(wText));
579 MapWidget(PWidget(scrollbarh));
580 MapWidget(PWidget(scrollbarv));
581 wMain.SetCursor(Window::cursorArrow);
582 scrollbarv.SetCursor(Window::cursorArrow);
583 scrollbarh.SetCursor(Window::cursorArrow);
584 ChangeSize();
585 gdk_window_show(PWindow(wMain));
586 } catch (...) {
587 errorStatus = SC_STATUS_FAILURE;
591 void ScintillaGTK::Map(GtkWidget *widget) {
592 ScintillaGTK *sciThis = ScintillaFromWidget(widget);
593 sciThis->MapThis();
596 void ScintillaGTK::UnMapThis() {
597 try {
598 //Platform::DebugPrintf("ScintillaGTK::unmap this\n");
599 #if GTK_CHECK_VERSION(2,20,0)
600 gtk_widget_set_mapped(PWidget(wMain), FALSE);
601 #else
602 GTK_WIDGET_UNSET_FLAGS(PWidget(wMain), GTK_MAPPED);
603 #endif
604 DropGraphics(false);
605 gdk_window_hide(PWindow(wMain));
606 gtk_widget_unmap(PWidget(wText));
607 gtk_widget_unmap(PWidget(scrollbarh));
608 gtk_widget_unmap(PWidget(scrollbarv));
609 } catch (...) {
610 errorStatus = SC_STATUS_FAILURE;
614 void ScintillaGTK::UnMap(GtkWidget *widget) {
615 ScintillaGTK *sciThis = ScintillaFromWidget(widget);
616 sciThis->UnMapThis();
619 void ScintillaGTK::ForAll(GtkCallback callback, gpointer callback_data) {
620 try {
621 (*callback) (PWidget(wText), callback_data);
622 if (PWidget(scrollbarv))
623 (*callback) (PWidget(scrollbarv), callback_data);
624 if (PWidget(scrollbarh))
625 (*callback) (PWidget(scrollbarh), callback_data);
626 } catch (...) {
627 errorStatus = SC_STATUS_FAILURE;
631 void ScintillaGTK::MainForAll(GtkContainer *container, gboolean include_internals, GtkCallback callback, gpointer callback_data) {
632 ScintillaGTK *sciThis = ScintillaFromWidget((GtkWidget *)container);
634 if (callback != NULL && include_internals) {
635 sciThis->ForAll(callback, callback_data);
639 namespace {
641 class PreEditString {
642 public:
643 gchar *str;
644 gint cursor_pos;
645 PangoAttrList *attrs;
646 gboolean validUTF8;
647 glong uniStrLen;
648 gunichar *uniStr;
649 PangoScript pscript;
651 explicit PreEditString(GtkIMContext *im_context) {
652 gtk_im_context_get_preedit_string(im_context, &str, &attrs, &cursor_pos);
653 validUTF8 = g_utf8_validate(str, strlen(str), NULL);
654 uniStr = g_utf8_to_ucs4_fast(str, strlen(str), &uniStrLen);
655 pscript = pango_script_for_unichar(uniStr[0]);
657 ~PreEditString() {
658 g_free(str);
659 g_free(uniStr);
660 pango_attr_list_unref(attrs);
666 gint ScintillaGTK::FocusInThis(GtkWidget *widget) {
667 try {
668 SetFocusState(true);
669 if (im_context != NULL) {
670 PreEditString pes(im_context);
671 if (PWidget(wPreedit) != NULL) {
672 if (strlen(pes.str) > 0) {
673 gtk_widget_show(PWidget(wPreedit));
674 } else {
675 gtk_widget_hide(PWidget(wPreedit));
678 gtk_im_context_focus_in(im_context);
681 } catch (...) {
682 errorStatus = SC_STATUS_FAILURE;
684 return FALSE;
687 gint ScintillaGTK::FocusIn(GtkWidget *widget, GdkEventFocus * /*event*/) {
688 ScintillaGTK *sciThis = ScintillaFromWidget(widget);
689 return sciThis->FocusInThis(widget);
692 gint ScintillaGTK::FocusOutThis(GtkWidget *widget) {
693 try {
694 SetFocusState(false);
696 if (PWidget(wPreedit) != NULL)
697 gtk_widget_hide(PWidget(wPreedit));
698 if (im_context != NULL)
699 gtk_im_context_focus_out(im_context);
701 } catch (...) {
702 errorStatus = SC_STATUS_FAILURE;
704 return FALSE;
707 gint ScintillaGTK::FocusOut(GtkWidget *widget, GdkEventFocus * /*event*/) {
708 ScintillaGTK *sciThis = ScintillaFromWidget(widget);
709 return sciThis->FocusOutThis(widget);
712 void ScintillaGTK::SizeRequest(GtkWidget *widget, GtkRequisition *requisition) {
713 ScintillaGTK *sciThis = ScintillaFromWidget(widget);
714 requisition->width = 1;
715 requisition->height = 1;
716 GtkRequisition child_requisition;
717 #if GTK_CHECK_VERSION(3,0,0)
718 gtk_widget_get_preferred_size(PWidget(sciThis->scrollbarh), NULL, &child_requisition);
719 gtk_widget_get_preferred_size(PWidget(sciThis->scrollbarv), NULL, &child_requisition);
720 #else
721 gtk_widget_size_request(PWidget(sciThis->scrollbarh), &child_requisition);
722 gtk_widget_size_request(PWidget(sciThis->scrollbarv), &child_requisition);
723 #endif
726 #if GTK_CHECK_VERSION(3,0,0)
728 void ScintillaGTK::GetPreferredWidth(GtkWidget *widget, gint *minimalWidth, gint *naturalWidth) {
729 GtkRequisition requisition;
730 SizeRequest(widget, &requisition);
731 *minimalWidth = *naturalWidth = requisition.width;
734 void ScintillaGTK::GetPreferredHeight(GtkWidget *widget, gint *minimalHeight, gint *naturalHeight) {
735 GtkRequisition requisition;
736 SizeRequest(widget, &requisition);
737 *minimalHeight = *naturalHeight = requisition.height;
740 #endif
742 void ScintillaGTK::SizeAllocate(GtkWidget *widget, GtkAllocation *allocation) {
743 ScintillaGTK *sciThis = ScintillaFromWidget(widget);
744 try {
745 gtk_widget_set_allocation(widget, allocation);
746 if (IS_WIDGET_REALIZED(widget))
747 gdk_window_move_resize(WindowFromWidget(widget),
748 allocation->x,
749 allocation->y,
750 allocation->width,
751 allocation->height);
753 sciThis->Resize(allocation->width, allocation->height);
755 } catch (...) {
756 sciThis->errorStatus = SC_STATUS_FAILURE;
760 void ScintillaGTK::Initialise() {
761 //Platform::DebugPrintf("ScintillaGTK::Initialise\n");
762 parentClass = reinterpret_cast<GtkWidgetClass *>(
763 g_type_class_ref(gtk_container_get_type()));
765 gtk_widget_set_can_focus(PWidget(wMain), TRUE);
766 gtk_widget_set_sensitive(PWidget(wMain), TRUE);
767 gtk_widget_set_events(PWidget(wMain),
768 GDK_EXPOSURE_MASK
769 | GDK_SCROLL_MASK
770 | GDK_STRUCTURE_MASK
771 | GDK_KEY_PRESS_MASK
772 | GDK_KEY_RELEASE_MASK
773 | GDK_FOCUS_CHANGE_MASK
774 | GDK_LEAVE_NOTIFY_MASK
775 | GDK_BUTTON_PRESS_MASK
776 | GDK_BUTTON_RELEASE_MASK
777 | GDK_POINTER_MOTION_MASK
778 | GDK_POINTER_MOTION_HINT_MASK);
780 wText = gtk_drawing_area_new();
781 gtk_widget_set_parent(PWidget(wText), PWidget(wMain));
782 GtkWidget *widtxt = PWidget(wText); // No code inside the G_OBJECT macro
783 gtk_widget_show(widtxt);
784 #if GTK_CHECK_VERSION(3,0,0)
785 g_signal_connect(G_OBJECT(widtxt), "draw",
786 G_CALLBACK(ScintillaGTK::DrawText), this);
787 #else
788 g_signal_connect(G_OBJECT(widtxt), "expose_event",
789 G_CALLBACK(ScintillaGTK::ExposeText), this);
790 #endif
791 #if GTK_CHECK_VERSION(3,0,0)
792 // we need a runtime check because we don't want double buffering when
793 // running on >= 3.9.2
794 if (gtk_check_version(3,9,2) != NULL /* on < 3.9.2 */)
795 #endif
797 #if !GTK_CHECK_VERSION(3,14,0)
798 // Avoid background drawing flash/missing redraws
799 gtk_widget_set_double_buffered(widtxt, FALSE);
800 #endif
802 gtk_widget_set_events(widtxt, GDK_EXPOSURE_MASK);
803 gtk_widget_set_size_request(widtxt, 100, 100);
804 adjustmentv = GTK_ADJUSTMENT(gtk_adjustment_new(0.0, 0.0, 201.0, 1.0, 20.0, 20.0));
805 #if GTK_CHECK_VERSION(3,0,0)
806 scrollbarv = gtk_scrollbar_new(GTK_ORIENTATION_VERTICAL, GTK_ADJUSTMENT(adjustmentv));
807 #else
808 scrollbarv = gtk_vscrollbar_new(GTK_ADJUSTMENT(adjustmentv));
809 #endif
810 gtk_widget_set_can_focus(PWidget(scrollbarv), FALSE);
811 g_signal_connect(G_OBJECT(adjustmentv), "value_changed",
812 G_CALLBACK(ScrollSignal), this);
813 gtk_widget_set_parent(PWidget(scrollbarv), PWidget(wMain));
814 gtk_widget_show(PWidget(scrollbarv));
816 adjustmenth = GTK_ADJUSTMENT(gtk_adjustment_new(0.0, 0.0, 101.0, 1.0, 20.0, 20.0));
817 #if GTK_CHECK_VERSION(3,0,0)
818 scrollbarh = gtk_scrollbar_new(GTK_ORIENTATION_HORIZONTAL, GTK_ADJUSTMENT(adjustmenth));
819 #else
820 scrollbarh = gtk_hscrollbar_new(GTK_ADJUSTMENT(adjustmenth));
821 #endif
822 gtk_widget_set_can_focus(PWidget(scrollbarh), FALSE);
823 g_signal_connect(G_OBJECT(adjustmenth), "value_changed",
824 G_CALLBACK(ScrollHSignal), this);
825 gtk_widget_set_parent(PWidget(scrollbarh), PWidget(wMain));
826 gtk_widget_show(PWidget(scrollbarh));
828 gtk_widget_grab_focus(PWidget(wMain));
830 gtk_drag_dest_set(GTK_WIDGET(PWidget(wMain)),
831 GTK_DEST_DEFAULT_ALL, clipboardPasteTargets, nClipboardPasteTargets,
832 static_cast<GdkDragAction>(GDK_ACTION_COPY | GDK_ACTION_MOVE));
834 /* create pre-edit window */
835 wPreedit = gtk_window_new(GTK_WINDOW_POPUP);
836 wPreeditDraw = gtk_drawing_area_new();
837 GtkWidget *predrw = PWidget(wPreeditDraw); // No code inside the G_OBJECT macro
838 #if GTK_CHECK_VERSION(3,0,0)
839 g_signal_connect(G_OBJECT(predrw), "draw",
840 G_CALLBACK(DrawPreedit), this);
841 #else
842 g_signal_connect(G_OBJECT(predrw), "expose_event",
843 G_CALLBACK(ExposePreedit), this);
844 #endif
845 gtk_container_add(GTK_CONTAINER(PWidget(wPreedit)), predrw);
846 gtk_widget_show(predrw);
848 // Set caret period based on GTK settings
849 gboolean blinkOn = false;
850 if (g_object_class_find_property(G_OBJECT_GET_CLASS(
851 G_OBJECT(gtk_settings_get_default())), "gtk-cursor-blink")) {
852 g_object_get(G_OBJECT(
853 gtk_settings_get_default()), "gtk-cursor-blink", &blinkOn, NULL);
855 if (blinkOn &&
856 g_object_class_find_property(G_OBJECT_GET_CLASS(
857 G_OBJECT(gtk_settings_get_default())), "gtk-cursor-blink-time")) {
858 gint value;
859 g_object_get(G_OBJECT(
860 gtk_settings_get_default()), "gtk-cursor-blink-time", &value, NULL);
861 caret.period = gint(value / 1.75);
862 } else {
863 caret.period = 0;
866 for (TickReason tr = tickCaret; tr <= tickDwell; tr = static_cast<TickReason>(tr + 1)) {
867 timers[tr].reason = tr;
868 timers[tr].scintilla = this;
870 vs.indicators[SC_INDICATOR_UNKNOWN] = Indicator(INDIC_HIDDEN, ColourDesired(0, 0, 0xff));
871 vs.indicators[SC_INDICATOR_INPUT] = Indicator(INDIC_DOTS, ColourDesired(0, 0, 0xff));
872 vs.indicators[SC_INDICATOR_CONVERTED] = Indicator(INDIC_COMPOSITIONTHICK, ColourDesired(0, 0, 0xff));
873 vs.indicators[SC_INDICATOR_TARGET] = Indicator(INDIC_STRAIGHTBOX, ColourDesired(0, 0, 0xff));
876 void ScintillaGTK::Finalise() {
877 for (TickReason tr = tickCaret; tr <= tickDwell; tr = static_cast<TickReason>(tr + 1)) {
878 FineTickerCancel(tr);
880 ScintillaBase::Finalise();
883 bool ScintillaGTK::AbandonPaint() {
884 if ((paintState == painting) && !paintingAllText) {
885 repaintFullWindow = true;
887 return false;
890 void ScintillaGTK::DisplayCursor(Window::Cursor c) {
891 if (cursorMode == SC_CURSORNORMAL)
892 wText.SetCursor(c);
893 else
894 wText.SetCursor(static_cast<Window::Cursor>(cursorMode));
897 bool ScintillaGTK::DragThreshold(Point ptStart, Point ptNow) {
898 return gtk_drag_check_threshold(GTK_WIDGET(PWidget(wMain)),
899 ptStart.x, ptStart.y, ptNow.x, ptNow.y);
902 void ScintillaGTK::StartDrag() {
903 PLATFORM_ASSERT(evbtn != 0);
904 dragWasDropped = false;
905 inDragDrop = ddDragging;
906 GtkTargetList *tl = gtk_target_list_new(clipboardCopyTargets, nClipboardCopyTargets);
907 #if GTK_CHECK_VERSION(3,10,0)
908 gtk_drag_begin_with_coordinates(GTK_WIDGET(PWidget(wMain)),
910 static_cast<GdkDragAction>(GDK_ACTION_COPY | GDK_ACTION_MOVE),
911 evbtn->button,
912 reinterpret_cast<GdkEvent *>(evbtn),
913 -1, -1);
914 #else
915 gtk_drag_begin(GTK_WIDGET(PWidget(wMain)),
917 static_cast<GdkDragAction>(GDK_ACTION_COPY | GDK_ACTION_MOVE),
918 evbtn->button,
919 reinterpret_cast<GdkEvent *>(evbtn));
920 #endif
923 static std::string ConvertText(const char *s, size_t len, const char *charSetDest,
924 const char *charSetSource, bool transliterations, bool silent=false) {
925 // s is not const because of different versions of iconv disagreeing about const
926 std::string destForm;
927 Converter conv(charSetDest, charSetSource, transliterations);
928 if (conv) {
929 size_t outLeft = len*3+1;
930 destForm = std::string(outLeft, '\0');
931 // g_iconv does not actually write to its input argument so safe to cast away const
932 char *pin = const_cast<char *>(s);
933 size_t inLeft = len;
934 char *putf = &destForm[0];
935 char *pout = putf;
936 size_t conversions = conv.Convert(&pin, &inLeft, &pout, &outLeft);
937 if (conversions == ((size_t)(-1))) {
938 if (!silent) {
939 if (len == 1)
940 fprintf(stderr, "iconv %s->%s failed for %0x '%s'\n",
941 charSetSource, charSetDest, (unsigned char)(*s), s);
942 else
943 fprintf(stderr, "iconv %s->%s failed for %s\n",
944 charSetSource, charSetDest, s);
946 destForm = std::string();
947 } else {
948 destForm.resize(pout - putf);
950 } else {
951 fprintf(stderr, "Can not iconv %s %s\n", charSetDest, charSetSource);
953 return destForm;
956 // Returns the target converted to UTF8.
957 // Return the length in bytes.
958 int ScintillaGTK::TargetAsUTF8(char *text) {
959 int targetLength = targetEnd - targetStart;
960 if (IsUnicodeMode()) {
961 if (text) {
962 pdoc->GetCharRange(text, targetStart, targetLength);
964 } else {
965 // Need to convert
966 const char *charSetBuffer = CharacterSetID();
967 if (*charSetBuffer) {
968 std::string s = RangeText(targetStart, targetEnd);
969 std::string tmputf = ConvertText(&s[0], targetLength, "UTF-8", charSetBuffer, false);
970 if (text) {
971 memcpy(text, tmputf.c_str(), tmputf.length());
973 return tmputf.length();
974 } else {
975 if (text) {
976 pdoc->GetCharRange(text, targetStart, targetLength);
980 return targetLength;
983 // Translates a nul terminated UTF8 string into the document encoding.
984 // Return the length of the result in bytes.
985 int ScintillaGTK::EncodedFromUTF8(char *utf8, char *encoded) const {
986 int inputLength = (lengthForEncode >= 0) ? lengthForEncode : strlen(utf8);
987 if (IsUnicodeMode()) {
988 if (encoded) {
989 memcpy(encoded, utf8, inputLength);
991 return inputLength;
992 } else {
993 // Need to convert
994 const char *charSetBuffer = CharacterSetID();
995 if (*charSetBuffer) {
996 std::string tmpEncoded = ConvertText(utf8, inputLength, charSetBuffer, "UTF-8", true);
997 if (encoded) {
998 memcpy(encoded, tmpEncoded.c_str(), tmpEncoded.length());
1000 return tmpEncoded.length();
1001 } else {
1002 if (encoded) {
1003 memcpy(encoded, utf8, inputLength);
1005 return inputLength;
1008 // Fail
1009 return 0;
1012 bool ScintillaGTK::ValidCodePage(int codePage) const {
1013 return codePage == 0
1014 || codePage == SC_CP_UTF8
1015 || codePage == 932
1016 || codePage == 936
1017 || codePage == 949
1018 || codePage == 950
1019 || codePage == 1361;
1022 sptr_t ScintillaGTK::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
1023 try {
1024 switch (iMessage) {
1026 case SCI_GRABFOCUS:
1027 gtk_widget_grab_focus(PWidget(wMain));
1028 break;
1030 case SCI_GETDIRECTFUNCTION:
1031 return reinterpret_cast<sptr_t>(DirectFunction);
1033 case SCI_GETDIRECTPOINTER:
1034 return reinterpret_cast<sptr_t>(this);
1036 #ifdef SCI_LEXER
1037 case SCI_LOADLEXERLIBRARY:
1038 LexerManager::GetInstance()->Load(reinterpret_cast<const char*>(lParam));
1039 break;
1040 #endif
1041 case SCI_TARGETASUTF8:
1042 return TargetAsUTF8(reinterpret_cast<char*>(lParam));
1044 case SCI_ENCODEDFROMUTF8:
1045 return EncodedFromUTF8(reinterpret_cast<char*>(wParam),
1046 reinterpret_cast<char*>(lParam));
1048 case SCI_SETRECTANGULARSELECTIONMODIFIER:
1049 rectangularSelectionModifier = wParam;
1050 break;
1052 case SCI_GETRECTANGULARSELECTIONMODIFIER:
1053 return rectangularSelectionModifier;
1055 default:
1056 return ScintillaBase::WndProc(iMessage, wParam, lParam);
1058 } catch (std::bad_alloc&) {
1059 errorStatus = SC_STATUS_BADALLOC;
1060 } catch (...) {
1061 errorStatus = SC_STATUS_FAILURE;
1063 return 0l;
1066 sptr_t ScintillaGTK::DefWndProc(unsigned int, uptr_t, sptr_t) {
1067 return 0;
1071 * Report that this Editor subclass has a working implementation of FineTickerStart.
1073 bool ScintillaGTK::FineTickerAvailable() {
1074 return true;
1077 bool ScintillaGTK::FineTickerRunning(TickReason reason) {
1078 return timers[reason].timer != 0;
1081 void ScintillaGTK::FineTickerStart(TickReason reason, int millis, int /* tolerance */) {
1082 FineTickerCancel(reason);
1083 timers[reason].timer = gdk_threads_add_timeout(millis, TimeOut, &timers[reason]);
1086 void ScintillaGTK::FineTickerCancel(TickReason reason) {
1087 if (timers[reason].timer) {
1088 g_source_remove(timers[reason].timer);
1089 timers[reason].timer = 0;
1093 bool ScintillaGTK::SetIdle(bool on) {
1094 if (on) {
1095 // Start idler, if it's not running.
1096 if (!idler.state) {
1097 idler.state = true;
1098 idler.idlerID = reinterpret_cast<IdlerID>(
1099 gdk_threads_add_idle_full(G_PRIORITY_DEFAULT_IDLE, IdleCallback, this, NULL));
1101 } else {
1102 // Stop idler, if it's running
1103 if (idler.state) {
1104 idler.state = false;
1105 g_source_remove(GPOINTER_TO_UINT(idler.idlerID));
1108 return true;
1111 void ScintillaGTK::SetMouseCapture(bool on) {
1112 if (mouseDownCaptures) {
1113 if (on) {
1114 gtk_grab_add(GTK_WIDGET(PWidget(wMain)));
1115 } else {
1116 gtk_grab_remove(GTK_WIDGET(PWidget(wMain)));
1119 capturedMouse = on;
1122 bool ScintillaGTK::HaveMouseCapture() {
1123 return capturedMouse;
1126 #if GTK_CHECK_VERSION(3,0,0)
1128 // Is crcTest completely in crcContainer?
1129 static bool CRectContains(const cairo_rectangle_t &crcContainer, const cairo_rectangle_t &crcTest) {
1130 return
1131 (crcTest.x >= crcContainer.x) && ((crcTest.x + crcTest.width) <= (crcContainer.x + crcContainer.width)) &&
1132 (crcTest.y >= crcContainer.y) && ((crcTest.y + crcTest.height) <= (crcContainer.y + crcContainer.height));
1135 // Is crcTest completely in crcListContainer?
1136 // May incorrectly return false if complex shape
1137 static bool CRectListContains(const cairo_rectangle_list_t *crcListContainer, const cairo_rectangle_t &crcTest) {
1138 for (int r=0; r<crcListContainer->num_rectangles; r++) {
1139 if (CRectContains(crcListContainer->rectangles[r], crcTest))
1140 return true;
1142 return false;
1145 #endif
1147 bool ScintillaGTK::PaintContains(PRectangle rc) {
1148 // This allows optimization when a rectangle is completely in the update region.
1149 // It is OK to return false when too difficult to determine as that just performs extra drawing
1150 bool contains = true;
1151 if (paintState == painting) {
1152 if (!rcPaint.Contains(rc)) {
1153 contains = false;
1154 } else if (rgnUpdate) {
1155 #if GTK_CHECK_VERSION(3,0,0)
1156 cairo_rectangle_t grc = {rc.left, rc.top,
1157 rc.right - rc.left, rc.bottom - rc.top};
1158 contains = CRectListContains(rgnUpdate, grc);
1159 #else
1160 GdkRectangle grc = {static_cast<gint>(rc.left), static_cast<gint>(rc.top),
1161 static_cast<gint>(rc.right - rc.left), static_cast<gint>(rc.bottom - rc.top)};
1162 if (gdk_region_rect_in(rgnUpdate, &grc) != GDK_OVERLAP_RECTANGLE_IN) {
1163 contains = false;
1165 #endif
1168 return contains;
1171 // Redraw all of text area. This paint will not be abandoned.
1172 void ScintillaGTK::FullPaint() {
1173 wText.InvalidateAll();
1176 PRectangle ScintillaGTK::GetClientRectangle() const {
1177 Window &win = const_cast<Window &>(wMain);
1178 PRectangle rc = win.GetClientPosition();
1179 if (verticalScrollBarVisible)
1180 rc.right -= verticalScrollBarWidth;
1181 if (horizontalScrollBarVisible && !Wrapping())
1182 rc.bottom -= horizontalScrollBarHeight;
1183 // Move to origin
1184 rc.right -= rc.left;
1185 rc.bottom -= rc.top;
1186 if (rc.bottom < 0)
1187 rc.bottom = 0;
1188 if (rc.right < 0)
1189 rc.right = 0;
1190 rc.left = 0;
1191 rc.top = 0;
1192 return rc;
1195 void ScintillaGTK::ScrollText(int linesToMove) {
1196 int diff = vs.lineHeight * -linesToMove;
1197 //Platform::DebugPrintf("ScintillaGTK::ScrollText %d %d %0d,%0d %0d,%0d\n", linesToMove, diff,
1198 // rc.left, rc.top, rc.right, rc.bottom);
1199 GtkWidget *wi = PWidget(wText);
1200 NotifyUpdateUI();
1202 if (IS_WIDGET_REALIZED(wi)) {
1203 gdk_window_scroll(WindowFromWidget(wi), 0, -diff);
1204 gdk_window_process_updates(WindowFromWidget(wi), FALSE);
1208 void ScintillaGTK::SetVerticalScrollPos() {
1209 DwellEnd(true);
1210 gtk_adjustment_set_value(GTK_ADJUSTMENT(adjustmentv), topLine);
1213 void ScintillaGTK::SetHorizontalScrollPos() {
1214 DwellEnd(true);
1215 gtk_adjustment_set_value(GTK_ADJUSTMENT(adjustmenth), xOffset);
1218 bool ScintillaGTK::ModifyScrollBars(int nMax, int nPage) {
1219 bool modified = false;
1220 int pageScroll = LinesToScroll();
1222 if (gtk_adjustment_get_upper(adjustmentv) != (nMax + 1) ||
1223 gtk_adjustment_get_page_size(adjustmentv) != nPage ||
1224 gtk_adjustment_get_page_increment(adjustmentv) != pageScroll) {
1225 gtk_adjustment_set_upper(adjustmentv, nMax + 1);
1226 gtk_adjustment_set_page_size(adjustmentv, nPage);
1227 gtk_adjustment_set_page_increment(adjustmentv, pageScroll);
1228 #if !GTK_CHECK_VERSION(3,18,0)
1229 gtk_adjustment_changed(GTK_ADJUSTMENT(adjustmentv));
1230 #endif
1231 modified = true;
1234 PRectangle rcText = GetTextRectangle();
1235 int horizEndPreferred = scrollWidth;
1236 if (horizEndPreferred < 0)
1237 horizEndPreferred = 0;
1238 unsigned int pageWidth = rcText.Width();
1239 unsigned int pageIncrement = pageWidth / 3;
1240 unsigned int charWidth = vs.styles[STYLE_DEFAULT].aveCharWidth;
1241 if (gtk_adjustment_get_upper(adjustmenth) != horizEndPreferred ||
1242 gtk_adjustment_get_page_size(adjustmenth) != pageWidth ||
1243 gtk_adjustment_get_page_increment(adjustmenth) != pageIncrement ||
1244 gtk_adjustment_get_step_increment(adjustmenth) != charWidth) {
1245 gtk_adjustment_set_upper(adjustmenth, horizEndPreferred);
1246 gtk_adjustment_set_page_size(adjustmenth, pageWidth);
1247 gtk_adjustment_set_page_increment(adjustmenth, pageIncrement);
1248 gtk_adjustment_set_step_increment(adjustmenth, charWidth);
1249 #if !GTK_CHECK_VERSION(3,18,0)
1250 gtk_adjustment_changed(GTK_ADJUSTMENT(adjustmenth));
1251 #endif
1252 modified = true;
1254 if (modified && (paintState == painting)) {
1255 repaintFullWindow = true;
1258 return modified;
1261 void ScintillaGTK::ReconfigureScrollBars() {
1262 PRectangle rc = wMain.GetClientPosition();
1263 Resize(rc.Width(), rc.Height());
1266 void ScintillaGTK::NotifyChange() {
1267 g_signal_emit(G_OBJECT(sci), scintilla_signals[COMMAND_SIGNAL], 0,
1268 Platform::LongFromTwoShorts(GetCtrlID(), SCEN_CHANGE), PWidget(wMain));
1271 void ScintillaGTK::NotifyFocus(bool focus) {
1272 g_signal_emit(G_OBJECT(sci), scintilla_signals[COMMAND_SIGNAL], 0,
1273 Platform::LongFromTwoShorts
1274 (GetCtrlID(), focus ? SCEN_SETFOCUS : SCEN_KILLFOCUS), PWidget(wMain));
1275 Editor::NotifyFocus(focus);
1278 void ScintillaGTK::NotifyParent(SCNotification scn) {
1279 scn.nmhdr.hwndFrom = PWidget(wMain);
1280 scn.nmhdr.idFrom = GetCtrlID();
1281 g_signal_emit(G_OBJECT(sci), scintilla_signals[NOTIFY_SIGNAL], 0,
1282 GetCtrlID(), &scn);
1285 void ScintillaGTK::NotifyKey(int key, int modifiers) {
1286 SCNotification scn = {};
1287 scn.nmhdr.code = SCN_KEY;
1288 scn.ch = key;
1289 scn.modifiers = modifiers;
1291 NotifyParent(scn);
1294 void ScintillaGTK::NotifyURIDropped(const char *list) {
1295 SCNotification scn = {};
1296 scn.nmhdr.code = SCN_URIDROPPED;
1297 scn.text = list;
1299 NotifyParent(scn);
1302 const char *CharacterSetID(int characterSet);
1304 const char *ScintillaGTK::CharacterSetID() const {
1305 return ::CharacterSetID(vs.styles[STYLE_DEFAULT].characterSet);
1308 class CaseFolderDBCS : public CaseFolderTable {
1309 const char *charSet;
1310 public:
1311 explicit CaseFolderDBCS(const char *charSet_) : charSet(charSet_) {
1312 StandardASCII();
1314 virtual size_t Fold(char *folded, size_t sizeFolded, const char *mixed, size_t lenMixed) {
1315 if ((lenMixed == 1) && (sizeFolded > 0)) {
1316 folded[0] = mapping[static_cast<unsigned char>(mixed[0])];
1317 return 1;
1318 } else if (*charSet) {
1319 std::string sUTF8 = ConvertText(mixed, lenMixed,
1320 "UTF-8", charSet, false);
1321 if (!sUTF8.empty()) {
1322 gchar *mapped = g_utf8_casefold(sUTF8.c_str(), sUTF8.length());
1323 size_t lenMapped = strlen(mapped);
1324 if (lenMapped < sizeFolded) {
1325 memcpy(folded, mapped, lenMapped);
1326 } else {
1327 folded[0] = '\0';
1328 lenMapped = 1;
1330 g_free(mapped);
1331 return lenMapped;
1334 // Something failed so return a single NUL byte
1335 folded[0] = '\0';
1336 return 1;
1340 CaseFolder *ScintillaGTK::CaseFolderForEncoding() {
1341 if (pdoc->dbcsCodePage == SC_CP_UTF8) {
1342 return new CaseFolderUnicode();
1343 } else {
1344 const char *charSetBuffer = CharacterSetID();
1345 if (charSetBuffer) {
1346 if (pdoc->dbcsCodePage == 0) {
1347 CaseFolderTable *pcf = new CaseFolderTable();
1348 pcf->StandardASCII();
1349 // Only for single byte encodings
1350 for (int i=0x80; i<0x100; i++) {
1351 char sCharacter[2] = "A";
1352 sCharacter[0] = i;
1353 // Silent as some bytes have no assigned character
1354 std::string sUTF8 = ConvertText(sCharacter, 1,
1355 "UTF-8", charSetBuffer, false, true);
1356 if (!sUTF8.empty()) {
1357 gchar *mapped = g_utf8_casefold(sUTF8.c_str(), sUTF8.length());
1358 if (mapped) {
1359 std::string mappedBack = ConvertText(mapped, strlen(mapped),
1360 charSetBuffer, "UTF-8", false, true);
1361 if ((mappedBack.length() == 1) && (mappedBack[0] != sCharacter[0])) {
1362 pcf->SetTranslation(sCharacter[0], mappedBack[0]);
1364 g_free(mapped);
1368 return pcf;
1369 } else {
1370 return new CaseFolderDBCS(charSetBuffer);
1373 return 0;
1377 namespace {
1379 struct CaseMapper {
1380 gchar *mapped; // Must be freed with g_free
1381 CaseMapper(const std::string &sUTF8, bool toUpperCase) {
1382 if (toUpperCase) {
1383 mapped = g_utf8_strup(sUTF8.c_str(), sUTF8.length());
1384 } else {
1385 mapped = g_utf8_strdown(sUTF8.c_str(), sUTF8.length());
1388 ~CaseMapper() {
1389 g_free(mapped);
1395 std::string ScintillaGTK::CaseMapString(const std::string &s, int caseMapping) {
1396 if ((s.size() == 0) || (caseMapping == cmSame))
1397 return s;
1399 if (IsUnicodeMode()) {
1400 std::string retMapped(s.length() * maxExpansionCaseConversion, 0);
1401 size_t lenMapped = CaseConvertString(&retMapped[0], retMapped.length(), s.c_str(), s.length(),
1402 (caseMapping == cmUpper) ? CaseConversionUpper : CaseConversionLower);
1403 retMapped.resize(lenMapped);
1404 return retMapped;
1407 const char *charSetBuffer = CharacterSetID();
1409 if (!*charSetBuffer) {
1410 CaseMapper mapper(s, caseMapping == cmUpper);
1411 return std::string(mapper.mapped, strlen(mapper.mapped));
1412 } else {
1413 // Change text to UTF-8
1414 std::string sUTF8 = ConvertText(s.c_str(), s.length(),
1415 "UTF-8", charSetBuffer, false);
1416 CaseMapper mapper(sUTF8, caseMapping == cmUpper);
1417 return ConvertText(mapper.mapped, strlen(mapper.mapped), charSetBuffer, "UTF-8", false);
1421 int ScintillaGTK::KeyDefault(int key, int modifiers) {
1422 // Pass up to container in case it is an accelerator
1423 NotifyKey(key, modifiers);
1424 return 0;
1427 void ScintillaGTK::CopyToClipboard(const SelectionText &selectedText) {
1428 SelectionText *clipText = new SelectionText();
1429 clipText->Copy(selectedText);
1430 StoreOnClipboard(clipText);
1433 void ScintillaGTK::Copy() {
1434 if (!sel.Empty()) {
1435 SelectionText *clipText = new SelectionText();
1436 CopySelectionRange(clipText);
1437 StoreOnClipboard(clipText);
1438 #if PLAT_GTK_WIN32
1439 if (sel.IsRectangular()) {
1440 ::OpenClipboard(NULL);
1441 ::SetClipboardData(cfColumnSelect, 0);
1442 ::CloseClipboard();
1444 #endif
1448 void ScintillaGTK::ClipboardReceived(GtkClipboard *clipboard, GtkSelectionData *selection_data, gpointer data) {
1449 ScintillaGTK *sciThis = static_cast<ScintillaGTK *>(data);
1450 sciThis->ReceivedSelection(selection_data);
1453 void ScintillaGTK::Paste() {
1454 atomSought = atomUTF8;
1455 GtkClipboard *clipBoard =
1456 gtk_widget_get_clipboard(GTK_WIDGET(PWidget(wMain)), atomClipboard);
1457 if (clipBoard == NULL)
1458 return;
1459 gtk_clipboard_request_contents(clipBoard, atomSought, ClipboardReceived, this);
1462 void ScintillaGTK::CreateCallTipWindow(PRectangle rc) {
1463 if (!ct.wCallTip.Created()) {
1464 ct.wCallTip = gtk_window_new(GTK_WINDOW_POPUP);
1465 ct.wDraw = gtk_drawing_area_new();
1466 GtkWidget *widcdrw = PWidget(ct.wDraw); // // No code inside the G_OBJECT macro
1467 gtk_container_add(GTK_CONTAINER(PWidget(ct.wCallTip)), widcdrw);
1468 #if GTK_CHECK_VERSION(3,0,0)
1469 g_signal_connect(G_OBJECT(widcdrw), "draw",
1470 G_CALLBACK(ScintillaGTK::DrawCT), &ct);
1471 #else
1472 g_signal_connect(G_OBJECT(widcdrw), "expose_event",
1473 G_CALLBACK(ScintillaGTK::ExposeCT), &ct);
1474 #endif
1475 g_signal_connect(G_OBJECT(widcdrw), "button_press_event",
1476 G_CALLBACK(ScintillaGTK::PressCT), static_cast<void *>(this));
1477 gtk_widget_set_events(widcdrw,
1478 GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK);
1480 gtk_widget_set_size_request(PWidget(ct.wDraw), rc.Width(), rc.Height());
1481 ct.wDraw.Show();
1482 if (PWindow(ct.wCallTip)) {
1483 gdk_window_resize(PWindow(ct.wCallTip), rc.Width(), rc.Height());
1487 void ScintillaGTK::AddToPopUp(const char *label, int cmd, bool enabled) {
1488 GtkWidget *menuItem;
1489 if (label[0])
1490 menuItem = gtk_menu_item_new_with_label(label);
1491 else
1492 menuItem = gtk_separator_menu_item_new();
1493 gtk_menu_shell_append(GTK_MENU_SHELL(popup.GetID()), menuItem);
1494 g_object_set_data(G_OBJECT(menuItem), "CmdNum", GINT_TO_POINTER(cmd));
1495 g_signal_connect(G_OBJECT(menuItem),"activate", G_CALLBACK(PopUpCB), this);
1497 if (cmd) {
1498 if (menuItem)
1499 gtk_widget_set_sensitive(menuItem, enabled);
1503 bool ScintillaGTK::OwnPrimarySelection() {
1504 return ((gdk_selection_owner_get(GDK_SELECTION_PRIMARY)
1505 == PWindow(wMain)) &&
1506 (PWindow(wMain) != NULL));
1509 void ScintillaGTK::ClaimSelection() {
1510 // X Windows has a 'primary selection' as well as the clipboard.
1511 // Whenever the user selects some text, we become the primary selection
1512 if (!sel.Empty() && IS_WIDGET_REALIZED(GTK_WIDGET(PWidget(wMain)))) {
1513 primarySelection = true;
1514 gtk_selection_owner_set(GTK_WIDGET(PWidget(wMain)),
1515 GDK_SELECTION_PRIMARY, GDK_CURRENT_TIME);
1516 primary.Clear();
1517 } else if (OwnPrimarySelection()) {
1518 primarySelection = true;
1519 if (primary.Empty())
1520 gtk_selection_owner_set(NULL, GDK_SELECTION_PRIMARY, GDK_CURRENT_TIME);
1521 } else {
1522 primarySelection = false;
1523 primary.Clear();
1527 static const guchar *DataOfGSD(GtkSelectionData *sd) { return gtk_selection_data_get_data(sd); }
1528 static gint LengthOfGSD(GtkSelectionData *sd) { return gtk_selection_data_get_length(sd); }
1529 static GdkAtom TypeOfGSD(GtkSelectionData *sd) { return gtk_selection_data_get_data_type(sd); }
1530 static GdkAtom SelectionOfGSD(GtkSelectionData *sd) { return gtk_selection_data_get_selection(sd); }
1532 // Detect rectangular text, convert line ends to current mode, convert from or to UTF-8
1533 void ScintillaGTK::GetGtkSelectionText(GtkSelectionData *selectionData, SelectionText &selText) {
1534 const char *data = reinterpret_cast<const char *>(DataOfGSD(selectionData));
1535 int len = LengthOfGSD(selectionData);
1536 GdkAtom selectionTypeData = TypeOfGSD(selectionData);
1538 // Return empty string if selection is not a string
1539 if ((selectionTypeData != GDK_TARGET_STRING) && (selectionTypeData != atomUTF8)) {
1540 selText.Clear();
1541 return;
1544 // Check for "\n\0" ending to string indicating that selection is rectangular
1545 bool isRectangular;
1546 #if PLAT_GTK_WIN32
1547 isRectangular = ::IsClipboardFormatAvailable(cfColumnSelect) != 0;
1548 #else
1549 isRectangular = ((len > 2) && (data[len - 1] == 0 && data[len - 2] == '\n'));
1550 if (isRectangular)
1551 len--; // Forget the extra '\0'
1552 #endif
1554 #if PLAT_GTK_WIN32
1555 // Win32 includes an ending '\0' byte in 'len' for clipboard text from
1556 // external applications; ignore it.
1557 if ((len > 0) && (data[len - 1] == '\0'))
1558 len--;
1559 #endif
1561 std::string dest(data, len);
1562 if (selectionTypeData == GDK_TARGET_STRING) {
1563 if (IsUnicodeMode()) {
1564 // Unknown encoding so assume in Latin1
1565 dest = UTF8FromLatin1(dest.c_str(), dest.length());
1566 selText.Copy(dest, SC_CP_UTF8, 0, isRectangular, false);
1567 } else {
1568 // Assume buffer is in same encoding as selection
1569 selText.Copy(dest, pdoc->dbcsCodePage,
1570 vs.styles[STYLE_DEFAULT].characterSet, isRectangular, false);
1572 } else { // UTF-8
1573 const char *charSetBuffer = CharacterSetID();
1574 if (!IsUnicodeMode() && *charSetBuffer) {
1575 // Convert to locale
1576 dest = ConvertText(dest.c_str(), dest.length(), charSetBuffer, "UTF-8", true);
1577 selText.Copy(dest, pdoc->dbcsCodePage,
1578 vs.styles[STYLE_DEFAULT].characterSet, isRectangular, false);
1579 } else {
1580 selText.Copy(dest, SC_CP_UTF8, 0, isRectangular, false);
1585 void ScintillaGTK::ReceivedSelection(GtkSelectionData *selection_data) {
1586 try {
1587 if ((SelectionOfGSD(selection_data) == atomClipboard) ||
1588 (SelectionOfGSD(selection_data) == GDK_SELECTION_PRIMARY)) {
1589 if ((atomSought == atomUTF8) && (LengthOfGSD(selection_data) <= 0)) {
1590 atomSought = atomString;
1591 gtk_selection_convert(GTK_WIDGET(PWidget(wMain)),
1592 SelectionOfGSD(selection_data), atomSought, GDK_CURRENT_TIME);
1593 } else if ((LengthOfGSD(selection_data) > 0) &&
1594 ((TypeOfGSD(selection_data) == GDK_TARGET_STRING) || (TypeOfGSD(selection_data) == atomUTF8))) {
1595 SelectionText selText;
1596 GetGtkSelectionText(selection_data, selText);
1598 UndoGroup ug(pdoc);
1599 if (SelectionOfGSD(selection_data) != GDK_SELECTION_PRIMARY) {
1600 ClearSelection(multiPasteMode == SC_MULTIPASTE_EACH);
1603 InsertPasteShape(selText.Data(), selText.Length(),
1604 selText.rectangular ? pasteRectangular : pasteStream);
1605 EnsureCaretVisible();
1608 // else fprintf(stderr, "Target non string %d %d\n", (int)(selection_data->type),
1609 // (int)(atomUTF8));
1610 Redraw();
1611 } catch (...) {
1612 errorStatus = SC_STATUS_FAILURE;
1616 void ScintillaGTK::ReceivedDrop(GtkSelectionData *selection_data) {
1617 dragWasDropped = true;
1618 if (TypeOfGSD(selection_data) == atomUriList || TypeOfGSD(selection_data) == atomDROPFILES_DND) {
1619 const char *data = reinterpret_cast<const char *>(DataOfGSD(selection_data));
1620 std::vector<char> drop(data, data + LengthOfGSD(selection_data));
1621 drop.push_back('\0');
1622 NotifyURIDropped(&drop[0]);
1623 } else if ((TypeOfGSD(selection_data) == GDK_TARGET_STRING) || (TypeOfGSD(selection_data) == atomUTF8)) {
1624 if (LengthOfGSD(selection_data) > 0) {
1625 SelectionText selText;
1626 GetGtkSelectionText(selection_data, selText);
1627 DropAt(posDrop, selText.Data(), selText.Length(), false, selText.rectangular);
1629 } else if (LengthOfGSD(selection_data) > 0) {
1630 //~ fprintf(stderr, "ReceivedDrop other %p\n", static_cast<void *>(selection_data->type));
1632 Redraw();
1637 void ScintillaGTK::GetSelection(GtkSelectionData *selection_data, guint info, SelectionText *text) {
1638 #if PLAT_GTK_WIN32
1639 // GDK on Win32 expands any \n into \r\n, so make a copy of
1640 // the clip text now with newlines converted to \n. Use { } to hide symbols
1641 // from code below
1642 SelectionText *newline_normalized = NULL;
1644 std::string tmpstr = Document::TransformLineEnds(text->Data(), text->Length(), SC_EOL_LF);
1645 newline_normalized = new SelectionText();
1646 newline_normalized->Copy(tmpstr, SC_CP_UTF8, 0, text->rectangular, false);
1647 text = newline_normalized;
1649 #endif
1651 // Convert text to utf8 if it isn't already
1652 SelectionText *converted = 0;
1653 if ((text->codePage != SC_CP_UTF8) && (info == TARGET_UTF8_STRING)) {
1654 const char *charSet = ::CharacterSetID(text->characterSet);
1655 if (*charSet) {
1656 std::string tmputf = ConvertText(text->Data(), text->Length(), "UTF-8", charSet, false);
1657 converted = new SelectionText();
1658 converted->Copy(tmputf, SC_CP_UTF8, 0, text->rectangular, false);
1659 text = converted;
1663 // Here is a somewhat evil kludge.
1664 // As I can not work out how to store data on the clipboard in multiple formats
1665 // and need some way to mark the clipping as being stream or rectangular,
1666 // the terminating \0 is included in the length for rectangular clippings.
1667 // All other tested aplications behave benignly by ignoring the \0.
1668 // The #if is here because on Windows cfColumnSelect clip entry is used
1669 // instead as standard indicator of rectangularness (so no need to kludge)
1670 const char *textData = text->Data();
1671 int len = text->Length();
1672 #if PLAT_GTK_WIN32 == 0
1673 if (text->rectangular)
1674 len++;
1675 #endif
1677 if (info == TARGET_UTF8_STRING) {
1678 gtk_selection_data_set_text(selection_data, textData, len);
1679 } else {
1680 gtk_selection_data_set(selection_data,
1681 static_cast<GdkAtom>(GDK_SELECTION_TYPE_STRING),
1682 8, reinterpret_cast<const unsigned char *>(textData), len);
1684 delete converted;
1686 #if PLAT_GTK_WIN32
1687 delete newline_normalized;
1688 #endif
1691 void ScintillaGTK::StoreOnClipboard(SelectionText *clipText) {
1692 GtkClipboard *clipBoard =
1693 gtk_widget_get_clipboard(GTK_WIDGET(PWidget(wMain)), atomClipboard);
1694 if (clipBoard == NULL) // Occurs if widget isn't in a toplevel
1695 return;
1697 if (gtk_clipboard_set_with_data(clipBoard, clipboardCopyTargets, nClipboardCopyTargets,
1698 ClipboardGetSelection, ClipboardClearSelection, clipText)) {
1699 gtk_clipboard_set_can_store(clipBoard, clipboardCopyTargets, nClipboardCopyTargets);
1703 void ScintillaGTK::ClipboardGetSelection(GtkClipboard *, GtkSelectionData *selection_data, guint info, void *data) {
1704 GetSelection(selection_data, info, static_cast<SelectionText*>(data));
1707 void ScintillaGTK::ClipboardClearSelection(GtkClipboard *, void *data) {
1708 SelectionText *obj = static_cast<SelectionText*>(data);
1709 delete obj;
1712 void ScintillaGTK::UnclaimSelection(GdkEventSelection *selection_event) {
1713 try {
1714 //Platform::DebugPrintf("UnclaimSelection\n");
1715 if (selection_event->selection == GDK_SELECTION_PRIMARY) {
1716 //Platform::DebugPrintf("UnclaimPrimarySelection\n");
1717 if (!OwnPrimarySelection()) {
1718 primary.Clear();
1719 primarySelection = false;
1720 FullPaint();
1723 } catch (...) {
1724 errorStatus = SC_STATUS_FAILURE;
1728 void ScintillaGTK::Resize(int width, int height) {
1729 //Platform::DebugPrintf("Resize %d %d\n", width, height);
1730 //printf("Resize %d %d\n", width, height);
1732 // Not always needed, but some themes can have different sizes of scrollbars
1733 #if GTK_CHECK_VERSION(3,0,0)
1734 GtkRequisition requisition;
1735 gtk_widget_get_preferred_size(PWidget(scrollbarv), NULL, &requisition);
1736 verticalScrollBarWidth = requisition.width;
1737 gtk_widget_get_preferred_size(PWidget(scrollbarh), NULL, &requisition);
1738 horizontalScrollBarHeight = requisition.height;
1739 #else
1740 verticalScrollBarWidth = GTK_WIDGET(PWidget(scrollbarv))->requisition.width;
1741 horizontalScrollBarHeight = GTK_WIDGET(PWidget(scrollbarh))->requisition.height;
1742 #endif
1744 // These allocations should never produce negative sizes as they would wrap around to huge
1745 // unsigned numbers inside GTK+ causing warnings.
1746 bool showSBHorizontal = horizontalScrollBarVisible && !Wrapping();
1748 GtkAllocation alloc;
1749 if (showSBHorizontal) {
1750 gtk_widget_show(GTK_WIDGET(PWidget(scrollbarh)));
1751 alloc.x = 0;
1752 alloc.y = height - horizontalScrollBarHeight;
1753 alloc.width = Platform::Maximum(1, width - verticalScrollBarWidth);
1754 alloc.height = horizontalScrollBarHeight;
1755 gtk_widget_size_allocate(GTK_WIDGET(PWidget(scrollbarh)), &alloc);
1756 } else {
1757 gtk_widget_hide(GTK_WIDGET(PWidget(scrollbarh)));
1758 horizontalScrollBarHeight = 0; // in case horizontalScrollBarVisible is true.
1761 if (verticalScrollBarVisible) {
1762 gtk_widget_show(GTK_WIDGET(PWidget(scrollbarv)));
1763 alloc.x = width - verticalScrollBarWidth;
1764 alloc.y = 0;
1765 alloc.width = verticalScrollBarWidth;
1766 alloc.height = Platform::Maximum(1, height - horizontalScrollBarHeight);
1767 gtk_widget_size_allocate(GTK_WIDGET(PWidget(scrollbarv)), &alloc);
1768 } else {
1769 gtk_widget_hide(GTK_WIDGET(PWidget(scrollbarv)));
1770 verticalScrollBarWidth = 0;
1772 if (IS_WIDGET_MAPPED(PWidget(wMain))) {
1773 ChangeSize();
1776 alloc.x = 0;
1777 alloc.y = 0;
1778 alloc.width = 1;
1779 alloc.height = 1;
1780 #if GTK_CHECK_VERSION(3, 0, 0)
1781 // please GTK 3.20 and ask wText what size it wants, although we know it doesn't really need
1782 // anything special as it's ours.
1783 gtk_widget_get_preferred_size(PWidget(wText), &requisition, NULL);
1784 alloc.width = requisition.width;
1785 alloc.height = requisition.height;
1786 #endif
1787 alloc.width = Platform::Maximum(alloc.width, width - verticalScrollBarWidth);
1788 alloc.height = Platform::Maximum(alloc.height, height - horizontalScrollBarHeight);
1789 gtk_widget_size_allocate(GTK_WIDGET(PWidget(wText)), &alloc);
1792 static void SetAdjustmentValue(GtkAdjustment *object, int value) {
1793 GtkAdjustment *adjustment = GTK_ADJUSTMENT(object);
1794 int maxValue = static_cast<int>(
1795 gtk_adjustment_get_upper(adjustment) - gtk_adjustment_get_page_size(adjustment));
1797 if (value > maxValue)
1798 value = maxValue;
1799 if (value < 0)
1800 value = 0;
1801 gtk_adjustment_set_value(adjustment, value);
1804 static int modifierTranslated(int sciModifier) {
1805 switch (sciModifier) {
1806 case SCMOD_SHIFT:
1807 return GDK_SHIFT_MASK;
1808 case SCMOD_CTRL:
1809 return GDK_CONTROL_MASK;
1810 case SCMOD_ALT:
1811 return GDK_MOD1_MASK;
1812 case SCMOD_SUPER:
1813 return GDK_MOD4_MASK;
1814 default:
1815 return 0;
1819 gint ScintillaGTK::PressThis(GdkEventButton *event) {
1820 try {
1821 //Platform::DebugPrintf("Press %x time=%d state = %x button = %x\n",this,event->time, event->state, event->button);
1822 // Do not use GTK+ double click events as Scintilla has its own double click detection
1823 if (event->type != GDK_BUTTON_PRESS)
1824 return FALSE;
1826 if (evbtn) {
1827 gdk_event_free(reinterpret_cast<GdkEvent *>(evbtn));
1828 evbtn = 0;
1830 evbtn = reinterpret_cast<GdkEventButton *>(gdk_event_copy(reinterpret_cast<GdkEvent *>(event)));
1831 Point pt;
1832 pt.x = int(event->x);
1833 pt.y = int(event->y);
1834 PRectangle rcClient = GetClientRectangle();
1835 //Platform::DebugPrintf("Press %0d,%0d in %0d,%0d %0d,%0d\n",
1836 // pt.x, pt.y, rcClient.left, rcClient.top, rcClient.right, rcClient.bottom);
1837 if ((pt.x > rcClient.right) || (pt.y > rcClient.bottom)) {
1838 Platform::DebugPrintf("Bad location\n");
1839 return FALSE;
1842 bool shift = (event->state & GDK_SHIFT_MASK) != 0;
1843 bool ctrl = (event->state & GDK_CONTROL_MASK) != 0;
1844 // On X, instead of sending literal modifiers use the user specified
1845 // modifier, defaulting to control instead of alt.
1846 // This is because most X window managers grab alt + click for moving
1847 bool alt = (event->state & modifierTranslated(rectangularSelectionModifier)) != 0;
1849 gtk_widget_grab_focus(PWidget(wMain));
1850 if (event->button == 1) {
1851 #if PLAT_GTK_MACOSX
1852 bool meta = ctrl;
1853 // GDK reports the Command modifer key as GDK_MOD2_MASK for button events,
1854 // not GDK_META_MASK like in key events.
1855 ctrl = (event->state & GDK_MOD2_MASK) != 0;
1856 #else
1857 bool meta = false;
1858 #endif
1859 ButtonDownWithModifiers(pt, event->time, ModifierFlags(shift, ctrl, alt, meta));
1860 } else if (event->button == 2) {
1861 // Grab the primary selection if it exists
1862 SelectionPosition pos = SPositionFromLocation(pt, false, false, UserVirtualSpace());
1863 if (OwnPrimarySelection() && primary.Empty())
1864 CopySelectionRange(&primary);
1866 sel.Clear();
1867 SetSelection(pos, pos);
1868 atomSought = atomUTF8;
1869 gtk_selection_convert(GTK_WIDGET(PWidget(wMain)), GDK_SELECTION_PRIMARY,
1870 atomSought, event->time);
1871 } else if (event->button == 3) {
1872 if (!PointInSelection(pt))
1873 SetEmptySelection(PositionFromLocation(pt));
1874 if (displayPopupMenu) {
1875 // PopUp menu
1876 // Convert to screen
1877 int ox = 0;
1878 int oy = 0;
1879 gdk_window_get_origin(PWindow(wMain), &ox, &oy);
1880 ContextMenu(Point(pt.x + ox, pt.y + oy));
1881 } else {
1882 return FALSE;
1884 } else if (event->button == 4) {
1885 // Wheel scrolling up (only GTK 1.x does it this way)
1886 if (ctrl)
1887 SetAdjustmentValue(adjustmenth, xOffset - 6);
1888 else
1889 SetAdjustmentValue(adjustmentv, topLine - 3);
1890 } else if (event->button == 5) {
1891 // Wheel scrolling down (only GTK 1.x does it this way)
1892 if (ctrl)
1893 SetAdjustmentValue(adjustmenth, xOffset + 6);
1894 else
1895 SetAdjustmentValue(adjustmentv, topLine + 3);
1897 } catch (...) {
1898 errorStatus = SC_STATUS_FAILURE;
1900 return TRUE;
1903 gint ScintillaGTK::Press(GtkWidget *widget, GdkEventButton *event) {
1904 if (event->window != WindowFromWidget(widget))
1905 return FALSE;
1906 ScintillaGTK *sciThis = ScintillaFromWidget(widget);
1907 return sciThis->PressThis(event);
1910 gint ScintillaGTK::MouseRelease(GtkWidget *widget, GdkEventButton *event) {
1911 ScintillaGTK *sciThis = ScintillaFromWidget(widget);
1912 try {
1913 //Platform::DebugPrintf("Release %x %d %d\n",sciThis,event->time,event->state);
1914 if (!sciThis->HaveMouseCapture())
1915 return FALSE;
1916 if (event->button == 1) {
1917 Point pt;
1918 pt.x = int(event->x);
1919 pt.y = int(event->y);
1920 //Platform::DebugPrintf("Up %x %x %d %d %d\n",
1921 // sciThis,event->window,event->time, pt.x, pt.y);
1922 if (event->window != PWindow(sciThis->wMain))
1923 // If mouse released on scroll bar then the position is relative to the
1924 // scrollbar, not the drawing window so just repeat the most recent point.
1925 pt = sciThis->ptMouseLast;
1926 sciThis->ButtonUp(pt, event->time, (event->state & GDK_CONTROL_MASK) != 0);
1928 } catch (...) {
1929 sciThis->errorStatus = SC_STATUS_FAILURE;
1931 return FALSE;
1934 // win32gtk and GTK >= 2 use SCROLL_* events instead of passing the
1935 // button4/5/6/7 events to the GTK app
1936 gint ScintillaGTK::ScrollEvent(GtkWidget *widget, GdkEventScroll *event) {
1937 ScintillaGTK *sciThis = ScintillaFromWidget(widget);
1938 try {
1940 if (widget == NULL || event == NULL)
1941 return FALSE;
1943 // Compute amount and direction to scroll (even tho on win32 there is
1944 // intensity of scrolling info in the native message, gtk doesn't
1945 // support this so we simulate similarly adaptive scrolling)
1946 // Note that this is disabled on OS X (Darwin) with the X11 backend
1947 // where the X11 server already has an adaptive scrolling algorithm
1948 // that fights with this one
1949 int cLineScroll;
1950 #if defined(__APPLE__) && !defined(GDK_WINDOWING_QUARTZ)
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 bool super = (event->state & GDK_MOD4_MASK) != 0;
2212 guint key = event->keyval;
2213 if ((ctrl || alt) && (key < 128))
2214 key = toupper(key);
2215 #if GTK_CHECK_VERSION(3,0,0)
2216 else if (!ctrl && (key >= GDK_KEY_KP_Multiply && key <= GDK_KEY_KP_9))
2217 #else
2218 else if (!ctrl && (key >= GDK_KP_Multiply && key <= GDK_KP_9))
2219 #endif
2220 key &= 0x7F;
2221 // Hack for keys over 256 and below command keys but makes Hungarian work.
2222 // This will have to change for Unicode
2223 else if (key >= 0xFE00)
2224 key = KeyTranslate(key);
2226 bool consumed = false;
2227 #if !(PLAT_GTK_MACOSX)
2228 bool meta = false;
2229 #else
2230 bool meta = ctrl;
2231 ctrl = (event->state & GDK_META_MASK) != 0;
2232 #endif
2233 bool added = KeyDownWithModifiers(key, ModifierFlags(shift, ctrl, alt, meta, super), &consumed) != 0;
2234 if (!consumed)
2235 consumed = added;
2236 //fprintf(stderr, "SK-key: %d %x %x\n",event->keyval, event->state, consumed);
2237 if (event->keyval == 0xffffff && event->length > 0) {
2238 ClearSelection();
2239 const int lengthInserted = pdoc->InsertString(CurrentPosition(), event->string, strlen(event->string));
2240 if (lengthInserted > 0) {
2241 MovePositionTo(CurrentPosition() + lengthInserted);
2244 return consumed;
2245 } catch (...) {
2246 errorStatus = SC_STATUS_FAILURE;
2248 return FALSE;
2251 gboolean ScintillaGTK::KeyPress(GtkWidget *widget, GdkEventKey *event) {
2252 ScintillaGTK *sciThis = ScintillaFromWidget(widget);
2253 return sciThis->KeyThis(event);
2256 gboolean ScintillaGTK::KeyRelease(GtkWidget *widget, GdkEventKey *event) {
2257 //Platform::DebugPrintf("SC-keyrel: %d %x %3s\n",event->keyval, event->state, event->string);
2258 ScintillaGTK *sciThis = ScintillaFromWidget(widget);
2259 if (gtk_im_context_filter_keypress(sciThis->im_context, event)) {
2260 return TRUE;
2262 return FALSE;
2265 #if GTK_CHECK_VERSION(3,0,0)
2267 gboolean ScintillaGTK::DrawPreeditThis(GtkWidget *widget, cairo_t *cr) {
2268 try {
2269 PreEditString pes(im_context);
2270 PangoLayout *layout = gtk_widget_create_pango_layout(PWidget(wText), pes.str);
2271 pango_layout_set_attributes(layout, pes.attrs);
2273 cairo_move_to(cr, 0, 0);
2274 pango_cairo_show_layout(cr, layout);
2276 g_object_unref(layout);
2277 } catch (...) {
2278 errorStatus = SC_STATUS_FAILURE;
2280 return TRUE;
2283 gboolean ScintillaGTK::DrawPreedit(GtkWidget *widget, cairo_t *cr, ScintillaGTK *sciThis) {
2284 return sciThis->DrawPreeditThis(widget, cr);
2287 #else
2289 gboolean ScintillaGTK::ExposePreeditThis(GtkWidget *widget, GdkEventExpose *ose) {
2290 try {
2291 PreEditString pes(im_context);
2292 PangoLayout *layout = gtk_widget_create_pango_layout(PWidget(wText), pes.str);
2293 pango_layout_set_attributes(layout, pes.attrs);
2295 cairo_t *context = gdk_cairo_create(reinterpret_cast<GdkDrawable *>(WindowFromWidget(widget)));
2296 cairo_move_to(context, 0, 0);
2297 pango_cairo_show_layout(context, layout);
2298 cairo_destroy(context);
2299 g_object_unref(layout);
2300 } catch (...) {
2301 errorStatus = SC_STATUS_FAILURE;
2303 return TRUE;
2306 gboolean ScintillaGTK::ExposePreedit(GtkWidget *widget, GdkEventExpose *ose, ScintillaGTK *sciThis) {
2307 return sciThis->ExposePreeditThis(widget, ose);
2310 #endif
2312 bool ScintillaGTK::KoreanIME() {
2313 PreEditString pes(im_context);
2314 if (pes.pscript != PANGO_SCRIPT_COMMON)
2315 lastNonCommonScript = pes.pscript;
2316 return lastNonCommonScript == PANGO_SCRIPT_HANGUL;
2319 void ScintillaGTK::MoveImeCarets(int pos) {
2320 // Move carets relatively by bytes
2321 for (size_t r=0; r<sel.Count(); r++) {
2322 int positionInsert = sel.Range(r).Start().Position();
2323 sel.Range(r).caret.SetPosition(positionInsert + pos);
2324 sel.Range(r).anchor.SetPosition(positionInsert + pos);
2328 void ScintillaGTK::DrawImeIndicator(int indicator, int len) {
2329 // Emulate the visual style of IME characters with indicators.
2330 // Draw an indicator on the character before caret by the character bytes of len
2331 // so it should be called after addCharUTF().
2332 // It does not affect caret positions.
2333 if (indicator < 8 || indicator > INDIC_MAX) {
2334 return;
2336 pdoc->decorations.SetCurrentIndicator(indicator);
2337 for (size_t r=0; r<sel.Count(); r++) {
2338 int positionInsert = sel.Range(r).Start().Position();
2339 pdoc->DecorationFillRange(positionInsert - len, 1, len);
2343 static std::vector<int> MapImeIndicators(PangoAttrList *attrs, const char *u8Str) {
2344 // Map input style to scintilla ime indicator.
2345 // Attrs position points between UTF-8 bytes.
2346 // Indicator index to be returned is character based though.
2347 glong charactersLen = g_utf8_strlen(u8Str, strlen(u8Str));
2348 std::vector<int> indicator(charactersLen, SC_INDICATOR_UNKNOWN);
2350 PangoAttrIterator *iterunderline = pango_attr_list_get_iterator(attrs);
2351 if (iterunderline) {
2352 do {
2353 PangoAttribute *attrunderline = pango_attr_iterator_get(iterunderline, PANGO_ATTR_UNDERLINE);
2354 if (attrunderline) {
2355 glong start = g_utf8_strlen(u8Str, attrunderline->start_index);
2356 glong end = g_utf8_strlen(u8Str, attrunderline->end_index);
2357 PangoUnderline uline = (PangoUnderline)((PangoAttrInt *)attrunderline)->value;
2358 for (glong i=start; i < end; ++i) {
2359 switch (uline) {
2360 case PANGO_UNDERLINE_NONE:
2361 indicator[i] = SC_INDICATOR_UNKNOWN;
2362 break;
2363 case PANGO_UNDERLINE_SINGLE: // normal input
2364 indicator[i] = SC_INDICATOR_INPUT;
2365 break;
2366 case PANGO_UNDERLINE_DOUBLE:
2367 case PANGO_UNDERLINE_LOW:
2368 case PANGO_UNDERLINE_ERROR:
2369 break;
2373 } while (pango_attr_iterator_next(iterunderline));
2374 pango_attr_iterator_destroy(iterunderline);
2377 PangoAttrIterator *itercolor = pango_attr_list_get_iterator(attrs);
2378 if (itercolor) {
2379 do {
2380 PangoAttribute *backcolor = pango_attr_iterator_get(itercolor, PANGO_ATTR_BACKGROUND);
2381 if (backcolor) {
2382 glong start = g_utf8_strlen(u8Str, backcolor->start_index);
2383 glong end = g_utf8_strlen(u8Str, backcolor->end_index);
2384 for (glong i=start; i < end; ++i) {
2385 indicator[i] = SC_INDICATOR_TARGET; // target converted
2388 } while (pango_attr_iterator_next(itercolor));
2389 pango_attr_iterator_destroy(itercolor);
2391 return indicator;
2394 void ScintillaGTK::SetCandidateWindowPos() {
2395 // Composition box accompanies candidate box.
2396 Point pt = PointMainCaret();
2397 GdkRectangle imeBox = {0}; // No need to set width
2398 imeBox.x = pt.x; // Only need positiion
2399 imeBox.y = pt.y + vs.lineHeight; // underneath the first charater
2400 gtk_im_context_set_cursor_location(im_context, &imeBox);
2403 void ScintillaGTK::CommitThis(char *commitStr) {
2404 try {
2405 //~ fprintf(stderr, "Commit '%s'\n", commitStr);
2406 view.imeCaretBlockOverride = false;
2408 if (pdoc->TentativeActive()) {
2409 pdoc->TentativeUndo();
2412 const char *charSetSource = CharacterSetID();
2414 glong uniStrLen = 0;
2415 gunichar *uniStr = g_utf8_to_ucs4_fast(commitStr, strlen(commitStr), &uniStrLen);
2416 for (glong i = 0; i < uniStrLen; i++) {
2417 gchar u8Char[UTF8MaxBytes+2] = {0};
2418 gint u8CharLen = g_unichar_to_utf8(uniStr[i], u8Char);
2419 std::string docChar = u8Char;
2420 if (!IsUnicodeMode())
2421 docChar = ConvertText(u8Char, u8CharLen, charSetSource, "UTF-8", true);
2423 AddCharUTF(docChar.c_str(), docChar.size());
2425 g_free(uniStr);
2426 ShowCaretAtCurrentPosition();
2427 } catch (...) {
2428 errorStatus = SC_STATUS_FAILURE;
2432 void ScintillaGTK::Commit(GtkIMContext *, char *str, ScintillaGTK *sciThis) {
2433 sciThis->CommitThis(str);
2436 void ScintillaGTK::PreeditChangedInlineThis() {
2437 // Copy & paste by johnsonj with a lot of helps of Neil
2438 // Great thanks for my foreruners, jiniya and BLUEnLIVE
2439 try {
2440 if (pdoc->IsReadOnly() || SelectionContainsProtected()) {
2441 gtk_im_context_reset(im_context);
2442 return;
2445 view.imeCaretBlockOverride = false; // If backspace.
2447 if (pdoc->TentativeActive()) {
2448 pdoc->TentativeUndo();
2449 } else {
2450 // No tentative undo means start of this composition so
2451 // fill in any virtual spaces.
2452 ClearBeforeTentativeStart();
2455 PreEditString preeditStr(im_context);
2456 const char *charSetSource = CharacterSetID();
2458 if (!preeditStr.validUTF8 || (charSetSource == NULL)) {
2459 ShowCaretAtCurrentPosition();
2460 return;
2463 if (preeditStr.uniStrLen == 0 || preeditStr.uniStrLen > maxLenInputIME) {
2464 //fprintf(stderr, "Do not allow over 200 chars: %i\n", preeditStr.uniStrLen);
2465 ShowCaretAtCurrentPosition();
2466 return;
2469 pdoc->TentativeStart(); // TentativeActive() from now on
2471 std::vector<int> indicator = MapImeIndicators(preeditStr.attrs, preeditStr.str);
2473 bool tmpRecordingMacro = recordingMacro;
2474 recordingMacro = false;
2475 for (glong i = 0; i < preeditStr.uniStrLen; i++) {
2476 gchar u8Char[UTF8MaxBytes+2] = {0};
2477 gint u8CharLen = g_unichar_to_utf8(preeditStr.uniStr[i], u8Char);
2478 std::string docChar = u8Char;
2479 if (!IsUnicodeMode())
2480 docChar = ConvertText(u8Char, u8CharLen, charSetSource, "UTF-8", true);
2482 AddCharUTF(docChar.c_str(), docChar.size());
2484 DrawImeIndicator(indicator[i], docChar.size());
2486 recordingMacro = tmpRecordingMacro;
2488 // Move caret to ime cursor position.
2489 int imeEndToImeCaretU32 = preeditStr.cursor_pos - preeditStr.uniStrLen;
2490 int imeCaretPosDoc = pdoc->GetRelativePosition(CurrentPosition(), imeEndToImeCaretU32);
2492 MoveImeCarets(- CurrentPosition() + imeCaretPosDoc);
2494 if (KoreanIME()) {
2495 #if !PLAT_GTK_WIN32
2496 if (preeditStr.cursor_pos > 0) {
2497 int oneCharBefore = pdoc->GetRelativePosition(CurrentPosition(), -1);
2498 MoveImeCarets(- CurrentPosition() + oneCharBefore);
2500 #endif
2501 view.imeCaretBlockOverride = true;
2504 EnsureCaretVisible();
2505 SetCandidateWindowPos();
2506 ShowCaretAtCurrentPosition();
2507 } catch (...) {
2508 errorStatus = SC_STATUS_FAILURE;
2512 void ScintillaGTK::PreeditChangedWindowedThis() {
2513 try {
2514 PreEditString pes(im_context);
2515 if (strlen(pes.str) > 0) {
2516 PangoLayout *layout = gtk_widget_create_pango_layout(PWidget(wText), pes.str);
2517 pango_layout_set_attributes(layout, pes.attrs);
2519 gint w, h;
2520 pango_layout_get_pixel_size(layout, &w, &h);
2521 g_object_unref(layout);
2523 gint x, y;
2524 gdk_window_get_origin(PWindow(wText), &x, &y);
2526 Point pt = PointMainCaret();
2527 if (pt.x < 0)
2528 pt.x = 0;
2529 if (pt.y < 0)
2530 pt.y = 0;
2532 gtk_window_move(GTK_WINDOW(PWidget(wPreedit)), x + pt.x, y + pt.y);
2533 gtk_window_resize(GTK_WINDOW(PWidget(wPreedit)), w, h);
2534 gtk_widget_show(PWidget(wPreedit));
2535 gtk_widget_queue_draw_area(PWidget(wPreeditDraw), 0, 0, w, h);
2536 } else {
2537 gtk_widget_hide(PWidget(wPreedit));
2539 } catch (...) {
2540 errorStatus = SC_STATUS_FAILURE;
2544 void ScintillaGTK::PreeditChanged(GtkIMContext *, ScintillaGTK *sciThis) {
2545 if ((sciThis->imeInteraction == imeInline) || (sciThis->KoreanIME())) {
2546 sciThis->PreeditChangedInlineThis();
2547 } else {
2548 sciThis->PreeditChangedWindowedThis();
2552 void ScintillaGTK::StyleSetText(GtkWidget *widget, GtkStyle *, void*) {
2553 RealizeText(widget, NULL);
2556 void ScintillaGTK::RealizeText(GtkWidget *widget, void*) {
2557 // Set NULL background to avoid automatic clearing so Scintilla responsible for all drawing
2558 if (WindowFromWidget(widget)) {
2559 #if GTK_CHECK_VERSION(3,0,0)
2560 gdk_window_set_background_pattern(WindowFromWidget(widget), NULL);
2561 #else
2562 gdk_window_set_back_pixmap(WindowFromWidget(widget), NULL, FALSE);
2563 #endif
2567 static GObjectClass *scintilla_class_parent_class;
2569 void ScintillaGTK::Dispose(GObject *object) {
2570 try {
2571 ScintillaObject *scio = reinterpret_cast<ScintillaObject *>(object);
2572 ScintillaGTK *sciThis = reinterpret_cast<ScintillaGTK *>(scio->pscin);
2574 if (PWidget(sciThis->scrollbarv)) {
2575 gtk_widget_unparent(PWidget(sciThis->scrollbarv));
2576 sciThis->scrollbarv = NULL;
2579 if (PWidget(sciThis->scrollbarh)) {
2580 gtk_widget_unparent(PWidget(sciThis->scrollbarh));
2581 sciThis->scrollbarh = NULL;
2584 scintilla_class_parent_class->dispose(object);
2585 } catch (...) {
2586 // Its dying so nowhere to save the status
2590 void ScintillaGTK::Destroy(GObject *object) {
2591 try {
2592 ScintillaObject *scio = SCINTILLA(object);
2594 // This avoids a double destruction
2595 if (!scio->pscin)
2596 return;
2597 ScintillaGTK *sciThis = static_cast<ScintillaGTK *>(scio->pscin);
2598 //Platform::DebugPrintf("Destroying %x %x\n", sciThis, object);
2599 sciThis->Finalise();
2601 delete sciThis;
2602 scio->pscin = 0;
2603 scintilla_class_parent_class->finalize(object);
2604 } catch (...) {
2605 // Its dead so nowhere to save the status
2609 #if GTK_CHECK_VERSION(3,0,0)
2611 gboolean ScintillaGTK::DrawTextThis(cairo_t *cr) {
2612 try {
2613 paintState = painting;
2614 repaintFullWindow = false;
2616 rcPaint = GetClientRectangle();
2618 PLATFORM_ASSERT(rgnUpdate == NULL);
2619 rgnUpdate = cairo_copy_clip_rectangle_list(cr);
2620 if (rgnUpdate && rgnUpdate->status != CAIRO_STATUS_SUCCESS) {
2621 // If not successful then ignore
2622 fprintf(stderr, "DrawTextThis failed to copy update region %d [%d]\n", rgnUpdate->status, rgnUpdate->num_rectangles);
2623 cairo_rectangle_list_destroy(rgnUpdate);
2624 rgnUpdate = 0;
2627 double x1, y1, x2, y2;
2628 cairo_clip_extents(cr, &x1, &y1, &x2, &y2);
2629 rcPaint.left = x1;
2630 rcPaint.top = y1;
2631 rcPaint.right = x2;
2632 rcPaint.bottom = y2;
2633 PRectangle rcClient = GetClientRectangle();
2634 paintingAllText = rcPaint.Contains(rcClient);
2635 Surface *surfaceWindow = Surface::Allocate(SC_TECHNOLOGY_DEFAULT);
2636 if (surfaceWindow) {
2637 surfaceWindow->Init(cr, PWidget(wText));
2638 Paint(surfaceWindow, rcPaint);
2639 surfaceWindow->Release();
2640 delete surfaceWindow;
2642 if ((paintState == paintAbandoned) || repaintFullWindow) {
2643 // Painting area was insufficient to cover new styling or brace highlight positions
2644 FullPaint();
2646 paintState = notPainting;
2647 repaintFullWindow = false;
2649 if (rgnUpdate) {
2650 cairo_rectangle_list_destroy(rgnUpdate);
2652 rgnUpdate = 0;
2653 paintState = notPainting;
2654 } catch (...) {
2655 errorStatus = SC_STATUS_FAILURE;
2658 return FALSE;
2661 gboolean ScintillaGTK::DrawText(GtkWidget *, cairo_t *cr, ScintillaGTK *sciThis) {
2662 return sciThis->DrawTextThis(cr);
2665 gboolean ScintillaGTK::DrawThis(cairo_t *cr) {
2666 try {
2667 #ifdef GTK_STYLE_CLASS_SCROLLBARS_JUNCTION /* GTK >= 3.4 */
2668 // if both scrollbars are visible, paint the little square on the bottom right corner
2669 if (verticalScrollBarVisible && horizontalScrollBarVisible && !Wrapping()) {
2670 GtkStyleContext *styleContext = gtk_widget_get_style_context(PWidget(wMain));
2671 PRectangle rc = GetClientRectangle();
2673 gtk_style_context_save(styleContext);
2674 gtk_style_context_add_class(styleContext, GTK_STYLE_CLASS_SCROLLBARS_JUNCTION);
2676 gtk_render_background(styleContext, cr, rc.right, rc.bottom,
2677 verticalScrollBarWidth, horizontalScrollBarHeight);
2678 gtk_render_frame(styleContext, cr, rc.right, rc.bottom,
2679 verticalScrollBarWidth, horizontalScrollBarHeight);
2681 gtk_style_context_restore(styleContext);
2683 #endif
2685 gtk_container_propagate_draw(
2686 GTK_CONTAINER(PWidget(wMain)), PWidget(scrollbarh), cr);
2687 gtk_container_propagate_draw(
2688 GTK_CONTAINER(PWidget(wMain)), PWidget(scrollbarv), cr);
2689 // Starting from the following version, the expose event are not propagated
2690 // for double buffered non native windows, so we need to call it ourselves
2691 // or keep the default handler
2692 #if GTK_CHECK_VERSION(3,0,0)
2693 // we want to forward on any >= 3.9.2 runtime
2694 if (gtk_check_version(3,9,2) == NULL) {
2695 gtk_container_propagate_draw(
2696 GTK_CONTAINER(PWidget(wMain)), PWidget(wText), cr);
2698 #endif
2699 } catch (...) {
2700 errorStatus = SC_STATUS_FAILURE;
2702 return FALSE;
2705 gboolean ScintillaGTK::DrawMain(GtkWidget *widget, cairo_t *cr) {
2706 ScintillaGTK *sciThis = ScintillaFromWidget(widget);
2707 return sciThis->DrawThis(cr);
2710 #else
2712 gboolean ScintillaGTK::ExposeTextThis(GtkWidget * /*widget*/, GdkEventExpose *ose) {
2713 try {
2714 paintState = painting;
2716 rcPaint.left = ose->area.x;
2717 rcPaint.top = ose->area.y;
2718 rcPaint.right = ose->area.x + ose->area.width;
2719 rcPaint.bottom = ose->area.y + ose->area.height;
2721 PLATFORM_ASSERT(rgnUpdate == NULL);
2722 rgnUpdate = gdk_region_copy(ose->region);
2723 PRectangle rcClient = GetClientRectangle();
2724 paintingAllText = rcPaint.Contains(rcClient);
2725 Surface *surfaceWindow = Surface::Allocate(SC_TECHNOLOGY_DEFAULT);
2726 if (surfaceWindow) {
2727 cairo_t *cr = gdk_cairo_create(PWindow(wText));
2728 surfaceWindow->Init(cr, PWidget(wText));
2729 Paint(surfaceWindow, rcPaint);
2730 surfaceWindow->Release();
2731 delete surfaceWindow;
2732 cairo_destroy(cr);
2734 if (paintState == paintAbandoned) {
2735 // Painting area was insufficient to cover new styling or brace highlight positions
2736 FullPaint();
2738 paintState = notPainting;
2740 if (rgnUpdate) {
2741 gdk_region_destroy(rgnUpdate);
2743 rgnUpdate = 0;
2744 } catch (...) {
2745 errorStatus = SC_STATUS_FAILURE;
2748 return FALSE;
2751 gboolean ScintillaGTK::ExposeText(GtkWidget *widget, GdkEventExpose *ose, ScintillaGTK *sciThis) {
2752 return sciThis->ExposeTextThis(widget, ose);
2755 gboolean ScintillaGTK::ExposeMain(GtkWidget *widget, GdkEventExpose *ose) {
2756 ScintillaGTK *sciThis = ScintillaFromWidget(widget);
2757 //Platform::DebugPrintf("Expose Main %0d,%0d %0d,%0d\n",
2758 //ose->area.x, ose->area.y, ose->area.width, ose->area.height);
2759 return sciThis->Expose(widget, ose);
2762 gboolean ScintillaGTK::Expose(GtkWidget *, GdkEventExpose *ose) {
2763 try {
2764 //fprintf(stderr, "Expose %0d,%0d %0d,%0d\n",
2765 //ose->area.x, ose->area.y, ose->area.width, ose->area.height);
2767 // The text is painted in ExposeText
2768 gtk_container_propagate_expose(
2769 GTK_CONTAINER(PWidget(wMain)), PWidget(scrollbarh), ose);
2770 gtk_container_propagate_expose(
2771 GTK_CONTAINER(PWidget(wMain)), PWidget(scrollbarv), ose);
2773 } catch (...) {
2774 errorStatus = SC_STATUS_FAILURE;
2776 return FALSE;
2779 #endif
2781 void ScintillaGTK::ScrollSignal(GtkAdjustment *adj, ScintillaGTK *sciThis) {
2782 try {
2783 sciThis->ScrollTo(static_cast<int>(gtk_adjustment_get_value(adj)), false);
2784 } catch (...) {
2785 sciThis->errorStatus = SC_STATUS_FAILURE;
2789 void ScintillaGTK::ScrollHSignal(GtkAdjustment *adj, ScintillaGTK *sciThis) {
2790 try {
2791 sciThis->HorizontalScrollTo(static_cast<int>(gtk_adjustment_get_value(adj)));
2792 } catch (...) {
2793 sciThis->errorStatus = SC_STATUS_FAILURE;
2797 void ScintillaGTK::SelectionReceived(GtkWidget *widget,
2798 GtkSelectionData *selection_data, guint) {
2799 ScintillaGTK *sciThis = ScintillaFromWidget(widget);
2800 //Platform::DebugPrintf("Selection received\n");
2801 sciThis->ReceivedSelection(selection_data);
2804 void ScintillaGTK::SelectionGet(GtkWidget *widget,
2805 GtkSelectionData *selection_data, guint info, guint) {
2806 ScintillaGTK *sciThis = ScintillaFromWidget(widget);
2807 try {
2808 //Platform::DebugPrintf("Selection get\n");
2809 if (SelectionOfGSD(selection_data) == GDK_SELECTION_PRIMARY) {
2810 if (sciThis->primary.Empty()) {
2811 sciThis->CopySelectionRange(&sciThis->primary);
2813 sciThis->GetSelection(selection_data, info, &sciThis->primary);
2815 } catch (...) {
2816 sciThis->errorStatus = SC_STATUS_FAILURE;
2820 gint ScintillaGTK::SelectionClear(GtkWidget *widget, GdkEventSelection *selection_event) {
2821 ScintillaGTK *sciThis = ScintillaFromWidget(widget);
2822 //Platform::DebugPrintf("Selection clear\n");
2823 sciThis->UnclaimSelection(selection_event);
2824 if (GTK_WIDGET_CLASS(sciThis->parentClass)->selection_clear_event) {
2825 return GTK_WIDGET_CLASS(sciThis->parentClass)->selection_clear_event(widget, selection_event);
2827 return TRUE;
2830 gboolean ScintillaGTK::DragMotionThis(GdkDragContext *context,
2831 gint x, gint y, guint dragtime) {
2832 try {
2833 Point npt(x, y);
2834 SetDragPosition(SPositionFromLocation(npt, false, false, UserVirtualSpace()));
2835 #if GTK_CHECK_VERSION(2,22,0)
2836 GdkDragAction preferredAction = gdk_drag_context_get_suggested_action(context);
2837 GdkDragAction actions = gdk_drag_context_get_actions(context);
2838 #else
2839 GdkDragAction preferredAction = context->suggested_action;
2840 GdkDragAction actions = context->actions;
2841 #endif
2842 SelectionPosition pos = SPositionFromLocation(npt);
2843 if ((inDragDrop == ddDragging) && (PositionInSelection(pos.Position()))) {
2844 // Avoid dragging selection onto itself as that produces a move
2845 // with no real effect but which creates undo actions.
2846 preferredAction = static_cast<GdkDragAction>(0);
2847 } else if (actions == static_cast<GdkDragAction>
2848 (GDK_ACTION_COPY | GDK_ACTION_MOVE)) {
2849 preferredAction = GDK_ACTION_MOVE;
2851 gdk_drag_status(context, preferredAction, dragtime);
2852 } catch (...) {
2853 errorStatus = SC_STATUS_FAILURE;
2855 return FALSE;
2858 gboolean ScintillaGTK::DragMotion(GtkWidget *widget, GdkDragContext *context,
2859 gint x, gint y, guint dragtime) {
2860 ScintillaGTK *sciThis = ScintillaFromWidget(widget);
2861 return sciThis->DragMotionThis(context, x, y, dragtime);
2864 void ScintillaGTK::DragLeave(GtkWidget *widget, GdkDragContext * /*context*/, guint) {
2865 ScintillaGTK *sciThis = ScintillaFromWidget(widget);
2866 try {
2867 sciThis->SetDragPosition(SelectionPosition(invalidPosition));
2868 //Platform::DebugPrintf("DragLeave %x\n", sciThis);
2869 } catch (...) {
2870 sciThis->errorStatus = SC_STATUS_FAILURE;
2874 void ScintillaGTK::DragEnd(GtkWidget *widget, GdkDragContext * /*context*/) {
2875 ScintillaGTK *sciThis = ScintillaFromWidget(widget);
2876 try {
2877 // If drag did not result in drop here or elsewhere
2878 if (!sciThis->dragWasDropped)
2879 sciThis->SetEmptySelection(sciThis->posDrag);
2880 sciThis->SetDragPosition(SelectionPosition(invalidPosition));
2881 //Platform::DebugPrintf("DragEnd %x %d\n", sciThis, sciThis->dragWasDropped);
2882 sciThis->inDragDrop = ddNone;
2883 } catch (...) {
2884 sciThis->errorStatus = SC_STATUS_FAILURE;
2888 gboolean ScintillaGTK::Drop(GtkWidget *widget, GdkDragContext * /*context*/,
2889 gint, gint, guint) {
2890 ScintillaGTK *sciThis = ScintillaFromWidget(widget);
2891 try {
2892 //Platform::DebugPrintf("Drop %x\n", sciThis);
2893 sciThis->SetDragPosition(SelectionPosition(invalidPosition));
2894 } catch (...) {
2895 sciThis->errorStatus = SC_STATUS_FAILURE;
2897 return FALSE;
2900 void ScintillaGTK::DragDataReceived(GtkWidget *widget, GdkDragContext * /*context*/,
2901 gint, gint, GtkSelectionData *selection_data, guint /*info*/, guint) {
2902 ScintillaGTK *sciThis = ScintillaFromWidget(widget);
2903 try {
2904 sciThis->ReceivedDrop(selection_data);
2905 sciThis->SetDragPosition(SelectionPosition(invalidPosition));
2906 } catch (...) {
2907 sciThis->errorStatus = SC_STATUS_FAILURE;
2911 void ScintillaGTK::DragDataGet(GtkWidget *widget, GdkDragContext *context,
2912 GtkSelectionData *selection_data, guint info, guint) {
2913 ScintillaGTK *sciThis = ScintillaFromWidget(widget);
2914 try {
2915 sciThis->dragWasDropped = true;
2916 if (!sciThis->sel.Empty()) {
2917 sciThis->GetSelection(selection_data, info, &sciThis->drag);
2919 #if GTK_CHECK_VERSION(2,22,0)
2920 GdkDragAction action = gdk_drag_context_get_selected_action(context);
2921 #else
2922 GdkDragAction action = context->action;
2923 #endif
2924 if (action == GDK_ACTION_MOVE) {
2925 for (size_t r=0; r<sciThis->sel.Count(); r++) {
2926 if (sciThis->posDrop >= sciThis->sel.Range(r).Start()) {
2927 if (sciThis->posDrop > sciThis->sel.Range(r).End()) {
2928 sciThis->posDrop.Add(-sciThis->sel.Range(r).Length());
2929 } else {
2930 sciThis->posDrop.Add(-SelectionRange(sciThis->posDrop, sciThis->sel.Range(r).Start()).Length());
2934 sciThis->ClearSelection();
2936 sciThis->SetDragPosition(SelectionPosition(invalidPosition));
2937 } catch (...) {
2938 sciThis->errorStatus = SC_STATUS_FAILURE;
2942 int ScintillaGTK::TimeOut(gpointer ptt) {
2943 TimeThunk *tt = static_cast<TimeThunk *>(ptt);
2944 tt->scintilla->TickFor(tt->reason);
2945 return 1;
2948 gboolean ScintillaGTK::IdleCallback(gpointer pSci) {
2949 ScintillaGTK *sciThis = static_cast<ScintillaGTK *>(pSci);
2950 // Idler will be automatically stopped, if there is nothing
2951 // to do while idle.
2952 bool ret = sciThis->Idle();
2953 if (ret == false) {
2954 // FIXME: This will remove the idler from GTK, we don't want to
2955 // remove it as it is removed automatically when this function
2956 // returns false (although, it should be harmless).
2957 sciThis->SetIdle(false);
2959 return ret;
2962 gboolean ScintillaGTK::StyleIdle(gpointer pSci) {
2963 ScintillaGTK *sciThis = static_cast<ScintillaGTK *>(pSci);
2964 sciThis->IdleWork();
2965 // Idler will be automatically stopped
2966 return FALSE;
2969 void ScintillaGTK::QueueIdleWork(WorkNeeded::workItems items, int upTo) {
2970 Editor::QueueIdleWork(items, upTo);
2971 if (!workNeeded.active) {
2972 // Only allow one style needed to be queued
2973 workNeeded.active = true;
2974 gdk_threads_add_idle_full(G_PRIORITY_HIGH_IDLE, StyleIdle, this, NULL);
2978 void ScintillaGTK::PopUpCB(GtkMenuItem *menuItem, ScintillaGTK *sciThis) {
2979 guint action = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(menuItem), "CmdNum"));
2980 if (action) {
2981 sciThis->Command(action);
2985 gboolean ScintillaGTK::PressCT(GtkWidget *widget, GdkEventButton *event, ScintillaGTK *sciThis) {
2986 try {
2987 if (event->window != WindowFromWidget(widget))
2988 return FALSE;
2989 if (event->type != GDK_BUTTON_PRESS)
2990 return FALSE;
2991 Point pt;
2992 pt.x = int(event->x);
2993 pt.y = int(event->y);
2994 sciThis->ct.MouseClick(pt);
2995 sciThis->CallTipClick();
2996 } catch (...) {
2998 return TRUE;
3001 #if GTK_CHECK_VERSION(3,0,0)
3003 gboolean ScintillaGTK::DrawCT(GtkWidget *widget, cairo_t *cr, CallTip *ctip) {
3004 try {
3005 Surface *surfaceWindow = Surface::Allocate(SC_TECHNOLOGY_DEFAULT);
3006 if (surfaceWindow) {
3007 surfaceWindow->Init(cr, widget);
3008 surfaceWindow->SetUnicodeMode(SC_CP_UTF8 == ctip->codePage);
3009 surfaceWindow->SetDBCSMode(ctip->codePage);
3010 ctip->PaintCT(surfaceWindow);
3011 surfaceWindow->Release();
3012 delete surfaceWindow;
3014 } catch (...) {
3015 // No pointer back to Scintilla to save status
3017 return TRUE;
3020 #else
3022 gboolean ScintillaGTK::ExposeCT(GtkWidget *widget, GdkEventExpose * /*ose*/, CallTip *ctip) {
3023 try {
3024 Surface *surfaceWindow = Surface::Allocate(SC_TECHNOLOGY_DEFAULT);
3025 if (surfaceWindow) {
3026 cairo_t *cr = gdk_cairo_create(WindowFromWidget(widget));
3027 surfaceWindow->Init(cr, widget);
3028 surfaceWindow->SetUnicodeMode(SC_CP_UTF8 == ctip->codePage);
3029 surfaceWindow->SetDBCSMode(ctip->codePage);
3030 ctip->PaintCT(surfaceWindow);
3031 surfaceWindow->Release();
3032 delete surfaceWindow;
3033 cairo_destroy(cr);
3035 } catch (...) {
3036 // No pointer back to Scintilla to save status
3038 return TRUE;
3041 #endif
3043 sptr_t ScintillaGTK::DirectFunction(
3044 sptr_t ptr, unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
3045 return reinterpret_cast<ScintillaGTK *>(ptr)->WndProc(iMessage, wParam, lParam);
3048 /* legacy name for scintilla_object_send_message */
3049 GEANY_API_SYMBOL
3050 sptr_t scintilla_send_message(ScintillaObject *sci, unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
3051 ScintillaGTK *psci = static_cast<ScintillaGTK *>(sci->pscin);
3052 return psci->WndProc(iMessage, wParam, lParam);
3055 GEANY_API_SYMBOL
3056 gintptr scintilla_object_send_message(ScintillaObject *sci, unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
3057 return scintilla_send_message(sci, iMessage, wParam, lParam);
3060 static void scintilla_class_init(ScintillaClass *klass);
3061 static void scintilla_init(ScintillaObject *sci);
3063 extern void Platform_Initialise();
3064 extern void Platform_Finalise();
3066 /* legacy name for scintilla_object_get_type */
3067 GEANY_API_SYMBOL
3068 GType scintilla_get_type() {
3069 static GType scintilla_type = 0;
3070 try {
3072 if (!scintilla_type) {
3073 scintilla_type = g_type_from_name("ScintillaObject");
3074 if (!scintilla_type) {
3075 static GTypeInfo scintilla_info = {
3076 (guint16) sizeof (ScintillaObjectClass),
3077 NULL, //(GBaseInitFunc)
3078 NULL, //(GBaseFinalizeFunc)
3079 (GClassInitFunc) scintilla_class_init,
3080 NULL, //(GClassFinalizeFunc)
3081 NULL, //gconstpointer data
3082 (guint16) sizeof (ScintillaObject),
3083 0, //n_preallocs
3084 (GInstanceInitFunc) scintilla_init,
3085 NULL //(GTypeValueTable*)
3087 scintilla_type = g_type_register_static(
3088 GTK_TYPE_CONTAINER, "ScintillaObject", &scintilla_info, (GTypeFlags) 0);
3092 } catch (...) {
3094 return scintilla_type;
3097 GEANY_API_SYMBOL
3098 GType scintilla_object_get_type() {
3099 return scintilla_get_type();
3102 void ScintillaGTK::ClassInit(OBJECT_CLASS* object_class, GtkWidgetClass *widget_class, GtkContainerClass *container_class) {
3103 Platform_Initialise();
3104 #ifdef SCI_LEXER
3105 Scintilla_LinkLexers();
3106 #endif
3107 atomClipboard = gdk_atom_intern("CLIPBOARD", FALSE);
3108 atomUTF8 = gdk_atom_intern("UTF8_STRING", FALSE);
3109 atomString = GDK_SELECTION_TYPE_STRING;
3110 atomUriList = gdk_atom_intern("text/uri-list", FALSE);
3111 atomDROPFILES_DND = gdk_atom_intern("DROPFILES_DND", FALSE);
3113 // Define default signal handlers for the class: Could move more
3114 // of the signal handlers here (those that currently attached to wDraw
3115 // in Initialise() may require coordinate translation?)
3117 object_class->dispose = Dispose;
3118 object_class->finalize = Destroy;
3119 #if GTK_CHECK_VERSION(3,0,0)
3120 widget_class->get_preferred_width = GetPreferredWidth;
3121 widget_class->get_preferred_height = GetPreferredHeight;
3122 #else
3123 widget_class->size_request = SizeRequest;
3124 #endif
3125 widget_class->size_allocate = SizeAllocate;
3126 #if GTK_CHECK_VERSION(3,0,0)
3127 widget_class->draw = DrawMain;
3128 #else
3129 widget_class->expose_event = ExposeMain;
3130 #endif
3131 widget_class->motion_notify_event = Motion;
3132 widget_class->button_press_event = Press;
3133 widget_class->button_release_event = MouseRelease;
3134 widget_class->scroll_event = ScrollEvent;
3135 widget_class->key_press_event = KeyPress;
3136 widget_class->key_release_event = KeyRelease;
3137 widget_class->focus_in_event = FocusIn;
3138 widget_class->focus_out_event = FocusOut;
3139 widget_class->selection_received = SelectionReceived;
3140 widget_class->selection_get = SelectionGet;
3141 widget_class->selection_clear_event = SelectionClear;
3143 widget_class->drag_data_received = DragDataReceived;
3144 widget_class->drag_motion = DragMotion;
3145 widget_class->drag_leave = DragLeave;
3146 widget_class->drag_end = DragEnd;
3147 widget_class->drag_drop = Drop;
3148 widget_class->drag_data_get = DragDataGet;
3150 widget_class->realize = Realize;
3151 widget_class->unrealize = UnRealize;
3152 widget_class->map = Map;
3153 widget_class->unmap = UnMap;
3155 container_class->forall = MainForAll;
3158 #define SIG_MARSHAL scintilla_marshal_NONE__INT_POINTER
3159 #define MARSHAL_ARGUMENTS G_TYPE_INT, G_TYPE_POINTER
3161 static void scintilla_class_init(ScintillaClass *klass) {
3162 try {
3163 OBJECT_CLASS *object_class = (OBJECT_CLASS*) klass;
3164 GtkWidgetClass *widget_class = (GtkWidgetClass*) klass;
3165 GtkContainerClass *container_class = (GtkContainerClass*) klass;
3167 GSignalFlags sigflags = GSignalFlags(G_SIGNAL_ACTION | G_SIGNAL_RUN_LAST);
3168 scintilla_signals[COMMAND_SIGNAL] = g_signal_new(
3169 "command",
3170 G_TYPE_FROM_CLASS(object_class),
3171 sigflags,
3172 G_STRUCT_OFFSET(ScintillaClass, command),
3173 NULL, //(GSignalAccumulator)
3174 NULL, //(gpointer)
3175 SIG_MARSHAL,
3176 G_TYPE_NONE,
3177 2, MARSHAL_ARGUMENTS);
3179 scintilla_signals[NOTIFY_SIGNAL] = g_signal_new(
3180 SCINTILLA_NOTIFY,
3181 G_TYPE_FROM_CLASS(object_class),
3182 sigflags,
3183 G_STRUCT_OFFSET(ScintillaClass, notify),
3184 NULL,
3185 NULL,
3186 SIG_MARSHAL,
3187 G_TYPE_NONE,
3188 2, MARSHAL_ARGUMENTS);
3190 klass->command = NULL;
3191 klass->notify = NULL;
3192 scintilla_class_parent_class = G_OBJECT_CLASS(g_type_class_peek_parent(klass));
3193 ScintillaGTK::ClassInit(object_class, widget_class, container_class);
3194 } catch (...) {
3198 static void scintilla_init(ScintillaObject *sci) {
3199 try {
3200 gtk_widget_set_can_focus(GTK_WIDGET(sci), TRUE);
3201 sci->pscin = new ScintillaGTK(sci);
3202 } catch (...) {
3206 /* legacy name for scintilla_object_new */
3207 GEANY_API_SYMBOL
3208 GtkWidget* scintilla_new() {
3209 GtkWidget *widget = GTK_WIDGET(g_object_new(scintilla_get_type(), NULL));
3210 gtk_widget_set_direction(widget, GTK_TEXT_DIR_LTR);
3212 return widget;
3215 GEANY_API_SYMBOL
3216 GtkWidget *scintilla_object_new() {
3217 return scintilla_new();
3220 void scintilla_set_id(ScintillaObject *sci, uptr_t id) {
3221 ScintillaGTK *psci = static_cast<ScintillaGTK *>(sci->pscin);
3222 psci->ctrlID = id;
3225 void scintilla_release_resources(void) {
3226 try {
3227 Platform_Finalise();
3228 } catch (...) {