• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2010 The Android Open Source Project
3  *
4  * Licensed under the Eclipse Public License, Version 1.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.eclipse.org/org/documents/epl-v10.php
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.android.ide.common.layout;
17 
18 import static com.android.ide.common.layout.LayoutConstants.FQCN_TABLE_ROW;
19 
20 import com.android.ide.common.api.DropFeedback;
21 import com.android.ide.common.api.IClientRulesEngine;
22 import com.android.ide.common.api.IMenuCallback;
23 import com.android.ide.common.api.INode;
24 import com.android.ide.common.api.INodeHandler;
25 import com.android.ide.common.api.IViewRule;
26 import com.android.ide.common.api.InsertType;
27 import com.android.ide.common.api.RuleAction;
28 import com.android.ide.common.api.SegmentType;
29 
30 import java.net.URL;
31 import java.util.Collections;
32 import java.util.HashSet;
33 import java.util.List;
34 import java.util.Set;
35 
36 /**
37  * An {@link IViewRule} for android.widget.TableLayout.
38  */
39 public class TableLayoutRule extends LinearLayoutRule {
40     // A table is a linear layout, but with a few differences:
41     // the default is vertical, not horizontal
42     // The fill of all children should be wrap_content
43 
44     private static final String ACTION_ADD_ROW = "_addrow"; //$NON-NLS-1$
45     private static final String ACTION_REMOVE_ROW = "_removerow"; //$NON-NLS-1$
46     private static final URL ICON_ADD_ROW =
47         TableLayoutRule.class.getResource("addrow.png"); //$NON-NLS-1$
48     private static final URL ICON_REMOVE_ROW =
49         TableLayoutRule.class.getResource("removerow.png"); //$NON-NLS-1$
50 
51     @Override
isVertical(INode node)52     protected boolean isVertical(INode node) {
53         // Tables are always vertical
54         return true;
55     }
56 
57     @Override
supportsOrientation()58     protected boolean supportsOrientation() {
59         return false;
60     }
61 
62     @Override
onChildInserted(INode child, INode parent, InsertType insertType)63     public void onChildInserted(INode child, INode parent, InsertType insertType) {
64         // Overridden to inhibit the setting of layout_width/layout_height since
65         // it should always be match_parent
66     }
67 
68     /**
69      * Add an explicit "Add Row" action to the context menu
70      */
71     @Override
addContextMenuActions(List<RuleAction> actions, final INode selectedNode)72     public void addContextMenuActions(List<RuleAction> actions, final INode selectedNode) {
73         super.addContextMenuActions(actions, selectedNode);
74 
75         IMenuCallback addTab = new IMenuCallback() {
76             @Override
77             public void action(RuleAction action, List<? extends INode> selectedNodes,
78                     final String valueId, Boolean newValue) {
79                 final INode node = selectedNode;
80                 INode newRow = node.appendChild(FQCN_TABLE_ROW);
81                 mRulesEngine.select(Collections.singletonList(newRow));
82             }
83         };
84         actions.add(RuleAction.createAction("_addrow", "Add Row", addTab, null, 5, false)); //$NON-NLS-1$
85     }
86 
87     @Override
addLayoutActions(List<RuleAction> actions, final INode parentNode, final List<? extends INode> children)88     public void addLayoutActions(List<RuleAction> actions, final INode parentNode,
89             final List<? extends INode> children) {
90         super.addLayoutActions(actions, parentNode, children);
91         addTableLayoutActions(mRulesEngine, actions, parentNode, children);
92     }
93 
94     /**
95      * Adds layout actions to add and remove toolbar items
96      */
addTableLayoutActions(final IClientRulesEngine rulesEngine, List<RuleAction> actions, final INode parentNode, final List<? extends INode> children)97     static void addTableLayoutActions(final IClientRulesEngine rulesEngine,
98             List<RuleAction> actions, final INode parentNode,
99             final List<? extends INode> children) {
100         IMenuCallback actionCallback = new IMenuCallback() {
101             @Override
102             public void action(final RuleAction action, List<? extends INode> selectedNodes,
103                     final String valueId, final Boolean newValue) {
104                 parentNode.editXml("Add/Remove Table Row", new INodeHandler() {
105                     @Override
106                     public void handle(INode n) {
107                         if (action.getId().equals(ACTION_ADD_ROW)) {
108                             // Determine the index of the selection, if any; if there is
109                             // a selection, insert the row before the current row, otherwise
110                             // append it to the table.
111                             int index = -1;
112                             INode[] rows = parentNode.getChildren();
113                             if (children != null) {
114                                 findTableIndex:
115                                 for (INode child : children) {
116                                     // Find direct child of table layout
117                                     while (child != null && child.getParent() != parentNode) {
118                                         child = child.getParent();
119                                     }
120                                     if (child != null) {
121                                         // Compute index of direct child of table layout
122                                         for (int i = 0; i < rows.length; i++) {
123                                             if (rows[i] == child) {
124                                                 index = i;
125                                                 break findTableIndex;
126                                             }
127                                         }
128                                     }
129                                 }
130                             }
131                             INode newRow;
132                             if (index == -1) {
133                                 newRow = parentNode.appendChild(FQCN_TABLE_ROW);
134                             } else {
135                                 newRow = parentNode.insertChildAt(FQCN_TABLE_ROW, index);
136                             }
137                             rulesEngine.select(Collections.singletonList(newRow));
138                         } else if (action.getId().equals(ACTION_REMOVE_ROW)) {
139                             // Find the direct children of the TableLayout to delete;
140                             // this is necessary since TableRow might also use
141                             // this implementation, so the parentNode is the true
142                             // TableLayout but the children might be grand children.
143                             Set<INode> targets = new HashSet<INode>();
144                             for (INode child : children) {
145                                 while (child != null && child.getParent() != parentNode) {
146                                     child = child.getParent();
147                                 }
148                                 if (child != null) {
149                                     targets.add(child);
150                                 }
151                             }
152                             for (INode target : targets) {
153                                 parentNode.removeChild(target);
154                             }
155                         }
156                     }
157                 });
158             }
159         };
160 
161         // Add Row
162         actions.add(RuleAction.createSeparator(150));
163         actions.add(RuleAction.createAction(ACTION_ADD_ROW, "Add Table Row", actionCallback,
164                 ICON_ADD_ROW, 160, false));
165 
166         // Remove Row (if something is selected)
167         if (children != null && children.size() > 0) {
168             actions.add(RuleAction.createAction(ACTION_REMOVE_ROW, "Remove Table Row",
169                     actionCallback, ICON_REMOVE_ROW, 170, false));
170         }
171     }
172 
173     @Override
onCreate(INode node, INode parent, InsertType insertType)174     public void onCreate(INode node, INode parent, InsertType insertType) {
175         super.onCreate(node, parent, insertType);
176 
177         if (insertType.isCreate()) {
178             // Start the table with 4 rows
179             for (int i = 0; i < 4; i++) {
180                 node.appendChild(FQCN_TABLE_ROW);
181             }
182         }
183     }
184 
185     @Override
onResizeBegin(INode child, INode parent, SegmentType horizontalEdge, SegmentType verticalEdge, Object childView, Object parentView)186     public DropFeedback onResizeBegin(INode child, INode parent, SegmentType horizontalEdge,
187             SegmentType verticalEdge, Object childView, Object parentView) {
188         // Children of a table layout cannot set their widths (it is controlled by column
189         // settings on the table). They can set their heights (though for TableRow, the
190         // height is always wrap_content).
191         if (horizontalEdge == null) { // Widths are edited by vertical edges.
192             // The user is not editing a vertical height so don't allow resizing at all
193             return null;
194         }
195         if (child.getFqcn().equals(FQCN_TABLE_ROW)) {
196             // TableRows are always WRAP_CONTENT
197             return null;
198         }
199 
200         // Allow resizing heights only
201         return super.onResizeBegin(child, parent, horizontalEdge, null /*verticalEdge*/,
202                 childView, parentView);
203     }
204 }
205