From e3ef5768c74c6814a2c73ca412537d519fe042cb Mon Sep 17 00:00:00 2001 From: "cmanske%netscape.com" Date: Tue, 21 Mar 2000 06:05:24 +0000 Subject: [PATCH] Finished implementation of menu-driven table selection. Implemented detection of row or column selection. r=mjudge --- editor/base/nsEditor.cpp | 13 +- editor/base/nsEditorShell.cpp | 81 +++- editor/base/nsHTMLEditUtils.cpp | 17 +- editor/base/nsHTMLEditUtils.h | 1 + editor/base/nsHTMLEditor.cpp | 45 +- editor/base/nsHTMLEditor.h | 36 +- editor/base/nsTableEditor.cpp | 405 +++++++++++++----- editor/composer/src/nsEditorShell.cpp | 81 +++- editor/idl/nsIEditorShell.idl | 52 ++- editor/libeditor/base/nsEditor.cpp | 13 +- editor/libeditor/html/nsHTMLEditUtils.cpp | 17 +- editor/libeditor/html/nsHTMLEditUtils.h | 1 + editor/libeditor/html/nsHTMLEditor.cpp | 45 +- editor/libeditor/html/nsHTMLEditor.h | 36 +- editor/libeditor/html/nsTableEditor.cpp | 405 +++++++++++++----- editor/public/nsITableEditor.h | 43 +- editor/ui/composer/content/EditorCommands.js | 5 +- .../composer/content/EditorCommandsDebug.js | 12 + editor/ui/composer/content/editor.xul | 2 + editor/ui/dialogs/content/EdTableProps.js | 80 +++- editor/ui/dialogs/content/EdTableProps.xul | 12 +- editor/ui/dialogs/skin/EditorDialog.css | 4 +- 22 files changed, 1055 insertions(+), 351 deletions(-) diff --git a/editor/base/nsEditor.cpp b/editor/base/nsEditor.cpp index 510f6346ec1..ad44a08e036 100644 --- a/editor/base/nsEditor.cpp +++ b/editor/base/nsEditor.cpp @@ -1857,7 +1857,6 @@ nsEditor::CloneAttributes(nsIDOMNode *aDestNode, nsIDOMNode *aSourceNode) { nsCOMPtr resultAttribute; destElement->RemoveAttributeNode(destAttribute, getter_AddRefs(resultAttribute)); - // Is the resultAttribute deleted automagically? } } } @@ -1881,7 +1880,7 @@ nsEditor::CloneAttributes(nsIDOMNode *aDestNode, nsIDOMNode *aSourceNode) // Do we ever get here? destElement->RemoveAttribute(sourceAttrName); #if DEBUG_cmanske - printf("Attribute in NamedNodeMap has empty value in nsEditor::CloneAttributes()\n"); + printf("Attribute in sourceAttribute has empty value in nsEditor::CloneAttributes()\n"); #endif } } @@ -5014,19 +5013,11 @@ nsEditor::GetFirstNodeInRange(nsIDOMRange *aRange, nsIDOMNode **aNode) if (NS_FAILED(res)) return res; if (!startParent) return NS_ERROR_FAILURE; - nsCOMPtr startNode; PRInt32 offset; res = aRange->GetStartOffset(&offset); if (NS_FAILED(res)) return res; - nsCOMPtr nodeList; - res = startParent->GetChildNodes(getter_AddRefs(nodeList)); - if (NS_FAILED(res)) return res; - if (!nodeList) return NS_ERROR_FAILURE; - - nsCOMPtr child; - res = nodeList->Item(offset,getter_AddRefs(child)); - if (NS_FAILED(res)) return res; + nsCOMPtr child = GetChildAt(startParent, offset); if (!child) return NS_ERROR_FAILURE; *aNode = child.get(); diff --git a/editor/base/nsEditorShell.cpp b/editor/base/nsEditorShell.cpp index 0198e93425c..e03c9dbcd1e 100644 --- a/editor/base/nsEditorShell.cpp +++ b/editor/base/nsEditorShell.cpp @@ -2847,6 +2847,59 @@ nsEditorShell::GetSelectedElement(const PRUnichar *aInTagName, nsIDOMElement **a return result; } +NS_IMETHODIMP +nsEditorShell::GetFirstSelectedCell(nsIDOMElement **aOutElement) +{ + if (!aOutElement) + return NS_ERROR_NULL_POINTER; + + nsresult result = NS_NOINTERFACE; + + switch (mEditorType) + { + case eHTMLTextEditorType: + { + nsCOMPtr tableEditor = do_QueryInterface(mEditor); + if (tableEditor) + result = tableEditor->GetFirstSelectedCell(aOutElement); + break; + } + + case ePlainTextEditorType: + default: + result = NS_ERROR_NOT_IMPLEMENTED; + } + + return result; +} + + +NS_IMETHODIMP +nsEditorShell::GetNextSelectedCell(nsIDOMElement **aOutElement) +{ + if (!aOutElement) + return NS_ERROR_NULL_POINTER; + + nsresult result = NS_NOINTERFACE; + + switch (mEditorType) + { + case eHTMLTextEditorType: + { + nsCOMPtr tableEditor = do_QueryInterface(mEditor); + if (tableEditor) + result = tableEditor->GetNextSelectedCell(aOutElement); + break; + } + case ePlainTextEditorType: + default: + result = NS_ERROR_NOT_IMPLEMENTED; + } + + return result; +} + + NS_IMETHODIMP nsEditorShell::GetElementOrParentByTagName(const PRUnichar *aInTagName, nsIDOMNode *node, nsIDOMElement **aOutElement) { @@ -3537,9 +3590,9 @@ nsEditorShell::GetNextRow(nsIDOMElement *aCurrentRow, nsIDOMElement **_retval) } NS_IMETHODIMP -nsEditorShell::GetSelectedOrParentTableElement(PRUnichar **aTagName, PRBool *aIsSelected, nsIDOMElement **_retval) +nsEditorShell::GetSelectedOrParentTableElement(PRUnichar **aTagName, PRInt32 *aSelectedCount, nsIDOMElement **_retval) { - if (!_retval || !aTagName || !aIsSelected) + if (!_retval || !aTagName || !aSelectedCount) return NS_ERROR_NULL_POINTER; nsresult result = NS_NOINTERFACE; @@ -3550,7 +3603,7 @@ nsEditorShell::GetSelectedOrParentTableElement(PRUnichar **aTagName, PRBool *aIs nsCOMPtr tableEditor = do_QueryInterface(mEditor); nsAutoString TagName(*aTagName); if (tableEditor) - result = tableEditor->GetSelectedOrParentTableElement(*_retval, TagName, *aIsSelected); + result = tableEditor->GetSelectedOrParentTableElement(*_retval, TagName, *aSelectedCount); *aTagName = TagName.ToNewUnicode(); } break; @@ -3560,6 +3613,28 @@ nsEditorShell::GetSelectedOrParentTableElement(PRUnichar **aTagName, PRBool *aIs return result; } +NS_IMETHODIMP +nsEditorShell::GetSelectedCellsType(nsIDOMElement *aElement, PRUint32 *_retval) +{ + if (!_retval) + return NS_ERROR_NULL_POINTER; + + nsresult result = NS_NOINTERFACE; + switch (mEditorType) + { + case eHTMLTextEditorType: + { + nsCOMPtr tableEditor = do_QueryInterface(mEditor); + if (tableEditor) + result = tableEditor->GetSelectedCellsType(aElement, *_retval); + } + break; + default: + result = NS_ERROR_NOT_IMPLEMENTED; + } + return result; +} + /* end of table editing */ NS_IMETHODIMP diff --git a/editor/base/nsHTMLEditUtils.cpp b/editor/base/nsHTMLEditUtils.cpp index 9521bcccca9..8eaf70b5a40 100644 --- a/editor/base/nsHTMLEditUtils.cpp +++ b/editor/base/nsHTMLEditUtils.cpp @@ -224,7 +224,22 @@ nsHTMLEditUtils::IsListItem(nsIDOMNode *node) /////////////////////////////////////////////////////////////////////////// -// IsTableCell: true if node an html td or th +// IsTable: true if node an html table +// +PRBool +nsHTMLEditUtils::IsTable(nsIDOMNode *node) +{ + NS_PRECONDITION(node, "null node passed to nsHTMLEditor::IsTable"); + nsAutoString tag; + nsEditor::GetTagString(node,tag); + if (tag == "table") + return PR_TRUE; + + return PR_FALSE; +} + +/////////////////////////////////////////////////////////////////////////// +// IsTableRow: true if node an html tr // PRBool nsHTMLEditUtils::IsTableRow(nsIDOMNode *node) diff --git a/editor/base/nsHTMLEditUtils.h b/editor/base/nsHTMLEditUtils.h index b6299ceca7c..16d3046584d 100644 --- a/editor/base/nsHTMLEditUtils.h +++ b/editor/base/nsHTMLEditUtils.h @@ -44,6 +44,7 @@ public: static PRBool IsHeader(nsIDOMNode *aNode); static PRBool IsParagraph(nsIDOMNode *aNode); static PRBool IsListItem(nsIDOMNode *aNode); + static PRBool IsTable(nsIDOMNode *aNode); static PRBool IsTableRow(nsIDOMNode *aNode); static PRBool IsTableCell(nsIDOMNode *aNode); static PRBool IsList(nsIDOMNode *aNode); diff --git a/editor/base/nsHTMLEditor.cpp b/editor/base/nsHTMLEditor.cpp index d549c634aec..359a053b5fb 100644 --- a/editor/base/nsHTMLEditor.cpp +++ b/editor/base/nsHTMLEditor.cpp @@ -40,8 +40,8 @@ #include "nsIDOMSelection.h" #include "nsIDOMHTMLAnchorElement.h" #include "nsIDOMHTMLImageElement.h" - #include "nsISelectionController.h" +#include "nsIFrameSelection.h" // For TABLESELECTION_ defines #include "nsICSSLoader.h" #include "nsICSSStyleSheet.h" @@ -196,11 +196,10 @@ static PRBool IsCellNode(nsIDOMNode *aNode) nsAutoString tagName; if (NS_SUCCEEDED(element->GetTagName(tagName))) { - tagName.ToLowerCase(); - // With only 3 tests, it doesn't + // With only 2 tests, it doesn't // seem worth using nsAtoms - if (tagName.Equals("td") || - tagName.Equals("th")) + if (tagName.EqualsIgnoreCase("td") || + tagName.EqualsIgnoreCase("th")) { return PR_TRUE; } @@ -217,7 +216,7 @@ nsHTMLEditor::nsHTMLEditor() , mRules(nsnull) , mIsComposing(PR_FALSE) , mMaxTextLength(-1) -, mSelectingTableCells(PR_FALSE) +, mSelectedCellIndex(0) { // Done in nsEditor // NS_INIT_REFCNT(); @@ -2620,11 +2619,10 @@ nsHTMLEditor::GetElementOrParentByTagName(const nsString &aTagName, nsIDOMNode * res = selection->GetAnchorOffset(&offset); if(NS_FAILED(res)) return res; currentNode = nsEditor::GetChildAt(anchorNode, offset); - if (!currentNode) return NS_ERROR_FAILURE; - } else { - // anchor node is probably a text node - just use that - currentNode = anchorNode; } + // anchor node is probably a text node - just use that + if (!currentNode) + currentNode = anchorNode; } nsAutoString TagName = aTagName; @@ -3053,14 +3051,30 @@ nsHTMLEditor::SetBackgroundColor(const nsString& aColor) NS_PRECONDITION(mDocWeak, "Missing Editor DOM Document"); // Find a selected or enclosing table element to set background on - // TODO: Handle case of > 1 table cell or row selected nsCOMPtr element; - PRBool isSelected; + PRInt32 selectedCount; nsAutoString tagName; - nsresult res = GetSelectedOrParentTableElement(*getter_AddRefs(element), tagName, isSelected); + nsresult res = GetSelectedOrParentTableElement(*getter_AddRefs(element), tagName, selectedCount); if (NS_FAILED(res)) return res; - if (!element) + if (element) { + if (selectedCount > 0) + { + // Traverse all selected cells + nsCOMPtr cell; + res = GetFirstSelectedCell(getter_AddRefs(cell)); + if (NS_SUCCEEDED(res) && cell) + { + while(cell) + { + SetAttribute(cell, "bgcolor", aColor); + GetNextSelectedCell(getter_AddRefs(cell)); + }; + return NS_OK; + } + } + // If we failed to find a cell, fall through to use originally-found element + } else { // No table element -- set the background color on the body tag res = nsEditor::GetBodyElement(getter_AddRefs(element)); if (NS_FAILED(res)) return res; @@ -5826,7 +5840,7 @@ nsHTMLEditor::IsTableCell(nsIDOMNode *node) NS_PRECONDITION(node, "null node passed to nsHTMLEditor::IsTableCell"); nsAutoString tag; nsEditor::GetTagString(node,tag); - if (tag == "td") + if (tag == "td" || tag == "th") { return PR_TRUE; } @@ -7844,4 +7858,3 @@ nsHTMLEditor::InsertContainerAbove(nsIDOMNode *inNode, return NS_OK; } - diff --git a/editor/base/nsHTMLEditor.h b/editor/base/nsHTMLEditor.h index ed7336c8313..75a3d30895e 100644 --- a/editor/base/nsHTMLEditor.h +++ b/editor/base/nsHTMLEditor.h @@ -203,9 +203,17 @@ public: NS_IMETHOD GetFirstRow(nsIDOMElement* aTableElement, nsIDOMElement* &aRow); NS_IMETHOD GetNextRow(nsIDOMElement* aTableElement, nsIDOMElement* &aRow); NS_IMETHOD SetCaretAfterTableEdit(nsIDOMElement* aTable, PRInt32 aRow, PRInt32 aCol, PRInt32 aDirection); - NS_IMETHOD GetSelectedOrParentTableElement(nsIDOMElement* &aTableElement, nsString& aTagName, PRBool &aIsSelected); - + NS_IMETHOD GetSelectedOrParentTableElement(nsIDOMElement* &aTableElement, nsString& aTagName, PRInt32 &aSelectedCount); + NS_IMETHOD GetSelectedCellsType(nsIDOMElement *aElement, PRUint32 &aSelectionType); + // Finds the first selected cell in first range of selection + // This is in the *order of selection*, not order in the table + // (i.e., each cell added to selection is added in another range + // in the selection's rangelist, independent of location in table) + NS_IMETHOD GetFirstSelectedCell(nsIDOMElement **aCell); + // Get next cell until no more are found. Always use GetFirstSelected cell first + NS_IMETHOD GetNextSelectedCell(nsIDOMElement **aCell); + // Selection and navigation /* obsolete NS_IMETHOD MoveSelectionUp(nsIAtom *aIncrement, PRBool aExtendSelection); @@ -348,24 +356,23 @@ protected: // Needed to do appropriate deleting when last cell or row is about to be deleted // This doesn't count cells that don't start in the given row (are spanning from row above) PRInt32 GetNumberOfCellsInRow(nsIDOMElement* aTable, PRInt32 rowIndex); - + // Test if all cells in row or column at given index are selected + PRBool AllCellsInRowSelected(nsIDOMElement *aTable, PRInt32 aRowIndex, PRInt32 aNumberOfColumns); + PRBool AllCellsInColumnSelected(nsIDOMElement *aTable, PRInt32 aColIndex, PRInt32 aNumberOfRows); + // Most insert methods need to get the same basic context data NS_IMETHOD GetCellContext(nsCOMPtr &aSelection, nsCOMPtr &aTable, nsCOMPtr &aCell, nsCOMPtr &aCellParent, PRInt32& aCellOffset, PRInt32& aRow, PRInt32& aCol); - // Finds the first selected cell in first range of selection - // This is in the *order of selection*, not order in the table - // (i.e., each cell added to selection is added in another range - // in the selection's rangelist, independent of location in table) - NS_IMETHOD GetFirstSelectedCell(nsIDOMElement **aCell); - // Fallback method: Call this after using ClearSelection() and you // failed to set selection to some other content in the document NS_IMETHOD SetSelectionAtDocumentStart(nsIDOMSelection *aSelection); - // end of table editing utilities + +// End of Table Editing utilities + NS_IMETHOD ReParentContentOfNode(nsIDOMNode *aNode, nsString &aParentTag, @@ -607,13 +614,8 @@ protected: PRBool mCachedUnderlineStyle; nsString mCachedFontName; - // True when selection consists of table cell(s) - PRBool mSelectingTableCells; - - // Used to monitor block of cells selected - // by dragging mouse across the table - nsCOMPtr mStartSelectedCell; - nsCOMPtr mCurrentSelectedCell; + // Used by GetFirstSelectedCell and GetNextSelectedCell + PRInt32 mSelectedCellIndex; public: static nsIAtom *gTypingTxnName; diff --git a/editor/base/nsTableEditor.cpp b/editor/base/nsTableEditor.cpp index 2badf4f2041..229b790d30c 100644 --- a/editor/base/nsTableEditor.cpp +++ b/editor/base/nsTableEditor.cpp @@ -40,6 +40,8 @@ #include "nsITableCellLayout.h" // For efficient access to table cell #include "nsITableLayout.h" // data owned by the table and cell frames #include "nsHTMLEditor.h" +#include "nsIFrameSelection.h" // For TABLESELECTION_ defines +#include "nsVoidArray.h" #include "nsEditorUtils.h" @@ -799,7 +801,7 @@ nsHTMLEditor::DeleteTableColumn(PRInt32 aNumber) if (curCell) { // This must always be >= 1 - NS_ASSERTION((actualRowSpan > 0),"Effective ROWSPAN = 0 in DeleteTableColumn"); + NS_ASSERTION((actualRowSpan > 0),"Actual ROWSPAN = 0 in DeleteTableColumn"); // Find cells that don't start in column we are deleting if (curStartColIndex < startColIndex || colSpan > 1 || colSpan == 0) @@ -1643,13 +1645,9 @@ nsHTMLEditor::GetCellContext(nsCOMPtr &aSelection, if (NS_FAILED(res)) return res; if (!aSelection) return NS_ERROR_FAILURE; - // Find the first selected cell -// res = GetFirstSelectedCell(getter_AddRefs(aCell)); if (!aCell) { - //If a cell wasn't selected, then assume the selection is INSIDE - // and use anchor node to search up to the containing cell - // Test if selected node (from anchor node) is a cell + // Get cell if it's the child of selection anchor node, // or get the enclosing by a cell res = GetElementOrParentByTagName("td", nsnull, getter_AddRefs(aCell)); if (NS_FAILED(res)) return res; @@ -1683,78 +1681,109 @@ nsHTMLEditor::GetFirstSelectedCell(nsIDOMElement **aCell) if (NS_FAILED(res)) return res; if (!selection) return NS_ERROR_FAILURE; -//TODO: Replace this with code below new "table cell mode" flag is implemented - - nsCOMPtr enumerator; - res = selection->GetEnumerator(getter_AddRefs(enumerator)); - - if (NS_FAILED(res)) return res; - if (!enumerator) return NS_ERROR_FAILURE; - enumerator->First(); - nsCOMPtr currentItem; - res = enumerator->CurrentItem(getter_AddRefs(currentItem)); - if ((NS_SUCCEEDED(res)) && currentItem) - { - nsCOMPtr range( do_QueryInterface(currentItem) ); - nsCOMPtr iter; - res = nsComponentManager::CreateInstance(kCContentIteratorCID, nsnull, - NS_GET_IID(nsIContentIterator), - getter_AddRefs(iter)); - if (NS_FAILED(res)) return res; - if (!iter) return NS_ERROR_FAILURE; - - iter->Init(range); - // loop through the content iterator for each content node - nsCOMPtr content; - - while (NS_ENUMERATOR_FALSE == iter->IsDone()) - { - res = iter->CurrentNode(getter_AddRefs(content)); - // Not likely! - if (NS_FAILED(res)) return NS_ERROR_FAILURE; - - nsCOMPtr atom; - content->GetTag(*getter_AddRefs(atom)); - if (atom.get() == nsIEditProperty::td || - atom.get() == nsIEditProperty::th ) - { - // We found a cell - nsCOMPtr cellElement = do_QueryInterface(content); - if (cellElement) - { - *aCell = cellElement.get(); - NS_ADDREF(*aCell); - } - return NS_OK; - } - iter->Next(); - } - } - return NS_EDITOR_ELEMENT_NOT_FOUND; - -#if 0 -//TODO: Do this only after checking the new "table cell mode" flag - // The first cell is the starting node in the first selection range nsCOMPtr firstRange; res = selection->GetRangeAt(0, getter_AddRefs(firstRange)); if (NS_FAILED(res)) return res; if (!firstRange) return NS_ERROR_FAILURE; +#ifdef DEBUG_cmanske + { + nsCOMPtr anchorNode; + PRInt32 anchorOffset = -1; + selection->GetAnchorNode(getter_AddRefs(anchorNode)); + selection->GetAnchorOffset(&anchorOffset); + + nsCOMPtr focusNode; + res = selection->GetFocusNode(getter_AddRefs(focusNode)); + if (NS_FAILED(res)) return res; + PRInt32 focusOffset = -1; + selection->GetFocusOffset(&focusOffset); + + nsAutoString name; + anchorNode->GetNodeName(name); + printf("GetFirstSelectedCell: Anchor node of selection: "); + wprintf(name.GetUnicode()); + printf(" Offset: %d\n", anchorOffset); + focusNode->GetNodeName(name); + printf("Focus node of selection: "); + wprintf(name.GetUnicode()); + printf(" Offset: %x\n", focusOffset); + + PRInt32 rangeCount; + res = selection->GetRangeCount(&rangeCount); + printf(" RangeCount: %d\n", rangeCount); + printf(" Range pointer = %d\n", firstRange); + } +#endif + + // This is failing -- range doesn't match that set when selecting cell! + nsCOMPtr cellNode; res = GetFirstNodeInRange(firstRange, getter_AddRefs(cellNode)); if (NS_FAILED(res)) return res; if (!cellNode) return NS_ERROR_FAILURE; - nsCOMPtr cellElement = do_QueryInterface(cellNode); - - if (cellElement) + + if (IsTableCell(cellNode)) { + nsCOMPtr cellElement = do_QueryInterface(cellNode); *aCell = cellElement.get(); NS_ADDREF(*aCell); } - else res = NS_EDITOR_ELEMENT_NOT_FOUND; + else + res = NS_EDITOR_ELEMENT_NOT_FOUND; + + mSelectedCellIndex = 1; + + return res; +} + +NS_IMETHODIMP +nsHTMLEditor::GetNextSelectedCell(nsIDOMElement **aCell) +{ + if (!aCell) return NS_ERROR_NULL_POINTER; + *aCell = nsnull; + + nsCOMPtr selection; + nsresult res = nsEditor::GetSelection(getter_AddRefs(selection)); + if (NS_FAILED(res)) return res; + if (!selection) return NS_ERROR_FAILURE; + + PRInt32 rangeCount; + res = selection->GetRangeCount(&rangeCount); + if (NS_FAILED(res)) return res; + + // Don't even try if index exceeds range count + if (mSelectedCellIndex >= rangeCount) + { + // Should we reset index? + // Maybe better to force recalling GetFirstSelectedCell() + //mSelectedCellIndex = 0; + return NS_EDITOR_ELEMENT_NOT_FOUND; + } + + // Get first node in first range of selection - test if it's a cell + nsCOMPtr range; + res = selection->GetRangeAt(mSelectedCellIndex, getter_AddRefs(range)); + if (NS_FAILED(res)) return res; + if (!range) return NS_ERROR_FAILURE; + + nsCOMPtr cellNode; + res = nsEditor::GetFirstNodeInRange(range, getter_AddRefs(cellNode)); + if (NS_FAILED(res)) return res; + if (!cellNode) return NS_ERROR_FAILURE; + if (IsTableCell(cellNode)) + { + nsCOMPtr cellElement = do_QueryInterface(cellNode); + *aCell = cellElement.get(); + NS_ADDREF(*aCell); + } + else + res = NS_EDITOR_ELEMENT_NOT_FOUND; + + // Setup for next cell + mSelectedCellIndex++; return res; -#endif } NS_IMETHODIMP @@ -1842,11 +1871,11 @@ nsHTMLEditor::SetCaretAfterTableEdit(nsIDOMElement* aTable, PRInt32 aRow, PRInt3 } NS_IMETHODIMP -nsHTMLEditor::GetSelectedOrParentTableElement(nsIDOMElement* &aTableElement, nsString& aTagName, PRBool &aIsSelected) +nsHTMLEditor::GetSelectedOrParentTableElement(nsIDOMElement* &aTableElement, nsString& aTagName, PRInt32 &aSelectedCount) { aTableElement = nsnull; aTagName = ""; - aIsSelected = PR_FALSE; + aSelectedCount = 0; nsCOMPtr selection; nsresult res = nsEditor::GetSelection(getter_AddRefs(selection)); @@ -1858,52 +1887,218 @@ nsHTMLEditor::GetSelectedOrParentTableElement(nsIDOMElement* &aTableElement, nsS nsAutoString tdName("td"); nsCOMPtr anchorNode; - - // Find the first selected cell - // TODO: Handle multiple cells selected! - nsCOMPtr firstCell; - - nsCOMPtr tableElement; - res = GetFirstSelectedCell(getter_AddRefs(tableElement)); - if(NS_FAILED(res)) return res; - if (tableElement) - { - aTagName = tdName; - aIsSelected = PR_TRUE; - goto SET_RETURN_ELEMENT; - } - - // See if table or row is selected - res = GetSelectedElement(tableName, getter_AddRefs(tableElement)); - if(NS_FAILED(res)) return res; - if (tableElement) - { - aTagName = tableName; - aIsSelected = PR_TRUE; - goto SET_RETURN_ELEMENT; - } - res = GetSelectedElement(trName, getter_AddRefs(tableElement)); - if(NS_FAILED(res)) return res; - if (tableElement) - { - aTagName = trName; - aIsSelected = PR_TRUE; - goto SET_RETURN_ELEMENT; - } - - // Look for a table cell parent res = selection->GetAnchorNode(getter_AddRefs(anchorNode)); - if (NS_FAILED(res)) return res; - if (!anchorNode) return NS_ERROR_FAILURE; - - res = GetElementOrParentByTagName(tdName, anchorNode, getter_AddRefs(tableElement)); if(NS_FAILED(res)) return res; + if (!anchorNode) return NS_ERROR_FAILURE; + + nsCOMPtr tableElement; + nsCOMPtr selectedNode; + + // Get child of anchor node, if exists + PRBool hasChildren; + anchorNode->HasChildNodes(&hasChildren); + + if (hasChildren) + { + PRInt32 anchorOffset; + res = selection->GetAnchorOffset(&anchorOffset); + if (NS_FAILED(res)) return res; + selectedNode = nsEditor::GetChildAt(anchorNode, anchorOffset); + if (!selectedNode) + selectedNode = anchorNode; + + nsAutoString tag; + nsEditor::GetTagString(selectedNode,tag); + + if (tag == tdName) + { + tableElement = do_QueryInterface(anchorNode); + aTagName = tdName; + // Each cell is in its own selection range, + // so count signals multiple-cell selection + res = selection->GetRangeCount(&aSelectedCount); + if (NS_FAILED(res)) return res; + } + else if(tag == tableName) + { + tableElement = do_QueryInterface(anchorNode); + aTagName = tableName; + aSelectedCount = 1; + } + else if(tag == trName) + { + tableElement = do_QueryInterface(anchorNode); + aTagName = trName; + aSelectedCount = 1; + } + } + if (!tableElement) + { + // Didn't find a table element -- find a cell parent + res = GetElementOrParentByTagName(tdName, anchorNode, getter_AddRefs(tableElement)); + if(NS_FAILED(res)) return res; + if (tableElement) + aTagName = tdName; + } if (tableElement) { - aTagName = tdName; -SET_RETURN_ELEMENT: aTableElement = tableElement.get(); NS_ADDREF(aTableElement); } return res; } + +static PRBool IndexNotTested(nsVoidArray *aArray, PRInt32 aIndex) +{ + if (aArray) + { + PRInt32 count = aArray->Count(); + for (PRInt32 i = 0; i < count; i++) + { + if(aIndex == (PRInt32)(aArray->ElementAt(i))) + return PR_FALSE; + } + } + return PR_TRUE; +} + +NS_IMETHODIMP +nsHTMLEditor::GetSelectedCellsType(nsIDOMElement *aElement, PRUint32 &aSelectionType) +{ + aSelectionType = 0; + + // Be sure we have a table element + // (if aElement is null, this uses selection's anchor node) + nsCOMPtr table; + + nsresult res = GetElementOrParentByTagName("table", aElement, getter_AddRefs(table)); + if (NS_FAILED(res)) return res; + + PRInt32 rowCount, colCount; + res = GetTableSize(table, rowCount, colCount); + if (NS_FAILED(res)) return res; + + // Traverse all selected cells + // (Failure here may indicate that aCellElement wasn't really a cell) + nsCOMPtr selectedCell; + res = GetFirstSelectedCell(getter_AddRefs(selectedCell)); + if (NS_FAILED(res)) return res; + + // We have at least one selected cell, so set return value + aSelectionType = TABLESELECTION_CELL; + + // Store indexes of each row/col to avoid duplication of searches + nsVoidArray indexArray; + + PRBool allCellsInRowAreSelected = PR_TRUE; + PRBool allCellsInColAreSelected = PR_FALSE; + while (NS_SUCCEEDED(res) && selectedCell) + { + // Get the cell's location in the cellmap + PRInt32 startRowIndex, startColIndex; + res = GetCellIndexes(selectedCell, startRowIndex, startColIndex); + if(NS_FAILED(res)) return res; + + if (IndexNotTested(&indexArray, startColIndex)) + { + indexArray.AppendElement((void*)startColIndex); + allCellsInRowAreSelected = AllCellsInRowSelected(table, startRowIndex, colCount); + // We're done as soon as we fail for any row + if (!allCellsInRowAreSelected) break; + } + res = GetNextSelectedCell(getter_AddRefs(selectedCell)); + } + + if (allCellsInRowAreSelected) + { + aSelectionType = TABLESELECTION_ROW; + return NS_OK; + } + // Test for columns + + // Empty the indexArray + indexArray.Clear(); + + // Start at first cell again + res = GetFirstSelectedCell(getter_AddRefs(selectedCell)); + while (NS_SUCCEEDED(res) && selectedCell) + { + // Get the cell's location in the cellmap + PRInt32 startRowIndex, startColIndex; + res = GetCellIndexes(selectedCell, startRowIndex, startColIndex); + if(NS_FAILED(res)) return res; + + if (IndexNotTested(&indexArray, startRowIndex)) + { + indexArray.AppendElement((void*)startColIndex); + allCellsInColAreSelected = AllCellsInColumnSelected(table, startColIndex, colCount); + // We're done as soon as we fail for any column + if (!allCellsInRowAreSelected) break; + } + res = GetNextSelectedCell(getter_AddRefs(selectedCell)); + } + if (allCellsInColAreSelected) + aSelectionType = TABLESELECTION_COLUMN; + + return NS_OK; +} + +PRBool +nsHTMLEditor::AllCellsInRowSelected(nsIDOMElement *aTable, PRInt32 aRowIndex, PRInt32 aNumberOfColumns) +{ + if (!aTable) return PR_FALSE; + + PRInt32 curStartRowIndex, curStartColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan; + PRBool isSelected; + + for( PRInt32 col = 0; col < aNumberOfColumns; col += actualColSpan) + { + nsCOMPtr cell; + nsresult res = GetCellDataAt(aTable, aRowIndex, col, *getter_AddRefs(cell), + curStartRowIndex, curStartColIndex, rowSpan, colSpan, + actualRowSpan, actualColSpan, isSelected); + + if (NS_FAILED(res)) return PR_FALSE; + // Skip cell spanning into this location from a row above + if (curStartRowIndex == aRowIndex) + { + // If no cell, we may have a "ragged" right edge, + // so return TRUE only if we already found a cell in the row + if (!cell) return (col > 0) ? PR_TRUE : PR_FALSE; + // Return as soon as a non-selected cell is found + if (!isSelected) return PR_FALSE; + } + NS_ASSERTION((actualColSpan > 0),"ActualColSpan = 0 in AllCellsInRowSelected"); + } + return PR_TRUE; +} + +PRBool +nsHTMLEditor::AllCellsInColumnSelected(nsIDOMElement *aTable, PRInt32 aColIndex, PRInt32 aNumberOfRows) +{ + if (!aTable) return PR_FALSE; + + PRInt32 curStartRowIndex, curStartColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan; + PRBool isSelected; + + for( PRInt32 row = 0; row < aNumberOfRows; row += actualRowSpan) + { + nsCOMPtr cell; + nsresult res = GetCellDataAt(aTable, row, aColIndex, *getter_AddRefs(cell), + curStartRowIndex, curStartColIndex, rowSpan, colSpan, + actualRowSpan, actualColSpan, isSelected); + + if (NS_FAILED(res)) return PR_FALSE; + // Skip cell spanning into this location from a column to the left + if (curStartColIndex == aColIndex) + { + // If no cell, we must have a "ragged" right edge on the last column + // so return TRUE only if we already found a cell in the row + if (!cell) return (row > 0) ? PR_TRUE : PR_FALSE; + // Return as soon as a non-selected cell is found + if (!isSelected) return PR_FALSE; + } + NS_ASSERTION((actualRowSpan > 0),"ActualRowSpan = 0 in AllCellsInColumnSelected"); + } + return PR_TRUE; +} diff --git a/editor/composer/src/nsEditorShell.cpp b/editor/composer/src/nsEditorShell.cpp index 0198e93425c..e03c9dbcd1e 100644 --- a/editor/composer/src/nsEditorShell.cpp +++ b/editor/composer/src/nsEditorShell.cpp @@ -2847,6 +2847,59 @@ nsEditorShell::GetSelectedElement(const PRUnichar *aInTagName, nsIDOMElement **a return result; } +NS_IMETHODIMP +nsEditorShell::GetFirstSelectedCell(nsIDOMElement **aOutElement) +{ + if (!aOutElement) + return NS_ERROR_NULL_POINTER; + + nsresult result = NS_NOINTERFACE; + + switch (mEditorType) + { + case eHTMLTextEditorType: + { + nsCOMPtr tableEditor = do_QueryInterface(mEditor); + if (tableEditor) + result = tableEditor->GetFirstSelectedCell(aOutElement); + break; + } + + case ePlainTextEditorType: + default: + result = NS_ERROR_NOT_IMPLEMENTED; + } + + return result; +} + + +NS_IMETHODIMP +nsEditorShell::GetNextSelectedCell(nsIDOMElement **aOutElement) +{ + if (!aOutElement) + return NS_ERROR_NULL_POINTER; + + nsresult result = NS_NOINTERFACE; + + switch (mEditorType) + { + case eHTMLTextEditorType: + { + nsCOMPtr tableEditor = do_QueryInterface(mEditor); + if (tableEditor) + result = tableEditor->GetNextSelectedCell(aOutElement); + break; + } + case ePlainTextEditorType: + default: + result = NS_ERROR_NOT_IMPLEMENTED; + } + + return result; +} + + NS_IMETHODIMP nsEditorShell::GetElementOrParentByTagName(const PRUnichar *aInTagName, nsIDOMNode *node, nsIDOMElement **aOutElement) { @@ -3537,9 +3590,9 @@ nsEditorShell::GetNextRow(nsIDOMElement *aCurrentRow, nsIDOMElement **_retval) } NS_IMETHODIMP -nsEditorShell::GetSelectedOrParentTableElement(PRUnichar **aTagName, PRBool *aIsSelected, nsIDOMElement **_retval) +nsEditorShell::GetSelectedOrParentTableElement(PRUnichar **aTagName, PRInt32 *aSelectedCount, nsIDOMElement **_retval) { - if (!_retval || !aTagName || !aIsSelected) + if (!_retval || !aTagName || !aSelectedCount) return NS_ERROR_NULL_POINTER; nsresult result = NS_NOINTERFACE; @@ -3550,7 +3603,7 @@ nsEditorShell::GetSelectedOrParentTableElement(PRUnichar **aTagName, PRBool *aIs nsCOMPtr tableEditor = do_QueryInterface(mEditor); nsAutoString TagName(*aTagName); if (tableEditor) - result = tableEditor->GetSelectedOrParentTableElement(*_retval, TagName, *aIsSelected); + result = tableEditor->GetSelectedOrParentTableElement(*_retval, TagName, *aSelectedCount); *aTagName = TagName.ToNewUnicode(); } break; @@ -3560,6 +3613,28 @@ nsEditorShell::GetSelectedOrParentTableElement(PRUnichar **aTagName, PRBool *aIs return result; } +NS_IMETHODIMP +nsEditorShell::GetSelectedCellsType(nsIDOMElement *aElement, PRUint32 *_retval) +{ + if (!_retval) + return NS_ERROR_NULL_POINTER; + + nsresult result = NS_NOINTERFACE; + switch (mEditorType) + { + case eHTMLTextEditorType: + { + nsCOMPtr tableEditor = do_QueryInterface(mEditor); + if (tableEditor) + result = tableEditor->GetSelectedCellsType(aElement, *_retval); + } + break; + default: + result = NS_ERROR_NOT_IMPLEMENTED; + } + return result; +} + /* end of table editing */ NS_IMETHODIMP diff --git a/editor/idl/nsIEditorShell.idl b/editor/idl/nsIEditorShell.idl index 11418114106..c4cf0611e6e 100644 --- a/editor/idl/nsIEditorShell.idl +++ b/editor/idl/nsIEditorShell.idl @@ -60,14 +60,6 @@ interface nsIEditorShell : nsISupports eDisplayModeEdit, eDisplayModeBrowserPreview }; - - enum { - eTable, - eTableRow, - eTableColumn, - eTableCell, - eTableCaption - }; %} readonly attribute boolean documentModified; readonly attribute boolean documentIsEmpty; @@ -201,6 +193,22 @@ interface nsIEditorShell : nsISupports */ nsIDOMElement GetSelectedElement(in wstring tagName); + /** Get first selected node from first selection range. + * Assumes cell-selection model where each cell + * is in a separate range (selection parent node is table row) + * Returns null if ranges don't contain cell selections + */ + nsIDOMElement GetFirstSelectedCell(); + + /** Get next selected cell element from first selection range. + * Assumes cell-selection model where each cell + * is in a separate range (selection parent node is table row) + * Always call GetFirstSelectedCell() to initialize stored index of "next" cell + * Returns null if after last cell or + * ranges don't contain cell selections + */ + nsIDOMElement GetNextSelectedCell(); + /** Return the input node or a parent matching the given aTagName, * starting the search at the supplied node. * An example of use is for testing if a node is in a table cell @@ -368,12 +376,34 @@ interface nsIEditorShell : nsISupports * * Returns: * The table element (table, row, or cell) found + * If multiple table cells are selected, this is the "focus" cell (last cell selected) + * * tagName The tagname of returned element * Note that "td" will be returned if name is actually "th" - * isSelected Tells if element returned is a selected element - * (false if element is a parent cell of selection) + * selectedCount How many table elements were selected + * This tells us if we have multiple cells selected + * (0 if element is a parent cell of selection) */ - nsIDOMElement GetSelectedOrParentTableElement(out wstring tagName, out boolean isSelected); + nsIDOMElement GetSelectedOrParentTableElement(out wstring tagName, out PRInt32 selectedCount); + + /** Generally used after GetSelectedOrParentTableElement + * to test if selected cells are complete rows or columns + * + * cellElement Any table, cell, or element inside a table + * Used to get enclosing table. + * If null, selection's focusNode is used + * + * Returns: (defines are from nsIFrameSelection.h) + * 0 cellElement was not a cell + * 1 (TABLESELECTION_CELL) There are 1 or more cells selected + * but complete rows or columns are not selected + * 2 (TABLESELECTION_ROW) All cells are in 1 or more rows + * and in each row, all cells selected + * Note: This is the value if all rows (thus all cells) are selected + * 3 (TABLESELECTION_COLUMN) All cells are in 1 or more columns + * and in each column, all cells are selected + */ + PRUint32 GetSelectedCellsType(in nsIDOMElement element); /**** end of table editing *****/ diff --git a/editor/libeditor/base/nsEditor.cpp b/editor/libeditor/base/nsEditor.cpp index 510f6346ec1..ad44a08e036 100644 --- a/editor/libeditor/base/nsEditor.cpp +++ b/editor/libeditor/base/nsEditor.cpp @@ -1857,7 +1857,6 @@ nsEditor::CloneAttributes(nsIDOMNode *aDestNode, nsIDOMNode *aSourceNode) { nsCOMPtr resultAttribute; destElement->RemoveAttributeNode(destAttribute, getter_AddRefs(resultAttribute)); - // Is the resultAttribute deleted automagically? } } } @@ -1881,7 +1880,7 @@ nsEditor::CloneAttributes(nsIDOMNode *aDestNode, nsIDOMNode *aSourceNode) // Do we ever get here? destElement->RemoveAttribute(sourceAttrName); #if DEBUG_cmanske - printf("Attribute in NamedNodeMap has empty value in nsEditor::CloneAttributes()\n"); + printf("Attribute in sourceAttribute has empty value in nsEditor::CloneAttributes()\n"); #endif } } @@ -5014,19 +5013,11 @@ nsEditor::GetFirstNodeInRange(nsIDOMRange *aRange, nsIDOMNode **aNode) if (NS_FAILED(res)) return res; if (!startParent) return NS_ERROR_FAILURE; - nsCOMPtr startNode; PRInt32 offset; res = aRange->GetStartOffset(&offset); if (NS_FAILED(res)) return res; - nsCOMPtr nodeList; - res = startParent->GetChildNodes(getter_AddRefs(nodeList)); - if (NS_FAILED(res)) return res; - if (!nodeList) return NS_ERROR_FAILURE; - - nsCOMPtr child; - res = nodeList->Item(offset,getter_AddRefs(child)); - if (NS_FAILED(res)) return res; + nsCOMPtr child = GetChildAt(startParent, offset); if (!child) return NS_ERROR_FAILURE; *aNode = child.get(); diff --git a/editor/libeditor/html/nsHTMLEditUtils.cpp b/editor/libeditor/html/nsHTMLEditUtils.cpp index 9521bcccca9..8eaf70b5a40 100644 --- a/editor/libeditor/html/nsHTMLEditUtils.cpp +++ b/editor/libeditor/html/nsHTMLEditUtils.cpp @@ -224,7 +224,22 @@ nsHTMLEditUtils::IsListItem(nsIDOMNode *node) /////////////////////////////////////////////////////////////////////////// -// IsTableCell: true if node an html td or th +// IsTable: true if node an html table +// +PRBool +nsHTMLEditUtils::IsTable(nsIDOMNode *node) +{ + NS_PRECONDITION(node, "null node passed to nsHTMLEditor::IsTable"); + nsAutoString tag; + nsEditor::GetTagString(node,tag); + if (tag == "table") + return PR_TRUE; + + return PR_FALSE; +} + +/////////////////////////////////////////////////////////////////////////// +// IsTableRow: true if node an html tr // PRBool nsHTMLEditUtils::IsTableRow(nsIDOMNode *node) diff --git a/editor/libeditor/html/nsHTMLEditUtils.h b/editor/libeditor/html/nsHTMLEditUtils.h index b6299ceca7c..16d3046584d 100644 --- a/editor/libeditor/html/nsHTMLEditUtils.h +++ b/editor/libeditor/html/nsHTMLEditUtils.h @@ -44,6 +44,7 @@ public: static PRBool IsHeader(nsIDOMNode *aNode); static PRBool IsParagraph(nsIDOMNode *aNode); static PRBool IsListItem(nsIDOMNode *aNode); + static PRBool IsTable(nsIDOMNode *aNode); static PRBool IsTableRow(nsIDOMNode *aNode); static PRBool IsTableCell(nsIDOMNode *aNode); static PRBool IsList(nsIDOMNode *aNode); diff --git a/editor/libeditor/html/nsHTMLEditor.cpp b/editor/libeditor/html/nsHTMLEditor.cpp index d549c634aec..359a053b5fb 100644 --- a/editor/libeditor/html/nsHTMLEditor.cpp +++ b/editor/libeditor/html/nsHTMLEditor.cpp @@ -40,8 +40,8 @@ #include "nsIDOMSelection.h" #include "nsIDOMHTMLAnchorElement.h" #include "nsIDOMHTMLImageElement.h" - #include "nsISelectionController.h" +#include "nsIFrameSelection.h" // For TABLESELECTION_ defines #include "nsICSSLoader.h" #include "nsICSSStyleSheet.h" @@ -196,11 +196,10 @@ static PRBool IsCellNode(nsIDOMNode *aNode) nsAutoString tagName; if (NS_SUCCEEDED(element->GetTagName(tagName))) { - tagName.ToLowerCase(); - // With only 3 tests, it doesn't + // With only 2 tests, it doesn't // seem worth using nsAtoms - if (tagName.Equals("td") || - tagName.Equals("th")) + if (tagName.EqualsIgnoreCase("td") || + tagName.EqualsIgnoreCase("th")) { return PR_TRUE; } @@ -217,7 +216,7 @@ nsHTMLEditor::nsHTMLEditor() , mRules(nsnull) , mIsComposing(PR_FALSE) , mMaxTextLength(-1) -, mSelectingTableCells(PR_FALSE) +, mSelectedCellIndex(0) { // Done in nsEditor // NS_INIT_REFCNT(); @@ -2620,11 +2619,10 @@ nsHTMLEditor::GetElementOrParentByTagName(const nsString &aTagName, nsIDOMNode * res = selection->GetAnchorOffset(&offset); if(NS_FAILED(res)) return res; currentNode = nsEditor::GetChildAt(anchorNode, offset); - if (!currentNode) return NS_ERROR_FAILURE; - } else { - // anchor node is probably a text node - just use that - currentNode = anchorNode; } + // anchor node is probably a text node - just use that + if (!currentNode) + currentNode = anchorNode; } nsAutoString TagName = aTagName; @@ -3053,14 +3051,30 @@ nsHTMLEditor::SetBackgroundColor(const nsString& aColor) NS_PRECONDITION(mDocWeak, "Missing Editor DOM Document"); // Find a selected or enclosing table element to set background on - // TODO: Handle case of > 1 table cell or row selected nsCOMPtr element; - PRBool isSelected; + PRInt32 selectedCount; nsAutoString tagName; - nsresult res = GetSelectedOrParentTableElement(*getter_AddRefs(element), tagName, isSelected); + nsresult res = GetSelectedOrParentTableElement(*getter_AddRefs(element), tagName, selectedCount); if (NS_FAILED(res)) return res; - if (!element) + if (element) { + if (selectedCount > 0) + { + // Traverse all selected cells + nsCOMPtr cell; + res = GetFirstSelectedCell(getter_AddRefs(cell)); + if (NS_SUCCEEDED(res) && cell) + { + while(cell) + { + SetAttribute(cell, "bgcolor", aColor); + GetNextSelectedCell(getter_AddRefs(cell)); + }; + return NS_OK; + } + } + // If we failed to find a cell, fall through to use originally-found element + } else { // No table element -- set the background color on the body tag res = nsEditor::GetBodyElement(getter_AddRefs(element)); if (NS_FAILED(res)) return res; @@ -5826,7 +5840,7 @@ nsHTMLEditor::IsTableCell(nsIDOMNode *node) NS_PRECONDITION(node, "null node passed to nsHTMLEditor::IsTableCell"); nsAutoString tag; nsEditor::GetTagString(node,tag); - if (tag == "td") + if (tag == "td" || tag == "th") { return PR_TRUE; } @@ -7844,4 +7858,3 @@ nsHTMLEditor::InsertContainerAbove(nsIDOMNode *inNode, return NS_OK; } - diff --git a/editor/libeditor/html/nsHTMLEditor.h b/editor/libeditor/html/nsHTMLEditor.h index ed7336c8313..75a3d30895e 100644 --- a/editor/libeditor/html/nsHTMLEditor.h +++ b/editor/libeditor/html/nsHTMLEditor.h @@ -203,9 +203,17 @@ public: NS_IMETHOD GetFirstRow(nsIDOMElement* aTableElement, nsIDOMElement* &aRow); NS_IMETHOD GetNextRow(nsIDOMElement* aTableElement, nsIDOMElement* &aRow); NS_IMETHOD SetCaretAfterTableEdit(nsIDOMElement* aTable, PRInt32 aRow, PRInt32 aCol, PRInt32 aDirection); - NS_IMETHOD GetSelectedOrParentTableElement(nsIDOMElement* &aTableElement, nsString& aTagName, PRBool &aIsSelected); - + NS_IMETHOD GetSelectedOrParentTableElement(nsIDOMElement* &aTableElement, nsString& aTagName, PRInt32 &aSelectedCount); + NS_IMETHOD GetSelectedCellsType(nsIDOMElement *aElement, PRUint32 &aSelectionType); + // Finds the first selected cell in first range of selection + // This is in the *order of selection*, not order in the table + // (i.e., each cell added to selection is added in another range + // in the selection's rangelist, independent of location in table) + NS_IMETHOD GetFirstSelectedCell(nsIDOMElement **aCell); + // Get next cell until no more are found. Always use GetFirstSelected cell first + NS_IMETHOD GetNextSelectedCell(nsIDOMElement **aCell); + // Selection and navigation /* obsolete NS_IMETHOD MoveSelectionUp(nsIAtom *aIncrement, PRBool aExtendSelection); @@ -348,24 +356,23 @@ protected: // Needed to do appropriate deleting when last cell or row is about to be deleted // This doesn't count cells that don't start in the given row (are spanning from row above) PRInt32 GetNumberOfCellsInRow(nsIDOMElement* aTable, PRInt32 rowIndex); - + // Test if all cells in row or column at given index are selected + PRBool AllCellsInRowSelected(nsIDOMElement *aTable, PRInt32 aRowIndex, PRInt32 aNumberOfColumns); + PRBool AllCellsInColumnSelected(nsIDOMElement *aTable, PRInt32 aColIndex, PRInt32 aNumberOfRows); + // Most insert methods need to get the same basic context data NS_IMETHOD GetCellContext(nsCOMPtr &aSelection, nsCOMPtr &aTable, nsCOMPtr &aCell, nsCOMPtr &aCellParent, PRInt32& aCellOffset, PRInt32& aRow, PRInt32& aCol); - // Finds the first selected cell in first range of selection - // This is in the *order of selection*, not order in the table - // (i.e., each cell added to selection is added in another range - // in the selection's rangelist, independent of location in table) - NS_IMETHOD GetFirstSelectedCell(nsIDOMElement **aCell); - // Fallback method: Call this after using ClearSelection() and you // failed to set selection to some other content in the document NS_IMETHOD SetSelectionAtDocumentStart(nsIDOMSelection *aSelection); - // end of table editing utilities + +// End of Table Editing utilities + NS_IMETHOD ReParentContentOfNode(nsIDOMNode *aNode, nsString &aParentTag, @@ -607,13 +614,8 @@ protected: PRBool mCachedUnderlineStyle; nsString mCachedFontName; - // True when selection consists of table cell(s) - PRBool mSelectingTableCells; - - // Used to monitor block of cells selected - // by dragging mouse across the table - nsCOMPtr mStartSelectedCell; - nsCOMPtr mCurrentSelectedCell; + // Used by GetFirstSelectedCell and GetNextSelectedCell + PRInt32 mSelectedCellIndex; public: static nsIAtom *gTypingTxnName; diff --git a/editor/libeditor/html/nsTableEditor.cpp b/editor/libeditor/html/nsTableEditor.cpp index 2badf4f2041..229b790d30c 100644 --- a/editor/libeditor/html/nsTableEditor.cpp +++ b/editor/libeditor/html/nsTableEditor.cpp @@ -40,6 +40,8 @@ #include "nsITableCellLayout.h" // For efficient access to table cell #include "nsITableLayout.h" // data owned by the table and cell frames #include "nsHTMLEditor.h" +#include "nsIFrameSelection.h" // For TABLESELECTION_ defines +#include "nsVoidArray.h" #include "nsEditorUtils.h" @@ -799,7 +801,7 @@ nsHTMLEditor::DeleteTableColumn(PRInt32 aNumber) if (curCell) { // This must always be >= 1 - NS_ASSERTION((actualRowSpan > 0),"Effective ROWSPAN = 0 in DeleteTableColumn"); + NS_ASSERTION((actualRowSpan > 0),"Actual ROWSPAN = 0 in DeleteTableColumn"); // Find cells that don't start in column we are deleting if (curStartColIndex < startColIndex || colSpan > 1 || colSpan == 0) @@ -1643,13 +1645,9 @@ nsHTMLEditor::GetCellContext(nsCOMPtr &aSelection, if (NS_FAILED(res)) return res; if (!aSelection) return NS_ERROR_FAILURE; - // Find the first selected cell -// res = GetFirstSelectedCell(getter_AddRefs(aCell)); if (!aCell) { - //If a cell wasn't selected, then assume the selection is INSIDE - // and use anchor node to search up to the containing cell - // Test if selected node (from anchor node) is a cell + // Get cell if it's the child of selection anchor node, // or get the enclosing by a cell res = GetElementOrParentByTagName("td", nsnull, getter_AddRefs(aCell)); if (NS_FAILED(res)) return res; @@ -1683,78 +1681,109 @@ nsHTMLEditor::GetFirstSelectedCell(nsIDOMElement **aCell) if (NS_FAILED(res)) return res; if (!selection) return NS_ERROR_FAILURE; -//TODO: Replace this with code below new "table cell mode" flag is implemented - - nsCOMPtr enumerator; - res = selection->GetEnumerator(getter_AddRefs(enumerator)); - - if (NS_FAILED(res)) return res; - if (!enumerator) return NS_ERROR_FAILURE; - enumerator->First(); - nsCOMPtr currentItem; - res = enumerator->CurrentItem(getter_AddRefs(currentItem)); - if ((NS_SUCCEEDED(res)) && currentItem) - { - nsCOMPtr range( do_QueryInterface(currentItem) ); - nsCOMPtr iter; - res = nsComponentManager::CreateInstance(kCContentIteratorCID, nsnull, - NS_GET_IID(nsIContentIterator), - getter_AddRefs(iter)); - if (NS_FAILED(res)) return res; - if (!iter) return NS_ERROR_FAILURE; - - iter->Init(range); - // loop through the content iterator for each content node - nsCOMPtr content; - - while (NS_ENUMERATOR_FALSE == iter->IsDone()) - { - res = iter->CurrentNode(getter_AddRefs(content)); - // Not likely! - if (NS_FAILED(res)) return NS_ERROR_FAILURE; - - nsCOMPtr atom; - content->GetTag(*getter_AddRefs(atom)); - if (atom.get() == nsIEditProperty::td || - atom.get() == nsIEditProperty::th ) - { - // We found a cell - nsCOMPtr cellElement = do_QueryInterface(content); - if (cellElement) - { - *aCell = cellElement.get(); - NS_ADDREF(*aCell); - } - return NS_OK; - } - iter->Next(); - } - } - return NS_EDITOR_ELEMENT_NOT_FOUND; - -#if 0 -//TODO: Do this only after checking the new "table cell mode" flag - // The first cell is the starting node in the first selection range nsCOMPtr firstRange; res = selection->GetRangeAt(0, getter_AddRefs(firstRange)); if (NS_FAILED(res)) return res; if (!firstRange) return NS_ERROR_FAILURE; +#ifdef DEBUG_cmanske + { + nsCOMPtr anchorNode; + PRInt32 anchorOffset = -1; + selection->GetAnchorNode(getter_AddRefs(anchorNode)); + selection->GetAnchorOffset(&anchorOffset); + + nsCOMPtr focusNode; + res = selection->GetFocusNode(getter_AddRefs(focusNode)); + if (NS_FAILED(res)) return res; + PRInt32 focusOffset = -1; + selection->GetFocusOffset(&focusOffset); + + nsAutoString name; + anchorNode->GetNodeName(name); + printf("GetFirstSelectedCell: Anchor node of selection: "); + wprintf(name.GetUnicode()); + printf(" Offset: %d\n", anchorOffset); + focusNode->GetNodeName(name); + printf("Focus node of selection: "); + wprintf(name.GetUnicode()); + printf(" Offset: %x\n", focusOffset); + + PRInt32 rangeCount; + res = selection->GetRangeCount(&rangeCount); + printf(" RangeCount: %d\n", rangeCount); + printf(" Range pointer = %d\n", firstRange); + } +#endif + + // This is failing -- range doesn't match that set when selecting cell! + nsCOMPtr cellNode; res = GetFirstNodeInRange(firstRange, getter_AddRefs(cellNode)); if (NS_FAILED(res)) return res; if (!cellNode) return NS_ERROR_FAILURE; - nsCOMPtr cellElement = do_QueryInterface(cellNode); - - if (cellElement) + + if (IsTableCell(cellNode)) { + nsCOMPtr cellElement = do_QueryInterface(cellNode); *aCell = cellElement.get(); NS_ADDREF(*aCell); } - else res = NS_EDITOR_ELEMENT_NOT_FOUND; + else + res = NS_EDITOR_ELEMENT_NOT_FOUND; + + mSelectedCellIndex = 1; + + return res; +} + +NS_IMETHODIMP +nsHTMLEditor::GetNextSelectedCell(nsIDOMElement **aCell) +{ + if (!aCell) return NS_ERROR_NULL_POINTER; + *aCell = nsnull; + + nsCOMPtr selection; + nsresult res = nsEditor::GetSelection(getter_AddRefs(selection)); + if (NS_FAILED(res)) return res; + if (!selection) return NS_ERROR_FAILURE; + + PRInt32 rangeCount; + res = selection->GetRangeCount(&rangeCount); + if (NS_FAILED(res)) return res; + + // Don't even try if index exceeds range count + if (mSelectedCellIndex >= rangeCount) + { + // Should we reset index? + // Maybe better to force recalling GetFirstSelectedCell() + //mSelectedCellIndex = 0; + return NS_EDITOR_ELEMENT_NOT_FOUND; + } + + // Get first node in first range of selection - test if it's a cell + nsCOMPtr range; + res = selection->GetRangeAt(mSelectedCellIndex, getter_AddRefs(range)); + if (NS_FAILED(res)) return res; + if (!range) return NS_ERROR_FAILURE; + + nsCOMPtr cellNode; + res = nsEditor::GetFirstNodeInRange(range, getter_AddRefs(cellNode)); + if (NS_FAILED(res)) return res; + if (!cellNode) return NS_ERROR_FAILURE; + if (IsTableCell(cellNode)) + { + nsCOMPtr cellElement = do_QueryInterface(cellNode); + *aCell = cellElement.get(); + NS_ADDREF(*aCell); + } + else + res = NS_EDITOR_ELEMENT_NOT_FOUND; + + // Setup for next cell + mSelectedCellIndex++; return res; -#endif } NS_IMETHODIMP @@ -1842,11 +1871,11 @@ nsHTMLEditor::SetCaretAfterTableEdit(nsIDOMElement* aTable, PRInt32 aRow, PRInt3 } NS_IMETHODIMP -nsHTMLEditor::GetSelectedOrParentTableElement(nsIDOMElement* &aTableElement, nsString& aTagName, PRBool &aIsSelected) +nsHTMLEditor::GetSelectedOrParentTableElement(nsIDOMElement* &aTableElement, nsString& aTagName, PRInt32 &aSelectedCount) { aTableElement = nsnull; aTagName = ""; - aIsSelected = PR_FALSE; + aSelectedCount = 0; nsCOMPtr selection; nsresult res = nsEditor::GetSelection(getter_AddRefs(selection)); @@ -1858,52 +1887,218 @@ nsHTMLEditor::GetSelectedOrParentTableElement(nsIDOMElement* &aTableElement, nsS nsAutoString tdName("td"); nsCOMPtr anchorNode; - - // Find the first selected cell - // TODO: Handle multiple cells selected! - nsCOMPtr firstCell; - - nsCOMPtr tableElement; - res = GetFirstSelectedCell(getter_AddRefs(tableElement)); - if(NS_FAILED(res)) return res; - if (tableElement) - { - aTagName = tdName; - aIsSelected = PR_TRUE; - goto SET_RETURN_ELEMENT; - } - - // See if table or row is selected - res = GetSelectedElement(tableName, getter_AddRefs(tableElement)); - if(NS_FAILED(res)) return res; - if (tableElement) - { - aTagName = tableName; - aIsSelected = PR_TRUE; - goto SET_RETURN_ELEMENT; - } - res = GetSelectedElement(trName, getter_AddRefs(tableElement)); - if(NS_FAILED(res)) return res; - if (tableElement) - { - aTagName = trName; - aIsSelected = PR_TRUE; - goto SET_RETURN_ELEMENT; - } - - // Look for a table cell parent res = selection->GetAnchorNode(getter_AddRefs(anchorNode)); - if (NS_FAILED(res)) return res; - if (!anchorNode) return NS_ERROR_FAILURE; - - res = GetElementOrParentByTagName(tdName, anchorNode, getter_AddRefs(tableElement)); if(NS_FAILED(res)) return res; + if (!anchorNode) return NS_ERROR_FAILURE; + + nsCOMPtr tableElement; + nsCOMPtr selectedNode; + + // Get child of anchor node, if exists + PRBool hasChildren; + anchorNode->HasChildNodes(&hasChildren); + + if (hasChildren) + { + PRInt32 anchorOffset; + res = selection->GetAnchorOffset(&anchorOffset); + if (NS_FAILED(res)) return res; + selectedNode = nsEditor::GetChildAt(anchorNode, anchorOffset); + if (!selectedNode) + selectedNode = anchorNode; + + nsAutoString tag; + nsEditor::GetTagString(selectedNode,tag); + + if (tag == tdName) + { + tableElement = do_QueryInterface(anchorNode); + aTagName = tdName; + // Each cell is in its own selection range, + // so count signals multiple-cell selection + res = selection->GetRangeCount(&aSelectedCount); + if (NS_FAILED(res)) return res; + } + else if(tag == tableName) + { + tableElement = do_QueryInterface(anchorNode); + aTagName = tableName; + aSelectedCount = 1; + } + else if(tag == trName) + { + tableElement = do_QueryInterface(anchorNode); + aTagName = trName; + aSelectedCount = 1; + } + } + if (!tableElement) + { + // Didn't find a table element -- find a cell parent + res = GetElementOrParentByTagName(tdName, anchorNode, getter_AddRefs(tableElement)); + if(NS_FAILED(res)) return res; + if (tableElement) + aTagName = tdName; + } if (tableElement) { - aTagName = tdName; -SET_RETURN_ELEMENT: aTableElement = tableElement.get(); NS_ADDREF(aTableElement); } return res; } + +static PRBool IndexNotTested(nsVoidArray *aArray, PRInt32 aIndex) +{ + if (aArray) + { + PRInt32 count = aArray->Count(); + for (PRInt32 i = 0; i < count; i++) + { + if(aIndex == (PRInt32)(aArray->ElementAt(i))) + return PR_FALSE; + } + } + return PR_TRUE; +} + +NS_IMETHODIMP +nsHTMLEditor::GetSelectedCellsType(nsIDOMElement *aElement, PRUint32 &aSelectionType) +{ + aSelectionType = 0; + + // Be sure we have a table element + // (if aElement is null, this uses selection's anchor node) + nsCOMPtr table; + + nsresult res = GetElementOrParentByTagName("table", aElement, getter_AddRefs(table)); + if (NS_FAILED(res)) return res; + + PRInt32 rowCount, colCount; + res = GetTableSize(table, rowCount, colCount); + if (NS_FAILED(res)) return res; + + // Traverse all selected cells + // (Failure here may indicate that aCellElement wasn't really a cell) + nsCOMPtr selectedCell; + res = GetFirstSelectedCell(getter_AddRefs(selectedCell)); + if (NS_FAILED(res)) return res; + + // We have at least one selected cell, so set return value + aSelectionType = TABLESELECTION_CELL; + + // Store indexes of each row/col to avoid duplication of searches + nsVoidArray indexArray; + + PRBool allCellsInRowAreSelected = PR_TRUE; + PRBool allCellsInColAreSelected = PR_FALSE; + while (NS_SUCCEEDED(res) && selectedCell) + { + // Get the cell's location in the cellmap + PRInt32 startRowIndex, startColIndex; + res = GetCellIndexes(selectedCell, startRowIndex, startColIndex); + if(NS_FAILED(res)) return res; + + if (IndexNotTested(&indexArray, startColIndex)) + { + indexArray.AppendElement((void*)startColIndex); + allCellsInRowAreSelected = AllCellsInRowSelected(table, startRowIndex, colCount); + // We're done as soon as we fail for any row + if (!allCellsInRowAreSelected) break; + } + res = GetNextSelectedCell(getter_AddRefs(selectedCell)); + } + + if (allCellsInRowAreSelected) + { + aSelectionType = TABLESELECTION_ROW; + return NS_OK; + } + // Test for columns + + // Empty the indexArray + indexArray.Clear(); + + // Start at first cell again + res = GetFirstSelectedCell(getter_AddRefs(selectedCell)); + while (NS_SUCCEEDED(res) && selectedCell) + { + // Get the cell's location in the cellmap + PRInt32 startRowIndex, startColIndex; + res = GetCellIndexes(selectedCell, startRowIndex, startColIndex); + if(NS_FAILED(res)) return res; + + if (IndexNotTested(&indexArray, startRowIndex)) + { + indexArray.AppendElement((void*)startColIndex); + allCellsInColAreSelected = AllCellsInColumnSelected(table, startColIndex, colCount); + // We're done as soon as we fail for any column + if (!allCellsInRowAreSelected) break; + } + res = GetNextSelectedCell(getter_AddRefs(selectedCell)); + } + if (allCellsInColAreSelected) + aSelectionType = TABLESELECTION_COLUMN; + + return NS_OK; +} + +PRBool +nsHTMLEditor::AllCellsInRowSelected(nsIDOMElement *aTable, PRInt32 aRowIndex, PRInt32 aNumberOfColumns) +{ + if (!aTable) return PR_FALSE; + + PRInt32 curStartRowIndex, curStartColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan; + PRBool isSelected; + + for( PRInt32 col = 0; col < aNumberOfColumns; col += actualColSpan) + { + nsCOMPtr cell; + nsresult res = GetCellDataAt(aTable, aRowIndex, col, *getter_AddRefs(cell), + curStartRowIndex, curStartColIndex, rowSpan, colSpan, + actualRowSpan, actualColSpan, isSelected); + + if (NS_FAILED(res)) return PR_FALSE; + // Skip cell spanning into this location from a row above + if (curStartRowIndex == aRowIndex) + { + // If no cell, we may have a "ragged" right edge, + // so return TRUE only if we already found a cell in the row + if (!cell) return (col > 0) ? PR_TRUE : PR_FALSE; + // Return as soon as a non-selected cell is found + if (!isSelected) return PR_FALSE; + } + NS_ASSERTION((actualColSpan > 0),"ActualColSpan = 0 in AllCellsInRowSelected"); + } + return PR_TRUE; +} + +PRBool +nsHTMLEditor::AllCellsInColumnSelected(nsIDOMElement *aTable, PRInt32 aColIndex, PRInt32 aNumberOfRows) +{ + if (!aTable) return PR_FALSE; + + PRInt32 curStartRowIndex, curStartColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan; + PRBool isSelected; + + for( PRInt32 row = 0; row < aNumberOfRows; row += actualRowSpan) + { + nsCOMPtr cell; + nsresult res = GetCellDataAt(aTable, row, aColIndex, *getter_AddRefs(cell), + curStartRowIndex, curStartColIndex, rowSpan, colSpan, + actualRowSpan, actualColSpan, isSelected); + + if (NS_FAILED(res)) return PR_FALSE; + // Skip cell spanning into this location from a column to the left + if (curStartColIndex == aColIndex) + { + // If no cell, we must have a "ragged" right edge on the last column + // so return TRUE only if we already found a cell in the row + if (!cell) return (row > 0) ? PR_TRUE : PR_FALSE; + // Return as soon as a non-selected cell is found + if (!isSelected) return PR_FALSE; + } + NS_ASSERTION((actualRowSpan > 0),"ActualRowSpan = 0 in AllCellsInColumnSelected"); + } + return PR_TRUE; +} diff --git a/editor/public/nsITableEditor.h b/editor/public/nsITableEditor.h index 9f3171bee44..7b188c0434a 100644 --- a/editor/public/nsITableEditor.h +++ b/editor/public/nsITableEditor.h @@ -237,13 +237,48 @@ public: * @param aTableElement The table element (table, row, or cell) returned * @param aTagName The tagname of returned element * Note that "td" will be returned if name is actually "th" - * @param aIsSelected Tells if element returned is a selected element - * (false if element is a parent cell of selection) + * @param aSelectedCount How many table elements were selected + * This tells us if we have multiple cells selected + * (0 if element is a parent cell of selection) + * * Returns NS_EDITOR_ELEMENT_NOT_FOUND if an element is not found (passes NS_SUCCEEDED macro) */ - NS_IMETHOD GetSelectedOrParentTableElement(nsIDOMElement* &aTableElement, nsString& aTagName, PRBool &aIsSelected)=0; + NS_IMETHOD GetSelectedOrParentTableElement(nsIDOMElement* &aTableElement, nsString& aTagName, PRInt32 &aSelectedCount)=0; + /** Generally used after GetSelectedOrParentTableElement + * to test if selected cells are complete rows or columns + * + * @param aElement Any table or cell element or any element inside a table + * Used to get enclosing table. + * If null, selection's anchorNode is used + * + * @param aSelectionType Returns: + * 0 aCellElement was not a cell (returned result = NS_ERROR_FAILURE) + * TABLESELECTION_CELL There are 1 or more cells selected + * but complete rows or columns are not selected + * TABLESELECTION_ROW All cells are in 1 or more rows + * and in each row, all cells selected + * Note: This is the value if all rows (thus all cells) are selected + * TABLESELECTION_COLUMN All cells are in 1 or more columns + * and in each column, all cells are selected + */ + NS_IMETHOD GetSelectedCellsType(nsIDOMElement *aElement, PRUint32 &aSelectionType)=0; + + /** Get first selected element from first selection range. + * Assumes cell-selection model where each cell + * is in a separate range (selection parent node is table row) + * @param aCell Selected cell or null if ranges don't contain cell selections + */ + NS_IMETHOD GetFirstSelectedCell(nsIDOMElement **aCell)=0; + + /** Get next selected cell element from first selection range. + * Assumes cell-selection model where each cell + * is in a separate range (selection parent node is table row) + * Always call GetFirstSelectedCell() to initialize stored index of "next" cell + * @param aCell Selected cell or null if no more selected cells + * or ranges don't contain cell selections + */ + NS_IMETHOD GetNextSelectedCell(nsIDOMElement **aCell)=0; }; - #endif // nsITableEditor_h__ diff --git a/editor/ui/composer/content/EditorCommands.js b/editor/ui/composer/content/EditorCommands.js index c1eb1475588..1d724fabc75 100644 --- a/editor/ui/composer/content/EditorCommands.js +++ b/editor/ui/composer/content/EditorCommands.js @@ -1153,9 +1153,9 @@ function SetBackColorString(xulElementID) if (xulElement) { var textVal; - var isSelectedObj = new Object(); + var selectedCountObj = new Object(); var tagNameObj = new Object(); - var element = editorShell.GetSelectedOrParentTableElement(tagNameObj, isSelectedObj); + var element = editorShell.GetSelectedOrParentTableElement(tagNameObj, selectedCountObj); if (tagNameObj.value == "table") textVal = editorShell.GetString("TableBackColor"); @@ -1564,6 +1564,7 @@ function EditorInsertOrEditTable(insertAllowed) function EditorTableCellProperties() { +dump("*** EditorTableCellProperties\n"); var cell = editorShell.GetElementOrParentByTagName("td", null); if (cell) { // Start Table Properties dialog on the "Cell" panel diff --git a/editor/ui/composer/content/EditorCommandsDebug.js b/editor/ui/composer/content/EditorCommandsDebug.js index 9d2bc1587c5..e22e23d4de4 100644 --- a/editor/ui/composer/content/EditorCommandsDebug.js +++ b/editor/ui/composer/content/EditorCommandsDebug.js @@ -107,6 +107,18 @@ function EditorTestSelection() function EditorTestTableLayout() { + dump("\n\n\n************ Dump Selection Ranges ************\n"); + var selection = editorShell.editorSelection; + for (i = 0; i < selection.rangeCount; i++) + { + var range = selection.getRangeAt(i); + if (range) + { + dump("Range "+i+": StartParent="+range.startParent+", offset="+range.startOffset+"\n"); + } + } + dump("\n\n"); + var table = editorShell.GetElementOrParentByTagName("table", null); if (!table) { dump("Enclosing Table not found: Place caret in a table cell to do this test\n\n"); diff --git a/editor/ui/composer/content/editor.xul b/editor/ui/composer/content/editor.xul index a2defd01021..ac357142def 100644 --- a/editor/ui/composer/content/editor.xul +++ b/editor/ui/composer/content/editor.xul @@ -50,6 +50,8 @@ + + diff --git a/editor/ui/dialogs/content/EdTableProps.js b/editor/ui/dialogs/content/EdTableProps.js index 55746c4dd71..44fc01f2290 100644 --- a/editor/ui/dialogs/content/EdTableProps.js +++ b/editor/ui/dialogs/content/EdTableProps.js @@ -50,6 +50,7 @@ var bgcolor = "bgcolor"; var CellIsHeader = false; var rowCount = 1; var colCount = 1; +var selectedCellCount = 0; var error = 0; // dialog initialization code @@ -81,7 +82,11 @@ function Startup() // dialog.TableLeaveLocCheck = document.getElementById("TableLeaveLocCheck"); // Cell Panel - dialog.SelectionSelect = document.getElementById("SelectionSelect"); + dialog.SelectionList = document.getElementById("SelectionList"); + dialog.SelectCellItem = document.getElementById("SelectCellItem"); + dialog.SelectRowItem = document.getElementById("SelectRowItem"); + dialog.SelectColumnItem = document.getElementById("SelectColumnItem"); + dialog.CellHeightInput = document.getElementById("CellHeightInput"); dialog.CellHeightUnits = document.getElementById("CellHeightUnits"); dialog.CellWidthInput = document.getElementById("CellWidthInput"); @@ -98,14 +103,28 @@ function Startup() // dialog.CellLeaveLocCheck = document.getElementById("CellLeaveLocCheck"); TableElement = editorShell.GetElementOrParentByTagName("table", null); - CellElement = editorShell.GetElementOrParentByTagName("td", null); + var tagNameObj = new Object; + var countObj = new Object; + var element = editorShell.GetSelectedOrParentTableElement(tagNameObj, countObj); + var tagName = tagNameObj.value; + + if (tagNameObj.value == "td") + { +dump("*** Found cell around selection\n"); + CellElement = element; + } + selectedCellCount = countObj.value; + + // If the count is 0, then we are inside the cell, so select it + if (selectedCellCount == 0) + editorShell.SelectCell(); - // We allow a missing cell -- see below if(!TableElement) { dump("Failed to get selected element or create a new one!\n"); window.close(); } + // We allow a missing cell -- see below TabPanel = document.getElementById("TabPanel"); var TableTab = document.getElementById("TableTab"); @@ -123,6 +142,7 @@ function Startup() // Activate the Cell Panel if requested if (currentPanel == CellPanel) { +dump("*** Requested CellPanel for Table Properties dialog.\n"); // We must have a cell element to start in this panel if(!CellElement) { @@ -156,11 +176,17 @@ function Startup() doSetOKCancel(onOK, null); + // Note: we must use TableElement, not globalTableElement for these, + // thus we should not put this in InitDialog. + // Instead, monitor desired counts with separate globals + rowCount = editorShell.GetTableRowCount(TableElement); + colCount = editorShell.GetTableColumnCount(TableElement); + // This uses values set on global Elements; InitDialog(); // Should be dialog.TableRowsInput, or - // SelectionSelect in cel panenl, + // SelectionList in cel panenl, // but this is disabled for Beta1disabled for Beta1 if (currentPanel == CellPanel) dialog.CellHeightInput.focus(); @@ -172,19 +198,8 @@ function Startup() function InitDialog() { // Get Table attributes - try { - // For some strange reason, we are failing to the "layoutObject" for the table! - rowCount = editorShell.GetTableRowCount(globalTableElement); - dialog.TableRowsInput.value = rowCount; - colCount = editorShell.GetTableColumnCount(tableGlobatlElement); - dialog.TableColumnsInput.value = colCount; - } - catch (ex) { - dump("Failed to get table rows and/or columns count\n"); - dialog.TableRowsInput.value = ""; - dialog.TableColumnsInput.value = ""; - } - + dialog.TableRowsInput.value = rowCount; + dialog.TableColumnsInput.value = colCount; dialog.TableHeightInput.value = InitPixelOrPercentCombobox(globalTableElement, "height", "TableHeightUnits"); dialog.TableWidthInput.value = InitPixelOrPercentCombobox(globalTableElement, "width", "TableWidthUnits"); dialog.BorderWidthInput.value = globalTableElement.border; @@ -220,12 +235,25 @@ dump("Caption Element = "+captionElement+"\n"); // Get cell attributes if (globalCellElement) { - //TODO: Test if all cells in selection are in a row or column, - // and set dialog.SelectionSelect.selectedIndex appropriately + // Test if entire rows or columns are selected and set menu appropriately + var SelectionType = editorShell.GetSelectedCellsType(TableElement); + dump("SelectionList.selectedIndex = "+dialog.SelectionList.selectedIndex+"\n"); + switch (SelectionType) + { + case 2: + dialog.SelectionList.selectedItem = dialog.SelectRowItem; + break; + case 3: + dialog.SelectionList.selectedItem = dialog.SelectColumnItem; + break; + default: + dialog.SelectionList.selectedItem = dialog.SelectCellItem; + break; + } dialog.CellHeightInput.value = InitPixelOrPercentCombobox(globalCellElement, "height", "CellHeightUnits"); dialog.CellWidthInput.value = InitPixelOrPercentCombobox(globalCellElement, "width", "CellWidthUnits"); -//BUG: We don't suppor "rowSpan" or "colSpan" JS attributes? +//BUG: We don't support "rowSpan" or "colSpan" JS attributes? dump("RowSpan="+globalCellElement.rowSpan+" ColSpan="+globalCellElement.colSpan+"\n"); dialog.RowSpanInput.value = globalCellElement.getAttribute("rowspan"); @@ -584,12 +612,22 @@ function onOK() { if (ValidateData()) { + editorShell.BeginBatchChanges(); editorShell.CloneAttributes(TableElement, globalTableElement); - editorShell.CloneAttributes(CellElement, globalCellElement); + // Apply changes to all selected cells + var selectedCell = editorShell.GetFirstSelectedCell(); + while (selectedCell) + { + // TODO: We need to change this to set only particular attributes + // Set an "ignore" attribute on the attribute node? + editorShell.CloneAttributes(selectedCell,globalCellElement); + selectedCell = editorShell.GetNextSelectedCell(); + } //TODO: DOM manipulation to add/remove table rows/columns, // Switch cell type to TH -- involves removing TD and replacing with TD // Creating and moving CAPTION element + editorShell.EndBatchChanges(); return true; } return false; diff --git a/editor/ui/dialogs/content/EdTableProps.xul b/editor/ui/dialogs/content/EdTableProps.xul index 9f607a1ca6b..9d0a2f18617 100644 --- a/editor/ui/dialogs/content/EdTableProps.xul +++ b/editor/ui/dialogs/content/EdTableProps.xul @@ -172,11 +172,13 @@ <text align="left" value="&cellSelection.label;"/> - - &cellSelectionCell.label; - &cellSelectionRow.label; - &cellSelectionColumn.label; - + + + + + + + diff --git a/editor/ui/dialogs/skin/EditorDialog.css b/editor/ui/dialogs/skin/EditorDialog.css index 5f82cce4dc6..3009c3bdba6 100644 --- a/editor/ui/dialogs/skin/EditorDialog.css +++ b/editor/ui/dialogs/skin/EditorDialog.css @@ -45,8 +45,8 @@ tr { input { height: 1em; max-height: 1em; - padding-top: 5px; - padding-bottom: 5px; + padding-top: 2px; + padding-bottom: 2px; margin: 0px; }