001package org.vishia.gral.widget; 002 003import java.util.ArrayList; 004import java.util.List; 005 006import org.vishia.gral.base.GralMenu; 007import org.vishia.gral.base.GralMng; 008import org.vishia.gral.base.GralWidgImpl_ifc; 009import org.vishia.gral.base.GralWidget; 010import org.vishia.gral.ifc.GralColor; 011import org.vishia.gral.ifc.GralMngBuild_ifc; 012import org.vishia.gral.ifc.GralUserAction; 013import org.vishia.gral.ifc.GralWidget_ifc; 014import org.vishia.util.KeyCode; 015 016/**This widget is a selector with tabs. It can be placed like a text field. 017 * Selecting a tab the {@link GralWidget#setActionChange(GralUserAction)} is called with the associated 018 * UserData of the tab is third parameter of {@link GralUserAction#exec(int, GralWidget_ifc, Object...)} 019 * with {@link KeyCode#activated}. 020 * That can focus, show or select some widgets, panels etc. With them a tab-panel is able to build, 021 * but in a more possibilities than usual implementations of such tab-panels. 022 * <br><br> 023 * The tabs are shift to left and right if the spaces is to less. 024 * <br><br> 025 * Tabs can be closed with right-mouse context menu. Then the {@link GralUserAction#exec(int, GralWidget_ifc, Object...)} 026 * will be called a last time for the barely known UserData the last time with {@link KeyCode#removed}. 027 * If the current selected tab will be removed, the tab left of them or the next if it was the first 028 * will be activated with calling of {@link GralUserAction#exec(int, GralWidget_ifc, Object...)} 029 * for the yet current selected tab if there is any one yet. 030 * <br><br> 031 * @author Hartmut Schorrig 032 * 033 */ 034public class GralHorizontalSelector<UserData> extends GralWidget 035{ 036 /**Version, history and copyright/copyleft. 037 * <ul> 038 * <li>2013-06-18 Hartmut created, new idea. 039 * </ul> 040 * 041 * <b>Copyright/Copyleft</b>:<br> 042 * For this source the LGPL Lesser General Public License, 043 * published by the Free Software Foundation is valid. 044 * It means: 045 * <ol> 046 * <li> You can use this source without any restriction for any desired purpose. 047 * <li> You can redistribute copies of this source to everybody. 048 * <li> Every user of this source, also the user of redistribute copies 049 * with or without payment, must accept this license for further using. 050 * <li> But the LPGL is not appropriate for a whole software product, 051 * if this source is only a part of them. It means, the user 052 * must publish this part of source, 053 * but doesn't need to publish the whole source of the own product. 054 * <li> You can study and modify (improve) this source 055 * for own using or for redistribution, but you have to license the 056 * modified sources likewise under this LGPL Lesser General Public License. 057 * You mustn't delete this Copyright/Copyleft inscription in this source file. 058 * </ol> 059 * If you intent to use this source without publishing its usage, you can get 060 * a second license subscribing a special contract with the author. 061 * 062 * @author Hartmut Schorrig = hartmut.schorrig@vishia.de 063 */ 064 @SuppressWarnings("hiding") 065 public static final int version = 20130618; 066 067 068 protected List<Item<UserData>> items = new ArrayList<Item<UserData>>(); 069 070 protected Item<UserData> actItem = null; 071 072 /**The item (index in {@link items} which is the current or actual used one. */ 073 protected int ixActItem = 0; 074 075 /**The item which is selected while the mouse button is pressed and hold, not released yet. */ 076 protected int ixDstItem = 0; 077 078 /**The item which is shown as left in the graphic. */ 079 protected int ixLeftItem = 0; 080 081 protected int minSize; 082 083 public GralColor colorText, colorSelect, colorBack, colorLine; 084 085 086 /**The constructor creates the instance but does nothing with the graphic appearance. 087 * @param name 088 * @param mng 089 */ 090 public GralHorizontalSelector(String name, GralUserAction actionOnSelect){ 091 super(name, 'n'); 092 colorText = GralColor.getColor("bk"); 093 colorSelect = GralColor.getColor("rd"); 094 colorBack = GralColor.getColor("wh"); 095 colorLine = GralColor.getColor("bk"); 096 097 setActionChange(actionOnSelect); 098 } 099 100 101 /**Adds a item to show. 102 * @param text 103 * @param position 104 * @param data 105 */ 106 public void addItem(String text, int position, UserData data){ 107 Item<UserData> item = new Item<UserData>(); 108 item.text = text; 109 item.xSize = 0; 110 item.data = data; 111 int pos1; 112 if(position < 0 || position > items.size()){ pos1 = items.size(); } 113 else{ pos1 = position; } 114 items.add(pos1, item); 115 ixActItem = ixDstItem = pos1; 116 actItem = item; 117 } 118 119 120 121 public boolean setActItem(String name){ 122 int ixItem = 0; 123 for(Item<UserData> item: items){ 124 if(item.text.equals(name)){ 125 actItem = item; 126 ixActItem = ixItem; 127 ixDstItem = ixItem; 128 return true; 129 } 130 ixItem +=1; 131 } 132 return false; //not found. 133 } 134 135 /**Remove a item. 136 * @param text 137 */ 138 public void removeItem(String text){ 139 for(Item<UserData> item: items){ 140 if(item.text.equals(text)){ 141 items.remove(item); 142 } 143 } 144 } 145 146 147 protected void setDstToActItem(){ 148 if(ixDstItem >=0){ 149 ixActItem = ixDstItem; 150 actItem = items.get(ixActItem); 151 GralWidget_ifc.ActionChange action = getActionChange(GralWidget_ifc.ActionChangeWhen.onEnter); 152 if(action !=null){ 153 Object[] args = action.args(); 154 if(args == null){ action.action().exec(KeyCode.activated, GralHorizontalSelector.this, actItem.data); } 155 else { action.action().exec(KeyCode.activated, GralHorizontalSelector.this, args, actItem.data); } 156 } 157 } 158 } 159 160 161 162 163 /**Called on mouse action in context menu. 164 * 165 */ 166 protected void removeTab(){ 167 boolean actItemRemoved = ixDstItem == ixActItem; 168 Item<UserData> removed = items.remove(ixDstItem); 169 GralWidget_ifc.ActionChange action = getActionChange(GralWidget_ifc.ActionChangeWhen.onEnter); 170 /* un-necessary for remove: 171 if(action !=null){ 172 Object[] args = action.args(); 173 if(args == null){ action.action().exec(KeyCode.activated, GralHorizontalSelector.this, actItem.data); } 174 else { action.action().exec(KeyCode.removed, GralHorizontalSelector.this, args, actItem.data); } 175 } 176 */ 177 if(ixDstItem < ixActItem){ ixActItem -=1; } 178 if(actItemRemoved){ 179 if(ixActItem >= items.size()){ 180 ixDstItem = ixActItem-1; 181 } 182 setDstToActItem(); //calls activation of the yet actual item. 183 } else { 184 ixDstItem = ixActItem; //unchanged 185 } 186 repaint(100, 300); 187 } 188 189 190 191 192 public static class Item<UserData> 193 { 194 public String text; 195 public int xSize; 196 protected UserData data; 197 198 protected boolean removeIfNotUsed; 199 } 200 201 202 GralUserAction actionRemoveTab = new GralUserAction("actionRemoveTab"){ 203 @Override public boolean exec(int actionCode, GralWidget_ifc widgd, Object... params) { 204 if(KeyCode.isControlFunctionMouseUpOrMenu(actionCode)){ removeTab(); } 205 return true; 206 } 207 208 }; 209 210 211 212 213 214 /**This class is not intent to use from an application, it is the super class for the implementation layer 215 * to access all necessary data and methods with protected access rights. 216 * The methods are protected because an application should not use it. This class is public because 217 * it should be visible from the graphic implementation which is located in another package. 218 */ 219 public static abstract class GraphicImplAccess<UserData> 220 extends GralWidget.ImplAccess //access to GralWidget 221 implements GralWidgImpl_ifc 222 { 223 224 //public void setWidgetImpl(GralWidgImpl_ifc widg){ wdgImpl = widg; } 225 226 protected final GralHorizontalSelector<UserData> outer; 227 228 protected GraphicImplAccess(GralHorizontalSelector<UserData> widgg, GralMng mng){ 229 super(widgg, mng); 230 outer = widgg; 231 } 232 233 protected List<Item<UserData>> items(){ return outer.items; } 234 235 protected Item<?> actItem(){ return outer.actItem; } 236 237 protected Item<?> tab(int ix){ return outer.items.get(ix); } 238 239 protected int nrItem(){ return outer.ixDstItem; } 240 241 protected int nrofTabs(){ return outer.items.size(); } 242 243 protected void calcLeftTab(int gwidth, int xArrow){ 244 int xBefore = 0; 245 int ixItem = outer.ixDstItem; 246 // 247 //search what tab should be shown left as first: 248 // 249 if(outer.items.size() >0){ 250 outer.ixLeftItem = 0; 251 while(outer.ixLeftItem ==0 && ixItem >=0){ 252 GralHorizontalSelector.Item<UserData> item = outer.items.get(ixItem); 253 if(item.xSize == 0){ 254 //item.xSize = 50; //TODO 255 } 256 if(xArrow + xBefore + item.xSize + xArrow +4 > gwidth){ //to much yet 257 outer.ixLeftItem = ixItem +1; 258 } else{ 259 xBefore += item.xSize; 260 ixItem -=1; 261 } 262 } 263 } else { 264 outer.ixLeftItem = -1; //not given 265 } 266 } 267 268 269 /**Searches the tab which is shown in the xMouse region. 270 * @param xMouse x-position from mouse button pressed in the implementation widget area. 271 */ 272 protected void findTab(int xMouse){ 273 int ixTab = outer.ixLeftItem; 274 int xPos = (ixTab == 0) ? 2: 22; 275 boolean found = false; 276 int zTabs = outer.items.size(); 277 GralHorizontalSelector.Item<?> tab; 278 do { 279 tab = tab(ixTab); 280 if(xPos + tab.xSize > xMouse){ 281 found = true; 282 } else { 283 xPos += tab.xSize; 284 ixTab +=1; 285 } 286 } while(ixTab < zTabs && !found); 287 if(found){ 288 outer.ixDstItem = ixTab; 289 //setActItem(tab.text); 290 } 291 } 292 293 /**Sets a chosen item to the current one, because the mouse was released inside this item. */ 294 protected void setDstToActItem(){ outer.setDstToActItem(); } 295 296 /**Removes a different choice of a destination item, because the mouse was released 297 * outside of the area where it is pressed and outside of the widget. */ 298 protected void clearDstItem(){ outer.ixDstItem = outer.ixActItem; } 299 300 protected void removeDstItem(){ 301 outer.items.remove(outer.ixDstItem); 302 if(outer.ixDstItem < outer.ixActItem){ outer.ixActItem -=1; } 303 clearDstItem(); 304 } 305 306 protected void execAfterCreationImplWidget(){ 307 GralMenu menu = outer.getContextMenu(); 308 menu.addMenuItem("&Close tab", outer.actionRemoveTab); 309 } 310 311 312 protected int nrLeftTab(){ return outer.ixLeftItem; } 313 314 315 316 } 317 318}