| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525 |
- IMPLEMENTATION MODULE DemosText ;
- (*
- See the GTK C originals in gtk/demos/gtk-demo:
- read_more.c -> DoReadMore
- rotated_text.c-> DoRotatedText
- textmask.c -> DoTextMask
- textscroll.c -> DoTextScroll
- textundo.c -> DoTextUndo
- The gtk-demo contract is: on the first call build the window and store
- it in a module-level variable; on later calls just toggle the window's
- visibility, destroying it (and clearing the variable) when it is
- already visible. Return the window, or NIL once destroyed.
- Several C helpers used by the originals are not part of the binding
- set under src/, so those spots are simplified and marked below:
- * ReadMore is a custom GtkWidget subclass with its own measure() and
- size_allocate(); subclassing is not exposed, so a plain wrapping
- GtkLabel plus a "Read More" button is used instead.
- * The Pango custom shape renderer / PangoAttrList of rotated_text
- are not bound; the layout is drawn with its plain font.
- * gtk_text_buffer_set_enable_undo(), *begin/end_irreversible_action()
- and gtk_text_view_scroll_mark_onscreen() are not bound.
- * cairo_fill_preserve() is not bound; textmask rebuilds the path to
- stroke the outline.
- *)
- FROM SYSTEM IMPORT ADDRESS, ADR, CAST;
- FROM GtkWindow IMPORT gtk_window_new, gtk_window_set_title,
- gtk_window_set_default_size, gtk_window_set_resizable,
- gtk_window_set_child, gtk_window_destroy;
- FROM GtkWidget IMPORT gtk_widget_set_visible, gtk_widget_get_visible,
- gtk_widget_set_size_request, gtk_widget_set_vexpand, gtk_widget_set_hexpand,
- gtk_widget_add_css_class;
- FROM GtkBox IMPORT gtk_box_new, gtk_box_append;
- FROM GtkEnums IMPORT GtkVertical, GtkHorizontal, GtkWrapWord;
- FROM GtkLabel IMPORT gtk_label_new, gtk_label_set_wrap,
- gtk_label_set_max_width_chars;
- FROM GtkButton IMPORT gtk_button_new_with_label;
- FROM GtkScrolledWindow IMPORT gtk_scrolled_window_new,
- gtk_scrolled_window_set_child, gtk_scrolled_window_set_policy,
- GtkPolicyAutomatic;
- FROM GtkTextView IMPORT gtk_text_view_new, gtk_text_view_get_buffer,
- gtk_text_view_set_wrap_mode, gtk_text_view_set_left_margin,
- gtk_text_view_set_right_margin, gtk_text_view_set_top_margin,
- gtk_text_view_set_bottom_margin, gtk_text_view_scroll_to_mark;
- FROM GtkTextBuffer IMPORT gtk_text_buffer_get_start_iter,
- gtk_text_buffer_insert, gtk_text_buffer_get_end_iter,
- gtk_text_buffer_create_mark, gtk_text_buffer_get_mark,
- gtk_text_buffer_get_iter_at_mark;
- FROM GtkTextIter IMPORT GtkTextIter;
- FROM GtkDrawingArea IMPORT gtk_drawing_area_new,
- gtk_drawing_area_set_content_width, gtk_drawing_area_set_content_height,
- gtk_drawing_area_set_draw_func;
- FROM Pango IMPORT pango_font_description_from_string,
- pango_font_description_free, pango_layout_set_text,
- pango_layout_set_font_description, pango_layout_get_pixel_size;
- FROM PangoCairo IMPORT pango_cairo_create_layout, pango_cairo_show_layout,
- pango_cairo_update_layout;
- FROM Cairo IMPORT cairo_move_to, cairo_rotate, cairo_scale, cairo_translate,
- cairo_set_source, cairo_save, cairo_restore, cairo_fill, cairo_stroke,
- cairo_set_source_rgb, cairo_set_line_width, cairo_select_font_face,
- cairo_set_font_size, cairo_text_path, cairo_pattern_create_linear,
- cairo_pattern_add_color_stop_rgb, cairo_pattern_destroy,
- CairoFontSlantNormal, CairoFontWeightBold;
- FROM GObject IMPORT g_signal_connect_data, GConnectDefault, g_object_unref;
- FROM GLib IMPORT g_timeout_add, g_source_remove, guint, GSourceContinue;
- CONST
- (* rotated_text *)
- Radius = 150; (* pixels; window is 4*Radius by 2*Radius *)
- NWords = 5;
- Pi = 3.141592653589793;
- RotatedText = "I \xE2\x99\xA5 GTK";
- ReadMoreText =
- "I'd just like to interject for a moment. What you're referring to as Linux, is in fact, GNU/Linux, or as I've recently taken to calling it, GNU plus Linux. Linux is not an operating system unto itself, but rather another free component of a fully functioning GNU system made useful by the GNU corelibs, shell utilities and vital system components comprising a full OS as defined by POSIX.\n\nMany computer users run a modified version of the GNU system every day, without realizing it. Through a peculiar turn of events, the version of GNU which is widely used today is often called \x22Linux\x22, and many of its users are not aware that it is basically the GNU system, developed by the GNU Project.\n\nThere really is a Linux, and these people are using it, but it is just a part of the system they use. Linux is the kernel: the program in the system that allocates the machine's resources to the other programs that you run. The kernel is an essential part of an operating system, but useless by itself; it can only function in the context of a complete operating system. Linux is normally used in combination with the GNU operating system: the whole system is basically GNU with Linux added, or GNU/Linux. All the so-called \x22Linux\x22 distributions are really distributions of GNU/Linux.";
- UndoText =
- "The GtkTextView supports undo and redo through the use of a GtkTextBuffer. You can enable or disable undo support using gtk_text_buffer_set_enable_undo().\nType to add more text.\nUse Control+z to undo and Control+Shift+z or Control+y to redo previously undone operations.";
- ScrollToEndPrefix =
- "Scroll to end scroll to end scroll to end scroll to end ";
- ScrollToBottomPrefix =
- "Scroll to bottom scroll to bottom scroll to bottom scroll to bottom ";
- VAR
- readMoreWindow, rotatedTextWindow, textMaskWindow,
- textScrollWindow, textUndoWindow: ADDRESS;
- (* static counters of the C originals *)
- endCount, bottomCount: INTEGER;
- (* ================================================================== *)
- (* read_more *)
- (* ================================================================== *)
- PROCEDURE OnReadMore (button: ADDRESS; data: ADDRESS);
- BEGIN
- (* The real demo grows the custom widget; with no subclassing the
- best we can do is hide the reveal button. *)
- gtk_widget_set_visible(button, 0)
- END OnReadMore;
- PROCEDURE DoReadMore (doWidget: ADDRESS) : ADDRESS;
- VAR
- box, label, button: ADDRESS;
- BEGIN
- IF readMoreWindow = NIL THEN
- readMoreWindow := gtk_window_new();
- gtk_window_set_title(readMoreWindow, "Read More");
- gtk_window_set_default_size(readMoreWindow, 400, 300);
- box := gtk_box_new(GtkVertical, 6);
- gtk_window_set_child(readMoreWindow, box);
- label := gtk_label_new(ReadMoreText);
- gtk_label_set_wrap(label, 1);
- gtk_label_set_max_width_chars(label, 30);
- gtk_widget_set_vexpand(label, 1);
- gtk_box_append(box, label);
- button := gtk_button_new_with_label("Read More");
- g_signal_connect_data(button, "clicked", ADR(OnReadMore), NIL, NIL,
- GConnectDefault);
- gtk_box_append(box, button)
- END;
- IF gtk_widget_get_visible(readMoreWindow) = 0 THEN
- gtk_widget_set_visible(readMoreWindow, 1)
- ELSE
- gtk_window_destroy(readMoreWindow);
- readMoreWindow := NIL
- END;
- RETURN readMoreWindow
- END DoReadMore;
- (* ================================================================== *)
- (* rotated_text *)
- (* ================================================================== *)
- PROCEDURE RotatedTextDraw (area: ADDRESS; cr: ADDRESS;
- width, height: INTEGER; data: ADDRESS);
- VAR
- layout, desc, pattern: ADDRESS;
- deviceRadius: REAL;
- lw, lh, i: INTEGER;
- BEGIN
- IF width < height THEN
- deviceRadius := VAL(REAL, width) / 2.0
- ELSE
- deviceRadius := VAL(REAL, height) / 2.0
- END;
- cairo_translate(cr,
- deviceRadius + (VAL(REAL, width) - 2.0 * deviceRadius) / 2.0,
- deviceRadius + (VAL(REAL, height) - 2.0 * deviceRadius) / 2.0);
- cairo_scale(cr, deviceRadius / 150.0, deviceRadius / 150.0);
- pattern := cairo_pattern_create_linear(-150.0, -150.0, 150.0, 150.0);
- cairo_pattern_add_color_stop_rgb(pattern, 0.0, 0.5, 0.0, 0.0);
- cairo_pattern_add_color_stop_rgb(pattern, 1.0, 0.0, 0.0, 0.5);
- cairo_set_source(cr, pattern);
- layout := pango_cairo_create_layout(cr);
- pango_layout_set_text(layout, RotatedText, -1);
- desc := pango_font_description_from_string("Serif 18");
- pango_layout_set_font_description(layout, desc);
- pango_font_description_free(desc);
- FOR i := 0 TO NWords - 1 DO
- pango_cairo_update_layout(cr, layout);
- pango_layout_get_pixel_size(layout, lw, lh);
- cairo_move_to(cr, -VAL(REAL, lw) / 2.0, -150.0 * 0.9);
- pango_cairo_show_layout(cr, layout);
- cairo_rotate(cr, 2.0 * Pi / VAL(REAL, NWords))
- END;
- g_object_unref(layout);
- cairo_pattern_destroy(pattern)
- END RotatedTextDraw;
- PROCEDURE DoRotatedText (doWidget: ADDRESS) : ADDRESS;
- VAR
- box, drawingArea, label: ADDRESS;
- BEGIN
- IF rotatedTextWindow = NIL THEN
- rotatedTextWindow := gtk_window_new();
- gtk_window_set_title(rotatedTextWindow, "Rotated Text");
- gtk_window_set_default_size(rotatedTextWindow, 4 * Radius, 2 * Radius);
- box := gtk_box_new(GtkHorizontal, 0);
- gtk_window_set_child(rotatedTextWindow, box);
- drawingArea := gtk_drawing_area_new();
- gtk_drawing_area_set_content_width(drawingArea, 2 * Radius);
- gtk_drawing_area_set_content_height(drawingArea, 2 * Radius);
- gtk_widget_set_hexpand(drawingArea, 1);
- gtk_widget_set_vexpand(drawingArea, 1);
- gtk_box_append(box, drawingArea);
- gtk_widget_add_css_class(drawingArea, "view");
- gtk_drawing_area_set_draw_func(drawingArea, ADR(RotatedTextDraw),
- NIL, NIL);
- label := gtk_label_new(RotatedText);
- gtk_widget_set_hexpand(label, 1);
- gtk_widget_set_vexpand(label, 1);
- gtk_box_append(box, label)
- END;
- IF gtk_widget_get_visible(rotatedTextWindow) = 0 THEN
- gtk_widget_set_visible(rotatedTextWindow, 1)
- ELSE
- gtk_window_destroy(rotatedTextWindow);
- rotatedTextWindow := NIL
- END;
- RETURN rotatedTextWindow
- END DoRotatedText;
- (* ================================================================== *)
- (* textmask *)
- (* ================================================================== *)
- PROCEDURE MaskPath (cr: ADDRESS);
- BEGIN
- cairo_move_to(cr, 30.0, 20.0);
- cairo_text_path(cr, "Pango power!");
- cairo_move_to(cr, 30.0, 60.0);
- cairo_text_path(cr, "Pango power!");
- cairo_move_to(cr, 30.0, 100.0);
- cairo_text_path(cr, "Pango power!")
- END MaskPath;
- PROCEDURE TextMaskDraw (area: ADDRESS; cr: ADDRESS;
- width, height: INTEGER; data: ADDRESS);
- VAR
- pattern: ADDRESS;
- BEGIN
- cairo_save(cr);
- cairo_select_font_face(cr, "sans", CairoFontSlantNormal,
- CairoFontWeightBold);
- cairo_set_font_size(cr, 34.0);
- MaskPath(cr);
- pattern := cairo_pattern_create_linear(0.0, 0.0,
- VAL(REAL, width), VAL(REAL, height));
- cairo_pattern_add_color_stop_rgb(pattern, 0.0, 1.0, 0.0, 0.0);
- cairo_pattern_add_color_stop_rgb(pattern, 0.2, 1.0, 0.0, 0.0);
- cairo_pattern_add_color_stop_rgb(pattern, 0.3, 1.0, 1.0, 0.0);
- cairo_pattern_add_color_stop_rgb(pattern, 0.4, 0.0, 1.0, 0.0);
- cairo_pattern_add_color_stop_rgb(pattern, 0.6, 0.0, 1.0, 1.0);
- cairo_pattern_add_color_stop_rgb(pattern, 0.7, 0.0, 0.0, 1.0);
- cairo_pattern_add_color_stop_rgb(pattern, 0.8, 1.0, 0.0, 1.0);
- cairo_pattern_add_color_stop_rgb(pattern, 1.0, 1.0, 0.0, 1.0);
- cairo_set_source(cr, pattern);
- cairo_fill(cr); (* cairo_fill_preserve is unbound *)
- cairo_pattern_destroy(pattern);
- (* Rebuild the path (fill consumed it) and outline it in black. *)
- MaskPath(cr);
- cairo_set_source_rgb(cr, 0.0, 0.0, 0.0);
- cairo_set_line_width(cr, 0.5);
- cairo_stroke(cr);
- cairo_restore(cr)
- END TextMaskDraw;
- PROCEDURE DoTextMask (doWidget: ADDRESS) : ADDRESS;
- VAR
- da: ADDRESS;
- BEGIN
- IF textMaskWindow = NIL THEN
- textMaskWindow := gtk_window_new();
- gtk_window_set_resizable(textMaskWindow, 1);
- gtk_widget_set_size_request(textMaskWindow, 400, 240);
- gtk_window_set_title(textMaskWindow, "Text Mask");
- da := gtk_drawing_area_new();
- gtk_window_set_child(textMaskWindow, da);
- gtk_drawing_area_set_draw_func(da, ADR(TextMaskDraw), NIL, NIL)
- END;
- IF gtk_widget_get_visible(textMaskWindow) = 0 THEN
- gtk_widget_set_visible(textMaskWindow, 1)
- ELSE
- gtk_window_destroy(textMaskWindow);
- textMaskWindow := NIL
- END;
- RETURN textMaskWindow
- END DoTextMask;
- (* ================================================================== *)
- (* textscroll *)
- (* ================================================================== *)
- (* Fill s with n spaces (NUL-terminated). *)
- PROCEDURE MakeSpaces (n: INTEGER; VAR s: ARRAY OF CHAR);
- VAR
- i, hi: INTEGER;
- BEGIN
- hi := VAL(INTEGER, HIGH(s));
- IF n > hi - 1 THEN
- n := hi - 1
- END;
- FOR i := 0 TO n - 1 DO
- s[i] := ' '
- END;
- s[n] := 0C
- END MakeSpaces;
- (* Copy prefix then append the decimal rendering of n (NUL-terminated). *)
- PROCEDURE BuildScrollText (prefix: ARRAY OF CHAR; n: INTEGER;
- VAR s: ARRAY OF CHAR);
- VAR
- pos, k, hi: INTEGER;
- digits: ARRAY [0..15] OF CHAR;
- BEGIN
- hi := VAL(INTEGER, HIGH(s));
- pos := 0;
- WHILE (pos < hi) AND (prefix[pos] # 0C) DO
- s[pos] := prefix[pos];
- INC(pos)
- END;
- k := 0;
- IF n = 0 THEN
- digits[0] := '0';
- k := 1
- ELSE
- WHILE n > 0 DO
- digits[k] := CHR(ORD('0') + VAL(CARDINAL, n MOD 10));
- n := n DIV 10;
- INC(k)
- END
- END;
- WHILE k > 0 DO
- DEC(k);
- IF pos < hi THEN
- s[pos] := digits[k];
- INC(pos)
- END
- END;
- s[pos] := 0C
- END BuildScrollText;
- PROCEDURE ScrollToEnd (textview: ADDRESS) : INTEGER;
- VAR
- buffer, mark: ADDRESS;
- iter: GtkTextIter;
- spaces, text: ARRAY [0..255] OF CHAR;
- BEGIN
- buffer := gtk_text_view_get_buffer(textview);
- mark := gtk_text_buffer_get_mark(buffer, "end");
- gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
- MakeSpaces(endCount, spaces);
- INC(endCount);
- gtk_text_buffer_insert(buffer, iter, "\n", -1);
- gtk_text_buffer_insert(buffer, iter, spaces, -1);
- BuildScrollText(ScrollToEndPrefix, endCount, text);
- gtk_text_buffer_insert(buffer, iter, text, -1);
- gtk_text_view_scroll_to_mark(textview, mark, 0.0, 0, 0.0, 0.0);
- IF endCount > 150 THEN
- endCount := 0
- END;
- RETURN GSourceContinue
- END ScrollToEnd;
- PROCEDURE ScrollToBottom (textview: ADDRESS) : INTEGER;
- VAR
- buffer, mark: ADDRESS;
- iter: GtkTextIter;
- spaces, text: ARRAY [0..255] OF CHAR;
- BEGIN
- buffer := gtk_text_view_get_buffer(textview);
- gtk_text_buffer_get_end_iter(buffer, iter);
- MakeSpaces(bottomCount, spaces);
- INC(bottomCount);
- gtk_text_buffer_insert(buffer, iter, "\n", -1);
- gtk_text_buffer_insert(buffer, iter, spaces, -1);
- BuildScrollText(ScrollToBottomPrefix, bottomCount, text);
- gtk_text_buffer_insert(buffer, iter, text, -1);
- (* gtk_text_iter_set_line_offset() and gtk_text_buffer_move_mark()
- are not bound, so the "scroll" mark stays at its original spot. *)
- mark := gtk_text_buffer_get_mark(buffer, "scroll");
- gtk_text_view_scroll_to_mark(textview, mark, 0.0, 0, 0.0, 0.0);
- IF bottomCount > 40 THEN
- bottomCount := 0
- END;
- RETURN GSourceContinue
- END ScrollToBottom;
- PROCEDURE SetupScroll (textview: ADDRESS; toEnd: BOOLEAN) : guint;
- VAR
- buffer, mark: ADDRESS;
- iter: GtkTextIter;
- BEGIN
- buffer := gtk_text_view_get_buffer(textview);
- gtk_text_buffer_get_end_iter(buffer, iter);
- IF toEnd THEN
- mark := gtk_text_buffer_create_mark(buffer, "end", iter, 0);
- RETURN g_timeout_add(50, ScrollToEnd, textview)
- ELSE
- mark := gtk_text_buffer_create_mark(buffer, "scroll", iter, 1);
- RETURN g_timeout_add(100, ScrollToBottom, textview)
- END
- END SetupScroll;
- (* void remove_timeout(GtkWidget *window, gpointer timeout) *)
- PROCEDURE RemoveTimeout (window: ADDRESS; data: ADDRESS);
- BEGIN
- g_source_remove(CAST(guint, data))
- END RemoveTimeout;
- PROCEDURE CreateTextView (hbox: ADDRESS; toEnd: BOOLEAN);
- VAR
- swindow, textview: ADDRESS;
- timeout: guint;
- BEGIN
- swindow := gtk_scrolled_window_new();
- gtk_widget_set_hexpand(swindow, 1);
- gtk_box_append(hbox, swindow);
- textview := gtk_text_view_new();
- gtk_scrolled_window_set_child(swindow, textview);
- timeout := SetupScroll(textview, toEnd);
- (* Remove the timeout when the view goes away, so it cannot scroll a
- destroyed widget. *)
- g_signal_connect_data(textview, "destroy", ADR(RemoveTimeout),
- CAST(ADDRESS, timeout), NIL, GConnectDefault)
- END CreateTextView;
- PROCEDURE DoTextScroll (doWidget: ADDRESS) : ADDRESS;
- VAR
- hbox: ADDRESS;
- BEGIN
- IF textScrollWindow = NIL THEN
- textScrollWindow := gtk_window_new();
- gtk_window_set_title(textScrollWindow, "Automatic Scrolling");
- gtk_window_set_default_size(textScrollWindow, 600, 400);
- hbox := gtk_box_new(GtkHorizontal, 6);
- gtk_window_set_child(textScrollWindow, hbox);
- CreateTextView(hbox, TRUE);
- CreateTextView(hbox, FALSE)
- END;
- IF gtk_widget_get_visible(textScrollWindow) = 0 THEN
- gtk_widget_set_visible(textScrollWindow, 1)
- ELSE
- gtk_window_destroy(textScrollWindow);
- textScrollWindow := NIL
- END;
- RETURN textScrollWindow
- END DoTextScroll;
- (* ================================================================== *)
- (* textundo *)
- (* ================================================================== *)
- PROCEDURE DoTextUndo (doWidget: ADDRESS) : ADDRESS;
- VAR
- view, sw, buffer: ADDRESS;
- iter: GtkTextIter;
- BEGIN
- IF textUndoWindow = NIL THEN
- textUndoWindow := gtk_window_new();
- gtk_window_set_default_size(textUndoWindow, 330, 330);
- gtk_window_set_resizable(textUndoWindow, 0);
- gtk_window_set_title(textUndoWindow, "Undo and Redo");
- view := gtk_text_view_new();
- gtk_text_view_set_wrap_mode(view, GtkWrapWord);
- gtk_text_view_set_left_margin(view, 20);
- gtk_text_view_set_right_margin(view, 20);
- gtk_text_view_set_top_margin(view, 20);
- gtk_text_view_set_bottom_margin(view, 20);
- buffer := gtk_text_view_get_buffer(view);
- (* gtk_text_buffer_set_enable_undo() is not bound; the buffer keeps
- its default undo support, so Ctrl+Z / Ctrl+Y still work. *)
- gtk_text_buffer_get_start_iter(buffer, iter);
- gtk_text_buffer_insert(buffer, iter, UndoText, -1);
- sw := gtk_scrolled_window_new();
- gtk_scrolled_window_set_policy(sw, GtkPolicyAutomatic,
- GtkPolicyAutomatic);
- gtk_window_set_child(textUndoWindow, sw);
- gtk_scrolled_window_set_child(sw, view)
- END;
- IF gtk_widget_get_visible(textUndoWindow) = 0 THEN
- gtk_widget_set_visible(textUndoWindow, 1)
- ELSE
- gtk_window_destroy(textUndoWindow);
- textUndoWindow := NIL
- END;
- RETURN textUndoWindow
- END DoTextUndo;
- BEGIN
- readMoreWindow := NIL;
- rotatedTextWindow := NIL;
- textMaskWindow := NIL;
- textScrollWindow := NIL;
- textUndoWindow := NIL;
- endCount := 0;
- bottomCount := 0
- END DemosText.
|