r5116 | eht16 | 2010-08-05 22:13:47 +0100 (Thu, 05 Aug 2010) | 3 lines
[geany-mirror.git] / scintilla / ScintillaGTK.cxx
bloba94cfcb1327651b1885dfe02101d0de92cfc5f8e
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 <new>
7 #include <stdlib.h>
8 #include <string.h>
9 #include <stdio.h>
10 #include <ctype.h>
11 #include <time.h>
13 #include <string>
14 #include <vector>
16 #include <gtk/gtk.h>
17 #include <gdk/gdkkeysyms.h>
19 #include "Platform.h"
21 #if PLAT_GTK_WIN32
22 #include "windows.h"
23 #endif
25 #include "Scintilla.h"
26 #include "ScintillaWidget.h"
27 #ifdef SCI_LEXER
28 #include "SciLexer.h"
29 #include "PropSet.h"
30 #include "PropSetSimple.h"
31 #include "Accessor.h"
32 #include "KeyWords.h"
33 #endif
34 #include "SVector.h"
35 #include "SplitVector.h"
36 #include "Partitioning.h"
37 #include "RunStyles.h"
38 #include "ContractionState.h"
39 #include "CellBuffer.h"
40 #include "CallTip.h"
41 #include "KeyMap.h"
42 #include "Indicator.h"
43 #include "XPM.h"
44 #include "LineMarker.h"
45 #include "Style.h"
46 #include "AutoComplete.h"
47 #include "ViewStyle.h"
48 #include "Decoration.h"
49 #include "CharClassify.h"
50 #include "Document.h"
51 #include "Selection.h"
52 #include "PositionCache.h"
53 #include "Editor.h"
54 #include "ScintillaBase.h"
55 #include "UniConversion.h"
57 #include "gtk/gtksignal.h"
58 #include "gtk/gtkmarshal.h"
59 #include "scintilla-marshal.h"
61 #ifdef SCI_LEXER
62 #include <glib.h>
63 #include <gmodule.h>
64 #include "ExternalLexer.h"
65 #endif
67 #include "Converter.h"
69 #ifdef _MSC_VER
70 // Constant conditional expressions are because of GTK+ headers
71 #pragma warning(disable: 4127)
72 // Ignore unreferenced local functions in GTK+ headers
73 #pragma warning(disable: 4505)
74 #endif
76 #if GTK_CHECK_VERSION(2,6,0)
77 #define USE_GTK_CLIPBOARD
78 #endif
80 #define OBJECT_CLASS GObjectClass
82 #ifdef SCI_NAMESPACE
83 using namespace Scintilla;
84 #endif
86 extern char *UTF8FromLatin1(const char *s, int &len);
88 class ScintillaGTK : public ScintillaBase {
89 _ScintillaObject *sci;
90 Window wText;
91 Window scrollbarv;
92 Window scrollbarh;
93 GtkObject *adjustmentv;
94 GtkObject *adjustmenth;
95 int scrollBarWidth;
96 int scrollBarHeight;
98 // Because clipboard access is asynchronous, copyText is created by Copy
99 #ifndef USE_GTK_CLIPBOARD
100 SelectionText copyText;
101 #endif
103 SelectionText primary;
105 GdkEventButton evbtn;
106 bool capturedMouse;
107 bool dragWasDropped;
108 int lastKey;
109 int rectangularSelectionModifier;
111 GtkWidgetClass *parentClass;
113 static GdkAtom atomClipboard;
114 static GdkAtom atomUTF8;
115 static GdkAtom atomString;
116 static GdkAtom atomUriList;
117 static GdkAtom atomDROPFILES_DND;
118 GdkAtom atomSought;
120 #if PLAT_GTK_WIN32
121 CLIPFORMAT cfColumnSelect;
122 #endif
124 Window wPreedit;
125 Window wPreeditDraw;
126 GtkIMContext *im_context;
128 // Wheel mouse support
129 unsigned int linesPerScroll;
130 GTimeVal lastWheelMouseTime;
131 gint lastWheelMouseDirection;
132 gint wheelMouseIntensity;
134 GdkRegion *rgnUpdate;
136 // Private so ScintillaGTK objects can not be copied
137 ScintillaGTK(const ScintillaGTK &);
138 ScintillaGTK &operator=(const ScintillaGTK &);
140 public:
141 ScintillaGTK(_ScintillaObject *sci_);
142 virtual ~ScintillaGTK();
143 static void ClassInit(OBJECT_CLASS* object_class, GtkWidgetClass *widget_class, GtkContainerClass *container_class);
144 private:
145 virtual void Initialise();
146 virtual void Finalise();
147 virtual void DisplayCursor(Window::Cursor c);
148 virtual bool DragThreshold(Point ptStart, Point ptNow);
149 virtual void StartDrag();
150 int TargetAsUTF8(char *text);
151 int EncodedFromUTF8(char *utf8, char *encoded);
152 virtual bool ValidCodePage(int codePage) const;
153 public: // Public for scintilla_send_message
154 virtual sptr_t WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam);
155 private:
156 virtual sptr_t DefWndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam);
157 virtual void SetTicking(bool on);
158 virtual bool SetIdle(bool on);
159 virtual void SetMouseCapture(bool on);
160 virtual bool HaveMouseCapture();
161 virtual bool PaintContains(PRectangle rc);
162 void FullPaint();
163 virtual PRectangle GetClientRectangle();
164 void SyncPaint(PRectangle rc);
165 virtual void ScrollText(int linesToMove);
166 virtual void SetVerticalScrollPos();
167 virtual void SetHorizontalScrollPos();
168 virtual bool ModifyScrollBars(int nMax, int nPage);
169 void ReconfigureScrollBars();
170 virtual void NotifyChange();
171 virtual void NotifyFocus(bool focus);
172 virtual void NotifyParent(SCNotification scn);
173 void NotifyKey(int key, int modifiers);
174 void NotifyURIDropped(const char *list);
175 const char *CharacterSetID() const;
176 virtual CaseFolder *CaseFolderForEncoding();
177 virtual std::string CaseMapString(const std::string &s, int caseMapping);
178 virtual int KeyDefault(int key, int modifiers);
179 virtual void CopyToClipboard(const SelectionText &selectedText);
180 virtual void Copy();
181 virtual void Paste();
182 virtual void CreateCallTipWindow(PRectangle rc);
183 virtual void AddToPopUp(const char *label, int cmd = 0, bool enabled = true);
184 bool OwnPrimarySelection();
185 virtual void ClaimSelection();
186 void GetGtkSelectionText(GtkSelectionData *selectionData, SelectionText &selText);
187 void ReceivedSelection(GtkSelectionData *selection_data);
188 void ReceivedDrop(GtkSelectionData *selection_data);
189 static void GetSelection(GtkSelectionData *selection_data, guint info, SelectionText *selected);
190 #ifdef USE_GTK_CLIPBOARD
191 void StoreOnClipboard(SelectionText *clipText);
192 static void ClipboardGetSelection(GtkClipboard* clip, GtkSelectionData *selection_data, guint info, void *data);
193 static void ClipboardClearSelection(GtkClipboard* clip, void *data);
194 #endif
196 void UnclaimSelection(GdkEventSelection *selection_event);
197 void Resize(int width, int height);
199 // Callback functions
200 void RealizeThis(GtkWidget *widget);
201 static void Realize(GtkWidget *widget);
202 void UnRealizeThis(GtkWidget *widget);
203 static void UnRealize(GtkWidget *widget);
204 void MapThis();
205 static void Map(GtkWidget *widget);
206 void UnMapThis();
207 static void UnMap(GtkWidget *widget);
208 static gint CursorMoved(GtkWidget *widget, int xoffset, int yoffset, ScintillaGTK *sciThis);
209 gint FocusInThis(GtkWidget *widget);
210 static gint FocusIn(GtkWidget *widget, GdkEventFocus *event);
211 gint FocusOutThis(GtkWidget *widget);
212 static gint FocusOut(GtkWidget *widget, GdkEventFocus *event);
213 static void SizeRequest(GtkWidget *widget, GtkRequisition *requisition);
214 static void SizeAllocate(GtkWidget *widget, GtkAllocation *allocation);
215 gint Expose(GtkWidget *widget, GdkEventExpose *ose);
216 static gint ExposeMain(GtkWidget *widget, GdkEventExpose *ose);
217 static void Draw(GtkWidget *widget, GdkRectangle *area);
218 void ForAll(GtkCallback callback, gpointer callback_data);
219 static void MainForAll(GtkContainer *container, gboolean include_internals, GtkCallback callback, gpointer callback_data);
221 static void ScrollSignal(GtkAdjustment *adj, ScintillaGTK *sciThis);
222 static void ScrollHSignal(GtkAdjustment *adj, ScintillaGTK *sciThis);
223 gint PressThis(GdkEventButton *event);
224 static gint Press(GtkWidget *widget, GdkEventButton *event);
225 static gint MouseRelease(GtkWidget *widget, GdkEventButton *event);
226 static gint ScrollEvent(GtkWidget *widget, GdkEventScroll *event);
227 static gint Motion(GtkWidget *widget, GdkEventMotion *event);
228 gboolean KeyThis(GdkEventKey *event);
229 static gboolean KeyPress(GtkWidget *widget, GdkEventKey *event);
230 static gboolean KeyRelease(GtkWidget *widget, GdkEventKey *event);
231 gboolean ExposePreeditThis(GtkWidget *widget, GdkEventExpose *ose);
232 static gboolean ExposePreedit(GtkWidget *widget, GdkEventExpose *ose, ScintillaGTK *sciThis);
233 void CommitThis(char *str);
234 static void Commit(GtkIMContext *context, char *str, ScintillaGTK *sciThis);
235 void PreeditChangedThis();
236 static void PreeditChanged(GtkIMContext *context, ScintillaGTK *sciThis);
237 static gint StyleSetText(GtkWidget *widget, GtkStyle *previous, void*);
238 static gint RealizeText(GtkWidget *widget, void*);
239 static void Destroy(GObject *object);
240 static void SelectionReceived(GtkWidget *widget, GtkSelectionData *selection_data,
241 guint time);
242 static void SelectionGet(GtkWidget *widget, GtkSelectionData *selection_data,
243 guint info, guint time);
244 static gint SelectionClear(GtkWidget *widget, GdkEventSelection *selection_event);
245 static void DragBegin(GtkWidget *widget, GdkDragContext *context);
246 gboolean DragMotionThis(GdkDragContext *context, gint x, gint y, guint dragtime);
247 static gboolean DragMotion(GtkWidget *widget, GdkDragContext *context,
248 gint x, gint y, guint dragtime);
249 static void DragLeave(GtkWidget *widget, GdkDragContext *context,
250 guint time);
251 static void DragEnd(GtkWidget *widget, GdkDragContext *context);
252 static gboolean Drop(GtkWidget *widget, GdkDragContext *context,
253 gint x, gint y, guint time);
254 static void DragDataReceived(GtkWidget *widget, GdkDragContext *context,
255 gint x, gint y, GtkSelectionData *selection_data, guint info, guint time);
256 static void DragDataGet(GtkWidget *widget, GdkDragContext *context,
257 GtkSelectionData *selection_data, guint info, guint time);
258 static gint TimeOut(ScintillaGTK *sciThis);
259 static gboolean IdleCallback(ScintillaGTK *sciThis);
260 static gboolean StyleIdle(ScintillaGTK *sciThis);
261 virtual void QueueStyling(int upTo);
262 static void PopUpCB(ScintillaGTK *sciThis, guint action, GtkWidget *widget);
264 gint ExposeTextThis(GtkWidget *widget, GdkEventExpose *ose);
265 static gint ExposeText(GtkWidget *widget, GdkEventExpose *ose, ScintillaGTK *sciThis);
267 static gint ExposeCT(GtkWidget *widget, GdkEventExpose *ose, CallTip *ct);
268 static gint PressCT(GtkWidget *widget, GdkEventButton *event, ScintillaGTK *sciThis);
270 static sptr_t DirectFunction(ScintillaGTK *sciThis,
271 unsigned int iMessage, uptr_t wParam, sptr_t lParam);
274 enum {
275 COMMAND_SIGNAL,
276 NOTIFY_SIGNAL,
277 LAST_SIGNAL
280 static gint scintilla_signals[LAST_SIGNAL] = { 0 };
282 enum {
283 TARGET_STRING,
284 TARGET_TEXT,
285 TARGET_COMPOUND_TEXT,
286 TARGET_UTF8_STRING,
287 TARGET_URI
290 GdkAtom ScintillaGTK::atomClipboard = 0;
291 GdkAtom ScintillaGTK::atomUTF8 = 0;
292 GdkAtom ScintillaGTK::atomString = 0;
293 GdkAtom ScintillaGTK::atomUriList = 0;
294 GdkAtom ScintillaGTK::atomDROPFILES_DND = 0;
296 static const GtkTargetEntry clipboardCopyTargets[] = {
297 { (gchar *) "UTF8_STRING", 0, TARGET_UTF8_STRING },
298 { (gchar *) "STRING", 0, TARGET_STRING },
300 static const gint nClipboardCopyTargets = sizeof(clipboardCopyTargets) / sizeof(clipboardCopyTargets[0]);
302 static const GtkTargetEntry clipboardPasteTargets[] = {
303 { (gchar *) "text/uri-list", 0, TARGET_URI },
304 { (gchar *) "UTF8_STRING", 0, TARGET_UTF8_STRING },
305 { (gchar *) "STRING", 0, TARGET_STRING },
307 static const gint nClipboardPasteTargets = sizeof(clipboardPasteTargets) / sizeof(clipboardPasteTargets[0]);
309 static GtkWidget *PWidget(Window &w) {
310 return reinterpret_cast<GtkWidget *>(w.GetID());
313 static ScintillaGTK *ScintillaFromWidget(GtkWidget *widget) {
314 ScintillaObject *scio = reinterpret_cast<ScintillaObject *>(widget);
315 return reinterpret_cast<ScintillaGTK *>(scio->pscin);
318 ScintillaGTK::ScintillaGTK(_ScintillaObject *sci_) :
319 adjustmentv(0), adjustmenth(0),
320 scrollBarWidth(30), scrollBarHeight(30),
321 capturedMouse(false), dragWasDropped(false),
322 lastKey(0), rectangularSelectionModifier(SCMOD_CTRL), parentClass(0),
323 im_context(NULL),
324 lastWheelMouseDirection(0),
325 wheelMouseIntensity(0),
326 rgnUpdate(0) {
327 sci = sci_;
328 wMain = GTK_WIDGET(sci);
330 #if PLAT_GTK_WIN32
331 rectangularSelectionModifier = SCMOD_ALT;
332 #else
333 rectangularSelectionModifier = SCMOD_CTRL;
334 #endif
336 #if PLAT_GTK_WIN32
337 // There does not seem to be a real standard for indicating that the clipboard
338 // contains a rectangular selection, so copy Developer Studio.
339 cfColumnSelect = static_cast<CLIPFORMAT>(
340 ::RegisterClipboardFormat("MSDEVColumnSelect"));
342 // Get intellimouse parameters when running on win32; otherwise use
343 // reasonable default
344 #ifndef SPI_GETWHEELSCROLLLINES
345 #define SPI_GETWHEELSCROLLLINES 104
346 #endif
347 ::SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &linesPerScroll, 0);
348 #else
349 linesPerScroll = 4;
350 #endif
351 lastWheelMouseTime.tv_sec = 0;
352 lastWheelMouseTime.tv_usec = 0;
354 Initialise();
357 ScintillaGTK::~ScintillaGTK() {
360 void ScintillaGTK::RealizeThis(GtkWidget *widget) {
361 //Platform::DebugPrintf("ScintillaGTK::realize this\n");
362 GTK_WIDGET_SET_FLAGS(widget, GTK_REALIZED);
363 GdkWindowAttr attrs;
364 attrs.window_type = GDK_WINDOW_CHILD;
365 attrs.x = widget->allocation.x;
366 attrs.y = widget->allocation.y;
367 attrs.width = widget->allocation.width;
368 attrs.height = widget->allocation.height;
369 attrs.wclass = GDK_INPUT_OUTPUT;
370 attrs.visual = gtk_widget_get_visual(widget);
371 attrs.colormap = gtk_widget_get_colormap(widget);
372 attrs.event_mask = gtk_widget_get_events(widget) | GDK_EXPOSURE_MASK;
373 GdkCursor *cursor = gdk_cursor_new(GDK_XTERM);
374 attrs.cursor = cursor;
375 widget->window = gdk_window_new(gtk_widget_get_parent_window(widget), &attrs,
376 GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP | GDK_WA_CURSOR);
377 gdk_window_set_user_data(widget->window, widget);
378 gdk_window_set_background(widget->window, &widget->style->bg[GTK_STATE_NORMAL]);
379 gdk_window_show(widget->window);
380 gdk_cursor_destroy(cursor);
381 widget->style = gtk_style_attach(widget->style, widget->window);
382 wPreedit = gtk_window_new(GTK_WINDOW_POPUP);
383 wPreeditDraw = gtk_drawing_area_new();
384 GtkWidget *predrw = PWidget(wPreeditDraw); // No code inside the G_OBJECT macro
385 g_signal_connect(G_OBJECT(predrw), "expose_event",
386 G_CALLBACK(ExposePreedit), this);
387 gtk_container_add(GTK_CONTAINER(PWidget(wPreedit)), predrw);
388 gtk_widget_realize(PWidget(wPreedit));
389 gtk_widget_realize(predrw);
390 gtk_widget_show(predrw);
392 im_context = gtk_im_multicontext_new();
393 g_signal_connect(G_OBJECT(im_context), "commit",
394 G_CALLBACK(Commit), this);
395 g_signal_connect(G_OBJECT(im_context), "preedit_changed",
396 G_CALLBACK(PreeditChanged), this);
397 gtk_im_context_set_client_window(im_context, widget->window);
398 GtkWidget *widtxt = PWidget(wText); // // No code inside the G_OBJECT macro
399 g_signal_connect_after(G_OBJECT(widtxt), "style_set",
400 G_CALLBACK(ScintillaGTK::StyleSetText), NULL);
401 g_signal_connect_after(G_OBJECT(widtxt), "realize",
402 G_CALLBACK(ScintillaGTK::RealizeText), NULL);
403 gtk_widget_realize(widtxt);
404 gtk_widget_realize(PWidget(scrollbarv));
405 gtk_widget_realize(PWidget(scrollbarh));
408 void ScintillaGTK::Realize(GtkWidget *widget) {
409 ScintillaGTK *sciThis = ScintillaFromWidget(widget);
410 sciThis->RealizeThis(widget);
413 void ScintillaGTK::UnRealizeThis(GtkWidget *widget) {
414 try {
415 if (GTK_WIDGET_MAPPED(widget)) {
416 gtk_widget_unmap(widget);
418 GTK_WIDGET_UNSET_FLAGS(widget, GTK_REALIZED);
419 gtk_widget_unrealize(PWidget(wText));
420 gtk_widget_unrealize(PWidget(scrollbarv));
421 gtk_widget_unrealize(PWidget(scrollbarh));
422 gtk_widget_unrealize(PWidget(wPreedit));
423 gtk_widget_unrealize(PWidget(wPreeditDraw));
424 g_object_unref(im_context);
425 im_context = NULL;
426 if (GTK_WIDGET_CLASS(parentClass)->unrealize)
427 GTK_WIDGET_CLASS(parentClass)->unrealize(widget);
429 Finalise();
430 } catch (...) {
431 errorStatus = SC_STATUS_FAILURE;
435 void ScintillaGTK::UnRealize(GtkWidget *widget) {
436 ScintillaGTK *sciThis = ScintillaFromWidget(widget);
437 sciThis->UnRealizeThis(widget);
440 static void MapWidget(GtkWidget *widget) {
441 if (widget &&
442 GTK_WIDGET_VISIBLE(widget) &&
443 !GTK_WIDGET_MAPPED(widget)) {
444 gtk_widget_map(widget);
448 void ScintillaGTK::MapThis() {
449 try {
450 //Platform::DebugPrintf("ScintillaGTK::map this\n");
451 GTK_WIDGET_SET_FLAGS(PWidget(wMain), GTK_MAPPED);
452 MapWidget(PWidget(wText));
453 MapWidget(PWidget(scrollbarh));
454 MapWidget(PWidget(scrollbarv));
455 wMain.SetCursor(Window::cursorArrow);
456 scrollbarv.SetCursor(Window::cursorArrow);
457 scrollbarh.SetCursor(Window::cursorArrow);
458 ChangeSize();
459 gdk_window_show(PWidget(wMain)->window);
460 } catch (...) {
461 errorStatus = SC_STATUS_FAILURE;
465 void ScintillaGTK::Map(GtkWidget *widget) {
466 ScintillaGTK *sciThis = ScintillaFromWidget(widget);
467 sciThis->MapThis();
470 void ScintillaGTK::UnMapThis() {
471 try {
472 //Platform::DebugPrintf("ScintillaGTK::unmap this\n");
473 GTK_WIDGET_UNSET_FLAGS(PWidget(wMain), GTK_MAPPED);
474 DropGraphics();
475 gdk_window_hide(PWidget(wMain)->window);
476 gtk_widget_unmap(PWidget(wText));
477 gtk_widget_unmap(PWidget(scrollbarh));
478 gtk_widget_unmap(PWidget(scrollbarv));
479 } catch (...) {
480 errorStatus = SC_STATUS_FAILURE;
484 void ScintillaGTK::UnMap(GtkWidget *widget) {
485 ScintillaGTK *sciThis = ScintillaFromWidget(widget);
486 sciThis->UnMapThis();
489 void ScintillaGTK::ForAll(GtkCallback callback, gpointer callback_data) {
490 try {
491 (*callback) (PWidget(wText), callback_data);
492 (*callback) (PWidget(scrollbarv), callback_data);
493 (*callback) (PWidget(scrollbarh), callback_data);
494 } catch (...) {
495 errorStatus = SC_STATUS_FAILURE;
499 void ScintillaGTK::MainForAll(GtkContainer *container, gboolean include_internals, GtkCallback callback, gpointer callback_data) {
500 ScintillaGTK *sciThis = ScintillaFromWidget((GtkWidget *)container);
502 if (callback != NULL && include_internals) {
503 sciThis->ForAll(callback, callback_data);
507 gint ScintillaGTK::CursorMoved(GtkWidget *, int xoffset, int yoffset, ScintillaGTK *sciThis) {
508 GdkRectangle area;
509 area.x = xoffset;
510 area.y = yoffset;
511 area.width = 1;
512 area.height = 1;
513 gtk_im_context_set_cursor_location(sciThis->im_context, &area);
514 return FALSE;
517 gint ScintillaGTK::FocusInThis(GtkWidget *widget) {
518 try {
519 GTK_WIDGET_SET_FLAGS(widget, GTK_HAS_FOCUS);
520 SetFocusState(true);
521 if (im_context != NULL) {
522 gchar *str = NULL;
523 gint cursor_pos;
525 gtk_im_context_get_preedit_string(im_context, &str, NULL, &cursor_pos);
526 if (PWidget(wPreedit) != NULL) {
527 if (strlen(str) > 0) {
528 gtk_widget_show(PWidget(wPreedit));
529 } else {
530 gtk_widget_hide(PWidget(wPreedit));
533 g_free(str);
534 gtk_im_context_focus_in(im_context);
537 } catch (...) {
538 errorStatus = SC_STATUS_FAILURE;
540 return FALSE;
543 gint ScintillaGTK::FocusIn(GtkWidget *widget, GdkEventFocus * /*event*/) {
544 ScintillaGTK *sciThis = ScintillaFromWidget(widget);
545 return sciThis->FocusInThis(widget);
548 gint ScintillaGTK::FocusOutThis(GtkWidget *widget) {
549 try {
550 GTK_WIDGET_UNSET_FLAGS(widget, GTK_HAS_FOCUS);
551 SetFocusState(false);
553 if (PWidget(wPreedit) != NULL)
554 gtk_widget_hide(PWidget(wPreedit));
555 if (im_context != NULL)
556 gtk_im_context_focus_out(im_context);
558 } catch (...) {
559 errorStatus = SC_STATUS_FAILURE;
561 return FALSE;
564 gint ScintillaGTK::FocusOut(GtkWidget *widget, GdkEventFocus * /*event*/) {
565 ScintillaGTK *sciThis = ScintillaFromWidget(widget);
566 return sciThis->FocusOutThis(widget);
569 void ScintillaGTK::SizeRequest(GtkWidget *widget, GtkRequisition *requisition) {
570 ScintillaGTK *sciThis = ScintillaFromWidget(widget);
571 requisition->width = 600;
572 requisition->height = gdk_screen_height();
573 GtkRequisition child_requisition;
574 gtk_widget_size_request(PWidget(sciThis->scrollbarh), &child_requisition);
575 gtk_widget_size_request(PWidget(sciThis->scrollbarv), &child_requisition);
578 void ScintillaGTK::SizeAllocate(GtkWidget *widget, GtkAllocation *allocation) {
579 ScintillaGTK *sciThis = ScintillaFromWidget(widget);
580 try {
581 widget->allocation = *allocation;
582 if (GTK_WIDGET_REALIZED(widget))
583 gdk_window_move_resize(widget->window,
584 widget->allocation.x,
585 widget->allocation.y,
586 widget->allocation.width,
587 widget->allocation.height);
589 sciThis->Resize(allocation->width, allocation->height);
591 } catch (...) {
592 sciThis->errorStatus = SC_STATUS_FAILURE;
596 void ScintillaGTK::Initialise() {
597 //Platform::DebugPrintf("ScintillaGTK::Initialise\n");
598 parentClass = reinterpret_cast<GtkWidgetClass *>(
599 gtk_type_class(gtk_container_get_type()));
601 GTK_WIDGET_SET_FLAGS(PWidget(wMain), GTK_CAN_FOCUS);
602 GTK_WIDGET_SET_FLAGS(GTK_WIDGET(PWidget(wMain)), GTK_SENSITIVE);
603 gtk_widget_set_events(PWidget(wMain),
604 GDK_EXPOSURE_MASK
605 | GDK_STRUCTURE_MASK
606 | GDK_KEY_PRESS_MASK
607 | GDK_KEY_RELEASE_MASK
608 | GDK_FOCUS_CHANGE_MASK
609 | GDK_LEAVE_NOTIFY_MASK
610 | GDK_BUTTON_PRESS_MASK
611 | GDK_BUTTON_RELEASE_MASK
612 | GDK_POINTER_MOTION_MASK
613 | GDK_POINTER_MOTION_HINT_MASK);
615 wText = gtk_drawing_area_new();
616 gtk_widget_set_parent(PWidget(wText), PWidget(wMain));
617 GtkWidget *widtxt = PWidget(wText); // No code inside the G_OBJECT macro
618 gtk_widget_show(widtxt);
619 g_signal_connect(G_OBJECT(widtxt), "expose_event",
620 G_CALLBACK(ScintillaGTK::ExposeText), this);
621 gtk_widget_set_events(widtxt, GDK_EXPOSURE_MASK);
622 // Avoid background drawing flash
623 gtk_widget_set_double_buffered(widtxt, FALSE);
624 gtk_drawing_area_size(GTK_DRAWING_AREA(widtxt),
625 100,100);
626 adjustmentv = gtk_adjustment_new(0.0, 0.0, 201.0, 1.0, 20.0, 20.0);
627 scrollbarv = gtk_vscrollbar_new(GTK_ADJUSTMENT(adjustmentv));
628 GTK_WIDGET_UNSET_FLAGS(PWidget(scrollbarv), GTK_CAN_FOCUS);
629 g_signal_connect(G_OBJECT(adjustmentv), "value_changed",
630 G_CALLBACK(ScrollSignal), this);
631 gtk_widget_set_parent(PWidget(scrollbarv), PWidget(wMain));
632 gtk_widget_show(PWidget(scrollbarv));
634 adjustmenth = gtk_adjustment_new(0.0, 0.0, 101.0, 1.0, 20.0, 20.0);
635 scrollbarh = gtk_hscrollbar_new(GTK_ADJUSTMENT(adjustmenth));
636 GTK_WIDGET_UNSET_FLAGS(PWidget(scrollbarh), GTK_CAN_FOCUS);
637 g_signal_connect(G_OBJECT(adjustmenth), "value_changed",
638 G_CALLBACK(ScrollHSignal), this);
639 gtk_widget_set_parent(PWidget(scrollbarh), PWidget(wMain));
640 gtk_widget_show(PWidget(scrollbarh));
642 gtk_widget_grab_focus(PWidget(wMain));
644 gtk_selection_add_targets(GTK_WIDGET(PWidget(wMain)), GDK_SELECTION_PRIMARY,
645 clipboardCopyTargets, nClipboardCopyTargets);
647 #ifndef USE_GTK_CLIPBOARD
648 gtk_selection_add_targets(GTK_WIDGET(PWidget(wMain)), atomClipboard,
649 clipboardPasteTargets, nClipboardPasteTargets);
650 #endif
652 gtk_drag_dest_set(GTK_WIDGET(PWidget(wMain)),
653 GTK_DEST_DEFAULT_ALL, clipboardPasteTargets, nClipboardPasteTargets,
654 static_cast<GdkDragAction>(GDK_ACTION_COPY | GDK_ACTION_MOVE));
656 // Set caret period based on GTK settings
657 gboolean blinkOn = false;
658 if (g_object_class_find_property(G_OBJECT_GET_CLASS(
659 G_OBJECT(gtk_settings_get_default())), "gtk-cursor-blink")) {
660 g_object_get(G_OBJECT(
661 gtk_settings_get_default()), "gtk-cursor-blink", &blinkOn, NULL);
663 if (blinkOn &&
664 g_object_class_find_property(G_OBJECT_GET_CLASS(
665 G_OBJECT(gtk_settings_get_default())), "gtk-cursor-blink-time")) {
666 gint value;
667 g_object_get(G_OBJECT(
668 gtk_settings_get_default()), "gtk-cursor-blink-time", &value, NULL);
669 caret.period = gint(value / 1.75);
670 } else {
671 caret.period = 0;
674 SetTicking(true);
677 void ScintillaGTK::Finalise() {
678 SetTicking(false);
679 ScintillaBase::Finalise();
682 void ScintillaGTK::DisplayCursor(Window::Cursor c) {
683 if (cursorMode == SC_CURSORNORMAL)
684 wText.SetCursor(c);
685 else
686 wText.SetCursor(static_cast<Window::Cursor>(cursorMode));
689 bool ScintillaGTK::DragThreshold(Point ptStart, Point ptNow) {
690 return gtk_drag_check_threshold(GTK_WIDGET(PWidget(wMain)),
691 ptStart.x, ptStart.y, ptNow.x, ptNow.y);
694 void ScintillaGTK::StartDrag() {
695 dragWasDropped = false;
696 inDragDrop = ddDragging;
697 GtkTargetList *tl = gtk_target_list_new(clipboardCopyTargets, nClipboardCopyTargets);
698 gtk_drag_begin(GTK_WIDGET(PWidget(wMain)),
700 static_cast<GdkDragAction>(GDK_ACTION_COPY | GDK_ACTION_MOVE),
701 evbtn.button,
702 reinterpret_cast<GdkEvent *>(&evbtn));
705 static char *ConvertText(int *lenResult, char *s, size_t len, const char *charSetDest,
706 const char *charSetSource, bool transliterations, bool silent=false) {
707 // s is not const because of different versions of iconv disagreeing about const
708 *lenResult = 0;
709 char *destForm = 0;
710 Converter conv(charSetDest, charSetSource, transliterations);
711 if (conv) {
712 destForm = new char[len*3+1];
713 char *pin = s;
714 size_t inLeft = len;
715 char *pout = destForm;
716 size_t outLeft = len*3+1;
717 size_t conversions = conv.Convert(&pin, &inLeft, &pout, &outLeft);
718 if (conversions == ((size_t)(-1))) {
719 if (!silent)
720 fprintf(stderr, "iconv %s->%s failed for %s\n",
721 charSetSource, charSetDest, static_cast<char *>(s));
722 delete []destForm;
723 destForm = 0;
724 } else {
725 //fprintf(stderr, "iconv OK %s %d\n", destForm, pout - destForm);
726 *pout = '\0';
727 *lenResult = pout - destForm;
729 } else {
730 fprintf(stderr, "Can not iconv %s %s\n", charSetDest, charSetSource);
732 if (!destForm) {
733 destForm = new char[1];
734 destForm[0] = '\0';
735 *lenResult = 0;
737 return destForm;
740 // Returns the target converted to UTF8.
741 // Return the length in bytes.
742 int ScintillaGTK::TargetAsUTF8(char *text) {
743 int targetLength = targetEnd - targetStart;
744 if (IsUnicodeMode()) {
745 if (text) {
746 pdoc->GetCharRange(text, targetStart, targetLength);
748 } else {
749 // Need to convert
750 const char *charSetBuffer = CharacterSetID();
751 if (*charSetBuffer) {
752 //~ fprintf(stderr, "AsUTF8 %s %d %0d-%0d\n", charSetBuffer, targetLength, targetStart, targetEnd);
753 char *s = new char[targetLength];
754 if (s) {
755 pdoc->GetCharRange(s, targetStart, targetLength);
756 //~ fprintf(stderr, " \"%s\"\n", s);
757 if (text) {
758 char *tmputf = ConvertText(&targetLength, s, targetLength, "UTF-8", charSetBuffer, false);
759 memcpy(text, tmputf, targetLength);
760 delete []tmputf;
761 //~ fprintf(stderr, " \"%s\"\n", text);
763 delete []s;
765 } else {
766 if (text) {
767 pdoc->GetCharRange(text, targetStart, targetLength);
771 //~ fprintf(stderr, "Length = %d bytes\n", targetLength);
772 return targetLength;
775 // Translates a nul terminated UTF8 string into the document encoding.
776 // Return the length of the result in bytes.
777 int ScintillaGTK::EncodedFromUTF8(char *utf8, char *encoded) {
778 int inputLength = (lengthForEncode >= 0) ? lengthForEncode : strlen(utf8);
779 if (IsUnicodeMode()) {
780 if (encoded) {
781 memcpy(encoded, utf8, inputLength);
783 return inputLength;
784 } else {
785 // Need to convert
786 const char *charSetBuffer = CharacterSetID();
787 if (*charSetBuffer) {
788 int outLength = 0;
789 char *tmpEncoded = ConvertText(&outLength, utf8, inputLength, charSetBuffer, "UTF-8", true);
790 if (tmpEncoded) {
791 if (encoded) {
792 memcpy(encoded, tmpEncoded, outLength);
794 delete []tmpEncoded;
796 return outLength;
797 } else {
798 if (encoded) {
799 memcpy(encoded, utf8, inputLength);
801 return inputLength;
804 // Fail
805 return 0;
808 bool ScintillaGTK::ValidCodePage(int codePage) const {
809 return codePage == 0
810 || codePage == SC_CP_UTF8
811 || codePage == 932
812 || codePage == 936
813 || codePage == 950
814 || codePage == SC_CP_DBCS;
817 sptr_t ScintillaGTK::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
818 try {
819 switch (iMessage) {
821 case SCI_GRABFOCUS:
822 gtk_widget_grab_focus(PWidget(wMain));
823 break;
825 case SCI_GETDIRECTFUNCTION:
826 return reinterpret_cast<sptr_t>(DirectFunction);
828 case SCI_GETDIRECTPOINTER:
829 return reinterpret_cast<sptr_t>(this);
831 #ifdef SCI_LEXER
832 case SCI_LOADLEXERLIBRARY:
833 LexerManager::GetInstance()->Load(reinterpret_cast<const char*>(lParam));
834 break;
835 #endif
836 case SCI_TARGETASUTF8:
837 return TargetAsUTF8(reinterpret_cast<char*>(lParam));
839 case SCI_ENCODEDFROMUTF8:
840 return EncodedFromUTF8(reinterpret_cast<char*>(wParam),
841 reinterpret_cast<char*>(lParam));
843 case SCI_SETRECTANGULARSELECTIONMODIFIER:
844 rectangularSelectionModifier = wParam;
845 break;
847 case SCI_GETRECTANGULARSELECTIONMODIFIER:
848 return rectangularSelectionModifier;
850 default:
851 return ScintillaBase::WndProc(iMessage, wParam, lParam);
853 } catch (std::bad_alloc&) {
854 errorStatus = SC_STATUS_BADALLOC;
855 } catch (...) {
856 errorStatus = SC_STATUS_FAILURE;
858 return 0l;
861 sptr_t ScintillaGTK::DefWndProc(unsigned int, uptr_t, sptr_t) {
862 return 0;
865 void ScintillaGTK::SetTicking(bool on) {
866 if (timer.ticking != on) {
867 timer.ticking = on;
868 if (timer.ticking) {
869 timer.tickerID = reinterpret_cast<TickerID>(gtk_timeout_add(timer.tickSize, (GtkFunction)TimeOut, this));
870 } else {
871 gtk_timeout_remove(GPOINTER_TO_UINT(timer.tickerID));
874 timer.ticksToWait = caret.period;
877 bool ScintillaGTK::SetIdle(bool on) {
878 if (on) {
879 // Start idler, if it's not running.
880 if (!idler.state) {
881 idler.state = true;
882 idler.idlerID = reinterpret_cast<IdlerID>(
883 g_idle_add_full(G_PRIORITY_DEFAULT_IDLE,
884 reinterpret_cast<GSourceFunc>(IdleCallback), this, NULL));
886 } else {
887 // Stop idler, if it's running
888 if (idler.state) {
889 idler.state = false;
890 g_source_remove(GPOINTER_TO_UINT(idler.idlerID));
893 return true;
896 void ScintillaGTK::SetMouseCapture(bool on) {
897 if (mouseDownCaptures) {
898 if (on) {
899 gtk_grab_add(GTK_WIDGET(PWidget(wMain)));
900 } else {
901 gtk_grab_remove(GTK_WIDGET(PWidget(wMain)));
904 capturedMouse = on;
907 bool ScintillaGTK::HaveMouseCapture() {
908 return capturedMouse;
911 bool ScintillaGTK::PaintContains(PRectangle rc) {
912 bool contains = true;
913 if (paintState == painting) {
914 if (!rcPaint.Contains(rc)) {
915 contains = false;
916 } else if (rgnUpdate) {
917 GdkRectangle grc = {rc.left, rc.top,
918 rc.right - rc.left, rc.bottom - rc.top};
919 if (gdk_region_rect_in(rgnUpdate, &grc) != GDK_OVERLAP_RECTANGLE_IN) {
920 contains = false;
924 return contains;
927 // Redraw all of text area. This paint will not be abandoned.
928 void ScintillaGTK::FullPaint() {
929 wText.InvalidateAll();
932 PRectangle ScintillaGTK::GetClientRectangle() {
933 PRectangle rc = wMain.GetClientPosition();
934 if (verticalScrollBarVisible)
935 rc.right -= scrollBarWidth;
936 if (horizontalScrollBarVisible && (wrapState == eWrapNone))
937 rc.bottom -= scrollBarHeight;
938 // Move to origin
939 rc.right -= rc.left;
940 rc.bottom -= rc.top;
941 rc.left = 0;
942 rc.top = 0;
943 return rc;
946 // Synchronously paint a rectangle of the window.
947 void ScintillaGTK::SyncPaint(PRectangle rc) {
948 paintState = painting;
949 rcPaint = rc;
950 PRectangle rcClient = GetClientRectangle();
951 paintingAllText = rcPaint.Contains(rcClient);
952 if ((PWidget(wText))->window) {
953 Surface *sw = Surface::Allocate();
954 if (sw) {
955 sw->Init(PWidget(wText)->window, PWidget(wText));
956 Paint(sw, rc);
957 sw->Release();
958 delete sw;
961 if (paintState == paintAbandoned) {
962 // Painting area was insufficient to cover new styling or brace highlight positions
963 FullPaint();
965 paintState = notPainting;
968 void ScintillaGTK::ScrollText(int linesToMove) {
969 int diff = vs.lineHeight * -linesToMove;
970 //Platform::DebugPrintf("ScintillaGTK::ScrollText %d %d %0d,%0d %0d,%0d\n", linesToMove, diff,
971 // rc.left, rc.top, rc.right, rc.bottom);
972 GtkWidget *wi = PWidget(wText);
974 gdk_window_scroll(wi->window, 0, -diff);
975 gdk_window_process_updates(wi->window, FALSE);
978 void ScintillaGTK::SetVerticalScrollPos() {
979 DwellEnd(true);
980 gtk_adjustment_set_value(GTK_ADJUSTMENT(adjustmentv), topLine);
983 void ScintillaGTK::SetHorizontalScrollPos() {
984 DwellEnd(true);
985 gtk_adjustment_set_value(GTK_ADJUSTMENT(adjustmenth), xOffset / 2);
988 bool ScintillaGTK::ModifyScrollBars(int nMax, int nPage) {
989 bool modified = false;
990 int pageScroll = LinesToScroll();
992 if (GTK_ADJUSTMENT(adjustmentv)->upper != (nMax + 1) ||
993 GTK_ADJUSTMENT(adjustmentv)->page_size != nPage ||
994 GTK_ADJUSTMENT(adjustmentv)->page_increment != pageScroll) {
995 GTK_ADJUSTMENT(adjustmentv)->upper = nMax + 1;
996 GTK_ADJUSTMENT(adjustmentv)->page_size = nPage;
997 GTK_ADJUSTMENT(adjustmentv)->page_increment = pageScroll;
998 gtk_adjustment_changed(GTK_ADJUSTMENT(adjustmentv));
999 modified = true;
1002 PRectangle rcText = GetTextRectangle();
1003 int horizEndPreferred = scrollWidth;
1004 if (horizEndPreferred < 0)
1005 horizEndPreferred = 0;
1006 unsigned int pageWidth = rcText.Width();
1007 unsigned int pageIncrement = pageWidth / 3;
1008 unsigned int charWidth = vs.styles[STYLE_DEFAULT].aveCharWidth;
1009 if (GTK_ADJUSTMENT(adjustmenth)->upper != horizEndPreferred ||
1010 GTK_ADJUSTMENT(adjustmenth)->page_size != pageWidth ||
1011 GTK_ADJUSTMENT(adjustmenth)->page_increment != pageIncrement ||
1012 GTK_ADJUSTMENT(adjustmenth)->step_increment != charWidth) {
1013 GTK_ADJUSTMENT(adjustmenth)->upper = horizEndPreferred;
1014 GTK_ADJUSTMENT(adjustmenth)->step_increment = charWidth;
1015 GTK_ADJUSTMENT(adjustmenth)->page_size = pageWidth;
1016 GTK_ADJUSTMENT(adjustmenth)->page_increment = pageIncrement;
1017 gtk_adjustment_changed(GTK_ADJUSTMENT(adjustmenth));
1018 modified = true;
1020 return modified;
1023 void ScintillaGTK::ReconfigureScrollBars() {
1024 PRectangle rc = wMain.GetClientPosition();
1025 Resize(rc.Width(), rc.Height());
1028 void ScintillaGTK::NotifyChange() {
1029 g_signal_emit(G_OBJECT(sci), scintilla_signals[COMMAND_SIGNAL], 0,
1030 Platform::LongFromTwoShorts(GetCtrlID(), SCEN_CHANGE), PWidget(wMain));
1033 void ScintillaGTK::NotifyFocus(bool focus) {
1034 g_signal_emit(G_OBJECT(sci), scintilla_signals[COMMAND_SIGNAL], 0,
1035 Platform::LongFromTwoShorts
1036 (GetCtrlID(), focus ? SCEN_SETFOCUS : SCEN_KILLFOCUS), PWidget(wMain));
1039 void ScintillaGTK::NotifyParent(SCNotification scn) {
1040 scn.nmhdr.hwndFrom = PWidget(wMain);
1041 scn.nmhdr.idFrom = GetCtrlID();
1042 g_signal_emit(G_OBJECT(sci), scintilla_signals[NOTIFY_SIGNAL], 0,
1043 GetCtrlID(), &scn);
1046 void ScintillaGTK::NotifyKey(int key, int modifiers) {
1047 SCNotification scn = {0};
1048 scn.nmhdr.code = SCN_KEY;
1049 scn.ch = key;
1050 scn.modifiers = modifiers;
1052 NotifyParent(scn);
1055 void ScintillaGTK::NotifyURIDropped(const char *list) {
1056 SCNotification scn = {0};
1057 scn.nmhdr.code = SCN_URIDROPPED;
1058 scn.text = list;
1060 NotifyParent(scn);
1063 const char *CharacterSetID(int characterSet);
1065 const char *ScintillaGTK::CharacterSetID() const {
1066 return ::CharacterSetID(vs.styles[STYLE_DEFAULT].characterSet);
1069 class CaseFolderUTF8 : public CaseFolderTable {
1070 public:
1071 CaseFolderUTF8() {
1072 StandardASCII();
1074 virtual size_t Fold(char *folded, size_t sizeFolded, const char *mixed, size_t lenMixed) {
1075 if ((lenMixed == 1) && (sizeFolded > 0)) {
1076 folded[0] = mapping[static_cast<unsigned char>(mixed[0])];
1077 return 1;
1078 } else {
1079 gchar *mapped = g_utf8_casefold(mixed, lenMixed);
1080 size_t lenMapped = strlen(mapped);
1081 if (lenMapped < sizeFolded) {
1082 memcpy(folded, mapped, lenMapped);
1083 } else {
1084 lenMapped = 0;
1086 g_free(mapped);
1087 return lenMapped;
1092 CaseFolder *ScintillaGTK::CaseFolderForEncoding() {
1093 if (pdoc->dbcsCodePage == SC_CP_UTF8) {
1094 return new CaseFolderUTF8();
1095 } else {
1096 CaseFolderTable *pcf = new CaseFolderTable();
1097 const char *charSetBuffer = CharacterSetID();
1098 if ((pdoc->dbcsCodePage == 0) && charSetBuffer) {
1099 pcf->StandardASCII();
1100 // Only for single byte encodings
1101 for (int i=0x80; i<0x100; i++) {
1102 char sCharacter[2] = "A";
1103 sCharacter[0] = i;
1104 int convertedLength = 1;
1105 const char *sUTF8 = ConvertText(&convertedLength, sCharacter, 1,
1106 "UTF-8", charSetBuffer, false);
1107 if (sUTF8) {
1108 gchar *mapped = g_utf8_casefold(sUTF8, strlen(sUTF8));
1109 if (mapped) {
1110 int mappedLength = strlen(mapped);
1111 const char *mappedBack = ConvertText(&mappedLength, mapped,
1112 mappedLength, charSetBuffer, "UTF-8", false, true);
1113 if (mappedBack && (strlen(mappedBack) == 1) && (mappedBack[0] != sCharacter[0])) {
1114 pcf->SetTranslation(sCharacter[0], mappedBack[0]);
1116 delete []mappedBack;
1117 g_free(mapped);
1120 delete []sUTF8;
1123 return pcf;
1127 std::string ScintillaGTK::CaseMapString(const std::string &s, int caseMapping) {
1128 if (s.size() == 0)
1129 return std::string();
1131 if (caseMapping == cmSame)
1132 return s;
1134 const char *needsFree1 = 0; // Must be freed with delete []
1135 const char *charSetBuffer = CharacterSetID();
1136 const char *sUTF8 = s.c_str();
1137 int rangeBytes = s.size();
1139 int convertedLength = rangeBytes;
1140 // Change text to UTF-8
1141 if (!IsUnicodeMode()) {
1142 // Need to convert
1143 if (*charSetBuffer) {
1144 sUTF8 = ConvertText(&convertedLength, const_cast<char *>(s.c_str()), rangeBytes,
1145 "UTF-8", charSetBuffer, false);
1146 needsFree1 = sUTF8;
1149 gchar *mapped; // Must be freed with g_free
1150 if (caseMapping == cmUpper) {
1151 mapped = g_utf8_strup(sUTF8, convertedLength);
1152 } else {
1153 mapped = g_utf8_strdown(sUTF8, convertedLength);
1155 int mappedLength = strlen(mapped);
1156 char *mappedBack = mapped;
1158 char *needsFree2 = 0; // Must be freed with delete []
1159 if (!IsUnicodeMode()) {
1160 if (*charSetBuffer) {
1161 mappedBack = ConvertText(&mappedLength, mapped, mappedLength, charSetBuffer, "UTF-8", false);
1162 needsFree2 = mappedBack;
1166 std::string ret(mappedBack, mappedLength);
1167 g_free(mapped);
1168 delete []needsFree1;
1169 delete []needsFree2;
1170 return ret;
1173 int ScintillaGTK::KeyDefault(int key, int modifiers) {
1174 if (!(modifiers & SCI_CTRL) && !(modifiers & SCI_ALT)) {
1175 if (key < 256) {
1176 NotifyKey(key, modifiers);
1177 return 0;
1178 } else {
1179 // Pass up to container in case it is an accelerator
1180 NotifyKey(key, modifiers);
1181 return 0;
1183 } else {
1184 // Pass up to container in case it is an accelerator
1185 NotifyKey(key, modifiers);
1186 return 0;
1188 //Platform::DebugPrintf("SK-key: %d %x %x\n",key, modifiers);
1191 void ScintillaGTK::CopyToClipboard(const SelectionText &selectedText) {
1192 #ifndef USE_GTK_CLIPBOARD
1193 copyText.Copy(selectedText);
1194 gtk_selection_owner_set(GTK_WIDGET(PWidget(wMain)),
1195 atomClipboard,
1196 GDK_CURRENT_TIME);
1197 #else
1198 SelectionText *clipText = new SelectionText();
1199 clipText->Copy(selectedText);
1200 StoreOnClipboard(clipText);
1201 #endif
1204 void ScintillaGTK::Copy() {
1205 if (!sel.Empty()) {
1206 #ifndef USE_GTK_CLIPBOARD
1207 CopySelectionRange(&copyText);
1208 gtk_selection_owner_set(GTK_WIDGET(PWidget(wMain)),
1209 atomClipboard,
1210 GDK_CURRENT_TIME);
1211 #else
1212 SelectionText *clipText = new SelectionText();
1213 CopySelectionRange(clipText);
1214 StoreOnClipboard(clipText);
1215 #endif
1216 #if PLAT_GTK_WIN32
1217 if (sel.IsRectangular()) {
1218 ::OpenClipboard(NULL);
1219 ::SetClipboardData(cfColumnSelect, 0);
1220 ::CloseClipboard();
1222 #endif
1226 void ScintillaGTK::Paste() {
1227 atomSought = atomUTF8;
1228 gtk_selection_convert(GTK_WIDGET(PWidget(wMain)),
1229 atomClipboard, atomSought, GDK_CURRENT_TIME);
1232 void ScintillaGTK::CreateCallTipWindow(PRectangle rc) {
1233 if (!ct.wCallTip.Created()) {
1234 ct.wCallTip = gtk_window_new(GTK_WINDOW_POPUP);
1235 ct.wDraw = gtk_drawing_area_new();
1236 GtkWidget *widcdrw = PWidget(ct.wDraw); // // No code inside the G_OBJECT macro
1237 gtk_container_add(GTK_CONTAINER(PWidget(ct.wCallTip)), widcdrw);
1238 g_signal_connect(G_OBJECT(widcdrw), "expose_event",
1239 G_CALLBACK(ScintillaGTK::ExposeCT), &ct);
1240 g_signal_connect(G_OBJECT(widcdrw), "button_press_event",
1241 G_CALLBACK(ScintillaGTK::PressCT), static_cast<void *>(this));
1242 gtk_widget_set_events(widcdrw,
1243 GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK);
1245 gtk_drawing_area_size(GTK_DRAWING_AREA(PWidget(ct.wDraw)),
1246 rc.Width(), rc.Height());
1247 ct.wDraw.Show();
1248 if (PWidget(ct.wCallTip)->window) {
1249 gdk_window_resize(PWidget(ct.wCallTip)->window, rc.Width(), rc.Height());
1253 void ScintillaGTK::AddToPopUp(const char *label, int cmd, bool enabled) {
1254 char fulllabel[200];
1255 strcpy(fulllabel, "/");
1256 strcat(fulllabel, label);
1257 GtkItemFactoryCallback menuSig = GtkItemFactoryCallback(PopUpCB);
1258 GtkItemFactoryEntry itemEntry = {
1259 fulllabel, NULL,
1260 menuSig,
1261 cmd,
1262 const_cast<gchar *>(label[0] ? "<Item>" : "<Separator>"),
1263 NULL
1265 gtk_item_factory_create_item(GTK_ITEM_FACTORY(popup.GetID()),
1266 &itemEntry, this, 1);
1267 if (cmd) {
1268 GtkWidget *item = gtk_item_factory_get_widget_by_action(
1269 reinterpret_cast<GtkItemFactory *>(popup.GetID()), cmd);
1270 if (item)
1271 gtk_widget_set_sensitive(item, enabled);
1275 bool ScintillaGTK::OwnPrimarySelection() {
1276 return ((gdk_selection_owner_get(GDK_SELECTION_PRIMARY)
1277 == GTK_WIDGET(PWidget(wMain))->window) &&
1278 (GTK_WIDGET(PWidget(wMain))->window != NULL));
1281 void ScintillaGTK::ClaimSelection() {
1282 // X Windows has a 'primary selection' as well as the clipboard.
1283 // Whenever the user selects some text, we become the primary selection
1284 if (!sel.Empty() && GTK_WIDGET_REALIZED(GTK_WIDGET(PWidget(wMain)))) {
1285 primarySelection = true;
1286 gtk_selection_owner_set(GTK_WIDGET(PWidget(wMain)),
1287 GDK_SELECTION_PRIMARY, GDK_CURRENT_TIME);
1288 primary.Free();
1289 } else if (OwnPrimarySelection()) {
1290 primarySelection = true;
1291 if (primary.s == NULL)
1292 gtk_selection_owner_set(NULL, GDK_SELECTION_PRIMARY, GDK_CURRENT_TIME);
1293 } else {
1294 primarySelection = false;
1295 primary.Free();
1299 // Detect rectangular text, convert line ends to current mode, convert from or to UTF-8
1300 void ScintillaGTK::GetGtkSelectionText(GtkSelectionData *selectionData, SelectionText &selText) {
1301 char *data = reinterpret_cast<char *>(selectionData->data);
1302 int len = selectionData->length;
1303 GdkAtom selectionTypeData = selectionData->type;
1305 // Return empty string if selection is not a string
1306 if ((selectionTypeData != GDK_TARGET_STRING) && (selectionTypeData != atomUTF8)) {
1307 char *empty = new char[1];
1308 empty[0] = '\0';
1309 selText.Set(empty, 0, SC_CP_UTF8, 0, false, false);
1310 return;
1313 // Check for "\n\0" ending to string indicating that selection is rectangular
1314 bool isRectangular;
1315 #if PLAT_GTK_WIN32
1316 isRectangular = ::IsClipboardFormatAvailable(cfColumnSelect) != 0;
1317 #else
1318 isRectangular = ((len > 2) && (data[len - 1] == 0 && data[len - 2] == '\n'));
1319 if (isRectangular)
1320 len--; // Forget the extra '\0'
1321 #endif
1323 char *dest;
1324 if (selectionTypeData == GDK_TARGET_STRING) {
1325 dest = Document::TransformLineEnds(&len, data, len, pdoc->eolMode);
1326 if (IsUnicodeMode()) {
1327 // Unknown encoding so assume in Latin1
1328 char *destPrevious = dest;
1329 dest = UTF8FromLatin1(dest, len);
1330 selText.Set(dest, len, SC_CP_UTF8, 0, selText.rectangular, false);
1331 delete []destPrevious;
1332 } else {
1333 // Assume buffer is in same encoding as selection
1334 selText.Set(dest, len, pdoc->dbcsCodePage,
1335 vs.styles[STYLE_DEFAULT].characterSet, isRectangular, false);
1337 } else { // UTF-8
1338 dest = Document::TransformLineEnds(&len, data, len, pdoc->eolMode);
1339 selText.Set(dest, len, SC_CP_UTF8, 0, isRectangular, false);
1340 const char *charSetBuffer = CharacterSetID();
1341 if (!IsUnicodeMode() && *charSetBuffer) {
1342 // Convert to locale
1343 dest = ConvertText(&len, selText.s, selText.len, charSetBuffer, "UTF-8", true);
1344 selText.Set(dest, len, pdoc->dbcsCodePage,
1345 vs.styles[STYLE_DEFAULT].characterSet, selText.rectangular, false);
1350 void ScintillaGTK::ReceivedSelection(GtkSelectionData *selection_data) {
1351 try {
1352 if ((selection_data->selection == atomClipboard) ||
1353 (selection_data->selection == GDK_SELECTION_PRIMARY)) {
1354 if ((atomSought == atomUTF8) && (selection_data->length <= 0)) {
1355 atomSought = atomString;
1356 gtk_selection_convert(GTK_WIDGET(PWidget(wMain)),
1357 selection_data->selection, atomSought, GDK_CURRENT_TIME);
1358 } else if ((selection_data->length > 0) &&
1359 ((selection_data->type == GDK_TARGET_STRING) || (selection_data->type == atomUTF8))) {
1360 SelectionText selText;
1361 GetGtkSelectionText(selection_data, selText);
1363 UndoGroup ug(pdoc);
1364 if (selection_data->selection != GDK_SELECTION_PRIMARY) {
1365 ClearSelection();
1367 SelectionPosition selStart = sel.IsRectangular() ?
1368 sel.Rectangular().Start() :
1369 sel.Range(sel.Main()).Start();
1371 if (selText.rectangular) {
1372 PasteRectangular(selStart, selText.s, selText.len);
1373 } else {
1374 InsertPaste(selStart, selText.s, selText.len);
1376 EnsureCaretVisible();
1379 // else fprintf(stderr, "Target non string %d %d\n", (int)(selection_data->type),
1380 // (int)(atomUTF8));
1381 Redraw();
1382 } catch (...) {
1383 errorStatus = SC_STATUS_FAILURE;
1387 void ScintillaGTK::ReceivedDrop(GtkSelectionData *selection_data) {
1388 dragWasDropped = true;
1389 if (selection_data->type == atomUriList || selection_data->type == atomDROPFILES_DND) {
1390 char *ptr = new char[selection_data->length + 1];
1391 ptr[selection_data->length] = '\0';
1392 memcpy(ptr, selection_data->data, selection_data->length);
1393 NotifyURIDropped(ptr);
1394 delete []ptr;
1395 } else if ((selection_data->type == GDK_TARGET_STRING) || (selection_data->type == atomUTF8)) {
1396 if (selection_data->length > 0) {
1397 SelectionText selText;
1398 GetGtkSelectionText(selection_data, selText);
1399 DropAt(posDrop, selText.s, false, selText.rectangular);
1401 } else if (selection_data->length > 0) {
1402 //~ fprintf(stderr, "ReceivedDrop other %p\n", static_cast<void *>(selection_data->type));
1404 Redraw();
1409 void ScintillaGTK::GetSelection(GtkSelectionData *selection_data, guint info, SelectionText *text) {
1410 #if PLAT_GTK_WIN32
1411 // GDK on Win32 expands any \n into \r\n, so make a copy of
1412 // the clip text now with newlines converted to \n. Use { } to hide symbols
1413 // from code below
1414 SelectionText *newline_normalized = NULL;
1416 int tmpstr_len;
1417 char *tmpstr = Document::TransformLineEnds(&tmpstr_len, text->s, text->len, SC_EOL_LF);
1418 newline_normalized = new SelectionText();
1419 newline_normalized->Set(tmpstr, tmpstr_len, SC_CP_UTF8, 0, text->rectangular, false);
1420 text = newline_normalized;
1422 #endif
1424 // Convert text to utf8 if it isn't already
1425 SelectionText *converted = 0;
1426 if ((text->codePage != SC_CP_UTF8) && (info == TARGET_UTF8_STRING)) {
1427 const char *charSet = ::CharacterSetID(text->characterSet);
1428 if (*charSet) {
1429 int new_len;
1430 char* tmputf = ConvertText(&new_len, text->s, text->len, "UTF-8", charSet, false);
1431 converted = new SelectionText();
1432 converted->Set(tmputf, new_len, SC_CP_UTF8, 0, text->rectangular, false);
1433 text = converted;
1437 // Here is a somewhat evil kludge.
1438 // As I can not work out how to store data on the clipboard in multiple formats
1439 // and need some way to mark the clipping as being stream or rectangular,
1440 // the terminating \0 is included in the length for rectangular clippings.
1441 // All other tested aplications behave benignly by ignoring the \0.
1442 // The #if is here because on Windows cfColumnSelect clip entry is used
1443 // instead as standard indicator of rectangularness (so no need to kludge)
1444 const char *textData = text->s ? text->s : "";
1445 int len = strlen(textData);
1446 #if PLAT_GTK_WIN32 == 0
1447 if (text->rectangular)
1448 len++;
1449 #endif
1451 if (info == TARGET_UTF8_STRING) {
1452 gtk_selection_data_set_text(selection_data, textData, len);
1453 } else {
1454 gtk_selection_data_set(selection_data,
1455 static_cast<GdkAtom>(GDK_SELECTION_TYPE_STRING),
1456 8, reinterpret_cast<const unsigned char *>(textData), len);
1458 delete converted;
1460 #if PLAT_GTK_WIN32
1461 delete newline_normalized;
1462 #endif
1465 #ifdef USE_GTK_CLIPBOARD
1466 void ScintillaGTK::StoreOnClipboard(SelectionText *clipText) {
1467 GtkClipboard *clipBoard =
1468 gtk_widget_get_clipboard(GTK_WIDGET(PWidget(wMain)), atomClipboard);
1469 if (clipBoard == NULL) // Occurs if widget isn't in a toplevel
1470 return;
1472 if (gtk_clipboard_set_with_data(clipBoard, clipboardCopyTargets, nClipboardCopyTargets,
1473 ClipboardGetSelection, ClipboardClearSelection, clipText)) {
1474 gtk_clipboard_set_can_store(clipBoard, clipboardCopyTargets, nClipboardCopyTargets);
1478 void ScintillaGTK::ClipboardGetSelection(GtkClipboard *, GtkSelectionData *selection_data, guint info, void *data) {
1479 GetSelection(selection_data, info, static_cast<SelectionText*>(data));
1482 void ScintillaGTK::ClipboardClearSelection(GtkClipboard *, void *data) {
1483 SelectionText *obj = static_cast<SelectionText*>(data);
1484 delete obj;
1486 #endif
1488 void ScintillaGTK::UnclaimSelection(GdkEventSelection *selection_event) {
1489 try {
1490 //Platform::DebugPrintf("UnclaimSelection\n");
1491 if (selection_event->selection == GDK_SELECTION_PRIMARY) {
1492 //Platform::DebugPrintf("UnclaimPrimarySelection\n");
1493 if (!OwnPrimarySelection()) {
1494 primary.Free();
1495 primarySelection = false;
1496 FullPaint();
1499 } catch (...) {
1500 errorStatus = SC_STATUS_FAILURE;
1504 void ScintillaGTK::Resize(int width, int height) {
1505 //Platform::DebugPrintf("Resize %d %d\n", width, height);
1506 //printf("Resize %d %d\n", width, height);
1508 // Not always needed, but some themes can have different sizes of scrollbars
1509 scrollBarWidth = GTK_WIDGET(PWidget(scrollbarv))->requisition.width;
1510 scrollBarHeight = GTK_WIDGET(PWidget(scrollbarh))->requisition.height;
1512 // These allocations should never produce negative sizes as they would wrap around to huge
1513 // unsigned numbers inside GTK+ causing warnings.
1514 bool showSBHorizontal = horizontalScrollBarVisible && (wrapState == eWrapNone);
1515 int horizontalScrollBarHeight = scrollBarHeight;
1516 if (!showSBHorizontal)
1517 horizontalScrollBarHeight = 0;
1519 GtkAllocation alloc;
1520 if (showSBHorizontal) {
1521 gtk_widget_show(GTK_WIDGET(PWidget(scrollbarh)));
1522 alloc.x = 0;
1523 alloc.y = height - scrollBarHeight;
1524 alloc.width = Platform::Maximum(1, width - scrollBarWidth) + 1;
1525 alloc.height = horizontalScrollBarHeight;
1526 gtk_widget_size_allocate(GTK_WIDGET(PWidget(scrollbarh)), &alloc);
1527 } else {
1528 gtk_widget_hide(GTK_WIDGET(PWidget(scrollbarh)));
1531 if (verticalScrollBarVisible) {
1532 gtk_widget_show(GTK_WIDGET(PWidget(scrollbarv)));
1533 alloc.x = width - scrollBarWidth;
1534 alloc.y = 0;
1535 alloc.width = scrollBarWidth;
1536 alloc.height = Platform::Maximum(1, height - scrollBarHeight) + 1;
1537 if (!showSBHorizontal)
1538 alloc.height += scrollBarWidth-1;
1539 gtk_widget_size_allocate(GTK_WIDGET(PWidget(scrollbarv)), &alloc);
1540 } else {
1541 gtk_widget_hide(GTK_WIDGET(PWidget(scrollbarv)));
1543 if (GTK_WIDGET_MAPPED(PWidget(wMain))) {
1544 ChangeSize();
1547 alloc.x = 0;
1548 alloc.y = 0;
1549 alloc.width = Platform::Maximum(1, width - scrollBarWidth);
1550 alloc.height = Platform::Maximum(1, height - scrollBarHeight);
1551 if (!showSBHorizontal)
1552 alloc.height += scrollBarHeight;
1553 if (!verticalScrollBarVisible)
1554 alloc.width += scrollBarWidth;
1555 gtk_widget_size_allocate(GTK_WIDGET(PWidget(wText)), &alloc);
1558 static void SetAdjustmentValue(GtkObject *object, int value) {
1559 GtkAdjustment *adjustment = GTK_ADJUSTMENT(object);
1560 int maxValue = static_cast<int>(
1561 adjustment->upper - adjustment->page_size);
1562 if (value > maxValue)
1563 value = maxValue;
1564 if (value < 0)
1565 value = 0;
1566 gtk_adjustment_set_value(adjustment, value);
1569 static int modifierTranslated(int sciModifier) {
1570 switch (sciModifier) {
1571 case SCMOD_SHIFT:
1572 return GDK_SHIFT_MASK;
1573 case SCMOD_CTRL:
1574 return GDK_CONTROL_MASK;
1575 case SCMOD_ALT:
1576 return GDK_MOD1_MASK;
1577 case SCMOD_SUPER:
1578 return GDK_MOD4_MASK;
1579 default:
1580 return 0;
1584 gint ScintillaGTK::PressThis(GdkEventButton *event) {
1585 try {
1586 //Platform::DebugPrintf("Press %x time=%d state = %x button = %x\n",this,event->time, event->state, event->button);
1587 // Do not use GTK+ double click events as Scintilla has its own double click detection
1588 if (event->type != GDK_BUTTON_PRESS)
1589 return FALSE;
1591 evbtn = *event;
1592 Point pt;
1593 pt.x = int(event->x);
1594 pt.y = int(event->y);
1595 PRectangle rcClient = GetClientRectangle();
1596 //Platform::DebugPrintf("Press %0d,%0d in %0d,%0d %0d,%0d\n",
1597 // pt.x, pt.y, rcClient.left, rcClient.top, rcClient.right, rcClient.bottom);
1598 if ((pt.x > rcClient.right) || (pt.y > rcClient.bottom)) {
1599 Platform::DebugPrintf("Bad location\n");
1600 return FALSE;
1603 bool ctrl = (event->state & GDK_CONTROL_MASK) != 0;
1605 gtk_widget_grab_focus(PWidget(wMain));
1606 if (event->button == 1) {
1607 // On X, instead of sending literal modifiers use the user specified
1608 // modifier, defaulting to control instead of alt.
1609 // This is because most X window managers grab alt + click for moving
1610 ButtonDown(pt, event->time,
1611 (event->state & GDK_SHIFT_MASK) != 0,
1612 (event->state & GDK_CONTROL_MASK) != 0,
1613 (event->state & modifierTranslated(rectangularSelectionModifier)) != 0);
1614 } else if (event->button == 2) {
1615 // Grab the primary selection if it exists
1616 SelectionPosition pos = SPositionFromLocation(pt, false, false, UserVirtualSpace());
1617 if (OwnPrimarySelection() && primary.s == NULL)
1618 CopySelectionRange(&primary);
1620 sel.Clear();
1621 SetSelection(pos, pos);
1622 atomSought = atomUTF8;
1623 gtk_selection_convert(GTK_WIDGET(PWidget(wMain)), GDK_SELECTION_PRIMARY,
1624 atomSought, event->time);
1625 } else if (event->button == 3) {
1626 if (displayPopupMenu) {
1627 // PopUp menu
1628 // Convert to screen
1629 int ox = 0;
1630 int oy = 0;
1631 gdk_window_get_origin(PWidget(wMain)->window, &ox, &oy);
1632 ContextMenu(Point(pt.x + ox, pt.y + oy));
1633 } else {
1634 return FALSE;
1636 } else if (event->button == 4) {
1637 // Wheel scrolling up (only GTK 1.x does it this way)
1638 if (ctrl)
1639 SetAdjustmentValue(adjustmenth, (xOffset / 2) - 6);
1640 else
1641 SetAdjustmentValue(adjustmentv, topLine - 3);
1642 } else if (event->button == 5) {
1643 // Wheel scrolling down (only GTK 1.x does it this way)
1644 if (ctrl)
1645 SetAdjustmentValue(adjustmenth, (xOffset / 2) + 6);
1646 else
1647 SetAdjustmentValue(adjustmentv, topLine + 3);
1649 } catch (...) {
1650 errorStatus = SC_STATUS_FAILURE;
1652 return TRUE;
1655 gint ScintillaGTK::Press(GtkWidget *widget, GdkEventButton *event) {
1656 if (event->window != widget->window)
1657 return FALSE;
1658 ScintillaGTK *sciThis = ScintillaFromWidget(widget);
1659 return sciThis->PressThis(event);
1662 gint ScintillaGTK::MouseRelease(GtkWidget *widget, GdkEventButton *event) {
1663 ScintillaGTK *sciThis = ScintillaFromWidget(widget);
1664 try {
1665 //Platform::DebugPrintf("Release %x %d %d\n",sciThis,event->time,event->state);
1666 if (!sciThis->HaveMouseCapture())
1667 return FALSE;
1668 if (event->button == 1) {
1669 Point pt;
1670 pt.x = int(event->x);
1671 pt.y = int(event->y);
1672 //Platform::DebugPrintf("Up %x %x %d %d %d\n",
1673 // sciThis,event->window,event->time, pt.x, pt.y);
1674 if (event->window != PWidget(sciThis->wMain)->window)
1675 // If mouse released on scroll bar then the position is relative to the
1676 // scrollbar, not the drawing window so just repeat the most recent point.
1677 pt = sciThis->ptMouseLast;
1678 sciThis->ButtonUp(pt, event->time, (event->state & 4) != 0);
1680 } catch (...) {
1681 sciThis->errorStatus = SC_STATUS_FAILURE;
1683 return FALSE;
1686 // win32gtk and GTK >= 2 use SCROLL_* events instead of passing the
1687 // button4/5/6/7 events to the GTK app
1688 gint ScintillaGTK::ScrollEvent(GtkWidget *widget,
1689 GdkEventScroll *event) {
1690 ScintillaGTK *sciThis = ScintillaFromWidget(widget);
1691 try {
1693 if (widget == NULL || event == NULL)
1694 return FALSE;
1696 // Compute amount and direction to scroll (even tho on win32 there is
1697 // intensity of scrolling info in the native message, gtk doesn't
1698 // support this so we simulate similarly adaptive scrolling)
1699 // Note that this is disabled on OS X (Darwin) where the X11 server already has
1700 // and adaptive scrolling algorithm that fights with this one
1701 int cLineScroll;
1702 #if defined(__MWERKS__) || defined(__APPLE_CPP__) || defined(__APPLE_CC__)
1703 cLineScroll = sciThis->linesPerScroll;
1704 if (cLineScroll == 0)
1705 cLineScroll = 4;
1706 sciThis->wheelMouseIntensity = cLineScroll;
1707 #else
1708 int timeDelta = 1000000;
1709 GTimeVal curTime;
1710 g_get_current_time(&curTime);
1711 if (curTime.tv_sec == sciThis->lastWheelMouseTime.tv_sec)
1712 timeDelta = curTime.tv_usec - sciThis->lastWheelMouseTime.tv_usec;
1713 else if (curTime.tv_sec == sciThis->lastWheelMouseTime.tv_sec + 1)
1714 timeDelta = 1000000 + (curTime.tv_usec - sciThis->lastWheelMouseTime.tv_usec);
1715 if ((event->direction == sciThis->lastWheelMouseDirection) && (timeDelta < 250000)) {
1716 if (sciThis->wheelMouseIntensity < 12)
1717 sciThis->wheelMouseIntensity++;
1718 cLineScroll = sciThis->wheelMouseIntensity;
1719 } else {
1720 cLineScroll = sciThis->linesPerScroll;
1721 if (cLineScroll == 0)
1722 cLineScroll = 4;
1723 sciThis->wheelMouseIntensity = cLineScroll;
1725 #endif
1726 if (event->direction == GDK_SCROLL_UP || event->direction == GDK_SCROLL_LEFT) {
1727 cLineScroll *= -1;
1729 g_get_current_time(&sciThis->lastWheelMouseTime);
1730 sciThis->lastWheelMouseDirection = event->direction;
1732 // Note: Unpatched versions of win32gtk don't set the 'state' value so
1733 // only regular scrolling is supported there. Also, unpatched win32gtk
1734 // issues spurious button 2 mouse events during wheeling, which can cause
1735 // problems (a patch for both was submitted by archaeopteryx.com on 13Jun2001)
1737 // Data zoom not supported
1738 if (event->state & GDK_SHIFT_MASK) {
1739 return FALSE;
1742 // Horizontal scrolling
1743 if (event->direction == GDK_SCROLL_LEFT || event->direction == GDK_SCROLL_RIGHT) {
1744 sciThis->HorizontalScrollTo(sciThis->xOffset + cLineScroll);
1746 // Text font size zoom
1747 } else if (event->state & GDK_CONTROL_MASK) {
1748 if (cLineScroll < 0) {
1749 sciThis->KeyCommand(SCI_ZOOMIN);
1750 } else {
1751 sciThis->KeyCommand(SCI_ZOOMOUT);
1754 // Regular scrolling
1755 } else {
1756 sciThis->ScrollTo(sciThis->topLine + cLineScroll);
1758 return TRUE;
1759 } catch (...) {
1760 sciThis->errorStatus = SC_STATUS_FAILURE;
1762 return FALSE;
1765 gint ScintillaGTK::Motion(GtkWidget *widget, GdkEventMotion *event) {
1766 ScintillaGTK *sciThis = ScintillaFromWidget(widget);
1767 try {
1768 //Platform::DebugPrintf("Motion %x %d\n",sciThis,event->time);
1769 if (event->window != widget->window)
1770 return FALSE;
1771 int x = 0;
1772 int y = 0;
1773 GdkModifierType state;
1774 if (event->is_hint) {
1775 gdk_window_get_pointer(event->window, &x, &y, &state);
1776 } else {
1777 x = static_cast<int>(event->x);
1778 y = static_cast<int>(event->y);
1779 state = static_cast<GdkModifierType>(event->state);
1781 //Platform::DebugPrintf("Move %x %x %d %c %d %d\n",
1782 // sciThis,event->window,event->time,event->is_hint? 'h' :'.', x, y);
1783 Point pt(x, y);
1784 sciThis->ButtonMove(pt);
1785 } catch (...) {
1786 sciThis->errorStatus = SC_STATUS_FAILURE;
1788 return FALSE;
1791 // Map the keypad keys to their equivalent functions
1792 static int KeyTranslate(int keyIn) {
1793 switch (keyIn) {
1794 case GDK_ISO_Left_Tab:
1795 return SCK_TAB;
1796 case GDK_KP_Down:
1797 return SCK_DOWN;
1798 case GDK_KP_Up:
1799 return SCK_UP;
1800 case GDK_KP_Left:
1801 return SCK_LEFT;
1802 case GDK_KP_Right:
1803 return SCK_RIGHT;
1804 case GDK_KP_Home:
1805 return SCK_HOME;
1806 case GDK_KP_End:
1807 return SCK_END;
1808 case GDK_KP_Page_Up:
1809 return SCK_PRIOR;
1810 case GDK_KP_Page_Down:
1811 return SCK_NEXT;
1812 case GDK_KP_Delete:
1813 return SCK_DELETE;
1814 case GDK_KP_Insert:
1815 return SCK_INSERT;
1816 case GDK_KP_Enter:
1817 return SCK_RETURN;
1819 case GDK_Down:
1820 return SCK_DOWN;
1821 case GDK_Up:
1822 return SCK_UP;
1823 case GDK_Left:
1824 return SCK_LEFT;
1825 case GDK_Right:
1826 return SCK_RIGHT;
1827 case GDK_Home:
1828 return SCK_HOME;
1829 case GDK_End:
1830 return SCK_END;
1831 case GDK_Page_Up:
1832 return SCK_PRIOR;
1833 case GDK_Page_Down:
1834 return SCK_NEXT;
1835 case GDK_Delete:
1836 return SCK_DELETE;
1837 case GDK_Insert:
1838 return SCK_INSERT;
1839 case GDK_Escape:
1840 return SCK_ESCAPE;
1841 case GDK_BackSpace:
1842 return SCK_BACK;
1843 case GDK_Tab:
1844 return SCK_TAB;
1845 case GDK_Return:
1846 return SCK_RETURN;
1847 case GDK_KP_Add:
1848 return SCK_ADD;
1849 case GDK_KP_Subtract:
1850 return SCK_SUBTRACT;
1851 case GDK_KP_Divide:
1852 return SCK_DIVIDE;
1853 case GDK_Super_L:
1854 return SCK_WIN;
1855 case GDK_Super_R:
1856 return SCK_RWIN;
1857 case GDK_Menu:
1858 return SCK_MENU;
1859 default:
1860 return keyIn;
1864 gboolean ScintillaGTK::KeyThis(GdkEventKey *event) {
1865 try {
1866 //fprintf(stderr, "SC-key: %d %x [%s]\n",
1867 // event->keyval, event->state, (event->length > 0) ? event->string : "empty");
1868 if (gtk_im_context_filter_keypress(im_context, event)) {
1869 return 1;
1871 if (!event->keyval) {
1872 return true;
1875 bool shift = (event->state & GDK_SHIFT_MASK) != 0;
1876 bool ctrl = (event->state & GDK_CONTROL_MASK) != 0;
1877 bool alt = (event->state & GDK_MOD1_MASK) != 0;
1878 guint key = event->keyval;
1879 if (ctrl && (key < 128))
1880 key = toupper(key);
1881 else if (!ctrl && (key >= GDK_KP_Multiply && key <= GDK_KP_9))
1882 key &= 0x7F;
1883 // Hack for keys over 256 and below command keys but makes Hungarian work.
1884 // This will have to change for Unicode
1885 else if (key >= 0xFE00)
1886 key = KeyTranslate(key);
1888 bool consumed = false;
1889 bool added = KeyDown(key, shift, ctrl, alt, &consumed) != 0;
1890 if (!consumed)
1891 consumed = added;
1892 //fprintf(stderr, "SK-key: %d %x %x\n",event->keyval, event->state, consumed);
1893 if (event->keyval == 0xffffff && event->length > 0) {
1894 ClearSelection();
1895 if (pdoc->InsertCString(CurrentPosition(), event->string)) {
1896 MovePositionTo(CurrentPosition() + event->length);
1899 return consumed;
1900 } catch (...) {
1901 errorStatus = SC_STATUS_FAILURE;
1903 return FALSE;
1906 gboolean ScintillaGTK::KeyPress(GtkWidget *widget, GdkEventKey *event) {
1907 ScintillaGTK *sciThis = ScintillaFromWidget(widget);
1908 return sciThis->KeyThis(event);
1911 gboolean ScintillaGTK::KeyRelease(GtkWidget *, GdkEventKey * /*event*/) {
1912 //Platform::DebugPrintf("SC-keyrel: %d %x %3s\n",event->keyval, event->state, event->string);
1913 return FALSE;
1916 gboolean ScintillaGTK::ExposePreeditThis(GtkWidget *widget, GdkEventExpose *ose) {
1917 try {
1918 gchar *str;
1919 gint cursor_pos;
1920 PangoAttrList *attrs;
1922 gtk_im_context_get_preedit_string(im_context, &str, &attrs, &cursor_pos);
1923 PangoLayout *layout = gtk_widget_create_pango_layout(PWidget(wText), str);
1924 pango_layout_set_attributes(layout, attrs);
1926 GdkGC *gc = gdk_gc_new(widget->window);
1927 GdkColor color[2] = { {0, 0x0000, 0x0000, 0x0000},
1928 {0, 0xffff, 0xffff, 0xffff}
1930 gdk_color_alloc(gdk_colormap_get_system(), color);
1931 gdk_color_alloc(gdk_colormap_get_system(), color + 1);
1933 gdk_gc_set_foreground(gc, color + 1);
1934 gdk_draw_rectangle(widget->window, gc, TRUE, ose->area.x, ose->area.y,
1935 ose->area.width, ose->area.height);
1937 gdk_gc_set_foreground(gc, color);
1938 gdk_gc_set_background(gc, color + 1);
1939 gdk_draw_layout(widget->window, gc, 0, 0, layout);
1941 gdk_gc_unref(gc);
1942 g_free(str);
1943 pango_attr_list_unref(attrs);
1944 g_object_unref(layout);
1945 } catch (...) {
1946 errorStatus = SC_STATUS_FAILURE;
1948 return TRUE;
1951 gboolean ScintillaGTK::ExposePreedit(GtkWidget *widget, GdkEventExpose *ose, ScintillaGTK *sciThis) {
1952 return sciThis->ExposePreeditThis(widget, ose);
1955 void ScintillaGTK::CommitThis(char *utfVal) {
1956 try {
1957 //~ fprintf(stderr, "Commit '%s'\n", utfVal);
1958 if (IsUnicodeMode()) {
1959 AddCharUTF(utfVal, strlen(utfVal));
1960 } else {
1961 const char *source = CharacterSetID();
1962 if (*source) {
1963 Converter conv(source, "UTF-8", true);
1964 if (conv) {
1965 char localeVal[4] = "\0\0\0";
1966 char *pin = utfVal;
1967 size_t inLeft = strlen(utfVal);
1968 char *pout = localeVal;
1969 size_t outLeft = sizeof(localeVal);
1970 size_t conversions = conv.Convert(&pin, &inLeft, &pout, &outLeft);
1971 if (conversions != ((size_t)(-1))) {
1972 *pout = '\0';
1973 for (int i = 0; localeVal[i]; i++) {
1974 AddChar(localeVal[i]);
1976 } else {
1977 fprintf(stderr, "Conversion failed '%s'\n", utfVal);
1982 } catch (...) {
1983 errorStatus = SC_STATUS_FAILURE;
1987 void ScintillaGTK::Commit(GtkIMContext *, char *str, ScintillaGTK *sciThis) {
1988 sciThis->CommitThis(str);
1991 void ScintillaGTK::PreeditChangedThis() {
1992 try {
1993 gchar *str;
1994 PangoAttrList *attrs;
1995 gint cursor_pos;
1996 gtk_im_context_get_preedit_string(im_context, &str, &attrs, &cursor_pos);
1997 if (strlen(str) > 0) {
1998 PangoLayout *layout = gtk_widget_create_pango_layout(PWidget(wText), str);
1999 pango_layout_set_attributes(layout, attrs);
2001 gint w, h;
2002 pango_layout_get_pixel_size(layout, &w, &h);
2003 g_object_unref(layout);
2005 gint x, y;
2006 gdk_window_get_origin((PWidget(wText))->window, &x, &y);
2008 Point pt = PointMainCaret();
2009 if (pt.x < 0)
2010 pt.x = 0;
2011 if (pt.y < 0)
2012 pt.y = 0;
2014 gtk_window_move(GTK_WINDOW(PWidget(wPreedit)), x + pt.x, y + pt.y);
2015 gtk_window_resize(GTK_WINDOW(PWidget(wPreedit)), w, h);
2016 gtk_widget_show(PWidget(wPreedit));
2017 gtk_widget_queue_draw_area(PWidget(wPreeditDraw), 0, 0, w, h);
2018 } else {
2019 gtk_widget_hide(PWidget(wPreedit));
2021 g_free(str);
2022 pango_attr_list_unref(attrs);
2023 } catch (...) {
2024 errorStatus = SC_STATUS_FAILURE;
2028 void ScintillaGTK::PreeditChanged(GtkIMContext *, ScintillaGTK *sciThis) {
2029 sciThis->PreeditChangedThis();
2032 gint ScintillaGTK::StyleSetText(GtkWidget *widget, GtkStyle *, void*) {
2033 if (widget->window != NULL)
2034 gdk_window_set_back_pixmap(widget->window, NULL, FALSE);
2035 return FALSE;
2038 gint ScintillaGTK::RealizeText(GtkWidget *widget, void*) {
2039 if (widget->window != NULL)
2040 gdk_window_set_back_pixmap(widget->window, NULL, FALSE);
2041 return FALSE;
2044 void ScintillaGTK::Destroy(GObject *object) {
2045 try {
2046 ScintillaObject *scio = reinterpret_cast<ScintillaObject *>(object);
2047 // This avoids a double destruction
2048 if (!scio->pscin)
2049 return;
2050 ScintillaGTK *sciThis = reinterpret_cast<ScintillaGTK *>(scio->pscin);
2051 //Platform::DebugPrintf("Destroying %x %x\n", sciThis, object);
2052 sciThis->Finalise();
2054 delete sciThis;
2055 scio->pscin = 0;
2056 } catch (...) {
2057 // Its dead so nowhere to save the status
2061 static void DrawChild(GtkWidget *widget, GdkRectangle *area) {
2062 GdkRectangle areaIntersect;
2063 if (widget &&
2064 GTK_WIDGET_DRAWABLE(widget) &&
2065 gtk_widget_intersect(widget, area, &areaIntersect)) {
2066 gtk_widget_draw(widget, &areaIntersect);
2070 void ScintillaGTK::Draw(GtkWidget *widget, GdkRectangle *area) {
2071 ScintillaGTK *sciThis = ScintillaFromWidget(widget);
2072 try {
2073 //Platform::DebugPrintf("Draw %p %0d,%0d %0d,%0d\n", widget, area->x, area->y, area->width, area->height);
2074 PRectangle rcPaint(area->x, area->y, area->x + area->width, area->y + area->height);
2075 sciThis->SyncPaint(rcPaint);
2076 if (GTK_WIDGET_DRAWABLE(PWidget(sciThis->wMain))) {
2077 DrawChild(PWidget(sciThis->scrollbarh), area);
2078 DrawChild(PWidget(sciThis->scrollbarv), area);
2081 Point pt = sciThis->PointMainCaret();
2082 pt.y += sciThis->vs.lineHeight - 2;
2083 if (pt.x < 0) pt.x = 0;
2084 if (pt.y < 0) pt.y = 0;
2085 CursorMoved(widget, pt.x, pt.y, sciThis);
2086 } catch (...) {
2087 sciThis->errorStatus = SC_STATUS_FAILURE;
2091 gint ScintillaGTK::ExposeTextThis(GtkWidget * /*widget*/, GdkEventExpose *ose) {
2092 try {
2093 paintState = painting;
2095 rcPaint.left = ose->area.x;
2096 rcPaint.top = ose->area.y;
2097 rcPaint.right = ose->area.x + ose->area.width;
2098 rcPaint.bottom = ose->area.y + ose->area.height;
2100 PLATFORM_ASSERT(rgnUpdate == NULL);
2101 rgnUpdate = gdk_region_copy(ose->region);
2102 PRectangle rcClient = GetClientRectangle();
2103 paintingAllText = rcPaint.Contains(rcClient);
2104 Surface *surfaceWindow = Surface::Allocate();
2105 if (surfaceWindow) {
2106 surfaceWindow->Init(PWidget(wText)->window, PWidget(wText));
2107 Paint(surfaceWindow, rcPaint);
2108 surfaceWindow->Release();
2109 delete surfaceWindow;
2111 if (paintState == paintAbandoned) {
2112 // Painting area was insufficient to cover new styling or brace highlight positions
2113 FullPaint();
2115 paintState = notPainting;
2117 if (rgnUpdate) {
2118 gdk_region_destroy(rgnUpdate);
2120 rgnUpdate = 0;
2121 } catch (...) {
2122 errorStatus = SC_STATUS_FAILURE;
2125 return FALSE;
2128 gint ScintillaGTK::ExposeText(GtkWidget *widget, GdkEventExpose *ose, ScintillaGTK *sciThis) {
2129 return sciThis->ExposeTextThis(widget, ose);
2132 gint ScintillaGTK::ExposeMain(GtkWidget *widget, GdkEventExpose *ose) {
2133 ScintillaGTK *sciThis = ScintillaFromWidget(widget);
2134 //Platform::DebugPrintf("Expose Main %0d,%0d %0d,%0d\n",
2135 //ose->area.x, ose->area.y, ose->area.width, ose->area.height);
2136 return sciThis->Expose(widget, ose);
2139 gint ScintillaGTK::Expose(GtkWidget *, GdkEventExpose *ose) {
2140 try {
2141 //fprintf(stderr, "Expose %0d,%0d %0d,%0d\n",
2142 //ose->area.x, ose->area.y, ose->area.width, ose->area.height);
2144 // The text is painted in ExposeText
2145 gtk_container_propagate_expose(
2146 GTK_CONTAINER(PWidget(wMain)), PWidget(scrollbarh), ose);
2147 gtk_container_propagate_expose(
2148 GTK_CONTAINER(PWidget(wMain)), PWidget(scrollbarv), ose);
2150 } catch (...) {
2151 errorStatus = SC_STATUS_FAILURE;
2153 return FALSE;
2156 void ScintillaGTK::ScrollSignal(GtkAdjustment *adj, ScintillaGTK *sciThis) {
2157 try {
2158 sciThis->ScrollTo(static_cast<int>(adj->value), false);
2159 } catch (...) {
2160 sciThis->errorStatus = SC_STATUS_FAILURE;
2164 void ScintillaGTK::ScrollHSignal(GtkAdjustment *adj, ScintillaGTK *sciThis) {
2165 try {
2166 sciThis->HorizontalScrollTo(static_cast<int>(adj->value * 2));
2167 } catch (...) {
2168 sciThis->errorStatus = SC_STATUS_FAILURE;
2172 void ScintillaGTK::SelectionReceived(GtkWidget *widget,
2173 GtkSelectionData *selection_data, guint) {
2174 ScintillaGTK *sciThis = ScintillaFromWidget(widget);
2175 //Platform::DebugPrintf("Selection received\n");
2176 sciThis->ReceivedSelection(selection_data);
2179 void ScintillaGTK::SelectionGet(GtkWidget *widget,
2180 GtkSelectionData *selection_data, guint info, guint) {
2181 ScintillaGTK *sciThis = ScintillaFromWidget(widget);
2182 try {
2183 //Platform::DebugPrintf("Selection get\n");
2184 if (selection_data->selection == GDK_SELECTION_PRIMARY) {
2185 if (sciThis->primary.s == NULL) {
2186 sciThis->CopySelectionRange(&sciThis->primary);
2188 sciThis->GetSelection(selection_data, info, &sciThis->primary);
2190 #ifndef USE_GTK_CLIPBOARD
2191 else {
2192 sciThis->GetSelection(selection_data, info, &sciThis->copyText);
2194 #endif
2195 } catch (...) {
2196 sciThis->errorStatus = SC_STATUS_FAILURE;
2200 gint ScintillaGTK::SelectionClear(GtkWidget *widget, GdkEventSelection *selection_event) {
2201 ScintillaGTK *sciThis = ScintillaFromWidget(widget);
2202 //Platform::DebugPrintf("Selection clear\n");
2203 sciThis->UnclaimSelection(selection_event);
2204 return gtk_selection_clear(widget, selection_event);
2207 void ScintillaGTK::DragBegin(GtkWidget *, GdkDragContext *) {
2208 //Platform::DebugPrintf("DragBegin\n");
2211 gboolean ScintillaGTK::DragMotionThis(GdkDragContext *context,
2212 gint x, gint y, guint dragtime) {
2213 try {
2214 Point npt(x, y);
2215 SetDragPosition(SPositionFromLocation(npt, false, false, UserVirtualSpace()));
2216 GdkDragAction preferredAction = context->suggested_action;
2217 SelectionPosition pos = SPositionFromLocation(npt);
2218 if ((inDragDrop == ddDragging) && (PositionInSelection(pos.Position()))) {
2219 // Avoid dragging selection onto itself as that produces a move
2220 // with no real effect but which creates undo actions.
2221 preferredAction = static_cast<GdkDragAction>(0);
2222 } else if (context->actions == static_cast<GdkDragAction>
2223 (GDK_ACTION_COPY | GDK_ACTION_MOVE)) {
2224 preferredAction = GDK_ACTION_MOVE;
2226 gdk_drag_status(context, preferredAction, dragtime);
2227 } catch (...) {
2228 errorStatus = SC_STATUS_FAILURE;
2230 return FALSE;
2233 gboolean ScintillaGTK::DragMotion(GtkWidget *widget, GdkDragContext *context,
2234 gint x, gint y, guint dragtime) {
2235 ScintillaGTK *sciThis = ScintillaFromWidget(widget);
2236 return sciThis->DragMotionThis(context, x, y, dragtime);
2239 void ScintillaGTK::DragLeave(GtkWidget *widget, GdkDragContext * /*context*/, guint) {
2240 ScintillaGTK *sciThis = ScintillaFromWidget(widget);
2241 try {
2242 sciThis->SetDragPosition(SelectionPosition(invalidPosition));
2243 //Platform::DebugPrintf("DragLeave %x\n", sciThis);
2244 } catch (...) {
2245 sciThis->errorStatus = SC_STATUS_FAILURE;
2249 void ScintillaGTK::DragEnd(GtkWidget *widget, GdkDragContext * /*context*/) {
2250 ScintillaGTK *sciThis = ScintillaFromWidget(widget);
2251 try {
2252 // If drag did not result in drop here or elsewhere
2253 if (!sciThis->dragWasDropped)
2254 sciThis->SetEmptySelection(sciThis->posDrag);
2255 sciThis->SetDragPosition(SelectionPosition(invalidPosition));
2256 //Platform::DebugPrintf("DragEnd %x %d\n", sciThis, sciThis->dragWasDropped);
2257 sciThis->inDragDrop = ddNone;
2258 } catch (...) {
2259 sciThis->errorStatus = SC_STATUS_FAILURE;
2263 gboolean ScintillaGTK::Drop(GtkWidget *widget, GdkDragContext * /*context*/,
2264 gint, gint, guint) {
2265 ScintillaGTK *sciThis = ScintillaFromWidget(widget);
2266 try {
2267 //Platform::DebugPrintf("Drop %x\n", sciThis);
2268 sciThis->SetDragPosition(SelectionPosition(invalidPosition));
2269 } catch (...) {
2270 sciThis->errorStatus = SC_STATUS_FAILURE;
2272 return FALSE;
2275 void ScintillaGTK::DragDataReceived(GtkWidget *widget, GdkDragContext * /*context*/,
2276 gint, gint, GtkSelectionData *selection_data, guint /*info*/, guint) {
2277 ScintillaGTK *sciThis = ScintillaFromWidget(widget);
2278 try {
2279 sciThis->ReceivedDrop(selection_data);
2280 sciThis->SetDragPosition(SelectionPosition(invalidPosition));
2281 } catch (...) {
2282 sciThis->errorStatus = SC_STATUS_FAILURE;
2286 void ScintillaGTK::DragDataGet(GtkWidget *widget, GdkDragContext *context,
2287 GtkSelectionData *selection_data, guint info, guint) {
2288 ScintillaGTK *sciThis = ScintillaFromWidget(widget);
2289 try {
2290 sciThis->dragWasDropped = true;
2291 if (!sciThis->sel.Empty()) {
2292 sciThis->GetSelection(selection_data, info, &sciThis->drag);
2294 if (context->action == GDK_ACTION_MOVE) {
2295 for (size_t r=0; r<sciThis->sel.Count(); r++) {
2296 if (sciThis->posDrop >= sciThis->sel.Range(r).Start()) {
2297 if (sciThis->posDrop > sciThis->sel.Range(r).End()) {
2298 sciThis->posDrop.Add(-sciThis->sel.Range(r).Length());
2299 } else {
2300 sciThis->posDrop.Add(-SelectionRange(sciThis->posDrop, sciThis->sel.Range(r).Start()).Length());
2304 sciThis->ClearSelection();
2306 sciThis->SetDragPosition(SelectionPosition(invalidPosition));
2307 } catch (...) {
2308 sciThis->errorStatus = SC_STATUS_FAILURE;
2312 int ScintillaGTK::TimeOut(ScintillaGTK *sciThis) {
2313 sciThis->Tick();
2314 return 1;
2317 gboolean ScintillaGTK::IdleCallback(ScintillaGTK *sciThis) {
2318 // Idler will be automatically stopped, if there is nothing
2319 // to do while idle.
2320 bool ret = sciThis->Idle();
2321 if (ret == false) {
2322 // FIXME: This will remove the idler from GTK, we don't want to
2323 // remove it as it is removed automatically when this function
2324 // returns false (although, it should be harmless).
2325 sciThis->SetIdle(false);
2327 return ret;
2330 gboolean ScintillaGTK::StyleIdle(ScintillaGTK *sciThis) {
2331 sciThis->IdleStyling();
2332 // Idler will be automatically stopped
2333 return FALSE;
2336 void ScintillaGTK::QueueStyling(int upTo) {
2337 Editor::QueueStyling(upTo);
2338 if (!styleNeeded.active) {
2339 // Only allow one style needed to be queued
2340 styleNeeded.active = true;
2341 g_idle_add_full(G_PRIORITY_HIGH_IDLE,
2342 reinterpret_cast<GSourceFunc>(StyleIdle), this, NULL);
2346 void ScintillaGTK::PopUpCB(ScintillaGTK *sciThis, guint action, GtkWidget *) {
2347 if (action) {
2348 sciThis->Command(action);
2352 gint ScintillaGTK::PressCT(GtkWidget *widget, GdkEventButton *event, ScintillaGTK *sciThis) {
2353 try {
2354 if (event->window != widget->window)
2355 return FALSE;
2356 if (event->type != GDK_BUTTON_PRESS)
2357 return FALSE;
2358 Point pt;
2359 pt.x = int(event->x);
2360 pt.y = int(event->y);
2361 sciThis->ct.MouseClick(pt);
2362 sciThis->CallTipClick();
2363 } catch (...) {
2365 return TRUE;
2368 gint ScintillaGTK::ExposeCT(GtkWidget *widget, GdkEventExpose * /*ose*/, CallTip *ctip) {
2369 try {
2370 Surface *surfaceWindow = Surface::Allocate();
2371 if (surfaceWindow) {
2372 surfaceWindow->Init(widget->window, widget);
2373 surfaceWindow->SetUnicodeMode(SC_CP_UTF8 == ctip->codePage);
2374 surfaceWindow->SetDBCSMode(ctip->codePage);
2375 ctip->PaintCT(surfaceWindow);
2376 surfaceWindow->Release();
2377 delete surfaceWindow;
2379 } catch (...) {
2380 // No pointer back to Scintilla to save status
2382 return TRUE;
2385 sptr_t ScintillaGTK::DirectFunction(
2386 ScintillaGTK *sciThis, unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
2387 return sciThis->WndProc(iMessage, wParam, lParam);
2390 sptr_t scintilla_send_message(ScintillaObject *sci, unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
2391 ScintillaGTK *psci = reinterpret_cast<ScintillaGTK *>(sci->pscin);
2392 return psci->WndProc(iMessage, wParam, lParam);
2395 static void scintilla_class_init(ScintillaClass *klass);
2396 static void scintilla_init(ScintillaObject *sci);
2398 extern void Platform_Initialise();
2399 extern void Platform_Finalise();
2401 GType scintilla_get_type() {
2402 static GType scintilla_type = 0;
2403 try {
2405 if (!scintilla_type) {
2406 scintilla_type = g_type_from_name("Scintilla");
2407 if (!scintilla_type) {
2408 static GTypeInfo scintilla_info = {
2409 (guint16) sizeof (ScintillaClass),
2410 NULL, //(GBaseInitFunc)
2411 NULL, //(GBaseFinalizeFunc)
2412 (GClassInitFunc) scintilla_class_init,
2413 NULL, //(GClassFinalizeFunc)
2414 NULL, //gconstpointer data
2415 (guint16) sizeof (ScintillaObject),
2416 0, //n_preallocs
2417 (GInstanceInitFunc) scintilla_init,
2418 NULL //(GTypeValueTable*)
2421 scintilla_type = g_type_register_static(
2422 GTK_TYPE_CONTAINER, "Scintilla", &scintilla_info, (GTypeFlags) 0);
2426 } catch (...) {
2428 return scintilla_type;
2431 void ScintillaGTK::ClassInit(OBJECT_CLASS* object_class, GtkWidgetClass *widget_class, GtkContainerClass *container_class) {
2432 Platform_Initialise();
2433 atomClipboard = gdk_atom_intern("CLIPBOARD", FALSE);
2434 atomUTF8 = gdk_atom_intern("UTF8_STRING", FALSE);
2435 atomString = GDK_SELECTION_TYPE_STRING;
2436 atomUriList = gdk_atom_intern("text/uri-list", FALSE);
2437 atomDROPFILES_DND = gdk_atom_intern("DROPFILES_DND", FALSE);
2439 // Define default signal handlers for the class: Could move more
2440 // of the signal handlers here (those that currently attached to wDraw
2441 // in Initialise() may require coordinate translation?)
2443 object_class->finalize = Destroy;
2444 widget_class->size_request = SizeRequest;
2445 widget_class->size_allocate = SizeAllocate;
2446 widget_class->expose_event = ExposeMain;
2447 widget_class->motion_notify_event = Motion;
2448 widget_class->button_press_event = Press;
2449 widget_class->button_release_event = MouseRelease;
2450 widget_class->scroll_event = ScrollEvent;
2451 widget_class->key_press_event = KeyPress;
2452 widget_class->key_release_event = KeyRelease;
2453 widget_class->focus_in_event = FocusIn;
2454 widget_class->focus_out_event = FocusOut;
2455 widget_class->selection_received = SelectionReceived;
2456 widget_class->selection_get = SelectionGet;
2457 widget_class->selection_clear_event = SelectionClear;
2459 widget_class->drag_data_received = DragDataReceived;
2460 widget_class->drag_motion = DragMotion;
2461 widget_class->drag_leave = DragLeave;
2462 widget_class->drag_end = DragEnd;
2463 widget_class->drag_drop = Drop;
2464 widget_class->drag_data_get = DragDataGet;
2466 widget_class->realize = Realize;
2467 widget_class->unrealize = UnRealize;
2468 widget_class->map = Map;
2469 widget_class->unmap = UnMap;
2471 container_class->forall = MainForAll;
2474 #define SIG_MARSHAL scintilla_marshal_NONE__INT_POINTER
2475 #define MARSHAL_ARGUMENTS G_TYPE_INT, G_TYPE_POINTER
2477 static void scintilla_class_init(ScintillaClass *klass) {
2478 try {
2479 OBJECT_CLASS *object_class = (OBJECT_CLASS*) klass;
2480 GtkWidgetClass *widget_class = (GtkWidgetClass*) klass;
2481 GtkContainerClass *container_class = (GtkContainerClass*) klass;
2483 GSignalFlags sigflags = GSignalFlags(G_SIGNAL_ACTION | G_SIGNAL_RUN_LAST);
2484 scintilla_signals[COMMAND_SIGNAL] = g_signal_new(
2485 "command",
2486 G_TYPE_FROM_CLASS(object_class),
2487 sigflags,
2488 G_STRUCT_OFFSET(ScintillaClass, command),
2489 NULL, //(GSignalAccumulator)
2490 NULL, //(gpointer)
2491 SIG_MARSHAL,
2492 G_TYPE_NONE,
2493 2, MARSHAL_ARGUMENTS);
2495 scintilla_signals[NOTIFY_SIGNAL] = g_signal_new(
2496 SCINTILLA_NOTIFY,
2497 G_TYPE_FROM_CLASS(object_class),
2498 sigflags,
2499 G_STRUCT_OFFSET(ScintillaClass, notify),
2500 NULL,
2501 NULL,
2502 SIG_MARSHAL,
2503 G_TYPE_NONE,
2504 2, MARSHAL_ARGUMENTS);
2506 klass->command = NULL;
2507 klass->notify = NULL;
2509 ScintillaGTK::ClassInit(object_class, widget_class, container_class);
2510 } catch (...) {
2514 static void scintilla_init(ScintillaObject *sci) {
2515 try {
2516 GTK_WIDGET_SET_FLAGS(sci, GTK_CAN_FOCUS);
2517 sci->pscin = new ScintillaGTK(sci);
2518 } catch (...) {
2522 GtkWidget* scintilla_new() {
2523 return GTK_WIDGET(g_object_new(scintilla_get_type(), NULL));
2526 void scintilla_set_id(ScintillaObject *sci, uptr_t id) {
2527 ScintillaGTK *psci = reinterpret_cast<ScintillaGTK *>(sci->pscin);
2528 psci->ctrlID = id;
2531 void scintilla_release_resources(void) {
2532 try {
2533 Platform_Finalise();
2534 } catch (...) {