diff options
| -rw-r--r-- | gtk/ScintillaGTK.cxx | 376 | ||||
| -rw-r--r-- | gtk/ScintillaGTK.h | 287 | ||||
| -rw-r--r-- | gtk/ScintillaGTKAccessible.cxx | 1197 | ||||
| -rw-r--r-- | gtk/ScintillaGTKAccessible.h | 163 | ||||
| -rw-r--r-- | gtk/makefile | 2 | ||||
| -rw-r--r-- | scripts/HeaderOrder.txt | 3 | 
6 files changed, 1739 insertions, 289 deletions
| diff --git a/gtk/ScintillaGTK.cxx b/gtk/ScintillaGTK.cxx index 33aec818b..2d5daa2f0 100644 --- a/gtk/ScintillaGTK.cxx +++ b/gtk/ScintillaGTK.cxx @@ -72,7 +72,9 @@  #include "ExternalLexer.h"  #endif +#include "ScintillaGTK.h"  #include "scintilla-marshal.h" +#include "ScintillaGTKAccessible.h"  #include "Converter.h" @@ -105,8 +107,6 @@ static GdkWindow *WindowFromWidget(GtkWidget *w) {  #pragma warning(disable: 4505)  #endif -#define OBJECT_CLASS GObjectClass -  #ifdef SCI_NAMESPACE  using namespace Scintilla;  #endif @@ -118,228 +118,6 @@ static GdkWindow *PWindow(const Window &w) {  extern std::string UTF8FromLatin1(const char *s, int len); -class ScintillaGTK : public ScintillaBase { -	_ScintillaObject *sci; -	Window wText; -	Window scrollbarv; -	Window scrollbarh; -	GtkAdjustment *adjustmentv; -	GtkAdjustment *adjustmenth; -	int verticalScrollBarWidth; -	int horizontalScrollBarHeight; - -	SelectionText primary; - -	GdkEventButton *evbtn; -	bool capturedMouse; -	bool dragWasDropped; -	int lastKey; -	int rectangularSelectionModifier; - -	GtkWidgetClass *parentClass; - -	static GdkAtom atomClipboard; -	static GdkAtom atomUTF8; -	static GdkAtom atomString; -	static GdkAtom atomUriList; -	static GdkAtom atomDROPFILES_DND; -	GdkAtom atomSought; - -#if PLAT_GTK_WIN32 -	CLIPFORMAT cfColumnSelect; -#endif - -	Window wPreedit; -	Window wPreeditDraw; -	GtkIMContext *im_context; -	PangoScript lastNonCommonScript; - -	// Wheel mouse support -	unsigned int linesPerScroll; -	GTimeVal lastWheelMouseTime; -	gint lastWheelMouseDirection; -	gint wheelMouseIntensity; - -#if GTK_CHECK_VERSION(3,0,0) -	cairo_rectangle_list_t *rgnUpdate; -#else -	GdkRegion *rgnUpdate; -#endif -	bool repaintFullWindow; - -	guint styleIdleID; - -	// Private so ScintillaGTK objects can not be copied -	ScintillaGTK(const ScintillaGTK &); -	ScintillaGTK &operator=(const ScintillaGTK &); - -public: -	explicit ScintillaGTK(_ScintillaObject *sci_); -	virtual ~ScintillaGTK(); -	static void ClassInit(OBJECT_CLASS* object_class, GtkWidgetClass *widget_class, GtkContainerClass *container_class); -private: -	virtual void Initialise(); -	virtual void Finalise(); -	virtual bool AbandonPaint(); -	virtual void DisplayCursor(Window::Cursor c); -	virtual bool DragThreshold(Point ptStart, Point ptNow); -	virtual void StartDrag(); -	int TargetAsUTF8(char *text); -	int EncodedFromUTF8(char *utf8, char *encoded) const; -	virtual bool ValidCodePage(int codePage) const; -public: 	// Public for scintilla_send_message -	virtual sptr_t WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam); -private: -	virtual sptr_t DefWndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam); -	struct TimeThunk { -		TickReason reason; -		ScintillaGTK *scintilla; -		guint timer; -		TimeThunk() : reason(tickCaret), scintilla(NULL), timer(0) {} -	}; -	TimeThunk timers[tickDwell+1]; -	virtual bool FineTickerAvailable(); -	virtual bool FineTickerRunning(TickReason reason); -	virtual void FineTickerStart(TickReason reason, int millis, int tolerance); -	virtual void FineTickerCancel(TickReason reason); -	virtual bool SetIdle(bool on); -	virtual void SetMouseCapture(bool on); -	virtual bool HaveMouseCapture(); -	virtual bool PaintContains(PRectangle rc); -	void FullPaint(); -	virtual PRectangle GetClientRectangle() const; -	virtual void ScrollText(int linesToMove); -	virtual void SetVerticalScrollPos(); -	virtual void SetHorizontalScrollPos(); -	virtual bool ModifyScrollBars(int nMax, int nPage); -	void ReconfigureScrollBars(); -	virtual void NotifyChange(); -	virtual void NotifyFocus(bool focus); -	virtual void NotifyParent(SCNotification scn); -	void NotifyKey(int key, int modifiers); -	void NotifyURIDropped(const char *list); -	const char *CharacterSetID() const; -	virtual CaseFolder *CaseFolderForEncoding(); -	virtual std::string CaseMapString(const std::string &s, int caseMapping); -	virtual int KeyDefault(int key, int modifiers); -	virtual void CopyToClipboard(const SelectionText &selectedText); -	virtual void Copy(); -	virtual void Paste(); -	virtual void CreateCallTipWindow(PRectangle rc); -	virtual void AddToPopUp(const char *label, int cmd = 0, bool enabled = true); -	bool OwnPrimarySelection(); -	virtual void ClaimSelection(); -	void GetGtkSelectionText(GtkSelectionData *selectionData, SelectionText &selText); -	void ReceivedSelection(GtkSelectionData *selection_data); -	void ReceivedDrop(GtkSelectionData *selection_data); -	static void GetSelection(GtkSelectionData *selection_data, guint info, SelectionText *selected); -	void StoreOnClipboard(SelectionText *clipText); -	static void ClipboardGetSelection(GtkClipboard* clip, GtkSelectionData *selection_data, guint info, void *data); -	static void ClipboardClearSelection(GtkClipboard* clip, void *data); - -	void UnclaimSelection(GdkEventSelection *selection_event); -	void Resize(int width, int height); - -	// Callback functions -	void RealizeThis(GtkWidget *widget); -	static void Realize(GtkWidget *widget); -	void UnRealizeThis(GtkWidget *widget); -	static void UnRealize(GtkWidget *widget); -	void MapThis(); -	static void Map(GtkWidget *widget); -	void UnMapThis(); -	static void UnMap(GtkWidget *widget); -	gint FocusInThis(GtkWidget *widget); -	static gint FocusIn(GtkWidget *widget, GdkEventFocus *event); -	gint FocusOutThis(GtkWidget *widget); -	static gint FocusOut(GtkWidget *widget, GdkEventFocus *event); -	static void SizeRequest(GtkWidget *widget, GtkRequisition *requisition); -#if GTK_CHECK_VERSION(3,0,0) -	static void GetPreferredWidth(GtkWidget *widget, gint *minimalWidth, gint *naturalWidth); -	static void GetPreferredHeight(GtkWidget *widget, gint *minimalHeight, gint *naturalHeight); -#endif -	static void SizeAllocate(GtkWidget *widget, GtkAllocation *allocation); -#if GTK_CHECK_VERSION(3,0,0) -	gboolean DrawTextThis(cairo_t *cr); -	static gboolean DrawText(GtkWidget *widget, cairo_t *cr, ScintillaGTK *sciThis); -	gboolean DrawThis(cairo_t *cr); -	static gboolean DrawMain(GtkWidget *widget, cairo_t *cr); -#else -	gboolean ExposeTextThis(GtkWidget *widget, GdkEventExpose *ose); -	static gboolean ExposeText(GtkWidget *widget, GdkEventExpose *ose, ScintillaGTK *sciThis); -	gboolean Expose(GtkWidget *widget, GdkEventExpose *ose); -	static gboolean ExposeMain(GtkWidget *widget, GdkEventExpose *ose); -#endif -	void ForAll(GtkCallback callback, gpointer callback_data); -	static void MainForAll(GtkContainer *container, gboolean include_internals, GtkCallback callback, gpointer callback_data); - -	static void ScrollSignal(GtkAdjustment *adj, ScintillaGTK *sciThis); -	static void ScrollHSignal(GtkAdjustment *adj, ScintillaGTK *sciThis); -	gint PressThis(GdkEventButton *event); -	static gint Press(GtkWidget *widget, GdkEventButton *event); -	static gint MouseRelease(GtkWidget *widget, GdkEventButton *event); -	static gint ScrollEvent(GtkWidget *widget, GdkEventScroll *event); -	static gint Motion(GtkWidget *widget, GdkEventMotion *event); -	gboolean KeyThis(GdkEventKey *event); -	static gboolean KeyPress(GtkWidget *widget, GdkEventKey *event); -	static gboolean KeyRelease(GtkWidget *widget, GdkEventKey *event); -#if GTK_CHECK_VERSION(3,0,0) -	gboolean DrawPreeditThis(GtkWidget *widget, cairo_t *cr); -	static gboolean DrawPreedit(GtkWidget *widget, cairo_t *cr, ScintillaGTK *sciThis); -#else -	gboolean ExposePreeditThis(GtkWidget *widget, GdkEventExpose *ose); -	static gboolean ExposePreedit(GtkWidget *widget, GdkEventExpose *ose, ScintillaGTK *sciThis); -#endif - -	bool KoreanIME(); -	void CommitThis(char *str); -	static void Commit(GtkIMContext *context, char *str, ScintillaGTK *sciThis); -	void PreeditChangedInlineThis(); -	void PreeditChangedWindowedThis(); -	static void PreeditChanged(GtkIMContext *context, ScintillaGTK *sciThis); -	void MoveImeCarets(int pos); -	void DrawImeIndicator(int indicator, int len); -	void SetCandidateWindowPos(); - -	static void StyleSetText(GtkWidget *widget, GtkStyle *previous, void*); -	static void RealizeText(GtkWidget *widget, void*); -	static void Dispose(GObject *object); -	static void Destroy(GObject *object); -	static void SelectionReceived(GtkWidget *widget, GtkSelectionData *selection_data, -	                              guint time); -	static void SelectionGet(GtkWidget *widget, GtkSelectionData *selection_data, -	                         guint info, guint time); -	static gint SelectionClear(GtkWidget *widget, GdkEventSelection *selection_event); -	gboolean DragMotionThis(GdkDragContext *context, gint x, gint y, guint dragtime); -	static gboolean DragMotion(GtkWidget *widget, GdkDragContext *context, -	                           gint x, gint y, guint dragtime); -	static void DragLeave(GtkWidget *widget, GdkDragContext *context, -	                      guint time); -	static void DragEnd(GtkWidget *widget, GdkDragContext *context); -	static gboolean Drop(GtkWidget *widget, GdkDragContext *context, -	                     gint x, gint y, guint time); -	static void DragDataReceived(GtkWidget *widget, GdkDragContext *context, -	                             gint x, gint y, GtkSelectionData *selection_data, guint info, guint time); -	static void DragDataGet(GtkWidget *widget, GdkDragContext *context, -	                        GtkSelectionData *selection_data, guint info, guint time); -	static gboolean TimeOut(gpointer ptt); -	static gboolean IdleCallback(gpointer pSci); -	static gboolean StyleIdle(gpointer pSci); -	virtual void IdleWork(); -	virtual void QueueIdleWork(WorkNeeded::workItems items, int upTo); -	static void PopUpCB(GtkMenuItem *menuItem, ScintillaGTK *sciThis); - -#if GTK_CHECK_VERSION(3,0,0) -	static gboolean DrawCT(GtkWidget *widget, cairo_t *cr, CallTip *ctip); -#else -	static gboolean ExposeCT(GtkWidget *widget, GdkEventExpose *ose, CallTip *ct); -#endif -	static gboolean PressCT(GtkWidget *widget, GdkEventButton *event, ScintillaGTK *sciThis); - -	static sptr_t DirectFunction(sptr_t ptr, -	                             unsigned int iMessage, uptr_t wParam, sptr_t lParam); -}; -  enum {      COMMAND_SIGNAL,      NOTIFY_SIGNAL, @@ -379,7 +157,7 @@ static GtkWidget *PWidget(Window &w) {  	return static_cast<GtkWidget *>(w.GetID());  } -static ScintillaGTK *ScintillaFromWidget(GtkWidget *widget) { +ScintillaGTK *ScintillaGTK::FromWidget(GtkWidget *widget) {  	ScintillaObject *scio = SCINTILLA(widget);  	return static_cast<ScintillaGTK *>(scio->pscin);  } @@ -394,7 +172,8 @@ ScintillaGTK::ScintillaGTK(_ScintillaObject *sci_) :  		wheelMouseIntensity(0),  		rgnUpdate(0),  		repaintFullWindow(false), -		styleIdleID(0) { +		styleIdleID(0), +		accessible(0) {  	sci = sci_;  	wMain = GTK_WIDGET(sci); @@ -527,7 +306,7 @@ void ScintillaGTK::RealizeThis(GtkWidget *widget) {  }  void ScintillaGTK::Realize(GtkWidget *widget) { -	ScintillaGTK *sciThis = ScintillaFromWidget(widget); +	ScintillaGTK *sciThis = FromWidget(widget);  	sciThis->RealizeThis(widget);  } @@ -560,7 +339,7 @@ void ScintillaGTK::UnRealizeThis(GtkWidget *widget) {  }  void ScintillaGTK::UnRealize(GtkWidget *widget) { -	ScintillaGTK *sciThis = ScintillaFromWidget(widget); +	ScintillaGTK *sciThis = FromWidget(widget);  	sciThis->UnRealizeThis(widget);  } @@ -594,7 +373,7 @@ void ScintillaGTK::MapThis() {  }  void ScintillaGTK::Map(GtkWidget *widget) { -	ScintillaGTK *sciThis = ScintillaFromWidget(widget); +	ScintillaGTK *sciThis = FromWidget(widget);  	sciThis->MapThis();  } @@ -617,7 +396,7 @@ void ScintillaGTK::UnMapThis() {  }  void ScintillaGTK::UnMap(GtkWidget *widget) { -	ScintillaGTK *sciThis = ScintillaFromWidget(widget); +	ScintillaGTK *sciThis = FromWidget(widget);  	sciThis->UnMapThis();  } @@ -634,7 +413,7 @@ void ScintillaGTK::ForAll(GtkCallback callback, gpointer callback_data) {  }  void ScintillaGTK::MainForAll(GtkContainer *container, gboolean include_internals, GtkCallback callback, gpointer callback_data) { -	ScintillaGTK *sciThis = ScintillaFromWidget((GtkWidget *)container); +	ScintillaGTK *sciThis = FromWidget((GtkWidget *)container);  	if (callback != NULL && include_internals) {  		sciThis->ForAll(callback, callback_data); @@ -690,7 +469,7 @@ gint ScintillaGTK::FocusInThis(GtkWidget *widget) {  }  gint ScintillaGTK::FocusIn(GtkWidget *widget, GdkEventFocus * /*event*/) { -	ScintillaGTK *sciThis = ScintillaFromWidget(widget); +	ScintillaGTK *sciThis = FromWidget(widget);  	return sciThis->FocusInThis(widget);  } @@ -710,12 +489,12 @@ gint ScintillaGTK::FocusOutThis(GtkWidget *widget) {  }  gint ScintillaGTK::FocusOut(GtkWidget *widget, GdkEventFocus * /*event*/) { -	ScintillaGTK *sciThis = ScintillaFromWidget(widget); +	ScintillaGTK *sciThis = FromWidget(widget);  	return sciThis->FocusOutThis(widget);  }  void ScintillaGTK::SizeRequest(GtkWidget *widget, GtkRequisition *requisition) { -	ScintillaGTK *sciThis = ScintillaFromWidget(widget); +	ScintillaGTK *sciThis = FromWidget(widget);  	requisition->width = 1;  	requisition->height = 1;  	GtkRequisition child_requisition; @@ -745,7 +524,7 @@ void ScintillaGTK::GetPreferredHeight(GtkWidget *widget, gint *minimalHeight, gi  #endif  void ScintillaGTK::SizeAllocate(GtkWidget *widget, GtkAllocation *allocation) { -	ScintillaGTK *sciThis = ScintillaFromWidget(widget); +	ScintillaGTK *sciThis = FromWidget(widget);  	try {  		gtk_widget_set_allocation(widget, allocation);  		if (IS_WIDGET_REALIZED(widget)) @@ -882,6 +661,12 @@ void ScintillaGTK::Finalise() {  	for (TickReason tr = tickCaret; tr <= tickDwell; tr = static_cast<TickReason>(tr + 1)) {  		FineTickerCancel(tr);  	} +	if (accessible) { +		gtk_accessible_set_widget(GTK_ACCESSIBLE(accessible), NULL); +		g_object_unref(accessible); +		accessible = 0; +	} +  	ScintillaBase::Finalise();  } @@ -925,8 +710,11 @@ void ScintillaGTK::StartDrag() {  #endif  } -static std::string ConvertText(const char *s, size_t len, const char *charSetDest, -	const char *charSetSource, bool transliterations, bool silent=false) { +#ifdef SCI_NAMESPACE +namespace Scintilla { +#endif +std::string ConvertText(const char *s, size_t len, const char *charSetDest, +	const char *charSetSource, bool transliterations, bool silent) {  	// s is not const because of different versions of iconv disagreeing about const  	std::string destForm;  	Converter conv(charSetDest, charSetSource, transliterations); @@ -957,6 +745,9 @@ static std::string ConvertText(const char *s, size_t len, const char *charSetDes  	}  	return destForm;  } +#ifdef SCI_NAMESPACE +} +#endif  // Returns the target converted to UTF8.  // Return the length in bytes. @@ -1057,6 +848,17 @@ sptr_t ScintillaGTK::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam  		case SCI_GETRECTANGULARSELECTIONMODIFIER:  			return rectangularSelectionModifier; +		case SCI_SETREADONLY: { +			sptr_t ret = ScintillaBase::WndProc(iMessage, wParam, lParam); +			if (accessible) { +				ScintillaGTKAccessible *sciAccessible = ScintillaGTKAccessible::FromAccessible(accessible); +				if (sciAccessible) { +					sciAccessible->NotifyReadOnly(); +				} +			} +			return ret; +		} +  		default:  			return ScintillaBase::WndProc(iMessage, wParam, lParam);  		} @@ -1450,40 +1252,6 @@ void ScintillaGTK::Copy() {  	}  } -// helper class to watch a GObject lifetime and get notified when it dies -class GObjectWatcher { -	GObject *weakRef; - -	void WeakNotifyThis(GObject *obj) { -		PLATFORM_ASSERT(obj == weakRef); - -		Destroyed(); -		weakRef = 0; -	} - -	static void WeakNotify(gpointer data, GObject *obj) { -		static_cast<GObjectWatcher*>(data)->WeakNotifyThis(obj); -	} - -public: -	GObjectWatcher(GObject *obj) : -			weakRef(obj) { -		g_object_weak_ref(weakRef, WeakNotify, this); -	} - -	virtual ~GObjectWatcher() { -		if (weakRef) { -			g_object_weak_unref(weakRef, WeakNotify, this); -		} -	} - -	virtual void Destroyed() {} - -	bool IsDestroyed() { -		return weakRef != 0; -	} -}; -  void ScintillaGTK::Paste() {  	atomSought = atomUTF8;  	GtkClipboard *clipBoard = @@ -1968,12 +1736,12 @@ gint ScintillaGTK::PressThis(GdkEventButton *event) {  gint ScintillaGTK::Press(GtkWidget *widget, GdkEventButton *event) {  	if (event->window != WindowFromWidget(widget))  		return FALSE; -	ScintillaGTK *sciThis = ScintillaFromWidget(widget); +	ScintillaGTK *sciThis = FromWidget(widget);  	return sciThis->PressThis(event);  }  gint ScintillaGTK::MouseRelease(GtkWidget *widget, GdkEventButton *event) { -	ScintillaGTK *sciThis = ScintillaFromWidget(widget); +	ScintillaGTK *sciThis = FromWidget(widget);  	try {  		//Platform::DebugPrintf("Release %x %d %d\n",sciThis,event->time,event->state);  		if (!sciThis->HaveMouseCapture()) @@ -1999,7 +1767,7 @@ gint ScintillaGTK::MouseRelease(GtkWidget *widget, GdkEventButton *event) {  // win32gtk and GTK >= 2 use SCROLL_* events instead of passing the  // button4/5/6/7 events to the GTK app  gint ScintillaGTK::ScrollEvent(GtkWidget *widget, GdkEventScroll *event) { -	ScintillaGTK *sciThis = ScintillaFromWidget(widget); +	ScintillaGTK *sciThis = FromWidget(widget);  	try {  		if (widget == NULL || event == NULL) @@ -2083,7 +1851,7 @@ gint ScintillaGTK::ScrollEvent(GtkWidget *widget, GdkEventScroll *event) {  }  gint ScintillaGTK::Motion(GtkWidget *widget, GdkEventMotion *event) { -	ScintillaGTK *sciThis = ScintillaFromWidget(widget); +	ScintillaGTK *sciThis = FromWidget(widget);  	try {  		//Platform::DebugPrintf("Motion %x %d\n",sciThis,event->time);  		if (event->window != WindowFromWidget(widget)) @@ -2314,13 +2082,13 @@ gboolean ScintillaGTK::KeyThis(GdkEventKey *event) {  }  gboolean ScintillaGTK::KeyPress(GtkWidget *widget, GdkEventKey *event) { -	ScintillaGTK *sciThis = ScintillaFromWidget(widget); +	ScintillaGTK *sciThis = FromWidget(widget);  	return sciThis->KeyThis(event);  }  gboolean ScintillaGTK::KeyRelease(GtkWidget *widget, GdkEventKey *event) {  	//Platform::DebugPrintf("SC-keyrel: %d %x %3s\n",event->keyval, event->state, event->string); -	ScintillaGTK *sciThis = ScintillaFromWidget(widget); +	ScintillaGTK *sciThis = FromWidget(widget);  	if (gtk_im_context_filter_keypress(sciThis->im_context, event)) {  		return TRUE;  	} @@ -2768,7 +2536,7 @@ gboolean ScintillaGTK::DrawThis(cairo_t *cr) {  }  gboolean ScintillaGTK::DrawMain(GtkWidget *widget, cairo_t *cr) { -	ScintillaGTK *sciThis = ScintillaFromWidget(widget); +	ScintillaGTK *sciThis = FromWidget(widget);  	return sciThis->DrawThis(cr);  } @@ -2818,7 +2586,7 @@ gboolean ScintillaGTK::ExposeText(GtkWidget *widget, GdkEventExpose *ose, Scinti  }  gboolean ScintillaGTK::ExposeMain(GtkWidget *widget, GdkEventExpose *ose) { -	ScintillaGTK *sciThis = ScintillaFromWidget(widget); +	ScintillaGTK *sciThis = FromWidget(widget);  	//Platform::DebugPrintf("Expose Main %0d,%0d %0d,%0d\n",  	//ose->area.x, ose->area.y, ose->area.width, ose->area.height);  	return sciThis->Expose(widget, ose); @@ -2861,14 +2629,14 @@ void ScintillaGTK::ScrollHSignal(GtkAdjustment *adj, ScintillaGTK *sciThis) {  void ScintillaGTK::SelectionReceived(GtkWidget *widget,                                       GtkSelectionData *selection_data, guint) { -	ScintillaGTK *sciThis = ScintillaFromWidget(widget); +	ScintillaGTK *sciThis = FromWidget(widget);  	//Platform::DebugPrintf("Selection received\n");  	sciThis->ReceivedSelection(selection_data);  }  void ScintillaGTK::SelectionGet(GtkWidget *widget,                                  GtkSelectionData *selection_data, guint info, guint) { -	ScintillaGTK *sciThis = ScintillaFromWidget(widget); +	ScintillaGTK *sciThis = FromWidget(widget);  	try {  		//Platform::DebugPrintf("Selection get\n");  		if (SelectionOfGSD(selection_data) == GDK_SELECTION_PRIMARY) { @@ -2883,7 +2651,7 @@ void ScintillaGTK::SelectionGet(GtkWidget *widget,  }  gint ScintillaGTK::SelectionClear(GtkWidget *widget, GdkEventSelection *selection_event) { -	ScintillaGTK *sciThis = ScintillaFromWidget(widget); +	ScintillaGTK *sciThis = FromWidget(widget);  	//Platform::DebugPrintf("Selection clear\n");  	sciThis->UnclaimSelection(selection_event);  	if (GTK_WIDGET_CLASS(sciThis->parentClass)->selection_clear_event) { @@ -2922,12 +2690,12 @@ gboolean ScintillaGTK::DragMotionThis(GdkDragContext *context,  gboolean ScintillaGTK::DragMotion(GtkWidget *widget, GdkDragContext *context,                                   gint x, gint y, guint dragtime) { -	ScintillaGTK *sciThis = ScintillaFromWidget(widget); +	ScintillaGTK *sciThis = FromWidget(widget);  	return sciThis->DragMotionThis(context, x, y, dragtime);  }  void ScintillaGTK::DragLeave(GtkWidget *widget, GdkDragContext * /*context*/, guint) { -	ScintillaGTK *sciThis = ScintillaFromWidget(widget); +	ScintillaGTK *sciThis = FromWidget(widget);  	try {  		sciThis->SetDragPosition(SelectionPosition(invalidPosition));  		//Platform::DebugPrintf("DragLeave %x\n", sciThis); @@ -2937,7 +2705,7 @@ void ScintillaGTK::DragLeave(GtkWidget *widget, GdkDragContext * /*context*/, gu  }  void ScintillaGTK::DragEnd(GtkWidget *widget, GdkDragContext * /*context*/) { -	ScintillaGTK *sciThis = ScintillaFromWidget(widget); +	ScintillaGTK *sciThis = FromWidget(widget);  	try {  		// If drag did not result in drop here or elsewhere  		if (!sciThis->dragWasDropped) @@ -2952,7 +2720,7 @@ void ScintillaGTK::DragEnd(GtkWidget *widget, GdkDragContext * /*context*/) {  gboolean ScintillaGTK::Drop(GtkWidget *widget, GdkDragContext * /*context*/,                              gint, gint, guint) { -	ScintillaGTK *sciThis = ScintillaFromWidget(widget); +	ScintillaGTK *sciThis = FromWidget(widget);  	try {  		//Platform::DebugPrintf("Drop %x\n", sciThis);  		sciThis->SetDragPosition(SelectionPosition(invalidPosition)); @@ -2964,7 +2732,7 @@ gboolean ScintillaGTK::Drop(GtkWidget *widget, GdkDragContext * /*context*/,  void ScintillaGTK::DragDataReceived(GtkWidget *widget, GdkDragContext * /*context*/,                                      gint, gint, GtkSelectionData *selection_data, guint /*info*/, guint) { -	ScintillaGTK *sciThis = ScintillaFromWidget(widget); +	ScintillaGTK *sciThis = FromWidget(widget);  	try {  		sciThis->ReceivedDrop(selection_data);  		sciThis->SetDragPosition(SelectionPosition(invalidPosition)); @@ -2975,7 +2743,7 @@ void ScintillaGTK::DragDataReceived(GtkWidget *widget, GdkDragContext * /*contex  void ScintillaGTK::DragDataGet(GtkWidget *widget, GdkDragContext *context,                                 GtkSelectionData *selection_data, guint info, guint) { -	ScintillaGTK *sciThis = ScintillaFromWidget(widget); +	ScintillaGTK *sciThis = FromWidget(widget);  	try {  		sciThis->dragWasDropped = true;  		if (!sciThis->sel.Empty()) { @@ -3044,6 +2812,28 @@ void ScintillaGTK::QueueIdleWork(WorkNeeded::workItems items, int upTo) {  	}  } +void ScintillaGTK::SetDocPointer(Document *document) { +	Document *oldDoc = 0; +	ScintillaGTKAccessible *sciAccessible = 0; +	if (accessible) { +		sciAccessible = ScintillaGTKAccessible::FromAccessible(accessible); +		if (sciAccessible && pdoc) { +			oldDoc = pdoc; +			oldDoc->AddRef(); +		} +	} + +	Editor::SetDocPointer(document); + +	if (sciAccessible) { +		// the accessible needs have the old Document, but also the new one active +		sciAccessible->ChangeDocument(oldDoc, pdoc); +	} +	if (oldDoc) { +		oldDoc->Release(); +	} +} +  void ScintillaGTK::PopUpCB(GtkMenuItem *menuItem, ScintillaGTK *sciThis) {  	guint action = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(menuItem), "CmdNum"));  	if (action) { @@ -3109,6 +2899,14 @@ gboolean ScintillaGTK::ExposeCT(GtkWidget *widget, GdkEventExpose * /*ose*/, Cal  #endif +AtkObject* ScintillaGTK::GetAccessibleThis(GtkWidget *widget) { +	return ScintillaGTKAccessible::WidgetGetAccessibleImpl(widget, &accessible, scintilla_class_parent_class); +} + +AtkObject* ScintillaGTK::GetAccessible(GtkWidget *widget) { +	return FromWidget(widget)->GetAccessibleThis(widget); +} +  sptr_t ScintillaGTK::DirectFunction(      sptr_t ptr, unsigned int iMessage, uptr_t wParam, sptr_t lParam) {  	return reinterpret_cast<ScintillaGTK *>(ptr)->WndProc(iMessage, wParam, lParam); @@ -3217,6 +3015,8 @@ void ScintillaGTK::ClassInit(OBJECT_CLASS* object_class, GtkWidgetClass *widget_  	widget_class->map = Map;  	widget_class->unmap = UnMap; +	widget_class->get_accessible = GetAccessible; +  	container_class->forall = MainForAll;  } diff --git a/gtk/ScintillaGTK.h b/gtk/ScintillaGTK.h new file mode 100644 index 000000000..e98176328 --- /dev/null +++ b/gtk/ScintillaGTK.h @@ -0,0 +1,287 @@ +// Scintilla source code edit control +// ScintillaGTK.h - GTK+ specific subclass of ScintillaBase +// Copyright 1998-2004 by Neil Hodgson <neilh@scintilla.org> +// The License.txt file describes the conditions under which this software may be distributed. + +#ifndef SCINTILLAGTK_H +#define SCINTILLAGTK_H + +#ifdef SCI_NAMESPACE +namespace Scintilla { +#endif + +class ScintillaGTKAccessible; + +#define OBJECT_CLASS GObjectClass + +class ScintillaGTK : public ScintillaBase { +	friend class ScintillaGTKAccessible; + +	_ScintillaObject *sci; +	Window wText; +	Window scrollbarv; +	Window scrollbarh; +	GtkAdjustment *adjustmentv; +	GtkAdjustment *adjustmenth; +	int verticalScrollBarWidth; +	int horizontalScrollBarHeight; + +	SelectionText primary; + +	GdkEventButton *evbtn; +	bool capturedMouse; +	bool dragWasDropped; +	int lastKey; +	int rectangularSelectionModifier; + +	GtkWidgetClass *parentClass; + +	static GdkAtom atomClipboard; +	static GdkAtom atomUTF8; +	static GdkAtom atomString; +	static GdkAtom atomUriList; +	static GdkAtom atomDROPFILES_DND; +	GdkAtom atomSought; + +#if PLAT_GTK_WIN32 +	CLIPFORMAT cfColumnSelect; +#endif + +	Window wPreedit; +	Window wPreeditDraw; +	GtkIMContext *im_context; +	PangoScript lastNonCommonScript; + +	// Wheel mouse support +	unsigned int linesPerScroll; +	GTimeVal lastWheelMouseTime; +	gint lastWheelMouseDirection; +	gint wheelMouseIntensity; + +#if GTK_CHECK_VERSION(3,0,0) +	cairo_rectangle_list_t *rgnUpdate; +#else +	GdkRegion *rgnUpdate; +#endif +	bool repaintFullWindow; + +	guint styleIdleID; +	AtkObject *accessible; + +	// Private so ScintillaGTK objects can not be copied +	ScintillaGTK(const ScintillaGTK &); +	ScintillaGTK &operator=(const ScintillaGTK &); + +public: +	explicit ScintillaGTK(_ScintillaObject *sci_); +	virtual ~ScintillaGTK(); +	static ScintillaGTK *FromWidget(GtkWidget *widget); +	static void ClassInit(OBJECT_CLASS* object_class, GtkWidgetClass *widget_class, GtkContainerClass *container_class); +private: +	virtual void Initialise(); +	virtual void Finalise(); +	virtual bool AbandonPaint(); +	virtual void DisplayCursor(Window::Cursor c); +	virtual bool DragThreshold(Point ptStart, Point ptNow); +	virtual void StartDrag(); +	int TargetAsUTF8(char *text); +	int EncodedFromUTF8(char *utf8, char *encoded) const; +	virtual bool ValidCodePage(int codePage) const; +public: 	// Public for scintilla_send_message +	virtual sptr_t WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam); +private: +	virtual sptr_t DefWndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam); +	struct TimeThunk { +		TickReason reason; +		ScintillaGTK *scintilla; +		guint timer; +		TimeThunk() : reason(tickCaret), scintilla(NULL), timer(0) {} +	}; +	TimeThunk timers[tickDwell+1]; +	virtual bool FineTickerAvailable(); +	virtual bool FineTickerRunning(TickReason reason); +	virtual void FineTickerStart(TickReason reason, int millis, int tolerance); +	virtual void FineTickerCancel(TickReason reason); +	virtual bool SetIdle(bool on); +	virtual void SetMouseCapture(bool on); +	virtual bool HaveMouseCapture(); +	virtual bool PaintContains(PRectangle rc); +	void FullPaint(); +	virtual PRectangle GetClientRectangle() const; +	virtual void ScrollText(int linesToMove); +	virtual void SetVerticalScrollPos(); +	virtual void SetHorizontalScrollPos(); +	virtual bool ModifyScrollBars(int nMax, int nPage); +	void ReconfigureScrollBars(); +	virtual void NotifyChange(); +	virtual void NotifyFocus(bool focus); +	virtual void NotifyParent(SCNotification scn); +	void NotifyKey(int key, int modifiers); +	void NotifyURIDropped(const char *list); +	const char *CharacterSetID() const; +	virtual CaseFolder *CaseFolderForEncoding(); +	virtual std::string CaseMapString(const std::string &s, int caseMapping); +	virtual int KeyDefault(int key, int modifiers); +	virtual void CopyToClipboard(const SelectionText &selectedText); +	virtual void Copy(); +	virtual void Paste(); +	virtual void CreateCallTipWindow(PRectangle rc); +	virtual void AddToPopUp(const char *label, int cmd = 0, bool enabled = true); +	bool OwnPrimarySelection(); +	virtual void ClaimSelection(); +	void GetGtkSelectionText(GtkSelectionData *selectionData, SelectionText &selText); +	void ReceivedSelection(GtkSelectionData *selection_data); +	void ReceivedDrop(GtkSelectionData *selection_data); +	static void GetSelection(GtkSelectionData *selection_data, guint info, SelectionText *selected); +	void StoreOnClipboard(SelectionText *clipText); +	static void ClipboardGetSelection(GtkClipboard* clip, GtkSelectionData *selection_data, guint info, void *data); +	static void ClipboardClearSelection(GtkClipboard* clip, void *data); + +	void UnclaimSelection(GdkEventSelection *selection_event); +	void Resize(int width, int height); + +	// Callback functions +	void RealizeThis(GtkWidget *widget); +	static void Realize(GtkWidget *widget); +	void UnRealizeThis(GtkWidget *widget); +	static void UnRealize(GtkWidget *widget); +	void MapThis(); +	static void Map(GtkWidget *widget); +	void UnMapThis(); +	static void UnMap(GtkWidget *widget); +	gint FocusInThis(GtkWidget *widget); +	static gint FocusIn(GtkWidget *widget, GdkEventFocus *event); +	gint FocusOutThis(GtkWidget *widget); +	static gint FocusOut(GtkWidget *widget, GdkEventFocus *event); +	static void SizeRequest(GtkWidget *widget, GtkRequisition *requisition); +#if GTK_CHECK_VERSION(3,0,0) +	static void GetPreferredWidth(GtkWidget *widget, gint *minimalWidth, gint *naturalWidth); +	static void GetPreferredHeight(GtkWidget *widget, gint *minimalHeight, gint *naturalHeight); +#endif +	static void SizeAllocate(GtkWidget *widget, GtkAllocation *allocation); +#if GTK_CHECK_VERSION(3,0,0) +	gboolean DrawTextThis(cairo_t *cr); +	static gboolean DrawText(GtkWidget *widget, cairo_t *cr, ScintillaGTK *sciThis); +	gboolean DrawThis(cairo_t *cr); +	static gboolean DrawMain(GtkWidget *widget, cairo_t *cr); +#else +	gboolean ExposeTextThis(GtkWidget *widget, GdkEventExpose *ose); +	static gboolean ExposeText(GtkWidget *widget, GdkEventExpose *ose, ScintillaGTK *sciThis); +	gboolean Expose(GtkWidget *widget, GdkEventExpose *ose); +	static gboolean ExposeMain(GtkWidget *widget, GdkEventExpose *ose); +#endif +	void ForAll(GtkCallback callback, gpointer callback_data); +	static void MainForAll(GtkContainer *container, gboolean include_internals, GtkCallback callback, gpointer callback_data); + +	static void ScrollSignal(GtkAdjustment *adj, ScintillaGTK *sciThis); +	static void ScrollHSignal(GtkAdjustment *adj, ScintillaGTK *sciThis); +	gint PressThis(GdkEventButton *event); +	static gint Press(GtkWidget *widget, GdkEventButton *event); +	static gint MouseRelease(GtkWidget *widget, GdkEventButton *event); +	static gint ScrollEvent(GtkWidget *widget, GdkEventScroll *event); +	static gint Motion(GtkWidget *widget, GdkEventMotion *event); +	gboolean KeyThis(GdkEventKey *event); +	static gboolean KeyPress(GtkWidget *widget, GdkEventKey *event); +	static gboolean KeyRelease(GtkWidget *widget, GdkEventKey *event); +#if GTK_CHECK_VERSION(3,0,0) +	gboolean DrawPreeditThis(GtkWidget *widget, cairo_t *cr); +	static gboolean DrawPreedit(GtkWidget *widget, cairo_t *cr, ScintillaGTK *sciThis); +#else +	gboolean ExposePreeditThis(GtkWidget *widget, GdkEventExpose *ose); +	static gboolean ExposePreedit(GtkWidget *widget, GdkEventExpose *ose, ScintillaGTK *sciThis); +#endif +	AtkObject* GetAccessibleThis(GtkWidget *widget); +	static AtkObject* GetAccessible(GtkWidget *widget); + +	bool KoreanIME(); +	void CommitThis(char *str); +	static void Commit(GtkIMContext *context, char *str, ScintillaGTK *sciThis); +	void PreeditChangedInlineThis(); +	void PreeditChangedWindowedThis(); +	static void PreeditChanged(GtkIMContext *context, ScintillaGTK *sciThis); +	void MoveImeCarets(int pos); +	void DrawImeIndicator(int indicator, int len); +	void SetCandidateWindowPos(); + +	static void StyleSetText(GtkWidget *widget, GtkStyle *previous, void*); +	static void RealizeText(GtkWidget *widget, void*); +	static void Dispose(GObject *object); +	static void Destroy(GObject *object); +	static void SelectionReceived(GtkWidget *widget, GtkSelectionData *selection_data, +	                              guint time); +	static void SelectionGet(GtkWidget *widget, GtkSelectionData *selection_data, +	                         guint info, guint time); +	static gint SelectionClear(GtkWidget *widget, GdkEventSelection *selection_event); +	gboolean DragMotionThis(GdkDragContext *context, gint x, gint y, guint dragtime); +	static gboolean DragMotion(GtkWidget *widget, GdkDragContext *context, +	                           gint x, gint y, guint dragtime); +	static void DragLeave(GtkWidget *widget, GdkDragContext *context, +	                      guint time); +	static void DragEnd(GtkWidget *widget, GdkDragContext *context); +	static gboolean Drop(GtkWidget *widget, GdkDragContext *context, +	                     gint x, gint y, guint time); +	static void DragDataReceived(GtkWidget *widget, GdkDragContext *context, +	                             gint x, gint y, GtkSelectionData *selection_data, guint info, guint time); +	static void DragDataGet(GtkWidget *widget, GdkDragContext *context, +	                        GtkSelectionData *selection_data, guint info, guint time); +	static gboolean TimeOut(gpointer ptt); +	static gboolean IdleCallback(gpointer pSci); +	static gboolean StyleIdle(gpointer pSci); +	virtual void IdleWork(); +	virtual void QueueIdleWork(WorkNeeded::workItems items, int upTo); +	virtual void SetDocPointer(Document *document); +	static void PopUpCB(GtkMenuItem *menuItem, ScintillaGTK *sciThis); + +#if GTK_CHECK_VERSION(3,0,0) +	static gboolean DrawCT(GtkWidget *widget, cairo_t *cr, CallTip *ctip); +#else +	static gboolean ExposeCT(GtkWidget *widget, GdkEventExpose *ose, CallTip *ct); +#endif +	static gboolean PressCT(GtkWidget *widget, GdkEventButton *event, ScintillaGTK *sciThis); + +	static sptr_t DirectFunction(sptr_t ptr, +	                             unsigned int iMessage, uptr_t wParam, sptr_t lParam); +}; + +// helper class to watch a GObject lifetime and get notified when it dies +class GObjectWatcher { +	GObject *weakRef; + +	void WeakNotifyThis(GObject *obj) { +		PLATFORM_ASSERT(obj == weakRef); + +		Destroyed(); +		weakRef = 0; +	} + +	static void WeakNotify(gpointer data, GObject *obj) { +		static_cast<GObjectWatcher*>(data)->WeakNotifyThis(obj); +	} + +public: +	GObjectWatcher(GObject *obj) : +			weakRef(obj) { +		g_object_weak_ref(weakRef, WeakNotify, this); +	} + +	virtual ~GObjectWatcher() { +		if (weakRef) { +			g_object_weak_unref(weakRef, WeakNotify, this); +		} +	} + +	virtual void Destroyed() {} + +	bool IsDestroyed() { +		return weakRef != 0; +	} +}; + +std::string ConvertText(const char *s, size_t len, const char *charSetDest, +                        const char *charSetSource, bool transliterations, bool silent=false); + +#ifdef SCI_NAMESPACE +} +#endif + +#endif diff --git a/gtk/ScintillaGTKAccessible.cxx b/gtk/ScintillaGTKAccessible.cxx new file mode 100644 index 000000000..e6b54be49 --- /dev/null +++ b/gtk/ScintillaGTKAccessible.cxx @@ -0,0 +1,1197 @@ +/* Scintilla source code edit control */ +/* ScintillaGTKAccessible.c - GTK+ accessibility for ScintillaGTK */ +/* Copyright 2016 by Colomban Wendling <colomban@geany.org> + * The License.txt file describes the conditions under which this software may be distributed. */ + +// On GTK < 3.2, we need to use the AtkObjectFactory.  We need to query +// the factory to see what type we should derive from, thus making use of +// dynamic inheritance.  It's tricky, but it works so long as it's done +// carefully enough. +// +// On GTK 3.2 through 3.6, we need to hack around because GTK stopped +// registering its accessible types in the factory, so we can't query +// them that way.  Unfortunately, the accessible types aren't exposed +// yet (not until 3.8), so there's no proper way to know which type to +// inherit from.  To work around this, we instantiate the parent's +// AtkObject temporarily, and use it's type.  It means creating an extra +// throwaway object and being able to pass the type information up to the +// type registration code, but it's the only solution I could find. +// +// On GTK 3.8 onward, we use the proper exposed GtkContainerAccessible as +// parent, and so a straightforward class. +// +// To hide and contain the complexity in type creation arising from the +// hackish support for GTK 3.2 to 3.8, the actual implementation for the +// widget's get_accessible() is located in the accessibility layer itself. + +// Initially based on GtkTextViewAccessible from GTK 3.20 +// Inspiration for the GTK < 3.2 part comes from Evince 2.24, thanks. + +// FIXME: optimize character/byte offset conversion (with a cache?) + +#include <stdlib.h> +#include <string.h> + +#include <stdexcept> +#include <new> +#include <string> +#include <vector> +#include <map> +#include <algorithm> + +#include <glib.h> +#include <gtk/gtk.h> + +// whether we have widget_set() and widget_unset() +#define HAVE_WIDGET_SET_UNSET (GTK_CHECK_VERSION(3, 3, 6)) +// whether GTK accessibility is available through the ATK factory +#define HAVE_GTK_FACTORY (! GTK_CHECK_VERSION(3, 1, 9)) +// whether we have gtk-a11y.h and the public GTK accessible types +#define HAVE_GTK_A11Y_H (GTK_CHECK_VERSION(3, 7, 6)) + +#if HAVE_GTK_A11Y_H +# include <gtk/gtk-a11y.h> +#endif + +// ScintillaGTK.h and stuff it needs +#include "Platform.h" + +#include "ILexer.h" +#include "Scintilla.h" +#include "ScintillaWidget.h" +#ifdef SCI_LEXER +#include "SciLexer.h" +#endif +#include "StringCopy.h" +#ifdef SCI_LEXER +#include "LexerModule.h" +#endif +#include "Position.h" +#include "SplitVector.h" +#include "Partitioning.h" +#include "RunStyles.h" +#include "ContractionState.h" +#include "CellBuffer.h" +#include "CallTip.h" +#include "KeyMap.h" +#include "Indicator.h" +#include "XPM.h" +#include "LineMarker.h" +#include "Style.h" +#include "ViewStyle.h" +#include "CharClassify.h" +#include "Decoration.h" +#include "CaseFolder.h" +#include "Document.h" +#include "CaseConvert.h" +#include "UniConversion.h" +#include "UnicodeFromUTF8.h" +#include "Selection.h" +#include "PositionCache.h" +#include "EditModel.h" +#include "MarginView.h" +#include "EditView.h" +#include "Editor.h" +#include "AutoComplete.h" +#include "ScintillaBase.h" + +#include "ScintillaGTK.h" +#include "ScintillaGTKAccessible.h" + +#ifdef SCI_NAMESPACE +using namespace Scintilla; +#endif + +struct ScintillaObjectAccessiblePrivate { +	ScintillaGTKAccessible *pscin; +}; + +typedef GtkAccessible ScintillaObjectAccessible; +typedef GtkAccessibleClass ScintillaObjectAccessibleClass; + +#define SCINTILLA_OBJECT_ACCESSIBLE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), SCINTILLA_TYPE_OBJECT_ACCESSIBLE, ScintillaObjectAccessible)) +#define SCINTILLA_TYPE_OBJECT_ACCESSIBLE (scintilla_object_accessible_get_type(0)) + +// We can't use priv member because of dynamic inheritance, so we don't actually know the offset.  Meh. +#define SCINTILLA_OBJECT_ACCESSIBLE_GET_PRIVATE(inst) (G_TYPE_INSTANCE_GET_PRIVATE((inst), SCINTILLA_TYPE_OBJECT_ACCESSIBLE, ScintillaObjectAccessiblePrivate)) + +static GType scintilla_object_accessible_get_type(GType parent_type); + +ScintillaGTKAccessible *ScintillaGTKAccessible::FromAccessible(GtkAccessible *accessible) { +	// FIXME: do we need the check below?  GTK checks that in all methods, so maybe +	GtkWidget *widget = gtk_accessible_get_widget(accessible); +	if (! widget) { +		return 0; +	} + +	return SCINTILLA_OBJECT_ACCESSIBLE_GET_PRIVATE(accessible)->pscin; +} + +ScintillaGTKAccessible::ScintillaGTKAccessible(GtkAccessible *accessible_, GtkWidget *widget_) : +		accessible(accessible_), +		sci(ScintillaGTK::FromWidget(widget_)), +		old_pos(-1) { +	g_signal_connect(widget_, "sci-notify", G_CALLBACK(SciNotify), this); +} + +ScintillaGTKAccessible::~ScintillaGTKAccessible() { +} + +gchar *ScintillaGTKAccessible::GetTextRangeUTF8(Position startByte, Position endByte) { +	g_return_val_if_fail(startByte >= 0, NULL); +	// FIXME: should we swap start/end if necessary? +	g_return_val_if_fail(endByte >= startByte, NULL); + +	gchar *utf8Text = NULL; +	const char *charSetBuffer; + +	// like TargetAsUTF8, but avoids a double conversion +	if (sci->IsUnicodeMode() || ! *(charSetBuffer = sci->CharacterSetID())) { +		int len = endByte - startByte; +		utf8Text = (char *) g_malloc(len + 1); +		sci->pdoc->GetCharRange(utf8Text, startByte, len); +		utf8Text[len] = '\0'; +	} else { +		// Need to convert +		std::string s = sci->RangeText(startByte, endByte); +		std::string tmputf = ConvertText(&s[0], s.length(), "UTF-8", charSetBuffer, false); +		size_t len = tmputf.length(); +		utf8Text = (char *) g_malloc(len + 1); +		memcpy(utf8Text, tmputf.c_str(), len); +		utf8Text[len] = '\0'; +	} + +	return utf8Text; +} + +gchar *ScintillaGTKAccessible::GetText(int startChar, int endChar) { +	Position startByte, endByte; +	if (endChar == -1) { +		startByte = ByteOffsetFromCharacterOffset(startChar); +		endByte = sci->pdoc->Length(); +	} else { +		ByteRangeFromCharacterRange(startChar, endChar, startByte, endByte); +	} +	return GetTextRangeUTF8(startByte, endByte); +} + +gchar *ScintillaGTKAccessible::GetTextAfterOffset(int charOffset, +		AtkTextBoundary boundaryType, int *startChar, int *endChar) { +	g_return_val_if_fail(charOffset >= 0, NULL); + +	Position startByte, endByte; +	Position byteOffset = ByteOffsetFromCharacterOffset(charOffset); + +	switch (boundaryType) { +		case ATK_TEXT_BOUNDARY_CHAR: +			startByte = PositionAfter(byteOffset); +			endByte = PositionAfter(startByte); +			// FIXME: optimize conversion back, as we can reasonably assume +1 char? +			break; + +		case ATK_TEXT_BOUNDARY_WORD_START: +			startByte = sci->WndProc(SCI_WORDENDPOSITION, byteOffset, 1); +			startByte = sci->WndProc(SCI_WORDENDPOSITION, startByte, 0); +			endByte = sci->WndProc(SCI_WORDENDPOSITION, startByte, 1); +			endByte = sci->WndProc(SCI_WORDENDPOSITION, endByte, 0); +			break; + +		case ATK_TEXT_BOUNDARY_WORD_END: +			startByte = sci->WndProc(SCI_WORDENDPOSITION, byteOffset, 0); +			startByte = sci->WndProc(SCI_WORDENDPOSITION, startByte, 1); +			endByte = sci->WndProc(SCI_WORDENDPOSITION, startByte, 0); +			endByte = sci->WndProc(SCI_WORDENDPOSITION, endByte, 1); +			break; + +		case ATK_TEXT_BOUNDARY_LINE_START: { +			int line = sci->WndProc(SCI_LINEFROMPOSITION, byteOffset, 0); +			startByte = sci->WndProc(SCI_POSITIONFROMLINE, line + 1, 0); +			endByte = sci->WndProc(SCI_POSITIONFROMLINE, line + 2, 0); +			break; +		} + +		case ATK_TEXT_BOUNDARY_LINE_END: { +			int line = sci->WndProc(SCI_LINEFROMPOSITION, byteOffset, 0); +			startByte = sci->WndProc(SCI_GETLINEENDPOSITION, line, 0); +			endByte = sci->WndProc(SCI_GETLINEENDPOSITION, line + 1, 0); +			break; +		} + +		default: +			*startChar = *endChar = -1; +			return NULL; +	} + +	CharacterRangeFromByteRange(startByte, endByte, startChar, endChar); +	return GetTextRangeUTF8(startByte, endByte); +} + +gchar *ScintillaGTKAccessible::GetTextBeforeOffset(int charOffset, +		AtkTextBoundary boundaryType, int *startChar, int *endChar) { +	g_return_val_if_fail(charOffset >= 0, NULL); + +	Position startByte, endByte; +	Position byteOffset = ByteOffsetFromCharacterOffset(charOffset); + +	switch (boundaryType) { +		case ATK_TEXT_BOUNDARY_CHAR: +			endByte = PositionBefore(byteOffset); +			startByte = PositionBefore(endByte); +			break; + +		case ATK_TEXT_BOUNDARY_WORD_START: +			endByte = sci->WndProc(SCI_WORDSTARTPOSITION, byteOffset, 0); +			endByte = sci->WndProc(SCI_WORDSTARTPOSITION, endByte, 1); +			startByte = sci->WndProc(SCI_WORDSTARTPOSITION, endByte, 0); +			startByte = sci->WndProc(SCI_WORDSTARTPOSITION, startByte, 1); +			break; + +		case ATK_TEXT_BOUNDARY_WORD_END: +			endByte = sci->WndProc(SCI_WORDSTARTPOSITION, byteOffset, 1); +			endByte = sci->WndProc(SCI_WORDSTARTPOSITION, endByte, 0); +			startByte = sci->WndProc(SCI_WORDSTARTPOSITION, endByte, 1); +			startByte = sci->WndProc(SCI_WORDSTARTPOSITION, startByte, 0); +			break; + +		case ATK_TEXT_BOUNDARY_LINE_START: { +			int line = sci->WndProc(SCI_LINEFROMPOSITION, byteOffset, 0); +			endByte = sci->WndProc(SCI_POSITIONFROMLINE, line, 0); +			if (line > 0) { +				startByte = sci->WndProc(SCI_POSITIONFROMLINE, line - 1, 0); +			} else { +				startByte = endByte; +			} +			break; +		} + +		case ATK_TEXT_BOUNDARY_LINE_END: { +			int line = sci->WndProc(SCI_LINEFROMPOSITION, byteOffset, 0); +			if (line > 0) { +				endByte = sci->WndProc(SCI_GETLINEENDPOSITION, line - 1, 0); +			} else { +				endByte = 0; +			} +			if (line > 1) { +				startByte = sci->WndProc(SCI_GETLINEENDPOSITION, line - 2, 0); +			} else { +				startByte = endByte; +			} +			break; +		} + +		default: +			*startChar = *endChar = -1; +			return NULL; +	} + +	CharacterRangeFromByteRange(startByte, endByte, startChar, endChar); +	return GetTextRangeUTF8(startByte, endByte); +} + +gchar *ScintillaGTKAccessible::GetTextAtOffset(int charOffset, +		AtkTextBoundary boundaryType, int *startChar, int *endChar) { +	g_return_val_if_fail(charOffset >= 0, NULL); + +	Position startByte, endByte; +	Position byteOffset = ByteOffsetFromCharacterOffset(charOffset); + +	switch (boundaryType) { +		case ATK_TEXT_BOUNDARY_CHAR: +			startByte = byteOffset; +			endByte = sci->WndProc(SCI_POSITIONAFTER, byteOffset, 0); +			break; + +		case ATK_TEXT_BOUNDARY_WORD_START: +			startByte = sci->WndProc(SCI_WORDSTARTPOSITION, byteOffset, 1); +			endByte = sci->WndProc(SCI_WORDENDPOSITION, byteOffset, 1); +			if (! sci->WndProc(SCI_ISRANGEWORD, startByte, endByte)) { +				// if the cursor was not on a word, forward back +				startByte = sci->WndProc(SCI_WORDSTARTPOSITION, startByte, 0); +				startByte = sci->WndProc(SCI_WORDSTARTPOSITION, startByte, 1); +			} +			endByte = sci->WndProc(SCI_WORDENDPOSITION, endByte, 0); +			break; + +		case ATK_TEXT_BOUNDARY_WORD_END: +			startByte = sci->WndProc(SCI_WORDSTARTPOSITION, byteOffset, 1); +			endByte = sci->WndProc(SCI_WORDENDPOSITION, byteOffset, 1); +			if (! sci->WndProc(SCI_ISRANGEWORD, startByte, endByte)) { +				// if the cursor was not on a word, forward back +				endByte = sci->WndProc(SCI_WORDENDPOSITION, endByte, 0); +				endByte = sci->WndProc(SCI_WORDENDPOSITION, endByte, 1); +			} +			startByte = sci->WndProc(SCI_WORDSTARTPOSITION, startByte, 0); +			break; + +		case ATK_TEXT_BOUNDARY_LINE_START: { +			int line = sci->WndProc(SCI_LINEFROMPOSITION, byteOffset, 0); +			startByte = sci->WndProc(SCI_POSITIONFROMLINE, line, 0); +			endByte = sci->WndProc(SCI_POSITIONFROMLINE, line + 1, 0); +			break; +		} + +		case ATK_TEXT_BOUNDARY_LINE_END: { +			int line = sci->WndProc(SCI_LINEFROMPOSITION, byteOffset, 0); +			if (line > 0) { +				startByte = sci->WndProc(SCI_GETLINEENDPOSITION, line - 1, 0); +			} else { +				startByte = 0; +			} +			endByte = sci->WndProc(SCI_GETLINEENDPOSITION, line, 0); +			break; +		} + +		default: +			*startChar = *endChar = -1; +			return NULL; +	} + +	CharacterRangeFromByteRange(startByte, endByte, startChar, endChar); +	return GetTextRangeUTF8(startByte, endByte); +} + +gchar *ScintillaGTKAccessible::GetStringAtOffset(int charOffset, +		AtkTextGranularity granularity, int *startChar, int *endChar) { +	g_return_val_if_fail(charOffset >= 0, NULL); + +	Position startByte, endByte; +	Position byteOffset = ByteOffsetFromCharacterOffset(charOffset); + +	switch (granularity) { +		case ATK_TEXT_GRANULARITY_CHAR: +			startByte = byteOffset; +			endByte = sci->WndProc(SCI_POSITIONAFTER, byteOffset, 0); +			break; +		case ATK_TEXT_GRANULARITY_WORD: +			startByte = sci->WndProc(SCI_WORDSTARTPOSITION, byteOffset, 1); +			endByte = sci->WndProc(SCI_WORDENDPOSITION, byteOffset, 1); +			break; +		case ATK_TEXT_GRANULARITY_LINE: { +			gint line = sci->WndProc(SCI_LINEFROMPOSITION, byteOffset, 0); +			startByte = sci->WndProc(SCI_POSITIONFROMLINE, line, 0); +			endByte = sci->WndProc(SCI_GETLINEENDPOSITION, line, 0); +			break; +		} +		default: +			*startChar = *endChar = -1; +			return NULL; +	} + +	CharacterRangeFromByteRange(startByte, endByte, startChar, endChar); +	return GetTextRangeUTF8(startByte, endByte); +} + +gunichar ScintillaGTKAccessible::GetCharacterAtOffset(int charOffset) { +	g_return_val_if_fail(charOffset >= 0, 0); + +	Position startByte = ByteOffsetFromCharacterOffset(charOffset); +	Position endByte = PositionAfter(startByte); +	gchar *ch = GetTextRangeUTF8(startByte, endByte); +	gunichar unichar = g_utf8_get_char_validated(ch, -1); +	g_free(ch); + +	return unichar; +} + +gint ScintillaGTKAccessible::GetCharacterCount() { +	return sci->pdoc->CountCharacters(0, sci->pdoc->Length()); +} + +gint ScintillaGTKAccessible::GetCaretOffset() { +	return CharacterOffsetFromByteOffset(sci->WndProc(SCI_GETCURRENTPOS, 0, 0)); +} + +gboolean ScintillaGTKAccessible::SetCaretOffset(int charOffset) { +	sci->WndProc(SCI_GOTOPOS, ByteOffsetFromCharacterOffset(charOffset), 0); +	return TRUE; +} + +gint ScintillaGTKAccessible::GetOffsetAtPoint(gint x, gint y, AtkCoordType coords) { +	gint x_widget, y_widget, x_window, y_window; +	GtkWidget *widget = gtk_accessible_get_widget(accessible); + +	GdkWindow *window = gtk_widget_get_window(widget); +	gdk_window_get_origin(window, &x_widget, &y_widget); +	if (coords == ATK_XY_SCREEN) { +		x = x - x_widget; +		y = y - y_widget; +	} else if (coords == ATK_XY_WINDOW) { +		window = gdk_window_get_toplevel(window); +		gdk_window_get_origin(window, &x_window, &y_window); + +		x = x - x_widget + x_window; +		y = y - y_widget + y_window; +	} else { +		return -1; +	} + +	// FIXME: should we handle scrolling? +	return CharacterOffsetFromByteOffset(sci->WndProc(SCI_CHARPOSITIONFROMPOINTCLOSE, x, y)); +} + +void ScintillaGTKAccessible::GetCharacterExtents(int charOffset, +		gint *x, gint *y, gint *width, gint *height, AtkCoordType coords) { +	*x = *y = *height = *width = 0; + +	Position byteOffset = ByteOffsetFromCharacterOffset(charOffset); + +	// FIXME: should we handle scrolling? +	*x = sci->WndProc(SCI_POINTXFROMPOSITION, 0, byteOffset); +	*y = sci->WndProc(SCI_POINTYFROMPOSITION, 0, byteOffset); + +	int line = sci->WndProc(SCI_LINEFROMPOSITION, byteOffset, 0); +	*height = sci->WndProc(SCI_TEXTHEIGHT, line, 0); + +	int nextByteOffset = PositionAfter(byteOffset); +	int next_x = sci->WndProc(SCI_POINTXFROMPOSITION, 0, nextByteOffset); +	if (next_x > *x) { +		*width = next_x - *x; +	} else if (nextByteOffset > byteOffset) { +		/* maybe next position was on the next line or something. +		 * just compute the expected character width */ +		int style = sci->pdoc->StyleAt(byteOffset); +		int len = nextByteOffset - byteOffset; +		char *ch = new char[len + 1]; +		sci->pdoc->GetCharRange(ch, byteOffset, len); +		ch[len] = '\0'; +		*width = sci->TextWidth(style, ch); +		delete[] ch; +	} + +	GtkWidget *widget = gtk_accessible_get_widget(accessible); +	GdkWindow *window = gtk_widget_get_window(widget); +	int x_widget, y_widget; +	gdk_window_get_origin(window, &x_widget, &y_widget); +	if (coords == ATK_XY_SCREEN) { +		*x += x_widget; +		*y += y_widget; +	} else if (coords == ATK_XY_WINDOW) { +		window = gdk_window_get_toplevel(window); +		int x_window, y_window; +		gdk_window_get_origin(window, &x_window, &y_window); + +		*x += x_widget - x_window; +		*y += y_widget - y_window; +	} else { +		*x = *y = *height = *width = 0; +	} +} + +static AtkAttributeSet *AddTextAttribute(AtkAttributeSet *attributes, AtkTextAttribute attr, gchar *value) { +	AtkAttribute *at = g_new(AtkAttribute, 1); +	at->name = g_strdup(atk_text_attribute_get_name(attr)); +	at->value = value; + +	return g_slist_prepend(attributes, at); +} + +static AtkAttributeSet *AddTextIntAttribute(AtkAttributeSet *attributes, AtkTextAttribute attr, gint i) { +	return AddTextAttribute(attributes, attr, g_strdup(atk_text_attribute_get_value(attr, i))); +} + +static AtkAttributeSet *AddTextColorAttribute(AtkAttributeSet *attributes, AtkTextAttribute attr, const ColourDesired &colour) { +	return AddTextAttribute(attributes, attr, +		g_strdup_printf("%u,%u,%u", colour.GetRed() * 257, colour.GetGreen() * 257, colour.GetBlue() * 257)); +} + +AtkAttributeSet *ScintillaGTKAccessible::GetAttributesForStyle(unsigned int styleNum) { +	AtkAttributeSet *attr_set = NULL; + +	if (styleNum >= sci->vs.styles.size()) +		return NULL; +	Style &style = sci->vs.styles[styleNum]; + +	attr_set = AddTextAttribute(attr_set, ATK_TEXT_ATTR_FAMILY_NAME, g_strdup(style.fontName)); +	attr_set = AddTextAttribute(attr_set, ATK_TEXT_ATTR_SIZE, g_strdup_printf("%d", style.size / SC_FONT_SIZE_MULTIPLIER)); +	attr_set = AddTextIntAttribute(attr_set, ATK_TEXT_ATTR_WEIGHT, CLAMP(style.weight, 100, 1000)); +	attr_set = AddTextIntAttribute(attr_set, ATK_TEXT_ATTR_STYLE, style.italic ? PANGO_STYLE_ITALIC : PANGO_STYLE_NORMAL); +	attr_set = AddTextIntAttribute(attr_set, ATK_TEXT_ATTR_UNDERLINE, style.underline ? PANGO_UNDERLINE_SINGLE : PANGO_UNDERLINE_NONE); +	attr_set = AddTextColorAttribute(attr_set, ATK_TEXT_ATTR_FG_COLOR, style.fore); +	attr_set = AddTextColorAttribute(attr_set, ATK_TEXT_ATTR_BG_COLOR, style.back); +	attr_set = AddTextIntAttribute(attr_set, ATK_TEXT_ATTR_INVISIBLE, style.visible ? 0 : 1); +	attr_set = AddTextIntAttribute(attr_set, ATK_TEXT_ATTR_EDITABLE, style.changeable ? 1 : 0); + +	return attr_set; +} + +AtkAttributeSet *ScintillaGTKAccessible::GetRunAttributes(int charOffset, int *startChar, int *endChar) { +	g_return_val_if_fail(charOffset >= -1, NULL); + +	Position byteOffset; +	if (charOffset == -1) { +		byteOffset = sci->WndProc(SCI_GETCURRENTPOS, 0, 0); +	} else { +		byteOffset = ByteOffsetFromCharacterOffset(charOffset); +	} +	int length = sci->pdoc->Length(); + +	g_return_val_if_fail(byteOffset < length, NULL); + +	const char style = sci->pdoc->StyleAt(byteOffset); +	// compute the range for this style +	Position startByte = byteOffset; +	while (startByte > 0 && sci->pdoc->StyleAt((startByte) - 1) == style) +		(startByte)--; +	Position endByte = byteOffset; +	while ((endByte) + 1 < length && sci->pdoc->StyleAt((endByte) + 1) == style) +		(endByte)++; + +	CharacterRangeFromByteRange(startByte, endByte, startChar, endChar); +	return GetAttributesForStyle((unsigned int) style); +} + +AtkAttributeSet *ScintillaGTKAccessible::GetDefaultAttributes() { +	return GetAttributesForStyle(0); +} + +gint ScintillaGTKAccessible::GetNSelections() { +	return sci->sel.Empty() ? 0 : sci->sel.Count(); +} + +gchar *ScintillaGTKAccessible::GetSelection(gint selection_num, int *startChar, int *endChar) { +	if (selection_num < 0 || (unsigned int) selection_num >= sci->sel.Count()) +		return NULL; + +	Position startByte = sci->sel.Range(selection_num).Start().Position(); +	Position endByte = sci->sel.Range(selection_num).End().Position(); + +	CharacterRangeFromByteRange(startByte, endByte, startChar, endChar); +	return GetTextRangeUTF8(startByte, endByte); +} + +gboolean ScintillaGTKAccessible::AddSelection(int startChar, int endChar) { +	size_t n_selections = sci->sel.Count(); +	Position startByte, endByte; +	ByteRangeFromCharacterRange(startChar, endChar, startByte, endByte); +	// use WndProc() to set the selections so it notifies as needed +	if (n_selections > 1 || ! sci->sel.Empty()) { +		sci->WndProc(SCI_ADDSELECTION, startByte, endByte); +	} else { +		sci->WndProc(SCI_SETSELECTION, startByte, endByte); +	} + +	return TRUE; +} + +gboolean ScintillaGTKAccessible::RemoveSelection(gint selection_num) { +	size_t n_selections = sci->sel.Count(); +	if (selection_num < 0 || (unsigned int) selection_num >= n_selections) +		return FALSE; + +	if (n_selections > 1) { +		sci->WndProc(SCI_DROPSELECTIONN, selection_num, 0); +	} else if (sci->sel.Empty()) { +		return FALSE; +	} else { +		sci->WndProc(SCI_CLEARSELECTIONS, 0, 0); +	} + +	return TRUE; +} + +gboolean ScintillaGTKAccessible::SetSelection(gint selection_num, int startChar, int endChar) { +	if (selection_num < 0 || (unsigned int) selection_num >= sci->sel.Count()) +		return FALSE; + +	Position startByte, endByte; +	ByteRangeFromCharacterRange(startChar, endChar, startByte, endByte); + +	sci->WndProc(SCI_SETSELECTIONNSTART, selection_num, startByte); +	sci->WndProc(SCI_SETSELECTIONNEND, selection_num, endByte); + +	return TRUE; +} + +void ScintillaGTKAccessible::AtkTextIface::init(::AtkTextIface *iface) { +	iface->get_text = GetText; +	iface->get_text_after_offset = GetTextAfterOffset; +	iface->get_text_at_offset = GetTextAtOffset; +	iface->get_text_before_offset = GetTextBeforeOffset; +#if ATK_CHECK_VERSION(2, 10, 0) +	iface->get_string_at_offset = GetStringAtOffset; +#endif +	iface->get_character_at_offset = GetCharacterAtOffset; +	iface->get_character_count = GetCharacterCount; +	iface->get_caret_offset = GetCaretOffset; +	iface->set_caret_offset = SetCaretOffset; +	iface->get_offset_at_point = GetOffsetAtPoint; +	iface->get_character_extents = GetCharacterExtents; +	iface->get_n_selections = GetNSelections; +	iface->get_selection = GetSelection; +	iface->add_selection = AddSelection; +	iface->remove_selection = RemoveSelection; +	iface->set_selection = SetSelection; +	iface->get_run_attributes = GetRunAttributes; +	iface->get_default_attributes = GetDefaultAttributes; +} + +/* atkeditabletext.h */ + +void ScintillaGTKAccessible::SetTextContents(const gchar *contents) { +	// FIXME: it's probably useless to check for READONLY here, SETTEXT probably does it just fine? +	if (! sci->pdoc->IsReadOnly()) { +		sci->WndProc(SCI_SETTEXT, 0, (sptr_t) contents); +	} +} + +bool ScintillaGTKAccessible::InsertStringUTF8(Position bytePos, const gchar *utf8, int lengthBytes) { +	if (sci->pdoc->IsReadOnly()) { +		return false; +	} + +	// like EncodedFromUTF8(), but avoids an extra copy +	// FIXME: update target? +	const char *charSetBuffer; +	if (sci->IsUnicodeMode() || ! *(charSetBuffer = sci->CharacterSetID())) { +		sci->pdoc->InsertString(bytePos, utf8, lengthBytes); +	} else { +		// conversion needed +		std::string encoded = ConvertText(utf8, lengthBytes, charSetBuffer, "UTF-8", true); +		sci->pdoc->InsertString(bytePos, encoded.c_str(), encoded.length()); +	} + +	return true; +} + +void ScintillaGTKAccessible::InsertText(const gchar *text, int lengthBytes, int *charPosition) { +	Position bytePosition = ByteOffsetFromCharacterOffset(*charPosition); + +	// FIXME: should we update the target? +	if (InsertStringUTF8(bytePosition, text, lengthBytes)) { +		(*charPosition) += sci->pdoc->CountCharacters(bytePosition, lengthBytes); +	} +} + +void ScintillaGTKAccessible::CopyText(int startChar, int endChar) { +	Position startByte, endByte; +	ByteRangeFromCharacterRange(startChar, endChar, startByte, endByte); +	sci->CopyRangeToClipboard(startByte, endByte); +} + +void ScintillaGTKAccessible::CutText(int startChar, int endChar) { +	g_return_if_fail(endChar >= startChar); + +	if (! sci->pdoc->IsReadOnly()) { +		// FIXME: have a byte variant of those and convert only once? +		CopyText(startChar, endChar); +		DeleteText(startChar, endChar); +	} +} + +void ScintillaGTKAccessible::DeleteText(int startChar, int endChar) { +	g_return_if_fail(endChar >= startChar); + +	if (! sci->pdoc->IsReadOnly()) { +		Position startByte, endByte; +		ByteRangeFromCharacterRange(startChar, endChar, startByte, endByte); + +		if (! sci->RangeContainsProtected(startByte, endByte)) { +			// FIXME: restore the target? +			sci->pdoc->DeleteChars(startByte, endByte - startByte); +		} +	} +} + +void ScintillaGTKAccessible::PasteText(int charPosition) { +	if (sci->pdoc->IsReadOnly()) +		return; + +	// Helper class holding the position for the asynchronous paste operation. +	// We can only hope that when the callback gets called scia is still valid, but ScintillaGTK +	// has always done that without problems, so let's guess it's a fairly safe bet. +	struct Helper : GObjectWatcher { +		ScintillaGTKAccessible *scia; +		Position bytePosition; + +		virtual void Destroyed() { +			scia = 0; +		} + +		Helper(ScintillaGTKAccessible *scia_, Position bytePos_) : +			GObjectWatcher(G_OBJECT(scia_->sci->sci)), +			scia(scia_), +			bytePosition(bytePos_) { +		} + +		void TextReceived(GtkClipboard *, const gchar *text) { +			if (text) { +				size_t len = strlen(text); +				std::string convertedText; +				if (len > 0 && scia->sci->convertPastes) { +					// Convert line endings of the paste into our local line-endings mode +					convertedText = Document::TransformLineEnds(text, len, scia->sci->pdoc->eolMode); +					len = convertedText.length(); +					text = convertedText.c_str(); +				} +				scia->InsertStringUTF8(bytePosition, text, static_cast<int>(len)); +			} +		} + +		static void TextReceivedCallback(GtkClipboard *clipboard, const gchar *text, gpointer data) { +			Helper *helper = reinterpret_cast<Helper*>(data); +			try { +				if (helper->scia != 0) { +					helper->TextReceived(clipboard, text); +				} +			} catch (...) {} +			delete helper; +		} +	}; + +	Helper *helper = new Helper(this, ByteOffsetFromCharacterOffset(charPosition)); +	GtkWidget *widget = gtk_accessible_get_widget(accessible); +	GtkClipboard *clipboard = gtk_widget_get_clipboard(widget, GDK_SELECTION_CLIPBOARD); +	gtk_clipboard_request_text(clipboard, helper->TextReceivedCallback, helper); +} + +void ScintillaGTKAccessible::AtkEditableTextIface::init(::AtkEditableTextIface *iface) { +	iface->set_text_contents = SetTextContents; +	iface->insert_text = InsertText; +	iface->copy_text = CopyText; +	iface->cut_text = CutText; +	iface->delete_text = DeleteText; +	iface->paste_text = PasteText; +	//~ iface->set_run_attributes = SetRunAttributes; +} + +// Callbacks + +void ScintillaGTKAccessible::UpdateCursor() { +	Position pos = sci->WndProc(SCI_GETCURRENTPOS, 0, 0); +	if (old_pos != pos) { +		int charPosition = CharacterOffsetFromByteOffset(pos); +		g_signal_emit_by_name(accessible, "text-caret-moved", charPosition); +		old_pos = pos; +	} + +	size_t n_selections = sci->sel.Count(); +	size_t prev_n_selections = old_sels.size(); +	bool selection_changed = n_selections != prev_n_selections; + +	old_sels.resize(n_selections); +	for (size_t i = 0; i < n_selections; i++) { +		SelectionRange &sel = sci->sel.Range(i); + +		if (i < prev_n_selections && ! selection_changed) { +			SelectionRange &old_sel = old_sels[i]; +			// do not consider a caret move to be a selection change +			selection_changed = ((! old_sel.Empty() || ! sel.Empty()) && ! (old_sel == sel)); +		} + +		old_sels[i] = sel; +	} + +	if (selection_changed) +		g_signal_emit_by_name(accessible, "text-selection-changed"); +} + +void ScintillaGTKAccessible::ChangeDocument(Document *oldDoc, Document *newDoc) { +	if (oldDoc == newDoc) { +		return; +	} + +	if (oldDoc) { +		int charLength = oldDoc->CountCharacters(0, oldDoc->Length()); +		g_signal_emit_by_name(accessible, "text-changed::delete", 0, charLength); +	} + +	if (newDoc) { +		PLATFORM_ASSERT(newDoc == sci->pdoc); + +		int charLength = newDoc->CountCharacters(0, newDoc->Length()); +		g_signal_emit_by_name(accessible, "text-changed::insert", 0, charLength); + +		if ((oldDoc ? oldDoc->IsReadOnly() : false) != newDoc->IsReadOnly()) { +			NotifyReadOnly(); +		} + +		// update cursor and selection +		old_pos = -1; +		old_sels.clear(); +		UpdateCursor(); +	} +} + +void ScintillaGTKAccessible::NotifyReadOnly() { +	bool readonly = sci->pdoc->IsReadOnly(); +	atk_object_notify_state_change(ATK_OBJECT(accessible), ATK_STATE_EDITABLE, ! readonly); +#if ATK_CHECK_VERSION(2, 16, 0) +	atk_object_notify_state_change(ATK_OBJECT(accessible), ATK_STATE_READ_ONLY, readonly); +#endif +} + +void ScintillaGTKAccessible::Notify(GtkWidget *, gint, SCNotification *nt) { +	switch (nt->nmhdr.code) { +		case SCN_MODIFIED: { +			if (nt->modificationType & SC_MOD_INSERTTEXT) { +				int startChar = CharacterOffsetFromByteOffset(nt->position); +				int lengthChar = sci->pdoc->CountCharacters(nt->position, nt->position + nt->length); +				g_signal_emit_by_name(accessible, "text-changed::insert", startChar, lengthChar); +				UpdateCursor(); +			} +			if (nt->modificationType & SC_MOD_DELETETEXT) { +				int startChar = CharacterOffsetFromByteOffset(nt->position); +				int lengthChar = sci->pdoc->CountCharacters(nt->position, nt->position + nt->length); +				g_signal_emit_by_name(accessible, "text-changed::delete", startChar, lengthChar); +				UpdateCursor(); +			} +			if (nt->modificationType & SC_MOD_CHANGESTYLE) { +				g_signal_emit_by_name(accessible, "text-attributes-changed"); +			} +		} break; +		case SCN_UPDATEUI: { +			if (nt->updated & SC_UPDATE_SELECTION) { +				UpdateCursor(); +			} +		} break; +	} +} + +// ATK method wrappers + +// wraps a call from the accessible object to the ScintillaGTKAccessible, and avoid leaking any exception +#define WRAPPER_METHOD_BODY(accessible, call, defret) \ +	try { \ +		ScintillaGTKAccessible *thisAccessible = FromAccessible(reinterpret_cast<GtkAccessible*>(accessible)); \ +		if (thisAccessible) { \ +			return thisAccessible->call; \ +		} else { \ +			return defret; \ +		} \ +	} catch (...) { \ +		return defret; \ +	} + +// AtkText +gchar *ScintillaGTKAccessible::AtkTextIface::GetText(AtkText *text, int start_offset, int end_offset) { +	WRAPPER_METHOD_BODY(text, GetText(start_offset, end_offset), NULL); +} +gchar *ScintillaGTKAccessible::AtkTextIface::GetTextAfterOffset(AtkText *text, int offset, AtkTextBoundary boundary_type, int *start_offset, int *end_offset) { +	WRAPPER_METHOD_BODY(text, GetTextAfterOffset(offset, boundary_type, start_offset, end_offset), NULL) +} +gchar *ScintillaGTKAccessible::AtkTextIface::GetTextBeforeOffset(AtkText *text, int offset, AtkTextBoundary boundary_type, int *start_offset, int *end_offset) { +	WRAPPER_METHOD_BODY(text, GetTextBeforeOffset(offset, boundary_type, start_offset, end_offset), NULL) +} +gchar *ScintillaGTKAccessible::AtkTextIface::GetTextAtOffset(AtkText *text, gint offset, AtkTextBoundary boundary_type, gint *start_offset, gint *end_offset) { +	WRAPPER_METHOD_BODY(text, GetTextAtOffset(offset, boundary_type, start_offset, end_offset), NULL) +} +gchar *ScintillaGTKAccessible::AtkTextIface::GetStringAtOffset(AtkText *text, gint offset, AtkTextGranularity granularity, gint *start_offset, gint *end_offset) { +	WRAPPER_METHOD_BODY(text, GetStringAtOffset(offset, granularity, start_offset, end_offset), NULL) +} +gunichar ScintillaGTKAccessible::AtkTextIface::GetCharacterAtOffset(AtkText *text, gint offset) { +	WRAPPER_METHOD_BODY(text, GetCharacterAtOffset(offset), 0) +} +gint ScintillaGTKAccessible::AtkTextIface::GetCharacterCount(AtkText *text) { +	WRAPPER_METHOD_BODY(text, GetCharacterCount(), 0) +} +gint ScintillaGTKAccessible::AtkTextIface::GetCaretOffset(AtkText *text) { +	WRAPPER_METHOD_BODY(text, GetCaretOffset(), 0) +} +gboolean ScintillaGTKAccessible::AtkTextIface::SetCaretOffset(AtkText *text, gint offset) { +	WRAPPER_METHOD_BODY(text, SetCaretOffset(offset), FALSE) +} +gint ScintillaGTKAccessible::AtkTextIface::GetOffsetAtPoint(AtkText *text, gint x, gint y, AtkCoordType coords) { +	WRAPPER_METHOD_BODY(text, GetOffsetAtPoint(x, y, coords), -1) +} +void ScintillaGTKAccessible::AtkTextIface::GetCharacterExtents(AtkText *text, gint offset, gint *x, gint *y, gint *width, gint *height, AtkCoordType coords) { +	WRAPPER_METHOD_BODY(text, GetCharacterExtents(offset, x, y, width, height, coords), ) +} +AtkAttributeSet *ScintillaGTKAccessible::AtkTextIface::GetRunAttributes(AtkText *text, gint offset, gint *start_offset, gint *end_offset) { +	WRAPPER_METHOD_BODY(text, GetRunAttributes(offset, start_offset, end_offset), NULL) +} +AtkAttributeSet *ScintillaGTKAccessible::AtkTextIface::GetDefaultAttributes(AtkText *text) { +	WRAPPER_METHOD_BODY(text, GetDefaultAttributes(), NULL) +} +gint ScintillaGTKAccessible::AtkTextIface::GetNSelections(AtkText *text) { +	WRAPPER_METHOD_BODY(text, GetNSelections(), 0) +} +gchar *ScintillaGTKAccessible::AtkTextIface::GetSelection(AtkText *text, gint selection_num, gint *start_pos, gint *end_pos) { +	WRAPPER_METHOD_BODY(text, GetSelection(selection_num, start_pos, end_pos), NULL) +} +gboolean ScintillaGTKAccessible::AtkTextIface::AddSelection(AtkText *text, gint start, gint end) { +	WRAPPER_METHOD_BODY(text, AddSelection(start, end), FALSE) +} +gboolean ScintillaGTKAccessible::AtkTextIface::RemoveSelection(AtkText *text, gint selection_num) { +	WRAPPER_METHOD_BODY(text, RemoveSelection(selection_num), FALSE) +} +gboolean ScintillaGTKAccessible::AtkTextIface::SetSelection(AtkText *text, gint selection_num, gint start, gint end) { +	WRAPPER_METHOD_BODY(text, SetSelection(selection_num, start, end), FALSE) +} +// AtkEditableText +void ScintillaGTKAccessible::AtkEditableTextIface::SetTextContents(AtkEditableText *text, const gchar *contents) { +	WRAPPER_METHOD_BODY(text, SetTextContents(contents), ) +} +void ScintillaGTKAccessible::AtkEditableTextIface::InsertText(AtkEditableText *text, const gchar *contents, gint length, gint *position) { +	WRAPPER_METHOD_BODY(text, InsertText(contents, length, position), ) +} +void ScintillaGTKAccessible::AtkEditableTextIface::CopyText(AtkEditableText *text, gint start, gint end) { +	WRAPPER_METHOD_BODY(text, CopyText(start, end), ) +} +void ScintillaGTKAccessible::AtkEditableTextIface::CutText(AtkEditableText *text, gint start, gint end) { +	WRAPPER_METHOD_BODY(text, CutText(start, end), ) +} +void ScintillaGTKAccessible::AtkEditableTextIface::DeleteText(AtkEditableText *text, gint start, gint end) { +	WRAPPER_METHOD_BODY(text, DeleteText(start, end), ) +} +void ScintillaGTKAccessible::AtkEditableTextIface::PasteText(AtkEditableText *text, gint position) { +	WRAPPER_METHOD_BODY(text, PasteText(position), ) +} + +// GObject glue + +#if HAVE_GTK_FACTORY +static GType scintilla_object_accessible_factory_get_type(void); +#endif + +static void scintilla_object_accessible_init(ScintillaObjectAccessible *accessible); +static void scintilla_object_accessible_class_init(ScintillaObjectAccessibleClass *klass); +static gpointer scintilla_object_accessible_parent_class = NULL; + + +// @p parent_type is only required on GTK 3.2 to 3.6, and only on the first call +static GType scintilla_object_accessible_get_type(GType parent_type) { +	static volatile gsize type_id_result = 0; + +	if (g_once_init_enter(&type_id_result)) { +		GTypeInfo tinfo = { +			0,															/* class size */ +			(GBaseInitFunc) NULL,										/* base init */ +			(GBaseFinalizeFunc) NULL,									/* base finalize */ +			(GClassInitFunc) scintilla_object_accessible_class_init,	/* class init */ +			(GClassFinalizeFunc) NULL,									/* class finalize */ +			NULL,														/* class data */ +			0,															/* instance size */ +			0,															/* nb preallocs */ +			(GInstanceInitFunc) scintilla_object_accessible_init,		/* instance init */ +			NULL														/* value table */ +		}; + +		const GInterfaceInfo atk_text_info = { +			(GInterfaceInitFunc) ScintillaGTKAccessible::AtkTextIface::init, +			(GInterfaceFinalizeFunc) NULL, +			NULL +		}; + +		const GInterfaceInfo atk_editable_text_info = { +			(GInterfaceInitFunc) ScintillaGTKAccessible::AtkEditableTextIface::init, +			(GInterfaceFinalizeFunc) NULL, +			NULL +		}; + +#if HAVE_GTK_A11Y_H +		// good, we have gtk-a11y.h, we can use that +		GType derived_atk_type = GTK_TYPE_CONTAINER_ACCESSIBLE; +		tinfo.class_size = sizeof (GtkContainerAccessibleClass); +		tinfo.instance_size = sizeof (GtkContainerAccessible); +#else // ! HAVE_GTK_A11Y_H +# if HAVE_GTK_FACTORY +		// Figure out the size of the class and instance we are deriving from through the registry +		GType derived_type = g_type_parent(SCINTILLA_TYPE_OBJECT); +		AtkObjectFactory *factory = atk_registry_get_factory(atk_get_default_registry(), derived_type); +		GType derived_atk_type = atk_object_factory_get_accessible_type(factory); +# else // ! HAVE_GTK_FACTORY +		// We're kind of screwed and can't determine the parent (no registry, and no public type) +		// Hack your way around by requiring the caller to give us our parent type.  The caller +		// might be able to trick its way into doing that, by e.g. instantiating the parent's +		// accessible type and get its GType.  It's ugly but we can't do better on GTK 3.2 to 3.6. +		g_assert(parent_type != 0); + +		GType derived_atk_type = parent_type; +# endif // ! HAVE_GTK_FACTORY + +		GTypeQuery query; +		g_type_query(derived_atk_type, &query); +		tinfo.class_size = query.class_size; +		tinfo.instance_size = query.instance_size; +#endif // ! HAVE_GTK_A11Y_H + +		GType type_id = g_type_register_static(derived_atk_type, "ScintillaObjectAccessible", &tinfo, (GTypeFlags) 0); +		g_type_add_interface_static(type_id, ATK_TYPE_TEXT, &atk_text_info); +		g_type_add_interface_static(type_id, ATK_TYPE_EDITABLE_TEXT, &atk_editable_text_info); + +		g_once_init_leave(&type_id_result, type_id); +	} + +	return type_id_result; +} + +static AtkObject *scintilla_object_accessible_new(GType parent_type, GObject *obj) { +	g_return_val_if_fail(SCINTILLA_IS_OBJECT(obj), NULL); + +	AtkObject *accessible = (AtkObject *) g_object_new(scintilla_object_accessible_get_type(parent_type), +#if HAVE_WIDGET_SET_UNSET +		"widget", obj, +#endif +		NULL); +	atk_object_initialize(accessible, obj); + +	return accessible; +} + +// implementation for gtk_widget_get_accessible(). +// See the comment at the top of the file for details on the implementation +// @p widget the widget. +// @p cache pointer to store the AtkObject between repeated calls.  Might or might not be filled. +// @p widget_parent_class pointer to the widget's parent class (to chain up method calls). +AtkObject *ScintillaGTKAccessible::WidgetGetAccessibleImpl(GtkWidget *widget, AtkObject **cache, gpointer widget_parent_class) { +#if HAVE_GTK_A11Y_H // just instantiate the accessible +	if (*cache == NULL) { +		*cache = scintilla_object_accessible_new(0, G_OBJECT(widget)); +	} +#elif HAVE_GTK_FACTORY // register in the factory and let GTK instantiate +	static volatile gsize registered = 0; + +	if (g_once_init_enter(®istered)) { +		// Figure out whether accessibility is enabled by looking at the type of the accessible +		// object which would be created for the parent type of ScintillaObject. +		GType derived_type = g_type_parent(SCINTILLA_TYPE_OBJECT); + +		AtkRegistry *registry = atk_get_default_registry(); +		AtkObjectFactory *factory = atk_registry_get_factory(registry, derived_type); +		GType derived_atk_type = atk_object_factory_get_accessible_type(factory); +		if (g_type_is_a(derived_atk_type, GTK_TYPE_ACCESSIBLE)) { +			atk_registry_set_factory_type(registry, SCINTILLA_TYPE_OBJECT, +			                              scintilla_object_accessible_factory_get_type()); +		} +		g_once_init_leave(®istered, 1); +	} +	*cache = GTK_WIDGET_CLASS(widget_parent_class)->get_accessible(widget); +#else // no public API, no factory, so guess from the parent and instantiate +	if (*cache == NULL) { +		static GType parent_atk_type = 0; + +		if (parent_atk_type == 0) { +			AtkObject *parent_obj = GTK_WIDGET_CLASS(widget_parent_class)->get_accessible(widget); +			if (parent_obj) { +				GType parent_atk_type = G_OBJECT_TYPE(parent_obj); + +				// Figure out whether accessibility is enabled by looking at the type of the accessible +				// object which would be created for the parent type of ScintillaObject. +				if (g_type_is_a(parent_atk_type, GTK_TYPE_ACCESSIBLE)) { +					*cache = scintilla_object_accessible_new(parent_atk_type, G_OBJECT(widget)); +					g_object_unref(parent_obj); +				} else { +					*cache = parent_obj; +				} +			} +		} +	} +#endif +	return *cache; +} + +static AtkStateSet *scintilla_object_accessible_ref_state_set(AtkObject *accessible) { +	AtkStateSet *state_set = ATK_OBJECT_CLASS(scintilla_object_accessible_parent_class)->ref_state_set(accessible); + +	GtkWidget *widget = gtk_accessible_get_widget(GTK_ACCESSIBLE(accessible)); +	if (widget == NULL) { +		atk_state_set_add_state(state_set, ATK_STATE_DEFUNCT); +	} else { +		if (! scintilla_send_message(SCINTILLA_OBJECT(widget), SCI_GETREADONLY, 0, 0)) +			atk_state_set_add_state(state_set, ATK_STATE_EDITABLE); +#if ATK_CHECK_VERSION(2, 16, 0) +		else +			atk_state_set_add_state(state_set, ATK_STATE_READ_ONLY); +#endif +		atk_state_set_add_state(state_set, ATK_STATE_MULTI_LINE); +		atk_state_set_add_state(state_set, ATK_STATE_MULTISELECTABLE); +		atk_state_set_add_state(state_set, ATK_STATE_SELECTABLE_TEXT); +		/*atk_state_set_add_state(state_set, ATK_STATE_SUPPORTS_AUTOCOMPLETION);*/ +	} + +	return state_set; +} + +static void scintilla_object_accessible_widget_set(GtkAccessible *accessible) { +	GtkWidget *widget = gtk_accessible_get_widget(accessible); +	if (widget == NULL) +		return; + +	ScintillaObjectAccessiblePrivate *priv = SCINTILLA_OBJECT_ACCESSIBLE_GET_PRIVATE(accessible); +	if (priv->pscin != 0) +		delete priv->pscin; +	priv->pscin = new ScintillaGTKAccessible(accessible, widget); +} + +#if HAVE_WIDGET_SET_UNSET +static void scintilla_object_accessible_widget_unset(GtkAccessible *accessible) { +	GtkWidget *widget = gtk_accessible_get_widget(accessible); +	if (widget == NULL) +		return; + +	ScintillaObjectAccessiblePrivate *priv = SCINTILLA_OBJECT_ACCESSIBLE_GET_PRIVATE(accessible); +	delete priv->pscin; +	priv->pscin = 0; +} +#endif + +static void scintilla_object_accessible_initialize(AtkObject *obj, gpointer data) { +	ATK_OBJECT_CLASS(scintilla_object_accessible_parent_class)->initialize(obj, data); + +#if ! HAVE_WIDGET_SET_UNSET +	scintilla_object_accessible_widget_set(GTK_ACCESSIBLE(obj)); +#endif + +	obj->role = ATK_ROLE_TEXT; +} + +static void scintilla_object_accessible_finalize(GObject *object) { +	ScintillaObjectAccessiblePrivate *priv = SCINTILLA_OBJECT_ACCESSIBLE_GET_PRIVATE(object); + +	if (priv->pscin) { +		delete priv->pscin; +		priv->pscin = 0; +	} + +	G_OBJECT_CLASS(scintilla_object_accessible_parent_class)->finalize(object); +} + +static void scintilla_object_accessible_class_init(ScintillaObjectAccessibleClass *klass) { +	GObjectClass *gobject_class = G_OBJECT_CLASS(klass); +	AtkObjectClass *object_class = ATK_OBJECT_CLASS(klass); + +#if HAVE_WIDGET_SET_UNSET +	GtkAccessibleClass *accessible_class = GTK_ACCESSIBLE_CLASS(klass); +	accessible_class->widget_set = scintilla_object_accessible_widget_set; +	accessible_class->widget_unset = scintilla_object_accessible_widget_unset; +#endif + +	object_class->ref_state_set = scintilla_object_accessible_ref_state_set; +	object_class->initialize = scintilla_object_accessible_initialize; + +	gobject_class->finalize = scintilla_object_accessible_finalize; + +	scintilla_object_accessible_parent_class = g_type_class_peek_parent(klass); + +	g_type_class_add_private(klass, sizeof (ScintillaObjectAccessiblePrivate)); +} + +static void scintilla_object_accessible_init(ScintillaObjectAccessible *accessible) { +	ScintillaObjectAccessiblePrivate *priv = SCINTILLA_OBJECT_ACCESSIBLE_GET_PRIVATE(accessible); + +	priv->pscin = 0; +} + +#if HAVE_GTK_FACTORY +// Object factory +typedef AtkObjectFactory ScintillaObjectAccessibleFactory; +typedef AtkObjectFactoryClass ScintillaObjectAccessibleFactoryClass; + +G_DEFINE_TYPE(ScintillaObjectAccessibleFactory, scintilla_object_accessible_factory, ATK_TYPE_OBJECT_FACTORY) + +static void scintilla_object_accessible_factory_init(ScintillaObjectAccessibleFactory *) { +} + +static GType scintilla_object_accessible_factory_get_accessible_type(void) { +	return SCINTILLA_TYPE_OBJECT_ACCESSIBLE; +} + +static AtkObject *scintilla_object_accessible_factory_create_accessible(GObject *obj) { +	return scintilla_object_accessible_new(0, obj); +} + +static void scintilla_object_accessible_factory_class_init(AtkObjectFactoryClass * klass) { +	klass->create_accessible = scintilla_object_accessible_factory_create_accessible; +	klass->get_accessible_type = scintilla_object_accessible_factory_get_accessible_type; +} +#endif diff --git a/gtk/ScintillaGTKAccessible.h b/gtk/ScintillaGTKAccessible.h new file mode 100644 index 000000000..de1584ded --- /dev/null +++ b/gtk/ScintillaGTKAccessible.h @@ -0,0 +1,163 @@ +/* Scintilla source code edit control */ +/* ScintillaGTKAccessible.h - GTK+ accessibility for ScintillaGTK */ +/* Copyright 2016 by Colomban Wendling <colomban@geany.org> + * The License.txt file describes the conditions under which this software may be distributed. */ + +#ifndef SCINTILLAGTKACCESSIBLE_H +#define SCINTILLAGTKACCESSIBLE_H + +#ifdef SCI_NAMESPACE +namespace Scintilla { +#endif + +class ScintillaGTKAccessible { +private: +	GtkAccessible *accessible; +	ScintillaGTK *sci; + +	// local state for comparing +	Position old_pos; +	std::vector<SelectionRange> old_sels; + +	void UpdateCursor(); +	void Notify(GtkWidget *widget, gint code, SCNotification *nt); +	static void SciNotify(GtkWidget *widget, gint code, SCNotification *nt, gpointer data) { +		try { +			reinterpret_cast<ScintillaGTKAccessible*>(data)->Notify(widget, code, nt); +		} catch (...) {} +	} + +	Position ByteOffsetFromCharacterOffset(Position startByte, int characterOffset) { +		Position pos = sci->pdoc->GetRelativePosition(startByte, characterOffset); +		if (pos == INVALID_POSITION) { +			// clamp invalid positions inside the document +			if (characterOffset > 0) { +				return sci->pdoc->Length(); +			} else { +				return 0; +			} +		} +		return pos; +	} + +	Position ByteOffsetFromCharacterOffset(int characterOffset) { +		return ByteOffsetFromCharacterOffset(0, characterOffset); +	} + +	int CharacterOffsetFromByteOffset(Position byteOffset) { +		return sci->pdoc->CountCharacters(0, byteOffset); +	} + +	void CharacterRangeFromByteRange(Position startByte, Position endByte, int *startChar, int *endChar) { +		*startChar = CharacterOffsetFromByteOffset(startByte); +		*endChar = *startChar + sci->pdoc->CountCharacters(startByte, endByte); +	} + +	void ByteRangeFromCharacterRange(int startChar, int endChar, Position& startByte, Position& endByte) { +		startByte = ByteOffsetFromCharacterOffset(startChar); +		endByte = ByteOffsetFromCharacterOffset(startByte, endChar - startChar); +	} + +	Position PositionBefore(Position pos) { +		return sci->pdoc->MovePositionOutsideChar(pos - 1, -1, true); +	} + +	Position PositionAfter(Position pos) { +		return sci->pdoc->MovePositionOutsideChar(pos + 1, 1, true); +	} + +	// For AtkText +	gchar *GetTextRangeUTF8(Position startByte, Position endByte); +	gchar *GetText(int startChar, int endChar); +	gchar *GetTextAfterOffset(int charOffset, AtkTextBoundary boundaryType, int *startChar, int *endChar); +	gchar *GetTextBeforeOffset(int charOffset, AtkTextBoundary boundaryType, int *startChar, int *endChar); +	gchar *GetTextAtOffset(int charOffset, AtkTextBoundary boundaryType, int *startChar, int *endChar); +	gchar *GetStringAtOffset(int charOffset, AtkTextGranularity granularity, int *startChar, int *endChar); +	gunichar GetCharacterAtOffset(int charOffset); +	gint GetCharacterCount(); +	gint GetCaretOffset(); +	gboolean SetCaretOffset(int charOffset); +	gint GetOffsetAtPoint(gint x, gint y, AtkCoordType coords); +	void GetCharacterExtents(int charOffset, gint *x, gint *y, gint *width, gint *height, AtkCoordType coords); +	AtkAttributeSet *GetAttributesForStyle(unsigned int style); +	AtkAttributeSet *GetRunAttributes(int charOffset, int *startChar, int *endChar); +	AtkAttributeSet *GetDefaultAttributes(); +	gint GetNSelections(); +	gchar *GetSelection(gint selection_num, int *startChar, int *endChar); +	gboolean AddSelection(int startChar, int endChar); +	gboolean RemoveSelection(int selection_num); +	gboolean SetSelection(gint selection_num, int startChar, int endChar); +	// for AtkEditableText +	bool InsertStringUTF8(Position bytePos, const gchar *utf8, int lengthBytes); +	void SetTextContents(const gchar *contents); +	void InsertText(const gchar *contents, int lengthBytes, int *charPosition); +	void CopyText(int startChar, int endChar); +	void CutText(int startChar, int endChar); +	void DeleteText(int startChar, int endChar); +	void PasteText(int charPosition); + +public: +	ScintillaGTKAccessible(GtkAccessible *accessible, GtkWidget *widget); +	~ScintillaGTKAccessible(); + +	static ScintillaGTKAccessible *FromAccessible(GtkAccessible *accessible); +	static ScintillaGTKAccessible *FromAccessible(AtkObject *accessible) { +		return FromAccessible(GTK_ACCESSIBLE(accessible)); +	} +	// So ScintillaGTK can notify us +	void ChangeDocument(Document *oldDoc, Document *newDoc); +	void NotifyReadOnly(); + +	// Helper GtkWidget methods +	static AtkObject *WidgetGetAccessibleImpl(GtkWidget *widget, AtkObject **cache, gpointer widget_parent_class); + +	// ATK methods + +	class AtkTextIface { +	public: +		static void init(::AtkTextIface *iface); + +	private: +		AtkTextIface(); + +		static gchar *GetText(AtkText *text, int start_offset, int end_offset); +		static gchar *GetTextAfterOffset(AtkText *text, int offset, AtkTextBoundary boundary_type, int *start_offset, int *end_offset); +		static gchar *GetTextBeforeOffset(AtkText *text, int offset, AtkTextBoundary boundary_type, int *start_offset, int *end_offset); +		static gchar *GetTextAtOffset(AtkText *text, gint offset, AtkTextBoundary boundary_type, gint *start_offset, gint *end_offset); +		static gchar *GetStringAtOffset(AtkText *text, gint offset, AtkTextGranularity granularity, gint *start_offset, gint *end_offset); +		static gunichar GetCharacterAtOffset(AtkText *text, gint offset); +		static gint GetCharacterCount(AtkText *text); +		static gint GetCaretOffset(AtkText *text); +		static gboolean SetCaretOffset(AtkText *text, gint offset); +		static gint GetOffsetAtPoint(AtkText *text, gint x, gint y, AtkCoordType coords); +		static void GetCharacterExtents(AtkText *text, gint offset, gint *x, gint *y, gint *width, gint *height, AtkCoordType coords); +		static AtkAttributeSet *GetRunAttributes(AtkText *text, gint offset, gint *start_offset, gint *end_offset); +		static AtkAttributeSet *GetDefaultAttributes(AtkText *text); +		static gint GetNSelections(AtkText *text); +		static gchar *GetSelection(AtkText *text, gint selection_num, gint *start_pos, gint *end_pos); +		static gboolean AddSelection(AtkText *text, gint start, gint end); +		static gboolean RemoveSelection(AtkText *text, gint selection_num); +		static gboolean SetSelection(AtkText *text, gint selection_num, gint start, gint end); +	}; +	class AtkEditableTextIface { +	public: +		static void init(::AtkEditableTextIface *iface); + +	private: +		AtkEditableTextIface(); + +		static void SetTextContents(AtkEditableText *text, const gchar *contents); +		static void InsertText(AtkEditableText *text, const gchar *contents, gint length, gint *position); +		static void CopyText(AtkEditableText *text, gint start, gint end); +		static void CutText(AtkEditableText *text, gint start, gint end); +		static void DeleteText(AtkEditableText *text, gint start, gint end); +		static void PasteText(AtkEditableText *text, gint position); +	}; +}; + +#ifdef SCI_NAMESPACE +} +#endif + + +#endif /* SCINTILLAGTKACCESSIBLE_H */ diff --git a/gtk/makefile b/gtk/makefile index 995c0c4c2..1b6659fc0 100644 --- a/gtk/makefile +++ b/gtk/makefile @@ -114,7 +114,7 @@ $(COMPLIB): Accessor.o CharacterSet.o LexerBase.o LexerModule.o LexerSimple.o St  	CharClassify.o Decoration.o Document.o PerLine.o Catalogue.o CallTip.o CaseConvert.o CaseFolder.o \  	ScintillaBase.o ContractionState.o EditModel.o Editor.o EditView.o ExternalLexer.o MarginView.o \  	PropSetSimple.o PlatGTK.o \ -	KeyMap.o LineMarker.o PositionCache.o ScintillaGTK.o CellBuffer.o CharacterCategory.o ViewStyle.o \ +	KeyMap.o LineMarker.o PositionCache.o ScintillaGTK.o ScintillaGTKAccessible.o CellBuffer.o CharacterCategory.o ViewStyle.o \  	RESearch.o RunStyles.o Selection.o Style.o Indicator.o AutoComplete.o UniConversion.o XPM.o \  	$(MARSHALLER) $(LEXOBJS)  	$(AR) $(ARFLAGS) $@ $^ diff --git a/scripts/HeaderOrder.txt b/scripts/HeaderOrder.txt index cbfdf6f3f..fdbb3a157 100644 --- a/scripts/HeaderOrder.txt +++ b/scripts/HeaderOrder.txt @@ -44,6 +44,7 @@  #include <gdk/gdk.h>  #include <gtk/gtk.h>  #include <gdk/gdkkeysyms.h> +#include <gtk/gtk-a11y.h>  // Windows headers  #include <windows.h> @@ -138,7 +139,9 @@  #include "HanjaDic.h"  // gtk +#include "ScintillaGTK.h"  #include "scintilla-marshal.h" +#include "ScintillaGTKAccessible.h"  #include "Converter.h"  // cocoa | 
