| /* |
| * Copyright (C) 2010 The Android Open Source Project |
| * |
| * Licensed under the Eclipse Public License, Version 1.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.eclipse.org/org/documents/epl-v10.php |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package com.android.ide.common.layout; |
| |
| import static com.android.SdkConstants.FQCN_TABLE_ROW; |
| |
| import com.android.annotations.NonNull; |
| import com.android.annotations.Nullable; |
| import com.android.ide.common.api.DropFeedback; |
| import com.android.ide.common.api.IClientRulesEngine; |
| import com.android.ide.common.api.IMenuCallback; |
| import com.android.ide.common.api.INode; |
| import com.android.ide.common.api.INodeHandler; |
| import com.android.ide.common.api.IViewRule; |
| import com.android.ide.common.api.InsertType; |
| import com.android.ide.common.api.RuleAction; |
| import com.android.ide.common.api.SegmentType; |
| |
| import java.net.URL; |
| import java.util.Collections; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Set; |
| |
| /** |
| * An {@link IViewRule} for android.widget.TableLayout. |
| */ |
| public class TableLayoutRule extends LinearLayoutRule { |
| // A table is a linear layout, but with a few differences: |
| // the default is vertical, not horizontal |
| // The fill of all children should be wrap_content |
| |
| private static final String ACTION_ADD_ROW = "_addrow"; //$NON-NLS-1$ |
| private static final String ACTION_REMOVE_ROW = "_removerow"; //$NON-NLS-1$ |
| private static final URL ICON_ADD_ROW = |
| TableLayoutRule.class.getResource("addrow.png"); //$NON-NLS-1$ |
| private static final URL ICON_REMOVE_ROW = |
| TableLayoutRule.class.getResource("removerow.png"); //$NON-NLS-1$ |
| |
| @Override |
| protected boolean isVertical(INode node) { |
| // Tables are always vertical |
| return true; |
| } |
| |
| @Override |
| protected boolean supportsOrientation() { |
| return false; |
| } |
| |
| @Override |
| public void onChildInserted(@NonNull INode child, @NonNull INode parent, |
| @NonNull InsertType insertType) { |
| // Overridden to inhibit the setting of layout_width/layout_height since |
| // it should always be match_parent |
| } |
| |
| /** |
| * Add an explicit "Add Row" action to the context menu |
| */ |
| @Override |
| public void addContextMenuActions(@NonNull List<RuleAction> actions, |
| final @NonNull INode selectedNode) { |
| super.addContextMenuActions(actions, selectedNode); |
| |
| IMenuCallback addTab = new IMenuCallback() { |
| @Override |
| public void action( |
| @NonNull RuleAction action, |
| @NonNull List<? extends INode> selectedNodes, |
| final @Nullable String valueId, |
| @Nullable Boolean newValue) { |
| final INode node = selectedNode; |
| INode newRow = node.appendChild(FQCN_TABLE_ROW); |
| mRulesEngine.select(Collections.singletonList(newRow)); |
| } |
| }; |
| actions.add(RuleAction.createAction("_addrow", "Add Row", addTab, null, 5, false)); //$NON-NLS-1$ |
| } |
| |
| @Override |
| public void addLayoutActions( |
| @NonNull List<RuleAction> actions, |
| final @NonNull INode parentNode, |
| final @NonNull List<? extends INode> children) { |
| super.addLayoutActions(actions, parentNode, children); |
| addTableLayoutActions(mRulesEngine, actions, parentNode, children); |
| } |
| |
| /** |
| * Adds layout actions to add and remove toolbar items |
| */ |
| static void addTableLayoutActions(final IClientRulesEngine rulesEngine, |
| List<RuleAction> actions, final INode parentNode, |
| final List<? extends INode> children) { |
| IMenuCallback actionCallback = new IMenuCallback() { |
| @Override |
| public void action( |
| final @NonNull RuleAction action, |
| @NonNull List<? extends INode> selectedNodes, |
| final @Nullable String valueId, |
| final @Nullable Boolean newValue) { |
| parentNode.editXml("Add/Remove Table Row", new INodeHandler() { |
| @Override |
| public void handle(@NonNull INode n) { |
| if (action.getId().equals(ACTION_ADD_ROW)) { |
| // Determine the index of the selection, if any; if there is |
| // a selection, insert the row before the current row, otherwise |
| // append it to the table. |
| int index = -1; |
| INode[] rows = parentNode.getChildren(); |
| if (children != null) { |
| findTableIndex: |
| for (INode child : children) { |
| // Find direct child of table layout |
| while (child != null && child.getParent() != parentNode) { |
| child = child.getParent(); |
| } |
| if (child != null) { |
| // Compute index of direct child of table layout |
| for (int i = 0; i < rows.length; i++) { |
| if (rows[i] == child) { |
| index = i; |
| break findTableIndex; |
| } |
| } |
| } |
| } |
| } |
| INode newRow; |
| if (index == -1) { |
| newRow = parentNode.appendChild(FQCN_TABLE_ROW); |
| } else { |
| newRow = parentNode.insertChildAt(FQCN_TABLE_ROW, index); |
| } |
| rulesEngine.select(Collections.singletonList(newRow)); |
| } else if (action.getId().equals(ACTION_REMOVE_ROW)) { |
| // Find the direct children of the TableLayout to delete; |
| // this is necessary since TableRow might also use |
| // this implementation, so the parentNode is the true |
| // TableLayout but the children might be grand children. |
| Set<INode> targets = new HashSet<INode>(); |
| for (INode child : children) { |
| while (child != null && child.getParent() != parentNode) { |
| child = child.getParent(); |
| } |
| if (child != null) { |
| targets.add(child); |
| } |
| } |
| for (INode target : targets) { |
| parentNode.removeChild(target); |
| } |
| } |
| } |
| }); |
| } |
| }; |
| |
| // Add Row |
| actions.add(RuleAction.createSeparator(150)); |
| actions.add(RuleAction.createAction(ACTION_ADD_ROW, "Add Table Row", actionCallback, |
| ICON_ADD_ROW, 160, false)); |
| |
| // Remove Row (if something is selected) |
| if (children != null && children.size() > 0) { |
| actions.add(RuleAction.createAction(ACTION_REMOVE_ROW, "Remove Table Row", |
| actionCallback, ICON_REMOVE_ROW, 170, false)); |
| } |
| } |
| |
| @Override |
| public void onCreate(@NonNull INode node, @NonNull INode parent, |
| @NonNull InsertType insertType) { |
| super.onCreate(node, parent, insertType); |
| |
| if (insertType.isCreate()) { |
| // Start the table with 4 rows |
| for (int i = 0; i < 4; i++) { |
| node.appendChild(FQCN_TABLE_ROW); |
| } |
| } |
| } |
| |
| @Override |
| public DropFeedback onResizeBegin(@NonNull INode child, @NonNull INode parent, |
| @Nullable SegmentType horizontalEdge, @Nullable SegmentType verticalEdge, |
| @Nullable Object childView, @Nullable Object parentView) { |
| // Children of a table layout cannot set their widths (it is controlled by column |
| // settings on the table). They can set their heights (though for TableRow, the |
| // height is always wrap_content). |
| if (horizontalEdge == null) { // Widths are edited by vertical edges. |
| // The user is not editing a vertical height so don't allow resizing at all |
| return null; |
| } |
| if (child.getFqcn().equals(FQCN_TABLE_ROW)) { |
| // TableRows are always WRAP_CONTENT |
| return null; |
| } |
| |
| // Allow resizing heights only |
| return super.onResizeBegin(child, parent, horizontalEdge, null /*verticalEdge*/, |
| childView, parentView); |
| } |
| } |