Hi,
I've working on a lightweight IDE, making it scroll a certain line to 1/3 of the view of the editor widget. We used to just send SCI_GOTOLINE in response to a GtkTreeView click - now we are using SCI_LINESCROLL to get the line in exactly the right place. My problem is we have code that may cause an initial scroll in reponse to SCI_GOTOLINE, but also code after a SCN_PAINTED notification that calls SCI_LINESCROLL. On the attempt to scroll the ScintillaObject the second time, Scintilla aborts with an assert failure in ScintillaGTK::ExposeTextThis.

I'm using Scintilla 1.72 with GTK 2.8.20 (also tested on GTK 2.6.9) on Fedora Core 5 Linux.

I've attached a small demo of this in scrollview.c (based on the GTK bait sample). Just click the Test button and it should abort.

I've patched scintilla (gtk-expose-event-scroll-safe.patch) to prevent the assert failure and it seems to be working fine. The patch assumes that the assert was just to prevent a memory leak, I'm not too familiar with widget internals. Is this a good idea?

Regards,
Nick
Index: ScintillaGTK.cxx
===================================================================
--- ScintillaGTK.cxx	(revision 1236)
+++ ScintillaGTK.cxx	(revision 1237)
@@ -2198,7 +2198,13 @@
 	rcPaint.right = ose->area.x + ose->area.width;
 	rcPaint.bottom = ose->area.y + ose->area.height;
 
-	PLATFORM_ASSERT(rgnUpdate == NULL);
+	/* We can receive an expose-event during an expose-event.
+	 * This can happen when two different scroll messages are sent at different times. */
+	if (rgnUpdate != NULL)
+	{
+		gdk_region_destroy(rgnUpdate);
+		rgnUpdate = NULL;
+	}
 #if GTK_MAJOR_VERSION >= 2
 	rgnUpdate = gdk_region_copy(ose->region);
 #endif

#include <gtk/gtk.h>

#include <Scintilla.h>
#include <SciLexer.h>
#define PLAT_GTK 1
#include <ScintillaWidget.h>


ScintillaObject	*sci;
gboolean		scroll_in_view = TRUE;

#define SSM(m, w, l) scintilla_send_message(sci, m, w, l)


static void scroll_to_line(ScintillaObject *sci, gint line, gfloat percent_of_view);


static void on_editor_notification(GtkWidget *editor, gint scn, gpointer lscn,
		gpointer user_data)
{
	struct SCNotification *nt = lscn;

	switch (nt->nmhdr.code)
	{
		case SCN_PAINTED:
		{
			if (scroll_in_view)
				scroll_to_line(sci, -1, 0.5F);
			scroll_in_view = FALSE;
			break;
		}
	}
}


static void on_button_clicked()
{
	SSM(SCI_GOTOLINE, 5, 0);	// also scrolls
	scroll_in_view = TRUE;
	/* because of the last scroll, the SCN_PAINTED scrolling will now cause
	 * Scintilla to abort. */
}


int main(int argc, char **argv)
{
	GtkWidget *app;
	GtkWidget *editor;
	GtkWidget *box;
	GtkWidget *button;
	gint i;

	gtk_init(&argc, &argv);
	app = gtk_window_new(GTK_WINDOW_TOPLEVEL);
	editor = scintilla_new();
	sci = SCINTILLA(editor);

	button = gtk_button_new_with_label("Test");
	g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(on_button_clicked), NULL);

	box = gtk_vbox_new(FALSE, 6);
	gtk_container_add(GTK_CONTAINER(box), button);
	gtk_container_add(GTK_CONTAINER(box), editor);
	gtk_container_add(GTK_CONTAINER(app), box);

	g_signal_connect(G_OBJECT(app), "delete_event", G_CALLBACK(gtk_main_quit), NULL);

	scintilla_set_id(sci, 0);
	gtk_widget_set_usize(editor, 500, 300);

	for (i = 0; i < 100; i++)
		SSM(SCI_INSERTTEXT, 0, (sptr_t) "\n");

	SSM(SCI_INSERTTEXT, 0, (sptr_t)
	 "int main(int argc, char **argv) {\n"
	 "	 // Start up the gnome\n"
	 "	 gnome_init(\"stest\", \"1.0\", argc, argv);\n}"
	);

	for (i = 0; i < 100; i++)
		SSM(SCI_INSERTTEXT, 0, (sptr_t) "\n");

	SSM(SCI_GOTOLINE, 100, 0);

	gtk_widget_show_all(app);

	//gtk_widget_grab_focus(GTK_WIDGET(editor));

	g_signal_connect((GtkWidget*) sci, "sci-notify",
				G_CALLBACK(on_editor_notification), NULL);

	//scroll_to_line(sci, -1, 0.5F);

	gtk_main();
	return 0;
}


gint sci_get_current_line(ScintillaObject *sci, gint pos)
{
	if (pos >= 0)
	{
		return SSM(SCI_LINEFROMPOSITION, pos, 0);
	}
	else
	{
		return SSM(SCI_LINEFROMPOSITION, SSM(SCI_GETCURRENTPOS, 0, 0), 0);
	}
}


/* Scroll the view to make line appear at percent_of_view.
 * line can be -1 to use the current position. */
static void scroll_to_line(ScintillaObject *sci, gint line, gfloat percent_of_view)
{
	gint vis1, los, delta;

	if (line == -1)
		line = sci_get_current_line(sci, -1);

	line = SSM(SCI_VISIBLEFROMDOCLINE, line, 0);
	vis1 = SSM(SCI_GETFIRSTVISIBLELINE, 0, 0);
	los = SSM(SCI_LINESONSCREEN, 0, 0);
	delta = (line - vis1) - los * percent_of_view;
	SSM(SCI_LINESCROLL, 0, delta);
}


_______________________________________________
Scintilla-interest mailing list
[email protected]
http://mailman.lyra.org/mailman/listinfo/scintilla-interest

Reply via email to