/* text widget for Pd Copyright 2003 Guenter Geiger Copyright 2004 Ben Bogart Copyright 2007 Hans-Christoph Steiner 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include "tkwidgets.h" /* TODO: append options messages to options_binbuf if not visible */ /* TODO: window name "handle1376fc00" already exists in parent */ /* TODO: figure out window vs. text width/height */ /* TODO: add x scrollbar */ /* TODO: make "insert" function based on the text widget "insert" */ /* TODO: make [key( support chars > 127 */ #define TEXT_DEFAULT_COLOR "grey90" #define TEXT_DEFAULT_WIDTH 200 #define TEXT_DEFAULT_HEIGHT 60 #define TEXT_MIN_WIDTH 40 #define TEXT_MIN_HEIGHT 20 #define DEBUG(x) //#define DEBUG(x) x static t_class *textwidget_class; static t_widgetbehavior textwidget_widgetbehavior; typedef struct _textwidget { t_object x_obj; t_canvas* x_canvas; /* canvas this widget is currently drawn in */ t_glist* x_glist; /* glist that owns this widget */ t_binbuf* options_binbuf;/* binbuf to save options state in */ t_binbuf* text_binbuf; /* store text on copy/paste and [loadbang] set*/ int width; int height; int have_scrollbars; int x_resizing; int x_selected; /* IDs for Tk widgets */ t_symbol* tcl_namespace; t_symbol* receive_name; /* name to bind to to receive callbacks */ t_symbol* canvas_id; t_symbol* frame_id; t_symbol* widget_id; t_symbol* scrollbar_id; t_symbol* handle_id; t_symbol* window_tag; t_symbol* iolets_tag; t_symbol* all_tag; t_outlet* x_data_outlet; t_outlet* x_status_outlet; } t_textwidget; static char *textwidget_tk_options[] = { "autoseparators", "background", "borderwidth", "cursor", "exportselection", "font", "foreground", "height", "highlightbackground", "highlightcolor", "highlightthickness", "insertbackground", "insertborderwidth", "insertofftime", "insertontime", "insertwidth", "maxundo", "padx", "pady", "relief", "selectbackground", "selectborderwidth", "selectforeground", "setgrid", "spacing1", "spacing2", "spacing3", "state", "tabs", "takefocus", "undo", "width", "wrap", // "xscrollcommand", /* problematic since it uses the canvas_id, etc. */ // "yscrollcommand", /* problematic since it uses the canvas_id, etc. */ }; /* common symbols to preload */ static t_symbol *backspace_symbol; static t_symbol *down_symbol; static t_symbol *escape_symbol; static t_symbol *id_symbol; static t_symbol *left_symbol; static t_symbol *query_callback_symbol; static t_symbol *return_symbol; static t_symbol *right_symbol; static t_symbol *scrollbars_symbol; static t_symbol *size_symbol; static t_symbol *space_symbol; static t_symbol *tab_symbol; static t_symbol *up_symbol; /* -------------------- function prototypes --------------------------------- */ static void textwidget_query_callback(t_textwidget *x, t_symbol *s, int argc, t_atom *argv); /* -------------------- widget helper functions ----------------------------- */ static void query_id(t_textwidget *x) { t_atom id[2]; t_symbol *widget_id = x->widget_id; SETSYMBOL(id, id_symbol); SETSYMBOL(id + 1, widget_id); textwidget_query_callback(x, query_callback_symbol, 2, id); } static void query_scrollbars(t_textwidget *x) { t_atom state[2]; SETSYMBOL(state, scrollbars_symbol); SETFLOAT(state + 1, (t_float)x->have_scrollbars); textwidget_query_callback(x, query_callback_symbol, 2, state); } static void query_size(t_textwidget *x) { t_atom coords[3]; SETSYMBOL(coords, size_symbol); SETFLOAT(coords + 1, (t_float)x->width); SETFLOAT(coords + 2, (t_float)x->height); textwidget_query_callback(x, query_callback_symbol, 3, coords); } static void set_tkwidgets_ids(t_textwidget *x, t_canvas *canvas) { x->x_canvas = canvas; x->canvas_id = tkwidgets_gen_canvas_id(x->x_canvas); x->frame_id = tkwidgets_gen_frame_id((t_object*)x, x->canvas_id); x->widget_id = tkwidgets_gen_widget_id((t_object*)x, x->frame_id); x->scrollbar_id = tkwidgets_gen_scrollbar_id((t_object*)x, x->frame_id); x->window_tag = tkwidgets_gen_window_tag((t_object*)x, x->frame_id); x->handle_id = tkwidgets_gen_handle_id((t_object *)x, x->canvas_id); } static void create_widget(t_textwidget *x) { DEBUG(post("create_widget");); sys_vgui("namespace eval %s {} \n", x->tcl_namespace->s_name); /* Seems we have to delete the widget in case it already exists (Provided by Guenter)*/ sys_vgui("destroy %s\n", x->frame_id->s_name); sys_vgui("frame %s \n", x->frame_id->s_name); sys_vgui("text %s -bd 1 -highlightbackground grey70 -highlightthickness 1 -bg %s\n", x->widget_id->s_name, TEXT_DEFAULT_COLOR); sys_vgui("pack %s -side left -fill both -expand 1 \n", x->widget_id->s_name); sys_vgui("pack %s -side bottom -fill both -expand 1 \n", x->frame_id->s_name); tkwidgets_bind_key_events(x->canvas_id, x->widget_id); tkwidgets_bind_mouse_events(x->canvas_id, x->widget_id); /* bind to KeyRelease events to send out right outlet one key at a time */ sys_vgui("bind %s {+pd %s keyup %%N \\;} \n", x->widget_id->s_name, x->receive_name->s_name); /* override the standard Pd bindings for these since they cause trouble */ #ifdef __APPLE__ sys_vgui("bind %s {%s tag add sel 1.0 end} \n", x->widget_id->s_name, x->widget_id->s_name); sys_vgui("bind %s {tk_textPaste %s} \n", x->widget_id->s_name, x->widget_id->s_name); #else sys_vgui("bind %s {%s tag add sel 1.0 end} \n", x->widget_id->s_name, x->widget_id->s_name); sys_vgui("bind %s {tk_textPaste %s} \n", x->widget_id->s_name, x->widget_id->s_name); #endif } static void drawme(t_textwidget *x, t_glist *glist) { char *buf; int bufsize; DEBUG(post("drawme: firsttime %d canvas %lx glist %lx", x->x_canvas, glist);); set_tkwidgets_ids(x,glist_getcanvas(glist)); create_widget(x); if(x->x_glist == x->x_canvas) // if GOP, don't draw inlets tkwidgets_draw_iolets((t_object*)x, glist, x->canvas_id, x->iolets_tag, x->all_tag, x->width, x->height); if(x->have_scrollbars) tkwidgets_draw_y_scrollbar(x->widget_id, x->scrollbar_id); sys_vgui("%s create window %d %d -anchor nw -window %s \ -tags {%s %s} -width %d -height %d \n", x->canvas_id->s_name, text_xpix(&x->x_obj, glist), text_ypix(&x->x_obj, glist), x->frame_id->s_name, x->window_tag->s_name, x->all_tag->s_name, x->width, x->height); tkwidgets_restore_options(x->widget_id, x->options_binbuf); binbuf_gettext(x->text_binbuf, &buf, &bufsize); buf[bufsize] = 0; // binbuf_gettext() doesn't terminate the string post("%s insert end {%s}\n", x->widget_id->s_name, buf); sys_vgui("%s insert end {%s}\n", x->widget_id->s_name, buf); } static void eraseme(t_textwidget* x) { DEBUG(post("eraseme: canvas %lx", x->x_canvas);); sys_vgui("destroy %s\n", x->frame_id->s_name); sys_vgui("%s delete %s\n", x->canvas_id->s_name, x->all_tag->s_name); } /* ------------------------ text widgetbehaviour----------------------------- */ static void textwidget_getrect(t_gobj *z, t_glist *owner, int *xp1, int *yp1, int *xp2, int *yp2) { // DEBUG(post("textwidget_getrect");); /* this one is very chatty :D */ t_textwidget *x = (t_textwidget*)z; *xp1 = text_xpix(&x->x_obj, owner); *yp1 = text_ypix(&x->x_obj, owner); *xp2 = *xp1 + x->width; *yp2 = *yp1 + x->height + 2; // add 2 to give space for outlets } static void textwidget_displace(t_gobj *z, t_glist *glist, int dx, int dy) { t_textwidget *x = (t_textwidget *)z; DEBUG(post("textwidget_displace: canvas %lx glist %lx", x->x_canvas, glist);); x->x_obj.te_xpix += dx; x->x_obj.te_ypix += dy; if (glist_isvisible(glist)) { // set_tkwidgets_ids(x,glist_getcanvas(glist)); /* TODO is this needed here? */ sys_vgui("%s move %s %d %d\n", x->canvas_id->s_name, x->all_tag->s_name, dx, dy); sys_vgui("%s move RESIZE %d %d\n", x->canvas_id->s_name, dx, dy); canvas_fixlinesfor(glist_getcanvas(glist), (t_text*) x); } DEBUG(post("displace end");); } static void textwidget_select(t_gobj *z, t_glist *glist, int state) { t_textwidget *x = (t_textwidget *)z; DEBUG(post("textwidget_select: canvas %lx glist %lx state %d", x->x_canvas, glist, state);); if( (state) && (!x->x_selected)) { sys_vgui("set ::%s::bg [%s cget -bg]\n", x->tcl_namespace->s_name, x->widget_id->s_name); sys_vgui("%s configure -bg %s -state disabled -cursor $cursor_editmode_nothing\n", x->widget_id->s_name, TKW_SELECTION_COLOR); x->x_selected = 1; } else if (!state) { sys_vgui("%s configure -bg $::%s::bg -state normal -cursor xterm\n", x->widget_id->s_name, x->tcl_namespace->s_name); /* activatefn never gets called with 0, so destroy handle here */ sys_vgui("destroy %s\n", x->handle_id->s_name); x->x_selected = 0; } } static void textwidget_activate(t_gobj *z, t_glist *glist, int state) { DEBUG(post("textwidget_activate");); t_textwidget *x = (t_textwidget *)z; int x1, y1, x2, y2; if(state) { textwidget_getrect(z, glist, &x1, &y1, &x2, &y2); sys_vgui("canvas %s -width %d -height %d -bg #ddd -bd 0 \ -highlightthickness 3 -highlightcolor {#f00} -cursor bottom_right_corner\n", x->handle_id->s_name, TKW_HANDLE_WIDTH, TKW_HANDLE_HEIGHT); int handle_x1 = x2 - TKW_HANDLE_WIDTH; int handle_y1 = y2 - (TKW_HANDLE_HEIGHT - TKW_HANDLE_INSET); // int handle_x2 = x2; // int handle_y2 = y2 - TKW_HANDLE_INSET; /* no worky, this should draw MAC OS X style lines on the resize handle */ /* sys_vgui("%s create line %d %d %d %d -fill black -tags RESIZE_LINES\n", */ /* x->handle_id->s_name, handle_x2, handle_y1, handle_x1, handle_y2); */ /* TODO split out the handle and the handle binding into common functions */ sys_vgui("%s create window %d %d -anchor nw -width %d -height %d -window %s -tags RESIZE\n", x->canvas_id->s_name, handle_x1, handle_y1, TKW_HANDLE_WIDTH, TKW_HANDLE_HEIGHT, x->handle_id->s_name, x->all_tag->s_name); sys_vgui("raise %s\n", x->handle_id->s_name); sys_vgui("bind %s