Index: generic/ttk/ttkManager.c ================================================================== --- generic/ttk/ttkManager.c +++ generic/ttk/ttkManager.c @@ -432,30 +432,39 @@ if (mgr->content[index]->window == window) return index; return -1; } -/* ++ Ttk_GetContentIndexFromObj(interp, mgr, objPtr, indexPtr) -- +/* ++ Ttk_GetContentIndexFromObj(interp, mgr, objPtr, lastOK, indexPtr) -- * Return the index of the content window specified by objPtr. * Content windows may be specified as an integer index or * as the name of the managed window. + * + * The parameter lastOK should be non-0 if the resolved index can be equal to + * the current size (i.e. one more than the current highest index) and 0 + * otherwise. * * Returns: * Standard Tcl completion code. Leaves an error message in case of error. */ int Ttk_GetContentIndexFromObj( - Tcl_Interp *interp, Ttk_Manager *mgr, Tcl_Obj *objPtr, Tcl_Size *indexPtr) + Tcl_Interp *interp, Ttk_Manager *mgr, Tcl_Obj *objPtr, int lastOK, Tcl_Size *indexPtr) { const char *string = Tcl_GetString(objPtr); Tcl_Size index = 0; Tk_Window tkwin; /* Try interpreting as an integer first: */ - if (TkGetIntForIndex(objPtr, mgr->nContent - 1, 1, &index) == TCL_OK) { - if (index < 0 || index > mgr->nContent) { + if (TkGetIntForIndex(objPtr, mgr->nContent - 1, lastOK, &index) == TCL_OK) { + /* + * Note despite passing lastOK above, we still need to check here + * as well as TkGetIntForIndex only uses lastOK for end-relative indices, + * not integers. + */ + if (index < 0 || (index - !!lastOK) >= mgr->nContent) { Tcl_SetObjResult(interp, Tcl_ObjPrintf( "Managed window index %d out of bounds", (int)index)); Tcl_SetErrorCode(interp, "TTK", "MANAGED", "INDEX", NULL); return TCL_ERROR; } Index: generic/ttk/ttkManager.h ================================================================== --- generic/ttk/ttkManager.h +++ generic/ttk/ttkManager.h @@ -79,11 +79,11 @@ MODULE_SCOPE Tcl_Size Ttk_ContentIndex(Ttk_Manager *, Tk_Window); /* Returns: index in content array of specified window, TCL_INDEX_NONE if not found */ #define Ttk_GetSlaveIndexFromObj Ttk_GetContentIndexFromObj MODULE_SCOPE int Ttk_GetContentIndexFromObj( - Tcl_Interp *, Ttk_Manager *, Tcl_Obj *, Tcl_Size *indexPtr); + Tcl_Interp *, Ttk_Manager *, Tcl_Obj *, int lastOK, Tcl_Size *indexPtr); /* Accessor functions: */ #define Ttk_NumberSlaves Ttk_NumberContent MODULE_SCOPE Tcl_Size Ttk_NumberContent(Ttk_Manager *); Index: generic/ttk/ttkNotebook.c ================================================================== --- generic/ttk/ttkNotebook.c +++ generic/ttk/ttkNotebook.c @@ -861,11 +861,11 @@ } /* ... or integer index or content window name: */ if (Ttk_GetContentIndexFromObj( - interp, nb->notebook.mgr, objPtr, index_rtn) == TCL_OK) + interp, nb->notebook.mgr, objPtr, 1, index_rtn) == TCL_OK) { return TCL_OK; } if (*index_rtn == Ttk_NumberContent(nb->notebook.mgr)) { Tcl_SetObjResult(interp, Tcl_ObjPrintf( @@ -960,15 +960,10 @@ if (objc < 4) { Tcl_WrongNumArgs(interp, 2,objv, "index window ?-option value ...?"); return TCL_ERROR; } - if (TCL_OK != Ttk_GetContentIndexFromObj( - interp, nb->notebook.mgr, objv[2], &destIndex)) { - return TCL_ERROR; - } - if (Tcl_GetString(objv[3])[0] == '.') { /* Window name -- could be new or existing content window. */ Tk_Window window = Tk_NameToWindow(interp,Tcl_GetString(objv[3]),nb->core.tkwin); @@ -977,19 +972,28 @@ return TCL_ERROR; } srcIndex = Ttk_ContentIndex(nb->notebook.mgr, window); if (srcIndex < 0) { /* New content window */ + if (TCL_OK != Ttk_GetContentIndexFromObj( + interp, nb->notebook.mgr, objv[2], 1, &destIndex)) { + return TCL_ERROR; + } return AddTab(interp, nb, destIndex, window, objc-4,objv+4); } } else if (Ttk_GetContentIndexFromObj( - interp, nb->notebook.mgr, objv[3], &srcIndex) != TCL_OK) + interp, nb->notebook.mgr, objv[3], 0, &srcIndex) != TCL_OK) { return TCL_ERROR; } else if (srcIndex >= Ttk_NumberContent(nb->notebook.mgr)) { srcIndex = Ttk_NumberContent(nb->notebook.mgr) - 1; } + + if (TCL_OK != Ttk_GetContentIndexFromObj( + interp, nb->notebook.mgr, objv[2], 0, &destIndex)) { + return TCL_ERROR; + } /* Move existing content window: */ if (ConfigureTab(interp, nb, (Tab *)Ttk_ContentData(nb->notebook.mgr, srcIndex), Index: generic/ttk/ttkPanedwindow.c ================================================================== --- generic/ttk/ttkPanedwindow.c +++ generic/ttk/ttkPanedwindow.c @@ -665,11 +665,11 @@ if (!window) { return TCL_ERROR; } if (TCL_OK != Ttk_GetContentIndexFromObj( - interp,pw->paned.mgr, objv[2], &destIndex)) + interp,pw->paned.mgr, objv[2], 1, &destIndex)) { return TCL_ERROR; } srcIndex = Ttk_ContentIndex(pw->paned.mgr, window); @@ -700,11 +700,11 @@ Tcl_WrongNumArgs(interp, 2,objv, "pane"); return TCL_ERROR; } if (TCL_OK != Ttk_GetContentIndexFromObj( - interp, pw->paned.mgr, objv[2], &paneIndex)) + interp, pw->paned.mgr, objv[2], 0, &paneIndex)) { return TCL_ERROR; } else if (paneIndex >= Ttk_NumberContent(pw->paned.mgr)) { paneIndex = Ttk_NumberContent(pw->paned.mgr) - 1; } @@ -781,11 +781,11 @@ Tcl_WrongNumArgs(interp, 2,objv, "pane ?-option value ...?"); return TCL_ERROR; } if (TCL_OK != Ttk_GetContentIndexFromObj( - interp,pw->paned.mgr, objv[2], &paneIndex)) + interp,pw->paned.mgr, objv[2], 0, &paneIndex)) { return TCL_ERROR; } else if (paneIndex >= Ttk_NumberContent(pw->paned.mgr)) { paneIndex = Ttk_NumberContent(pw->paned.mgr) - 1; } Index: tests/ttk/notebook.test ================================================================== --- tests/ttk/notebook.test +++ tests/ttk/notebook.test @@ -551,6 +551,29 @@ list [.w cget -style] [.w style] [winfo class .w] } -cleanup { destroy .w } -result {customStyle.TNotebook customStyle.TNotebook TNotebook} +test notebook-9.1 "move last tab by numerical index" -body { + ::ttk::notebook .n + foreach tabs {TabA TabB TabC} { + ::ttk::entry .n.[string tolower $tabs] + .n add .n.[string tolower $tabs] -text $tabs + } + .n insert 0 2 ; # allowed: TabC moves to first tab position + .n insert 0 3 ; # not allowed: position 3 is after last tab +} -cleanup { + destroy .n +} -result {Managed window index 3 out of bounds} -returnCodes error +test notebook-9.2 "move first tab to last position by numerical index" -body { + ::ttk::notebook .n + foreach tabs {TabA TabB TabC} { + ::ttk::entry .n.[string tolower $tabs] + .n add .n.[string tolower $tabs] -text $tabs + } + .n insert 2 0 ; # allowed: TabA moves to last tab position + .n insert 3 0 ; # not allowed: position 3 is after last tab +} -cleanup { + destroy .n +} -result {Managed window index 3 out of bounds} -returnCodes error + tcltest::cleanupTests Index: tests/ttk/panedwindow.test ================================================================== --- tests/ttk/panedwindow.test +++ tests/ttk/panedwindow.test @@ -8,10 +8,26 @@ # Basic sanity checks: # test panedwindow-1.0 "Setup" -body { ttk::panedwindow .pw } -result .pw + +test panedwindow-1.0.1 "Make sure pane 0 command doesn't crash on empty pane - bug e6140f3404" -body { + .pw pane 0 +} -result {Managed window index 0 out of bounds} -returnCodes error + +test panedwindow-1.0.2 "Make sure pane end command doesn't crash on empty pane - bug e6140f3404" -body { + .pw pane end +} -result {Managed window index -1 out of bounds} -returnCodes error + +test panedwindow-1.0.3 "Make sure forget 0 command doesn't crash on empty pane - bug e6140f3404" -body { + .pw forget 0 +} -result {Managed window index 0 out of bounds} -returnCodes error + +test panedwindow-1.0.4 "Make sure forget end command doesn't crash on empty pane - bug e6140f3404" -body { + .pw forget end +} -result {Managed window index -1 out of bounds} -returnCodes error test panedwindow-1.1 "Make sure empty panedwindow doesn't crash" -body { pack .pw -expand true -fill both update }