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