GtkSharp/sample/GtkDemo/DemoHyperText.cs
2012-11-04 16:58:39 +01:00

208 lines
6.4 KiB
C#

/* Text Widget/Hypertext
*
* Usually, tags modify the appearance of text in the view, e.g. making it
* bold or colored or underlined. But tags are not restricted to appearance.
* They can also affect the behavior of mouse and key presses, as this demo
* shows.
*/
using System;
using System.Collections.Generic;
using Gtk;
namespace GtkDemo
{
[Demo ("Hyper Text", "DemoHyperText.cs", "Text Widget")]
public class DemoHyperText : Gtk.Window
{
bool hoveringOverLink = false;
Gdk.Cursor handCursor, regularCursor;
public DemoHyperText () : base ("HyperText")
{
handCursor = new Gdk.Cursor (Gdk.CursorType.Hand2);
regularCursor = new Gdk.Cursor (Gdk.CursorType.Xterm);
SetDefaultSize (450, 450);
TextView view = new TextView ();
view.WrapMode = WrapMode.Word;
view.KeyPressEvent += new KeyPressEventHandler (KeyPress);
view.WidgetEventAfter += new WidgetEventAfterHandler (EventAfter);
view.MotionNotifyEvent += new MotionNotifyEventHandler (MotionNotify);
view.VisibilityNotifyEvent += new VisibilityNotifyEventHandler (VisibilityNotify);
ScrolledWindow sw = new ScrolledWindow ();
sw.SetPolicy (Gtk.PolicyType.Automatic, Gtk.PolicyType.Automatic);
Add (sw);
sw.Add (view);
ShowPage (view.Buffer, 1);
ShowAll ();
}
Dictionary<TextTag, int> tag_pages = new Dictionary<TextTag, int> ();
// Inserts a piece of text into the buffer, giving it the usual
// appearance of a hyperlink in a web browser: blue and underlined.
// Additionally, attaches some data on the tag, to make it recognizable
// as a link.
void InsertLink (TextBuffer buffer, ref TextIter iter, string text, int page)
{
TextTag tag = new TextTag (null);
tag.Foreground = "blue";
tag.Underline = Pango.Underline.Single;
tag_pages [tag] = page;
buffer.TagTable.Add (tag);
buffer.InsertWithTags (ref iter, text, tag);
}
// Fills the buffer with text and interspersed links. In any real
// hypertext app, this method would parse a file to identify the links.
void ShowPage (TextBuffer buffer, int page)
{
buffer.Text = "";
TextIter iter = buffer.StartIter;
if (page == 1) {
buffer.Insert (ref iter, "Some text to show that simple ");
InsertLink (buffer, ref iter, "hypertext", 3);
buffer.Insert (ref iter, " can easily be realized with ");
InsertLink (buffer, ref iter, "tags", 2);
buffer.Insert (ref iter, ".");
} else if (page == 2) {
buffer.Insert (ref iter,
"A tag is an attribute that can be applied to some range of text. " +
"For example, a tag might be called \"bold\" and make the text inside " +
"the tag bold. However, the tag concept is more general than that; " +
"tags don't have to affect appearance. They can instead affect the " +
"behavior of mouse and key presses, \"lock\" a range of text so the " +
"user can't edit it, or countless other things.\n");
InsertLink (buffer, ref iter, "Go back", 1);
} else if (page == 3) {
TextTag tag = buffer.TagTable.Lookup ("bold");
if (tag == null) {
tag = new TextTag ("bold");
tag.Weight = Pango.Weight.Bold;
buffer.TagTable.Add (tag);
}
buffer.InsertWithTags (ref iter, "hypertext:\n", tag);
buffer.Insert (ref iter,
"machine-readable text that is not sequential but is organized " +
"so that related items of information are connected.\n");
InsertLink (buffer, ref iter, "Go back", 1);
}
}
// Looks at all tags covering the position of iter in the text view,
// and if one of them is a link, follow it by showing the page identified
// by the data attached to it.
void FollowIfLink (TextView view, TextIter iter)
{
foreach (TextTag tag in iter.Tags) {
int page = tag_pages [tag];
ShowPage (view.Buffer, (int)page);
}
}
// Looks at all tags covering the position (x, y) in the text view,
// and if one of them is a link, change the cursor to the "hands" cursor
// typically used by web browsers.
void SetCursorIfAppropriate (TextView view, int x, int y)
{
bool hovering = false;
TextIter iter = view.GetIterAtLocation (x, y);
foreach (TextTag tag in iter.Tags) {
if (tag_pages [tag] is int) {
hovering = true;
break;
}
}
if (hovering != hoveringOverLink) {
Gdk.Window window = view.GetWindow (Gtk.TextWindowType.Text);
hoveringOverLink = hovering;
if (hoveringOverLink)
window.Cursor = handCursor;
else
window.Cursor = regularCursor;
}
}
// Links can be activated by pressing Enter.
void KeyPress (object sender, KeyPressEventArgs args)
{
TextView view = sender as TextView;
switch ((Gdk.Key) args.Event.KeyValue) {
case Gdk.Key.Return:
case Gdk.Key.KP_Enter:
TextIter iter = view.Buffer.GetIterAtMark (view.Buffer.InsertMark);
FollowIfLink (view, iter);
break;
default:
break;
}
}
// Links can also be activated by clicking.
void EventAfter (object sender, WidgetEventAfterArgs args)
{
if (args.Event.Type != Gdk.EventType.ButtonRelease)
return;
Gdk.EventButton evt = (Gdk.EventButton)args.Event;
if (evt.Button != 1)
return;
TextView view = sender as TextView;
TextIter start, end, iter;
int x, y;
// we shouldn't follow a link if the user has selected something
view.Buffer.GetSelectionBounds (out start, out end);
if (start.Offset != end.Offset)
return;
view.WindowToBufferCoords (TextWindowType.Widget, (int) evt.X, (int) evt.Y, out x, out y);
iter = view.GetIterAtLocation (x, y);
FollowIfLink (view, iter);
}
// Update the cursor image if the pointer moved.
void MotionNotify (object sender, MotionNotifyEventArgs args)
{
TextView view = sender as TextView;
int x, y;
Gdk.ModifierType state;
view.WindowToBufferCoords (TextWindowType.Widget, (int) args.Event.X, (int) args.Event.Y, out x, out y);
SetCursorIfAppropriate (view, x, y);
view.Window.GetPointer (out x, out y, out state);
}
// Also update the cursor image if the window becomes visible
// (e.g. when a window covering it got iconified).
void VisibilityNotify (object sender, VisibilityNotifyEventArgs a)
{
TextView view = sender as TextView;
int wx, wy, bx, by;
view.GetPointer (out wx, out wy);
view.WindowToBufferCoords (TextWindowType.Widget, wx, wy, out bx, out by);
SetCursorIfAppropriate (view, bx, by);
}
protected override bool OnDeleteEvent (Gdk.Event evt)
{
Destroy ();
return true;
}
}
}