From 916e70fb90cb972726e8e580ff8dabf5e3da7aa1 Mon Sep 17 00:00:00 2001 From: mdejong Date: Sun, 17 Dec 2006 00:43:05 +0000 Subject: * doc/cursors.n: Mention "none" in supported cursor list. Fix comment that incorrectly claims that the Win32 "no" cursor hides the cursor. * tests/cursor.test: Test "none" cursor. * unix/tkUnixCursor.c (CreateCursorFromTableOrFile, TkGetCursorByName): Define a table of Tk cursors that is searched in addition to the X cursor table. A Tk cursor is loaded from a data string and works with the same options as the built in X cursors. This code makes it possible to use "none" as a cursor name under Unix. * win/rc/cursor9a.cur: Added none Win32 cursor. * win/rc/tk_base.rc: Define a built-in Win32 cursor named "none". [Patch 1615427] --- ChangeLog | 19 +++ doc/cursors.n | 8 +- tests/cursor.test | 57 +++++++- unix/tkUnixCursor.c | 387 +++++++++++++++++++++++++++++++++++++++++++--------- win/rc/cursor9a.cur | Bin 0 -> 326 bytes win/rc/tk_base.rc | 4 +- 6 files changed, 403 insertions(+), 72 deletions(-) create mode 100644 win/rc/cursor9a.cur diff --git a/ChangeLog b/ChangeLog index 9de8803..38923b7 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,22 @@ +2006-12-16 Mo DeJong + + * doc/cursors.n: Mention "none" in supported + cursor list. Fix comment that incorrectly + claims that the Win32 "no" cursor hides + the cursor. + * tests/cursor.test: Test "none" cursor. + * unix/tkUnixCursor.c (CreateCursorFromTableOrFile, + TkGetCursorByName): Define a table of Tk cursors + that is searched in addition to the X cursor + table. A Tk cursor is loaded from a data string + and works with the same options as the built + in X cursors. This code makes it possible to + use "none" as a cursor name under Unix. + * win/rc/cursor9a.cur: Added none Win32 cursor. + * win/rc/tk_base.rc: Define a built-in Win32 + cursor named "none". + [Patch 1615427] + 2006-12-14 Joe English * generic/ttk/ttkButton.c, generic/ttk/ttkElements.c, diff --git a/doc/cursors.n b/doc/cursors.n index 0855f5f..fba41bb 100644 --- a/doc/cursors.n +++ b/doc/cursors.n @@ -2,7 +2,7 @@ '\" Copyright (c) 1998-2000 by Scriptics Corporation. '\" All rights reserved. '\" -'\" RCS: @(#) $Id: cursors.n,v 1.3 2002/11/15 15:35:55 dkf Exp $ +'\" RCS: @(#) $Id: cursors.n,v 1.4 2006/12/17 00:43:06 mdejong Exp $ '\" '\" .so man.macros @@ -62,6 +62,7 @@ lr_angle man middlebutton mouse +none pencil pirate plus @@ -96,6 +97,8 @@ umbrella ur_angle watch xterm + +The \fBnone\fR cursor can be specified to eliminate the cursor. .CE .SH "PORTABILITY ISSUES" @@ -111,6 +114,7 @@ crosshair fleur ibeam icon +none sb_h_double_arrow sb_v_double_arrow watch @@ -128,7 +132,6 @@ size_we uparrow wait .CE -The \fBno\fR cursor can be specified to eliminate the cursor. .RE .TP @@ -140,6 +143,7 @@ arrow cross crosshair ibeam +none plus watch xterm diff --git a/tests/cursor.test b/tests/cursor.test index ebe58e1..d5dabcf 100644 --- a/tests/cursor.test +++ b/tests/cursor.test @@ -6,7 +6,7 @@ # Copyright (c) 1998-1999 by Scriptics Corporation. # All rights reserved. # -# RCS: @(#) $Id: cursor.test,v 1.15 2004/12/07 10:07:59 dkf Exp $ +# RCS: @(#) $Id: cursor.test,v 1.16 2006/12/17 00:43:06 mdejong Exp $ package require tcltest 2.1 eval tcltest::configure $argv @@ -228,6 +228,61 @@ foreach {testName cursor} { } -result {} } +# Test cursor named "none", it is not defined in +# the X cursor table. It is defined in a Tk specific +# table of named cursors and should be available on +# all platforms. + +test cursor-6.80 {} -setup { + button .b -text CButton +} -body { + .b configure -cursor none + .b cget -cursor +} -cleanup { + destroy .b +} -result none + +test cursor-6.81 {} -setup { + button .b -text CButton +} -body { + .b configure -cursor none + .b configure -cursor {} + .b cget -cursor +} -cleanup { + destroy .b +} -result {} + +test cursor-6.82 {} -setup { + button .b -text CButton +} -body { + .b configure -cursor none + .b configure -cursor {} + .b configure -cursor none + .b cget -cursor +} -cleanup { + destroy .b +} -result none + +test cursor-6.83 {} -setup { + button .b -text CButton +} -body { + # Setting fg and bg does nothing for the none cursor + # because it displays no fg or bg pixels. + set results [list] + .b configure -cursor none + lappend results [.b cget -cursor] + .b configure -cursor {none blue} + lappend results [.b cget -cursor] + .b configure -cursor {none blue green} + lappend results [.b cget -cursor] + .b configure -cursor {} + lappend results [.b cget -cursor] + set results +} -cleanup { + destroy .b + unset results +} -result {none {none blue} {none blue green} {}} + # ------------------------------------------------------------------------- # Check the Windows specific cursors diff --git a/unix/tkUnixCursor.c b/unix/tkUnixCursor.c index 858025c..e3873a7 100644 --- a/unix/tkUnixCursor.c +++ b/unix/tkUnixCursor.c @@ -8,7 +8,7 @@ * See the file "license.terms" for information on usage and redistribution of * this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tkUnixCursor.c,v 1.7 2005/10/21 01:51:45 dkf Exp $ + * RCS: @(#) $Id: tkUnixCursor.c,v 1.8 2006/12/17 00:43:06 mdejong Exp $ */ #include "tkPort.h" @@ -116,12 +116,89 @@ static struct CursorName { }; /* + * The table below is used to map from a cursor name to the data + * that defines the cursor. This table is used for cursors defined + * by Tk that don't exist in the X cursor table. + */ + +#define CURSOR_NONE_DATA \ +"#define none_width 1 + #define none_height 1 + #define none_x_hot 0 + #define none_y_hot 0 + static unsigned char none_bits[] = { + 0x00};" + + +/* Define test cursor to check that mask + * fg and bg color settings are working. + * + * . configure -cursor {center_ptr green red} + * + * . configure -cursor {@myarrow.xbm myarrow-mask.xbm green red} + * + * . configure -cursor {myarrow green red} + */ + +/*#define DEFINE_MYARROW_CURSOR*/ + + +#ifdef DEFINE_MYARROW_CURSOR + +#define CURSOR_MYARROW_DATA \ +"#define myarrow_width 16 +#define myarrow_height 16 +#define myarrow_x_hot 7 +#define myarrow_y_hot 0 +static unsigned char myarrow_bits[] = { + 0x7f, 0xff, 0xbf, 0xfe, 0xdf, 0xfd, 0xef, 0xfb, 0xf7, 0xf7, 0xfb, 0xef, + 0xfd, 0xdf, 0xfe, 0xbf, 0x80, 0x00, 0xbf, 0xfe, 0xbf, 0xfe, 0xbf, 0xfe, + 0xbf, 0xfe, 0xbf, 0xfe, 0xbf, 0xfe, 0x3f, 0xfe};" + +#define CURSOR_MYARROW_MASK \ +"#define myarrow-mask_width 16 +#define myarrow-mask_height 16 +#define myarrow-mask_x_hot 7 +#define myarrow-mask_y_hot 0 +static unsigned char myarrow-mask_bits[] = { + 0x80, 0x00, 0xc0, 0x01, 0xe0, 0x03, 0xf0, 0x07, 0xf8, 0x0f, 0xfc, 0x1f, + 0xfe, 0x3f, 0xff, 0x7f, 0xff, 0xff, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, + 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01};" + +#endif /* DEFINE_MYARROW_CURSOR */ + + +static struct TkCursorName { + char *name; + char *data; + char *mask; +} tkCursorNames[] = { + + {"none", CURSOR_NONE_DATA, NULL}, + +#ifdef DEFINE_MYARROW_CURSOR + {"myarrow", CURSOR_MYARROW_DATA, CURSOR_MYARROW_MASK}, +#endif /* DEFINE_MYARROW_CURSOR */ + + {NULL, NULL, NULL} +}; + +/* * Font to use for cursors: */ #ifndef CURSORFONT #define CURSORFONT "cursor" #endif + +MODULE_SCOPE Cursor CreateCursorFromTableOrFile( + Tcl_Interp *interp, + Tk_Window tkwin, + int argc, + CONST char **argv, + struct TkCursorName *tkCursorPtr); + + /* *---------------------------------------------------------------------- @@ -152,9 +229,9 @@ TkGetCursorByName( Cursor cursor = None; int argc; CONST char **argv = NULL; - Pixmap source = None; - Pixmap mask = None; Display *display = Tk_Display(tkwin); + int inTkTable = 0; + struct TkCursorName* tkCursorPtr = NULL; if (Tcl_SplitList(interp, string, &argc, &argv) != TCL_OK) { return NULL; @@ -162,7 +239,28 @@ TkGetCursorByName( if (argc == 0) { goto badString; } + + /* + * Check Tk specific table of cursor names. The + * cursor names don't overlap with cursors defined + * in the X table so search order does not matter. + */ + if (argv[0][0] != '@') { + for ( tkCursorPtr = tkCursorNames; ; tkCursorPtr++) { + if (tkCursorPtr->name == NULL) { + tkCursorPtr = NULL; + break; + } + if ((tkCursorPtr->name[0] == argv[0][0]) && + (strcmp(tkCursorPtr->name, argv[0]) == 0)) { + inTkTable = 1; + break; + } + } + } + + if ((argv[0][0] != '@') && !inTkTable) { XColor fg, bg; unsigned int maskIndex; register struct CursorName *namePtr; @@ -188,6 +286,7 @@ TkGetCursorByName( break; } } + maskIndex = namePtr->shape + 1; if (argc == 1) { fg.red = fg.green = fg.blue = 0; @@ -220,15 +319,11 @@ TkGetCursorByName( dispPtr->cursorFont, namePtr->shape, maskIndex, &fg, &bg); } else { - int width, height, maskWidth, maskHeight; - int xHot, yHot, dummy1, dummy2; - XColor fg, bg; - /* * Prevent file system access in safe interpreters. */ - if (Tcl_IsSafe(interp)) { + if (!inTkTable && Tcl_IsSafe(interp)) { Tcl_AppendResult(interp, "can't get cursor from a file in", " a safe interpreter", NULL); cursorPtr = NULL; @@ -236,65 +331,22 @@ TkGetCursorByName( } /* - * The cursor is to be created by reading bitmap files. There should - * be either two elements in the list (source, color) or four (source - * mask fg bg). + * If the cursor is to be created from bitmap files, then there + * should be either two elements in the list (source, color) + * or four (source mask fg bg). A cursor defined in the Tk + * table accepts the same arguments as an X cursor. */ - if ((argc != 2) && (argc != 4)) { + if (inTkTable && (argc != 1) && (argc != 2) && (argc != 3)) { goto badString; } - if (TkReadBitmapFile(display, - RootWindowOfScreen(Tk_Screen(tkwin)), &argv[0][1], - (unsigned int *) &width, (unsigned int *) &height, - &source, &xHot, &yHot) != BitmapSuccess) { - Tcl_AppendResult(interp, "cleanup reading bitmap file \"", - &argv[0][1], "\"", NULL); - goto cleanup; - } - if ((xHot < 0) || (yHot < 0) || (xHot >= width) || (yHot >= height)) { - Tcl_AppendResult(interp, "bad hot spot in bitmap file \"", - &argv[0][1], "\"", NULL); - goto cleanup; - } - if (argc == 2) { - if (XParseColor(display, Tk_Colormap(tkwin), argv[1], &fg) == 0) { - Tcl_AppendResult(interp, "invalid color name \"", - argv[1], "\"", NULL); - goto cleanup; - } - cursor = XCreatePixmapCursor(display, source, source, - &fg, &fg, (unsigned) xHot, (unsigned) yHot); - } else { - if (TkReadBitmapFile(display, - RootWindowOfScreen(Tk_Screen(tkwin)), argv[1], - (unsigned int *) &maskWidth, (unsigned int *) &maskHeight, - &mask, &dummy1, &dummy2) != BitmapSuccess) { - Tcl_AppendResult(interp, "cleanup reading bitmap file \"", - argv[1], "\"", NULL); - goto cleanup; - } - if ((maskWidth != width) && (maskHeight != height)) { - Tcl_SetResult(interp, - "source and mask bitmaps have different sizes", - TCL_STATIC); - goto cleanup; - } - if (XParseColor(display, Tk_Colormap(tkwin), argv[2], - &fg) == 0) { - Tcl_AppendResult(interp, "invalid color name \"", argv[2], - "\"", NULL); - goto cleanup; - } - if (XParseColor(display, Tk_Colormap(tkwin), argv[3], - &bg) == 0) { - Tcl_AppendResult(interp, "invalid color name \"", argv[3], - "\"", NULL); - goto cleanup; - } - cursor = XCreatePixmapCursor(display, source, mask, - &fg, &bg, (unsigned) xHot, (unsigned) yHot); + + if (!inTkTable && (argc != 2) && (argc != 4)) { + goto badString; } + + cursor = CreateCursorFromTableOrFile(interp, tkwin, + argc, argv, tkCursorPtr); } if (cursor != None) { @@ -307,12 +359,6 @@ TkGetCursorByName( if (argv != NULL) { ckfree((char *) argv); } - if (source != None) { - Tk_FreePixmap(display, source); - } - if (mask != None) { - Tk_FreePixmap(display, mask); - } return (TkCursor *) cursorPtr; badString: @@ -322,6 +368,211 @@ TkGetCursorByName( Tcl_AppendResult(interp, "bad cursor spec \"", string, "\"", NULL); return NULL; } + +/* + *---------------------------------------------------------------------- + * + * CreateCursorFromTableOrFile -- + * + * Create a cursor defined in a file or the Tk static cursor table. + * A cursor defined in a file starts with the '@' character. This + * method assumes that the number of arguments in argv has been + * validated already. + * + * Results: + * Returns a new cursor, or None on error. + * + * Side effects: + * Allocates a new X cursor. + * + *---------------------------------------------------------------------- + */ + +Cursor +CreateCursorFromTableOrFile( + Tcl_Interp *interp, /* Interpreter to use for error reporting. */ + Tk_Window tkwin, /* Window in which cursor will be used. */ + int argc, + CONST char **argv, /* Cursor spec parsed into elements. */ + struct TkCursorName *tkCursorPtr) /* Non-NULL when cursor is defined in Tk table */ +{ + Cursor cursor = None; + + int width, height, maskWidth, maskHeight; + int xHot = -1, yHot = -1; + int dummy1, dummy2; + XColor fg, bg; + CONST char *fgColor; + CONST char *bgColor; + int inTkTable = (tkCursorPtr != NULL); + + Display *display = Tk_Display(tkwin); + Drawable drawable = RootWindowOfScreen(Tk_Screen(tkwin)); + + Pixmap source = None; + Pixmap mask = None; + + /* + * A cursor defined in a file accepts either 2 or 4 arguments. + * + * {srcfile fg} + * {srcfile maskfile fg bg} + * + * A cursor defined in the Tk table accepts 1, 2, or 3 arguments. + * + * {tkcursorname} + * {tkcursorname fg} + * {tkcursorname fg bg} + */ + + if (inTkTable) { + /* This logic is like TkReadBitmapFile() */ + char *data; + + data = TkGetBitmapData( + /* interp */ NULL, + /* string */ tkCursorPtr->data, + /* filename */ NULL, + &width, &height, + &xHot, &yHot); + if (data == NULL) { + Tcl_AppendResult(interp, "error reading bitmap data for \"", + argv[0], "\"", NULL); + goto cleanup; + } + + source = XCreateBitmapFromData(display, drawable, + data, width, height); + + ckfree(data); + } else { + if (TkReadBitmapFile(display, drawable, + &argv[0][1], + (unsigned int *) &width, (unsigned int *) &height, + &source, &xHot, &yHot) != BitmapSuccess) { + Tcl_AppendResult(interp, "cleanup reading bitmap file \"", + &argv[0][1], "\"", NULL); + goto cleanup; + } + } + + if ((xHot < 0) || (yHot < 0) || (xHot >= width) || (yHot >= height)) { + if (inTkTable) { + Tcl_AppendResult(interp, "bad hot spot in bitmap data for \"", + argv[0], "\"", NULL); + } else { + Tcl_AppendResult(interp, "bad hot spot in bitmap file \"", + &argv[0][1], "\"", NULL); + } + goto cleanup; + } + + /* + * Parse color names from optional fg and bg arguments + */ + + if (argc == 1) { + fg.red = fg.green = fg.blue = 0; + bg.red = bg.green = bg.blue = 65535; + } else if (argc == 2) { + fgColor = argv[1]; + if (XParseColor(display, Tk_Colormap(tkwin), fgColor, &fg) == 0) { + Tcl_AppendResult(interp, "invalid color name \"", + fgColor, "\"", NULL); + goto cleanup; + } + if (inTkTable) { + bg.red = bg.green = bg.blue = 0; + } else { + bg = fg; + } + } else { + /* 3 or 4 arguments */ + if (inTkTable) { + fgColor = argv[1]; + bgColor = argv[2]; + } else { + fgColor = argv[2]; + bgColor = argv[3]; + } + if (XParseColor(display, Tk_Colormap(tkwin), fgColor, &fg) == 0) { + Tcl_AppendResult(interp, "invalid color name \"", + fgColor, "\"", NULL); + goto cleanup; + } + if (XParseColor(display, Tk_Colormap(tkwin), bgColor, &bg) == 0) { + Tcl_AppendResult(interp, "invalid color name \"", + bgColor, "\"", NULL); + goto cleanup; + } + } + + /* + * If there is no mask data, then create the cursor now. + */ + + if ((!inTkTable && (argc == 2)) || (tkCursorPtr->mask == NULL)) { + cursor = XCreatePixmapCursor(display, source, source, + &fg, &fg, (unsigned) xHot, (unsigned) yHot); + goto cleanup; + } + + /* + * Parse bitmap mask data and create cursor with fg and bg colors. + */ + + if (inTkTable) { + /* This logic is like TkReadBitmapFile() */ + char *data; + + data = TkGetBitmapData( + /* interp */ NULL, + /* string */ tkCursorPtr->mask, + /* filename */ NULL, + &maskWidth, &maskHeight, + &dummy1, &dummy2); + if (data == NULL) { + Tcl_AppendResult(interp, "error reading bitmap mask data for \"", + argv[0], "\"", NULL); + goto cleanup; + } + + mask = XCreateBitmapFromData(display, drawable, + data, maskWidth, maskHeight); + + ckfree(data); + } else { + if (TkReadBitmapFile(display, + drawable, + argv[1], + (unsigned int *) &maskWidth, (unsigned int *) &maskHeight, + &mask, + &dummy1, &dummy2) != BitmapSuccess) { + Tcl_AppendResult(interp, "cleanup reading bitmap file \"", + argv[1], "\"", NULL); + goto cleanup; + } + } + + if ((maskWidth != width) && (maskHeight != height)) { + Tcl_SetResult(interp, + "source and mask bitmaps have different sizes", + TCL_STATIC); + goto cleanup; + } + + cursor = XCreatePixmapCursor(display, source, mask, + &fg, &bg, (unsigned) xHot, (unsigned) yHot); + + cleanup: + if (source != None) { + Tk_FreePixmap(display, source); + } + if (mask != None) { + Tk_FreePixmap(display, mask); + } + return cursor; +} /* *---------------------------------------------------------------------- diff --git a/win/rc/cursor9a.cur b/win/rc/cursor9a.cur new file mode 100644 index 0000000..048f06b Binary files /dev/null and b/win/rc/cursor9a.cur differ diff --git a/win/rc/tk_base.rc b/win/rc/tk_base.rc index 456a5aa..1f5fe28 100644 --- a/win/rc/tk_base.rc +++ b/win/rc/tk_base.rc @@ -1,4 +1,4 @@ -// RCS: @(#) $Id: tk_base.rc,v 1.4 2006/12/12 23:38:22 mdejong Exp $ +// RCS: @(#) $Id: tk_base.rc,v 1.5 2006/12/17 00:43:06 mdejong Exp $ // // Base resources needed by Tk whether it's a DLL or a static library. // @@ -137,3 +137,5 @@ umbrella CURSOR DISCARDABLE "cursor92.cur" ur_angle CURSOR DISCARDABLE "cursor94.cur" watch CURSOR DISCARDABLE "cursor96.cur" xterm CURSOR DISCARDABLE "cursor98.cur" +none CURSOR DISCARDABLE "cursor9a.cur" + -- cgit v0.12