From ae00b954846645cd2d9a8602b40498cd2b0e9f3d Mon Sep 17 00:00:00 2001 From: "rods%netscape.com" Date: Thu, 17 Aug 2000 21:38:15 +0000 Subject: [PATCH] listbox - selecting with keyboard no longer allows you to stop on a disabled option, it skips them as does page up and page down combobox - selecting a disabled option didn't clear the "mouse down" flag and it still thought it was selecting combobox - selecting a disabled option clears mSelectedIndex, this side effect could be seen by selected a disabled item then clicking on a link and then hit "back", b=48903 r=kmcclusk --- layout/forms/nsListControlFrame.cpp | 167 ++++++++++++++++--- layout/forms/nsListControlFrame.h | 4 + layout/html/forms/src/nsListControlFrame.cpp | 167 ++++++++++++++++--- layout/html/forms/src/nsListControlFrame.h | 4 + 4 files changed, 290 insertions(+), 52 deletions(-) diff --git a/layout/forms/nsListControlFrame.cpp b/layout/forms/nsListControlFrame.cpp index cee053375b7e..d7ad9c5015e0 100644 --- a/layout/forms/nsListControlFrame.cpp +++ b/layout/forms/nsListControlFrame.cpp @@ -2769,6 +2769,24 @@ nsListControlFrame::IsTargetOptionDisabled(PRBool &aIsDisabled) return rv; } +//---------------------------------------------------------------------- +NS_IMETHODIMP +nsListControlFrame::IsOptionDisabled(PRInt32 anIndex, PRBool &aIsDisabled) +{ + PRBool isOptDisabled = PR_FALSE; + nsCOMPtr options = getter_AddRefs(GetOptions(mContent)); + nsCOMPtr optionElement; + if (options) { + optionElement = getter_AddRefs(GetOption(*options, anIndex)); + nsCOMPtr content = do_QueryInterface(optionElement); + if (content) { + aIsDisabled = nsFormFrame::GetDisabled(nsnull, content); + return NS_OK; + } + } + return NS_ERROR_FAILURE; +} + //---------------------------------------------------------------------- // This is used to reset the the list and it's selection because the // selection was cancelled and the list rolled up. @@ -2820,10 +2838,16 @@ nsListControlFrame::MouseUp(nsIDOMEvent* aMouseEvent) aMouseEvent->PreventCapture(); aMouseEvent->PreventBubble(); } else { + mButtonDown = PR_FALSE; + CaptureMouseEvents(mPresContext, PR_FALSE); return NS_OK; } + mButtonDown = PR_FALSE; + CaptureMouseEvents(mPresContext, PR_FALSE); return NS_ERROR_FAILURE; // means consume event } else { + mButtonDown = PR_FALSE; + CaptureMouseEvents(mPresContext, PR_FALSE); return NS_OK; } } @@ -2838,6 +2862,10 @@ nsListControlFrame::MouseUp(nsIDOMEvent* aMouseEvent) mComboboxFrame->ListWasSelected(mPresContext, PR_FALSE); } REFLOW_DEBUG_MSG(">>>>>> Option is disabled"); + mButtonDown = PR_FALSE; + CaptureMouseEvents(mPresContext, PR_FALSE); + SetContentSelected(mSelectedIndex, PR_FALSE); + mSelectedIndex = kNothingSelected; return NS_OK; } } @@ -3300,6 +3328,92 @@ nsListControlFrame::ScrollToFrame(nsIContent* aOptElement) return NS_OK; } +//--------------------------------------------------------------------- +// Ok, the entire idea of this routine is to move to the next item that +// is suppose to be selected. If the item is disabled then we search in +// the same direction looking for the next item to select. If we run off +// the end of the list then we start at the end of the list and search +// backwards until we get back to the original item or an enabled option +// +// anNewIndex - will get set to the new index if it finds one +// anOldIndex - gets sets to the old index if a new index is found +// aDoSetNewIndex - indicates that a new item was found and it can be selected +// aWasDisabled - means it found a new item but it was disabled +// aNumOptions - the total number of options in the list +// aDoAdjustInc - the initial increment 1-n +// aDoAdjustIncNext - the increment used to search for the next enabled option +void +nsListControlFrame::AdjustIndexForDisabledOpt(PRInt32 &anNewIndex, PRInt32 &anOldIndex, + PRBool &aDoSetNewIndex, PRBool &aWasDisabled, + PRInt32 aNumOptions, PRInt32 aDoAdjustInc, + PRInt32 aDoAdjustIncNext) +{ + // the aDoAdjustInc could be a "1" for a single item or + // any number greater representing a page of items + // + PRInt32 newIndex = anNewIndex + aDoAdjustInc; + PRBool doingReverse = PR_FALSE; // means we reached the end of the list and now we are searching backwards + PRInt32 bottom = 0; // lowest index in the search range + PRInt32 top = aNumOptions;// highest index in the search range + + // make sure we start off in the range + if (newIndex < bottom) { + newIndex = 0; + } else if (newIndex >= top) { + newIndex = aNumOptions; + } + + + aWasDisabled = PR_FALSE; + while (1) { + // Special Debug Code + //printf("T:%d B:%d I:%d R:%d IM:%d I:%d\n", top, bottom, newIndex, aDoAdjustInc, aDoAdjustIncNext, doingReverse); + //if (newIndex < -30 || newIndex > 30) { + // printf("********************************* Stopped!\n"); + // return; + //} + // if the newIndex isn't disabled, we are golden, bail out + if (NS_OK == IsOptionDisabled(newIndex, aWasDisabled) && !aWasDisabled) { + break; + } + + // it WAS disabled, so sart looking ahead for the next enabled option + newIndex += aDoAdjustIncNext; + + // well, if we reach end reverse the search + if (newIndex < bottom) { + if (doingReverse) { + return; // if we are in reverse mode and reach the end bail out + } else { + // reset the newIndex to the end of the list we hit + // reverse the incrementer + // set the other end of the list to our original starting index + newIndex = bottom; + aDoAdjustIncNext = -aDoAdjustIncNext; + doingReverse = PR_TRUE; + top = anNewIndex; + } + } else if (newIndex >= top) { + if (doingReverse) { + return; // if we are in reverse mode and reach the end bail out + } else { + // reset the newIndex to the end of the list we hit + // reverse the incrementer + // set the other end of the list to our original starting index + newIndex = top - 1; + aDoAdjustIncNext = -aDoAdjustIncNext; + doingReverse = PR_TRUE; + bottom = anNewIndex; + } + } + } + + // Looks like we found one + anOldIndex = anNewIndex; + anNewIndex = newIndex; + aDoSetNewIndex = PR_TRUE; +} + nsresult nsListControlFrame::KeyPress(nsIDOMEvent* aKeyEvent) { @@ -3366,9 +3480,10 @@ nsListControlFrame::KeyPress(nsIDOMEvent* aKeyEvent) case nsIDOMKeyEvent::DOM_VK_LEFT: { REFLOW_DEBUG_MSG2("DOM_VK_UP mSelectedIndex: %d ", mSelectedIndex); if (mSelectedIndex > 0) { - mOldSelectedIndex = mSelectedIndex; - mSelectedIndex--; - doSetNewIndex = PR_TRUE; + PRBool wasDisabled; + AdjustIndexForDisabledOpt(mSelectedIndex, mOldSelectedIndex, + doSetNewIndex, wasDisabled, + (PRInt32)numOptions, -1, -1); } REFLOW_DEBUG_MSG2(" After: %d\n", mSelectedIndex); } break; @@ -3376,22 +3491,26 @@ nsListControlFrame::KeyPress(nsIDOMEvent* aKeyEvent) case nsIDOMKeyEvent::DOM_VK_DOWN: case nsIDOMKeyEvent::DOM_VK_RIGHT: { REFLOW_DEBUG_MSG2("DOM_VK_DOWN mSelectedIndex: %d ", mSelectedIndex); - if ((mSelectedIndex+1) < (PRInt32)numOptions) { - mOldSelectedIndex = mSelectedIndex; - mSelectedIndex++; - doSetNewIndex = PR_TRUE; + + if (mSelectedIndex < (PRInt32)numOptions) { + PRBool wasDisabled; + AdjustIndexForDisabledOpt(mSelectedIndex, mOldSelectedIndex, + doSetNewIndex, wasDisabled, + (PRInt32)numOptions, 1, 1); } REFLOW_DEBUG_MSG2(" After: %d\n", mSelectedIndex); } break; case nsIDOMKeyEvent::DOM_VK_RETURN: { - PRBool isDroppedDown; - mComboboxFrame->IsDroppedDown(&isDroppedDown); - if (IsInDropDownMode() == PR_TRUE && mComboboxFrame) { - mComboboxFrame->ListWasSelected(mPresContext, isDroppedDown); - } else { - UpdateSelection(PR_TRUE, PR_FALSE, mContent); - } + if (mComboboxFrame != nsnull) { + PRBool isDroppedDown; + mComboboxFrame->IsDroppedDown(&isDroppedDown); + if (IsInDropDownMode() == PR_TRUE && mComboboxFrame) { + mComboboxFrame->ListWasSelected(mPresContext, isDroppedDown); + } else { + UpdateSelection(PR_TRUE, PR_FALSE, mContent); + } + } } break; case nsIDOMKeyEvent::DOM_VK_ESCAPE: { @@ -3403,23 +3522,19 @@ nsListControlFrame::KeyPress(nsIDOMEvent* aKeyEvent) case nsIDOMKeyEvent::DOM_VK_PAGE_UP: { if (mSelectedIndex > 0) { - mOldSelectedIndex = mSelectedIndex; - mSelectedIndex -= (mNumDisplayRows-1); - if (mSelectedIndex < 0) { - mSelectedIndex = 0; - } - doSetNewIndex = PR_TRUE; + PRBool wasDisabled; + AdjustIndexForDisabledOpt(mSelectedIndex, mOldSelectedIndex, + doSetNewIndex, wasDisabled, + (PRInt32)numOptions, -(mNumDisplayRows-1), -1); } } break; case nsIDOMKeyEvent::DOM_VK_PAGE_DOWN: { if (mSelectedIndex < (PRInt32)numOptions) { - mOldSelectedIndex = mSelectedIndex; - mSelectedIndex += (mNumDisplayRows-1); - if (mSelectedIndex > (PRInt32)numOptions-1) { - mSelectedIndex = (PRInt32)numOptions-1; - } - doSetNewIndex = PR_TRUE; + PRBool wasDisabled; + AdjustIndexForDisabledOpt(mSelectedIndex, mOldSelectedIndex, + doSetNewIndex, wasDisabled, + (PRInt32)numOptions, (mNumDisplayRows-1), 1); } } break; diff --git a/layout/forms/nsListControlFrame.h b/layout/forms/nsListControlFrame.h index 65d85f637ea6..e4a4d150b3a3 100644 --- a/layout/forms/nsListControlFrame.h +++ b/layout/forms/nsListControlFrame.h @@ -287,8 +287,12 @@ protected: NS_IMETHOD GetSelectedIndexFromDOM(PRInt32* aIndex); // from DOM NS_IMETHOD IsTargetOptionDisabled(PRBool &aIsDisabled); + NS_IMETHOD IsOptionDisabled(PRInt32 anIndex, PRBool &aIsDisabled); nsresult ScrollToFrame(nsIContent * aOptElement); PRBool IsClickingInCombobox(nsIDOMEvent* aMouseEvent); + void AdjustIndexForDisabledOpt(PRInt32 &anNewIndex, PRInt32 &anOldIndex, + PRBool &aDoSetNewIndex, PRBool &aWasDisabled, + PRInt32 aNumOptions, PRInt32 aDoAdjustInc, PRInt32 aDoAdjustIncNext); nsListControlFrame(); virtual ~nsListControlFrame(); diff --git a/layout/html/forms/src/nsListControlFrame.cpp b/layout/html/forms/src/nsListControlFrame.cpp index cee053375b7e..d7ad9c5015e0 100644 --- a/layout/html/forms/src/nsListControlFrame.cpp +++ b/layout/html/forms/src/nsListControlFrame.cpp @@ -2769,6 +2769,24 @@ nsListControlFrame::IsTargetOptionDisabled(PRBool &aIsDisabled) return rv; } +//---------------------------------------------------------------------- +NS_IMETHODIMP +nsListControlFrame::IsOptionDisabled(PRInt32 anIndex, PRBool &aIsDisabled) +{ + PRBool isOptDisabled = PR_FALSE; + nsCOMPtr options = getter_AddRefs(GetOptions(mContent)); + nsCOMPtr optionElement; + if (options) { + optionElement = getter_AddRefs(GetOption(*options, anIndex)); + nsCOMPtr content = do_QueryInterface(optionElement); + if (content) { + aIsDisabled = nsFormFrame::GetDisabled(nsnull, content); + return NS_OK; + } + } + return NS_ERROR_FAILURE; +} + //---------------------------------------------------------------------- // This is used to reset the the list and it's selection because the // selection was cancelled and the list rolled up. @@ -2820,10 +2838,16 @@ nsListControlFrame::MouseUp(nsIDOMEvent* aMouseEvent) aMouseEvent->PreventCapture(); aMouseEvent->PreventBubble(); } else { + mButtonDown = PR_FALSE; + CaptureMouseEvents(mPresContext, PR_FALSE); return NS_OK; } + mButtonDown = PR_FALSE; + CaptureMouseEvents(mPresContext, PR_FALSE); return NS_ERROR_FAILURE; // means consume event } else { + mButtonDown = PR_FALSE; + CaptureMouseEvents(mPresContext, PR_FALSE); return NS_OK; } } @@ -2838,6 +2862,10 @@ nsListControlFrame::MouseUp(nsIDOMEvent* aMouseEvent) mComboboxFrame->ListWasSelected(mPresContext, PR_FALSE); } REFLOW_DEBUG_MSG(">>>>>> Option is disabled"); + mButtonDown = PR_FALSE; + CaptureMouseEvents(mPresContext, PR_FALSE); + SetContentSelected(mSelectedIndex, PR_FALSE); + mSelectedIndex = kNothingSelected; return NS_OK; } } @@ -3300,6 +3328,92 @@ nsListControlFrame::ScrollToFrame(nsIContent* aOptElement) return NS_OK; } +//--------------------------------------------------------------------- +// Ok, the entire idea of this routine is to move to the next item that +// is suppose to be selected. If the item is disabled then we search in +// the same direction looking for the next item to select. If we run off +// the end of the list then we start at the end of the list and search +// backwards until we get back to the original item or an enabled option +// +// anNewIndex - will get set to the new index if it finds one +// anOldIndex - gets sets to the old index if a new index is found +// aDoSetNewIndex - indicates that a new item was found and it can be selected +// aWasDisabled - means it found a new item but it was disabled +// aNumOptions - the total number of options in the list +// aDoAdjustInc - the initial increment 1-n +// aDoAdjustIncNext - the increment used to search for the next enabled option +void +nsListControlFrame::AdjustIndexForDisabledOpt(PRInt32 &anNewIndex, PRInt32 &anOldIndex, + PRBool &aDoSetNewIndex, PRBool &aWasDisabled, + PRInt32 aNumOptions, PRInt32 aDoAdjustInc, + PRInt32 aDoAdjustIncNext) +{ + // the aDoAdjustInc could be a "1" for a single item or + // any number greater representing a page of items + // + PRInt32 newIndex = anNewIndex + aDoAdjustInc; + PRBool doingReverse = PR_FALSE; // means we reached the end of the list and now we are searching backwards + PRInt32 bottom = 0; // lowest index in the search range + PRInt32 top = aNumOptions;// highest index in the search range + + // make sure we start off in the range + if (newIndex < bottom) { + newIndex = 0; + } else if (newIndex >= top) { + newIndex = aNumOptions; + } + + + aWasDisabled = PR_FALSE; + while (1) { + // Special Debug Code + //printf("T:%d B:%d I:%d R:%d IM:%d I:%d\n", top, bottom, newIndex, aDoAdjustInc, aDoAdjustIncNext, doingReverse); + //if (newIndex < -30 || newIndex > 30) { + // printf("********************************* Stopped!\n"); + // return; + //} + // if the newIndex isn't disabled, we are golden, bail out + if (NS_OK == IsOptionDisabled(newIndex, aWasDisabled) && !aWasDisabled) { + break; + } + + // it WAS disabled, so sart looking ahead for the next enabled option + newIndex += aDoAdjustIncNext; + + // well, if we reach end reverse the search + if (newIndex < bottom) { + if (doingReverse) { + return; // if we are in reverse mode and reach the end bail out + } else { + // reset the newIndex to the end of the list we hit + // reverse the incrementer + // set the other end of the list to our original starting index + newIndex = bottom; + aDoAdjustIncNext = -aDoAdjustIncNext; + doingReverse = PR_TRUE; + top = anNewIndex; + } + } else if (newIndex >= top) { + if (doingReverse) { + return; // if we are in reverse mode and reach the end bail out + } else { + // reset the newIndex to the end of the list we hit + // reverse the incrementer + // set the other end of the list to our original starting index + newIndex = top - 1; + aDoAdjustIncNext = -aDoAdjustIncNext; + doingReverse = PR_TRUE; + bottom = anNewIndex; + } + } + } + + // Looks like we found one + anOldIndex = anNewIndex; + anNewIndex = newIndex; + aDoSetNewIndex = PR_TRUE; +} + nsresult nsListControlFrame::KeyPress(nsIDOMEvent* aKeyEvent) { @@ -3366,9 +3480,10 @@ nsListControlFrame::KeyPress(nsIDOMEvent* aKeyEvent) case nsIDOMKeyEvent::DOM_VK_LEFT: { REFLOW_DEBUG_MSG2("DOM_VK_UP mSelectedIndex: %d ", mSelectedIndex); if (mSelectedIndex > 0) { - mOldSelectedIndex = mSelectedIndex; - mSelectedIndex--; - doSetNewIndex = PR_TRUE; + PRBool wasDisabled; + AdjustIndexForDisabledOpt(mSelectedIndex, mOldSelectedIndex, + doSetNewIndex, wasDisabled, + (PRInt32)numOptions, -1, -1); } REFLOW_DEBUG_MSG2(" After: %d\n", mSelectedIndex); } break; @@ -3376,22 +3491,26 @@ nsListControlFrame::KeyPress(nsIDOMEvent* aKeyEvent) case nsIDOMKeyEvent::DOM_VK_DOWN: case nsIDOMKeyEvent::DOM_VK_RIGHT: { REFLOW_DEBUG_MSG2("DOM_VK_DOWN mSelectedIndex: %d ", mSelectedIndex); - if ((mSelectedIndex+1) < (PRInt32)numOptions) { - mOldSelectedIndex = mSelectedIndex; - mSelectedIndex++; - doSetNewIndex = PR_TRUE; + + if (mSelectedIndex < (PRInt32)numOptions) { + PRBool wasDisabled; + AdjustIndexForDisabledOpt(mSelectedIndex, mOldSelectedIndex, + doSetNewIndex, wasDisabled, + (PRInt32)numOptions, 1, 1); } REFLOW_DEBUG_MSG2(" After: %d\n", mSelectedIndex); } break; case nsIDOMKeyEvent::DOM_VK_RETURN: { - PRBool isDroppedDown; - mComboboxFrame->IsDroppedDown(&isDroppedDown); - if (IsInDropDownMode() == PR_TRUE && mComboboxFrame) { - mComboboxFrame->ListWasSelected(mPresContext, isDroppedDown); - } else { - UpdateSelection(PR_TRUE, PR_FALSE, mContent); - } + if (mComboboxFrame != nsnull) { + PRBool isDroppedDown; + mComboboxFrame->IsDroppedDown(&isDroppedDown); + if (IsInDropDownMode() == PR_TRUE && mComboboxFrame) { + mComboboxFrame->ListWasSelected(mPresContext, isDroppedDown); + } else { + UpdateSelection(PR_TRUE, PR_FALSE, mContent); + } + } } break; case nsIDOMKeyEvent::DOM_VK_ESCAPE: { @@ -3403,23 +3522,19 @@ nsListControlFrame::KeyPress(nsIDOMEvent* aKeyEvent) case nsIDOMKeyEvent::DOM_VK_PAGE_UP: { if (mSelectedIndex > 0) { - mOldSelectedIndex = mSelectedIndex; - mSelectedIndex -= (mNumDisplayRows-1); - if (mSelectedIndex < 0) { - mSelectedIndex = 0; - } - doSetNewIndex = PR_TRUE; + PRBool wasDisabled; + AdjustIndexForDisabledOpt(mSelectedIndex, mOldSelectedIndex, + doSetNewIndex, wasDisabled, + (PRInt32)numOptions, -(mNumDisplayRows-1), -1); } } break; case nsIDOMKeyEvent::DOM_VK_PAGE_DOWN: { if (mSelectedIndex < (PRInt32)numOptions) { - mOldSelectedIndex = mSelectedIndex; - mSelectedIndex += (mNumDisplayRows-1); - if (mSelectedIndex > (PRInt32)numOptions-1) { - mSelectedIndex = (PRInt32)numOptions-1; - } - doSetNewIndex = PR_TRUE; + PRBool wasDisabled; + AdjustIndexForDisabledOpt(mSelectedIndex, mOldSelectedIndex, + doSetNewIndex, wasDisabled, + (PRInt32)numOptions, (mNumDisplayRows-1), 1); } } break; diff --git a/layout/html/forms/src/nsListControlFrame.h b/layout/html/forms/src/nsListControlFrame.h index 65d85f637ea6..e4a4d150b3a3 100644 --- a/layout/html/forms/src/nsListControlFrame.h +++ b/layout/html/forms/src/nsListControlFrame.h @@ -287,8 +287,12 @@ protected: NS_IMETHOD GetSelectedIndexFromDOM(PRInt32* aIndex); // from DOM NS_IMETHOD IsTargetOptionDisabled(PRBool &aIsDisabled); + NS_IMETHOD IsOptionDisabled(PRInt32 anIndex, PRBool &aIsDisabled); nsresult ScrollToFrame(nsIContent * aOptElement); PRBool IsClickingInCombobox(nsIDOMEvent* aMouseEvent); + void AdjustIndexForDisabledOpt(PRInt32 &anNewIndex, PRInt32 &anOldIndex, + PRBool &aDoSetNewIndex, PRBool &aWasDisabled, + PRInt32 aNumOptions, PRInt32 aDoAdjustInc, PRInt32 aDoAdjustIncNext); nsListControlFrame(); virtual ~nsListControlFrame();