summaryrefslogtreecommitdiff
path: root/app/wlib/gtklib/text.c
diff options
context:
space:
mode:
Diffstat (limited to 'app/wlib/gtklib/text.c')
-rw-r--r--app/wlib/gtklib/text.c590
1 files changed, 590 insertions, 0 deletions
diff --git a/app/wlib/gtklib/text.c b/app/wlib/gtklib/text.c
new file mode 100644
index 0000000..5445984
--- /dev/null
+++ b/app/wlib/gtklib/text.c
@@ -0,0 +1,590 @@
+/** \file text.c
+ * multi line text entry widget
+ */
+
+/* XTrkCad - Model Railroad CAD
+ * Copyright (C) 2005 Dave Bullis
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+#define GTK_DISABLE_SINGLE_INCLUDES
+#define GDK_DISABLE_DEPRECATED
+#define GTK_DISABLE_DEPRECATED
+#define GSEAL_ENABLE
+
+#include <gtk/gtk.h>
+#include <gdk/gdk.h>
+
+#include "i18n.h"
+#include "gtkint.h"
+
+struct PrintData {
+ wText_p tb;
+ gint lines_per_page;
+ gdouble font_size;
+ gchar **lines;
+ gint total_lines;
+ gint total_pages;
+};
+
+#define HEADER_HEIGHT 20.0
+#define HEADER_GAP 8.5
+
+
+/*
+ *****************************************************************************
+ *
+ * Multi-line Text Boxes
+ *
+ *****************************************************************************
+ */
+
+struct wText_t {
+ WOBJ_COMMON
+ wPos_t width, height;
+ int changed;
+ GtkWidget *text;
+};
+
+/**
+ * Reset a text entry by clearing text and resetting the readonly and the
+ * change flag.
+ *
+ * \param bt IN text entry
+ * \return
+ */
+
+void wTextClear(wText_p bt)
+{
+ GtkTextBuffer *tb;
+ tb = gtk_text_view_get_buffer(GTK_TEXT_VIEW(bt->text));
+ gtk_text_buffer_set_text(tb, "", -1);
+
+ if (bt->option & BO_READONLY) {
+ gtk_text_view_set_editable(GTK_TEXT_VIEW(bt->text), FALSE);
+ }
+
+ bt->changed = FALSE;
+}
+
+/**
+ * Append text to the end of the entry field's text buffer.
+ *
+ * \param bt IN text buffer
+ * \param text IN text to append
+ * \return
+ */
+
+void wTextAppend(wText_p bt,
+ const char *text)
+{
+ GtkTextBuffer *tb;
+ GtkTextIter ti1;
+
+ if (bt->text == 0) {
+ abort();
+ }
+
+ tb = gtk_text_view_get_buffer(GTK_TEXT_VIEW(bt->text));
+ // convert to utf-8
+ text = wlibConvertInput(text);
+ // append to end of buffer
+ gtk_text_buffer_get_end_iter(tb, &ti1);
+ gtk_text_buffer_insert(tb, &ti1, text, -1);
+ bt->changed = FALSE;
+}
+
+/**
+ * Get the text from a text buffer in system codepage
+ * The caller is responsible for free'ing the allocated storage.
+ *
+ * \todo handling of return from gtkConvertOutput can be improved
+ *
+ * \param bt IN the text widget
+ * \return pointer to the converted text
+ */
+
+static char *wlibGetText(wText_p bt)
+{
+ GtkTextBuffer *tb;
+ GtkTextIter ti1, ti2;
+ char *cp, *cp1, *res;
+
+ if (bt->text == 0) {
+ abort();
+ }
+
+ tb = gtk_text_view_get_buffer(GTK_TEXT_VIEW(bt->text));
+ gtk_text_buffer_get_bounds(tb, &ti1, &ti2);
+ cp = gtk_text_buffer_get_text(tb, &ti1, &ti2, FALSE);
+ cp1 = wlibConvertOutput(cp);
+ res = strdup(cp1);
+ g_free(cp);
+ return res;
+}
+
+/**
+ * Save the text from the widget to a file
+ *
+ * \param bt IN the text widget
+ * \param fileName IN name of save file
+ * \return TRUE is success, FALSE if not
+ */
+
+wBool_t wTextSave(wText_p bt, const char *fileName)
+{
+ FILE *f;
+ char *cp;
+ f = fopen(fileName, "w");
+
+ if (f==NULL) {
+ wNoticeEx(NT_ERROR, fileName, "Ok", NULL);
+ return FALSE;
+ }
+
+ cp = wlibGetText(bt);
+ fwrite(cp, 1, strlen(cp), f);
+ free(cp);
+ fclose(f);
+ return TRUE;
+}
+
+/**
+ * Begin the printing by retrieving the contents of the text box and
+ * count the lines of text.
+ *
+ * \param operation IN the GTK print operation
+ * \param context IN print context
+ * \param pd IN data structure for user data
+ *
+ */
+
+static void
+begin_print(GtkPrintOperation *operation,
+ GtkPrintContext *context,
+ struct PrintData *pd)
+{
+ gchar *contents;
+ gdouble height;
+ contents = wlibGetText(pd->tb);
+ pd->lines = g_strsplit(contents, "\n", 0);
+ /* Count the total number of lines in the file. */
+ /* ignore the header lines */
+ pd->total_lines = 6;
+
+ while (pd->lines[pd->total_lines] != NULL) {
+ pd->total_lines++;
+ }
+
+ /* Based on the height of the page and font size, calculate how many lines can be
+ * rendered on a single page. A padding of 3 is placed between lines as well.
+ * Space for page header, table header and footer lines is subtracted from the total size
+ */
+ height = gtk_print_context_get_height(context) - (pd->font_size + 3) - 2 *
+ (HEADER_HEIGHT + HEADER_GAP);
+ pd->lines_per_page = floor(height / (pd->font_size + 3));
+ pd->total_pages = (pd->total_lines - 1) / pd->lines_per_page + 1;
+ gtk_print_operation_set_n_pages(operation, pd->total_pages);
+ free(contents);
+}
+
+/**
+ * Draw the page, which includes a header with the file name and page number along
+ * with one page of text with a font of "Monospace 10".
+ *
+ * \param operation IN the GTK print operation
+ * \param context IN print context
+ * \param page_nr IN page to print
+ * \param pd IN data structure for user data
+ *
+ *
+ */
+
+static void
+draw_page(GtkPrintOperation *operation,
+ GtkPrintContext *context,
+ gint page_nr,
+ struct PrintData *pd)
+{
+ cairo_t *cr;
+ PangoLayout *layout;
+ gdouble width, text_height, height;
+ gint line, i, text_width, layout_height;
+ PangoFontDescription *desc;
+ gchar *page_str;
+ cr = gtk_print_context_get_cairo_context(context);
+ width = gtk_print_context_get_width(context);
+ layout = gtk_print_context_create_pango_layout(context);
+ desc = pango_font_description_from_string("Monospace");
+ pango_font_description_set_size(desc, pd->font_size * PANGO_SCALE);
+ /*
+ * render the header line with document type parts list on left and
+ * first line of layout title on right
+ */
+ pango_layout_set_font_description(layout, desc);
+ pango_layout_set_text(layout, pd->lines[ 0 ], -1); // document type
+ pango_layout_set_width(layout, -1);
+ pango_layout_set_alignment(layout, PANGO_ALIGN_LEFT);
+ pango_layout_get_size(layout, NULL, &layout_height);
+ text_height = (gdouble) layout_height / PANGO_SCALE;
+ cairo_move_to(cr, 0, (HEADER_HEIGHT - text_height) / 2);
+ pango_cairo_show_layout(cr, layout);
+ pango_layout_set_text(layout, pd->lines[ 2 ], -1); // layout title
+ pango_layout_get_size(layout, &text_width, NULL);
+ pango_layout_set_alignment(layout, PANGO_ALIGN_RIGHT);
+ cairo_move_to(cr, width - (text_width / PANGO_SCALE),
+ (HEADER_HEIGHT - text_height) / 2);
+ pango_cairo_show_layout(cr, layout);
+ /* Render the column header */
+ cairo_move_to(cr, 0, HEADER_HEIGHT + HEADER_GAP + pd->font_size + 3);
+ pango_layout_set_text(layout, pd->lines[ 6 ], -1);
+ pango_cairo_show_layout(cr, layout);
+ cairo_rel_move_to(cr, 0, pd->font_size + 3);
+ pango_layout_set_text(layout, pd->lines[ 7 ], -1);
+ pango_cairo_show_layout(cr, layout);
+ /* Render the page text with the specified font and size. */
+ cairo_rel_move_to(cr, 0, pd->font_size + 3);
+ line = page_nr * pd->lines_per_page + 8;
+
+ for (i = 0; i < pd->lines_per_page && line < pd->total_lines; i++) {
+ pango_layout_set_text(layout, pd->lines[line], -1);
+ pango_cairo_show_layout(cr, layout);
+ cairo_rel_move_to(cr, 0, pd->font_size + 3);
+ line++;
+ }
+
+ /*
+ * Render the footer line with date on the left and page number
+ * on the right
+ */
+ pango_layout_set_text(layout, pd->lines[ 5 ], -1); // date
+ pango_layout_set_width(layout, -1);
+ pango_layout_set_alignment(layout, PANGO_ALIGN_LEFT);
+ pango_layout_get_size(layout, NULL, &layout_height);
+ text_height = (gdouble) layout_height / PANGO_SCALE;
+ height = gtk_print_context_get_height(context);
+ cairo_move_to(cr, 0, height - ((HEADER_HEIGHT - text_height) / 2));
+ pango_cairo_show_layout(cr, layout);
+ page_str = g_strdup_printf(_("%d of %d"), page_nr + 1,
+ pd->total_pages); // page number
+ pango_layout_set_text(layout, page_str, -1);
+ pango_layout_get_size(layout, &text_width, NULL);
+ pango_layout_set_alignment(layout, PANGO_ALIGN_RIGHT);
+ cairo_move_to(cr, width - (text_width / PANGO_SCALE),
+ height - ((HEADER_HEIGHT - text_height) / 2));
+ pango_cairo_show_layout(cr, layout);
+ g_free(page_str);
+ g_object_unref(layout);
+ pango_font_description_free(desc);
+}
+
+/**
+ * Clean up after the printing operation since it is done.
+ *
+ * \param operation IN the GTK print operation
+ * \param context IN print context
+ * \param pd IN data structure for user data
+ *
+ *
+ */
+static void
+end_print(GtkPrintOperation *operation,
+ GtkPrintContext *context,
+ struct PrintData *pd)
+{
+ g_strfreev(pd->lines);
+ free(pd);
+}
+
+/**
+ * Print the content of a multi line text box. This function is only used
+ * for printing the parts list. So it makes some assumptions on the structure
+ * and the content. Change if the multi line entry is changed.
+ * The deprecated gtk_text is not supported by this function.
+ *
+ * Thanks to Andrew Krause's book for a good starting point.
+ *
+ * \param bt IN the text field
+ * \return TRUE on success, FALSE on error
+ */
+
+wBool_t wTextPrint(
+ wText_p bt)
+{
+ GtkPrintOperation *operation;
+ GtkWidget *dialog;
+ GError *error = NULL;
+ gint res;
+ struct PrintData *data;
+ /* Create a new print operation, applying saved print settings if they exist. */
+ operation = gtk_print_operation_new();
+ WlibApplySettings(operation);
+ data = malloc(sizeof(struct PrintData));
+ data->font_size = 10.0;
+ data->tb = bt;
+ g_signal_connect(G_OBJECT(operation), "begin_print",
+ G_CALLBACK(begin_print), (gpointer) data);
+ g_signal_connect(G_OBJECT(operation), "draw_page",
+ G_CALLBACK(draw_page), (gpointer) data);
+ g_signal_connect(G_OBJECT(operation), "end_print",
+ G_CALLBACK(end_print), (gpointer) data);
+ /* Run the default print operation that will print the selected file. */
+ res = gtk_print_operation_run(operation,
+ GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG,
+ GTK_WINDOW(gtkMainW->gtkwin), &error);
+
+ /* If the print operation was accepted, save the new print settings. */
+ if (res == GTK_PRINT_OPERATION_RESULT_APPLY) {
+ WlibSaveSettings(operation);
+ }
+ /* Otherwise, report that the print operation has failed. */
+ else if (error) {
+ dialog = gtk_message_dialog_new(GTK_WINDOW(gtkMainW->gtkwin),
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,
+ error->message);
+ g_error_free(error);
+ gtk_dialog_run(GTK_DIALOG(dialog));
+ gtk_widget_destroy(dialog);
+ }
+
+ g_object_unref(operation);
+ return TRUE;
+}
+
+
+/**
+ * Get the length of text
+ *
+ * \param bt IN the text widget
+ * \return length of string
+ */
+
+int wTextGetSize(wText_p bt)
+{
+ char *cp = wlibGetText(bt);
+ int len = strlen(cp);
+ free(cp);
+ return len;
+}
+
+/**
+ * Get the text
+ *
+ * \param bt IN the text widget
+ * \param text IN the buffer
+ * \param len IN maximum number of bytes to return
+ * \return
+ */
+
+void wTextGetText(wText_p bt, char *text, int len)
+{
+ char *cp;
+ cp = wlibGetText(bt);
+ strncpy(text, cp, len);
+
+ if (len > 0) {
+ text[len - 1] = '\0';
+ }
+
+ free(cp);
+}
+
+/**
+ * Get the read-only state of the text entry
+ *
+ * \param bt IN the text widget
+ * \param ro IN read only flag
+ * \return
+ */
+
+void wTextSetReadonly(wText_p bt, wBool_t ro)
+{
+ gtk_text_view_set_editable(GTK_TEXT_VIEW(bt->text), !ro);
+
+ if (ro) {
+ bt->option |= BO_READONLY;
+ } else {
+ bt->option &= ~BO_READONLY;
+ }
+}
+
+/**
+ * check whether text has been modified
+ *
+ * \param bt IN the text widget
+ * \return TRUE of changed, FALSE otherwise
+ */
+
+wBool_t wTextGetModified(wText_p bt)
+{
+ return bt->changed;
+}
+
+/**
+ * set the size of the text widget
+ *
+ * \param bt IN the text widget
+ * \param w IN width
+ * \param h IN height
+ * \return
+ */
+
+void wTextSetSize(wText_p bt, wPos_t w, wPos_t h)
+{
+ gtk_widget_set_size_request(bt->widget, w, h);
+ bt->w = w;
+ bt->h = h;
+}
+
+/**
+ * calculate the required size of the widget based on number of lines and columns
+ * \todo this calculation is based on a fixed size font and is not useful in a generic setup
+ *
+ * \param bt IN the text widget
+ * \param rows IN text rows
+ * \param cols IN text columns
+ * \param width OUT width in pixel
+ * \param height OUT height in pixel
+ * \return
+ */
+
+void wTextComputeSize(wText_p bt, int rows, int cols, wPos_t *width,
+ wPos_t *height)
+{
+ *width = rows * 7;
+ *height = cols * 14;
+}
+
+/**
+ * set the position of the text widget ????
+ *
+ * \param bt IN the text widget
+ * \param pos IN position
+ * \return
+ */
+
+void wTextSetPosition(wText_p bt, int pos)
+{
+ /* TODO */
+}
+
+/**
+ * signal handler for changed signal
+ *
+ * \param widget IN
+ * \param bt IN text entry field
+ * \return
+ */
+
+static void textChanged(GtkWidget *widget, wText_p bt)
+{
+ if (bt == 0) {
+ return;
+ }
+
+ bt->changed = TRUE;
+}
+
+/**
+ * Create a multiline text entry field
+ *
+ * \param parent IN parent window
+ * \param x IN x position
+ * \param Y IN y position
+ * \param helpStr IN balloon help string
+ * \param labelStr IN Button label ???
+ * \param option IN
+ * \param width IN
+ * \param valueP IN Current color ???
+ * \param action IN Button callback procedure
+ * \param data IN ???
+ * \return bb handle for created text widget
+ */
+
+wText_p
+wTextCreate(wWin_p parent,
+ wPos_t x,
+ wPos_t y,
+ const char *helpStr,
+ const char *labelStr,
+ long option,
+ wPos_t width,
+ wPos_t height)
+{
+ wText_p bt;
+ GtkTextBuffer *tb;
+ // create the widget
+ bt = wlibAlloc(parent, B_MULTITEXT, x, y, labelStr, sizeof *bt, NULL);
+ bt->width = width;
+ bt->height = height;
+ bt->option = option;
+ wlibComputePos((wControl_p)bt);
+ // create a scroll window with scroll bars that are automatically created
+ bt->widget = gtk_scrolled_window_new(NULL, NULL);
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(bt->widget),
+ GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_AUTOMATIC);
+ // create a text view and place it inside the scroll widget
+ bt->text = gtk_text_view_new();
+
+ if (bt->text == 0) {
+ abort();
+ }
+
+ gtk_container_add(GTK_CONTAINER(bt->widget), bt->text);
+ // get the text buffer and add a bold tag to it
+ tb = gtk_text_view_get_buffer(GTK_TEXT_VIEW(bt->text));
+ gtk_text_buffer_create_tag(tb, "bold", "weight", PANGO_WEIGHT_BOLD, NULL);
+
+ // this seems to assume some fixed size fonts, not really helpful
+ if (option&BT_CHARUNITS) {
+ width *= 7;
+ height *= 14;
+ }
+
+ // show the widgets
+ gtk_widget_show(bt->text);
+ gtk_widget_show(bt->widget);
+ // set the size???
+ gtk_widget_set_size_request(GTK_WIDGET(bt->widget),
+ width+15/*requisition.width*/, height);
+
+ // configure read-only mode
+ if (bt->option&BO_READONLY) {
+ gtk_text_view_set_editable(GTK_TEXT_VIEW(bt->text), FALSE);
+ gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(bt->text), FALSE);
+ }
+
+ if (labelStr) {
+ bt->labelW = wlibAddLabel((wControl_p)bt, labelStr);
+ }
+
+ wlibAddHelpString(bt->widget, helpStr);
+ gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(bt->text), GTK_WRAP_WORD);
+ g_signal_connect(G_OBJECT(tb), "changed", G_CALLBACK(textChanged), bt);
+ // place the widget in a fixed position of the parent
+ gtk_fixed_put(GTK_FIXED(parent->widget), bt->widget, bt->realX, bt->realY);
+ wlibControlGetSize((wControl_p)bt);
+ wlibAddButton((wControl_p)bt);
+ // done, return the finished widget
+ return bt;
+}