Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.

Table.java 116KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519
  1. /*
  2. @ITMillApache2LicenseForJavaFiles@
  3. */
  4. package com.vaadin.ui;
  5. import java.io.Serializable;
  6. import java.util.ArrayList;
  7. import java.util.Collection;
  8. import java.util.HashMap;
  9. import java.util.HashSet;
  10. import java.util.Iterator;
  11. import java.util.LinkedHashMap;
  12. import java.util.LinkedHashSet;
  13. import java.util.LinkedList;
  14. import java.util.Map;
  15. import java.util.Set;
  16. import java.util.StringTokenizer;
  17. import com.vaadin.data.Container;
  18. import com.vaadin.data.Item;
  19. import com.vaadin.data.Property;
  20. import com.vaadin.data.util.ContainerOrderedWrapper;
  21. import com.vaadin.data.util.IndexedContainer;
  22. import com.vaadin.event.Action;
  23. import com.vaadin.event.DataBoundTransferable;
  24. import com.vaadin.event.ItemClickEvent;
  25. import com.vaadin.event.Action.Handler;
  26. import com.vaadin.event.ItemClickEvent.ItemClickListener;
  27. import com.vaadin.event.ItemClickEvent.ItemClickSource;
  28. import com.vaadin.event.dd.DragAndDropEvent;
  29. import com.vaadin.event.dd.DragSource;
  30. import com.vaadin.event.dd.DropHandler;
  31. import com.vaadin.event.dd.DropTarget;
  32. import com.vaadin.event.dd.acceptcriteria.ClientCriterion;
  33. import com.vaadin.event.dd.acceptcriteria.ServerSideCriterion;
  34. import com.vaadin.terminal.KeyMapper;
  35. import com.vaadin.terminal.PaintException;
  36. import com.vaadin.terminal.PaintTarget;
  37. import com.vaadin.terminal.Resource;
  38. import com.vaadin.terminal.gwt.client.MouseEventDetails;
  39. import com.vaadin.terminal.gwt.client.ui.VScrollTable;
  40. import com.vaadin.terminal.gwt.client.ui.dd.VLazyInitItemIdentifiers;
  41. /**
  42. * <p>
  43. * <code>Table</code> is used for representing data or components in a pageable
  44. * and selectable table.
  45. * </p>
  46. *
  47. * <p>
  48. * Scalability of the Table is largely dictated by the container. A table does
  49. * not have a limit for the number of items and is just as fast with hundreds of
  50. * thousands of items as with just a few. The current GWT implementation with
  51. * scrolling however limits the number of rows to around 500000, depending on
  52. * the browser and the pixel height of rows.
  53. * </p>
  54. *
  55. * <p>
  56. * Components in a Table will not have their caption nor icon rendered.
  57. * </p>
  58. *
  59. * @author IT Mill Ltd.
  60. * @version
  61. * @VERSION@
  62. * @since 3.0
  63. */
  64. @SuppressWarnings("serial")
  65. @ClientWidget(VScrollTable.class)
  66. public class Table extends AbstractSelect implements Action.Container,
  67. Container.Ordered, Container.Sortable, ItemClickSource, DragSource,
  68. DropTarget {
  69. /**
  70. * Modes that Table support as drag sourse.
  71. */
  72. public enum TableDragMode {
  73. /**
  74. * Table does not start drag and drop events. HTM5 style events started
  75. * by browser may still happen.
  76. */
  77. NONE,
  78. /**
  79. * Table starts drag from rows.
  80. */
  81. ROWS
  82. }
  83. private static final int CELL_KEY = 0;
  84. private static final int CELL_HEADER = 1;
  85. private static final int CELL_ICON = 2;
  86. private static final int CELL_ITEMID = 3;
  87. private static final int CELL_FIRSTCOL = 4;
  88. /**
  89. * Left column alignment. <b>This is the default behaviour. </b>
  90. */
  91. public static final String ALIGN_LEFT = "b";
  92. /**
  93. * Center column alignment.
  94. */
  95. public static final String ALIGN_CENTER = "c";
  96. /**
  97. * Right column alignment.
  98. */
  99. public static final String ALIGN_RIGHT = "e";
  100. /**
  101. * Column header mode: Column headers are hidden. <b>This is the default
  102. * behavior. </b>
  103. */
  104. public static final int COLUMN_HEADER_MODE_HIDDEN = -1;
  105. /**
  106. * Column header mode: Property ID:s are used as column headers.
  107. */
  108. public static final int COLUMN_HEADER_MODE_ID = 0;
  109. /**
  110. * Column header mode: Column headers are explicitly specified with
  111. * <code>setColumnHeaders</code>.
  112. */
  113. public static final int COLUMN_HEADER_MODE_EXPLICIT = 1;
  114. /**
  115. * Column header mode: Column headers are explicitly specified with
  116. * <code>setColumnHeaders</code>
  117. */
  118. public static final int COLUMN_HEADER_MODE_EXPLICIT_DEFAULTS_ID = 2;
  119. /**
  120. * Row caption mode: The row headers are hidden. <b>This is the default
  121. * mode. </b>
  122. */
  123. public static final int ROW_HEADER_MODE_HIDDEN = -1;
  124. /**
  125. * Row caption mode: Items Id-objects toString is used as row caption.
  126. */
  127. public static final int ROW_HEADER_MODE_ID = AbstractSelect.ITEM_CAPTION_MODE_ID;
  128. /**
  129. * Row caption mode: Item-objects toString is used as row caption.
  130. */
  131. public static final int ROW_HEADER_MODE_ITEM = AbstractSelect.ITEM_CAPTION_MODE_ITEM;
  132. /**
  133. * Row caption mode: Index of the item is used as item caption. The index
  134. * mode can only be used with the containers implementing Container.Indexed
  135. * interface.
  136. */
  137. public static final int ROW_HEADER_MODE_INDEX = AbstractSelect.ITEM_CAPTION_MODE_INDEX;
  138. /**
  139. * Row caption mode: Item captions are explicitly specified.
  140. */
  141. public static final int ROW_HEADER_MODE_EXPLICIT = AbstractSelect.ITEM_CAPTION_MODE_EXPLICIT;
  142. /**
  143. * Row caption mode: Item captions are read from property specified with
  144. * <code>setItemCaptionPropertyId</code>.
  145. */
  146. public static final int ROW_HEADER_MODE_PROPERTY = AbstractSelect.ITEM_CAPTION_MODE_PROPERTY;
  147. /**
  148. * Row caption mode: Only icons are shown, the captions are hidden.
  149. */
  150. public static final int ROW_HEADER_MODE_ICON_ONLY = AbstractSelect.ITEM_CAPTION_MODE_ICON_ONLY;
  151. /**
  152. * Row caption mode: Item captions are explicitly specified, but if the
  153. * caption is missing, the item id objects <code>toString()</code> is used
  154. * instead.
  155. */
  156. public static final int ROW_HEADER_MODE_EXPLICIT_DEFAULTS_ID = AbstractSelect.ITEM_CAPTION_MODE_EXPLICIT_DEFAULTS_ID;
  157. /**
  158. * The default rate that table caches rows for smooth scrolling.
  159. */
  160. private static final double CACHE_RATE_DEFAULT = 2;
  161. /* Private table extensions to Select */
  162. /**
  163. * True if column collapsing is allowed.
  164. */
  165. private boolean columnCollapsingAllowed = false;
  166. /**
  167. * True if reordering of columns is allowed on the client side.
  168. */
  169. private boolean columnReorderingAllowed = false;
  170. /**
  171. * Keymapper for column ids.
  172. */
  173. private final KeyMapper columnIdMap = new KeyMapper();
  174. /**
  175. * Holds visible column propertyIds - in order.
  176. */
  177. private LinkedList<Object> visibleColumns = new LinkedList<Object>();
  178. /**
  179. * Holds propertyIds of currently collapsed columns.
  180. */
  181. private final HashSet<Object> collapsedColumns = new HashSet<Object>();
  182. /**
  183. * Holds headers for visible columns (by propertyId).
  184. */
  185. private final HashMap<Object, String> columnHeaders = new HashMap<Object, String>();
  186. /**
  187. * Holds icons for visible columns (by propertyId).
  188. */
  189. private final HashMap<Object, Resource> columnIcons = new HashMap<Object, Resource>();
  190. /**
  191. * Holds alignments for visible columns (by propertyId).
  192. */
  193. private HashMap<Object, String> columnAlignments = new HashMap<Object, String>();
  194. /**
  195. * Holds column widths in pixels (Integer) or expand ratios (Float) for
  196. * visible columns (by propertyId).
  197. */
  198. private final HashMap<Object, Object> columnWidths = new HashMap<Object, Object>();
  199. /**
  200. * Holds column generators
  201. */
  202. private final HashMap<Object, ColumnGenerator> columnGenerators = new LinkedHashMap<Object, ColumnGenerator>();
  203. /**
  204. * Holds value of property pageLength. 0 disables paging.
  205. */
  206. private int pageLength = 15;
  207. /**
  208. * Id the first item on the current page.
  209. */
  210. private Object currentPageFirstItemId = null;
  211. /**
  212. * Index of the first item on the current page.
  213. */
  214. private int currentPageFirstItemIndex = 0;
  215. /**
  216. * Holds value of property selectable.
  217. */
  218. private boolean selectable = false;
  219. /**
  220. * Holds value of property columnHeaderMode.
  221. */
  222. private int columnHeaderMode = COLUMN_HEADER_MODE_EXPLICIT_DEFAULTS_ID;
  223. /**
  224. * True iff the row captions are hidden.
  225. */
  226. private boolean rowCaptionsAreHidden = true;
  227. /**
  228. * Page contents buffer used in buffered mode.
  229. */
  230. private Object[][] pageBuffer = null;
  231. /**
  232. * Set of properties listened - the list is kept to release the listeners
  233. * later.
  234. */
  235. private HashSet<Property> listenedProperties = null;
  236. /**
  237. * Set of visible components - the is used for needsRepaint calculation.
  238. */
  239. private HashSet<Component> visibleComponents = null;
  240. /**
  241. * List of action handlers.
  242. */
  243. private LinkedList<Handler> actionHandlers = null;
  244. /**
  245. * Action mapper.
  246. */
  247. private KeyMapper actionMapper = null;
  248. /**
  249. * Table cell editor factory.
  250. */
  251. private TableFieldFactory fieldFactory = DefaultFieldFactory.get();
  252. /**
  253. * Is table editable.
  254. */
  255. private boolean editable = false;
  256. /**
  257. * Current sorting direction.
  258. */
  259. private boolean sortAscending = true;
  260. /**
  261. * Currently table is sorted on this propertyId.
  262. */
  263. private Object sortContainerPropertyId = null;
  264. /**
  265. * Is table sorting disabled alltogether; even if some of the properties
  266. * would be sortable.
  267. */
  268. private boolean sortDisabled = false;
  269. /**
  270. * Number of rows explicitly requested by the client to be painted on next
  271. * paint. This is -1 if no request by the client is made. Painting the
  272. * component will automatically reset this to -1.
  273. */
  274. private int reqRowsToPaint = -1;
  275. /**
  276. * Index of the first rows explicitly requested by the client to be painted.
  277. * This is -1 if no request by the client is made. Painting the component
  278. * will automatically reset this to -1.
  279. */
  280. private int reqFirstRowToPaint = -1;
  281. private int firstToBeRenderedInClient = -1;
  282. private int lastToBeRenderedInClient = -1;
  283. private boolean isContentRefreshesEnabled = true;
  284. private int pageBufferFirstIndex;
  285. private boolean containerChangeToBeRendered = false;
  286. /**
  287. * Table cell specific style generator
  288. */
  289. private CellStyleGenerator cellStyleGenerator = null;
  290. /*
  291. * EXPERIMENTAL feature: will tell the client to re-calculate column widths
  292. * if set to true. Currently no setter: extend to enable.
  293. */
  294. protected boolean alwaysRecalculateColumnWidths = false;
  295. private double cacheRate = CACHE_RATE_DEFAULT;
  296. private TableDragMode dragMode = TableDragMode.NONE;
  297. private DropHandler dropHandler;
  298. /* Table constructors */
  299. /**
  300. * Creates a new empty table.
  301. */
  302. public Table() {
  303. setRowHeaderMode(ROW_HEADER_MODE_HIDDEN);
  304. }
  305. /**
  306. * Creates a new empty table with caption.
  307. *
  308. * @param caption
  309. */
  310. public Table(String caption) {
  311. this();
  312. setCaption(caption);
  313. }
  314. /**
  315. * Creates a new table with caption and connect it to a Container.
  316. *
  317. * @param caption
  318. * @param dataSource
  319. */
  320. public Table(String caption, Container dataSource) {
  321. this();
  322. setCaption(caption);
  323. setContainerDataSource(dataSource);
  324. }
  325. /* Table functionality */
  326. /**
  327. * Gets the array of visible column id:s, including generated columns.
  328. *
  329. * <p>
  330. * The columns are show in the order of their appearance in this array.
  331. * </p>
  332. *
  333. * @return an array of currently visible propertyIds and generated column
  334. * ids.
  335. */
  336. public Object[] getVisibleColumns() {
  337. if (visibleColumns == null) {
  338. return null;
  339. }
  340. return visibleColumns.toArray();
  341. }
  342. /**
  343. * Sets the array of visible column property id:s.
  344. *
  345. * <p>
  346. * The columns are show in the order of their appearance in this array.
  347. * </p>
  348. *
  349. * @param visibleColumns
  350. * the Array of shown property id:s.
  351. */
  352. public void setVisibleColumns(Object[] visibleColumns) {
  353. // Visible columns must exist
  354. if (visibleColumns == null) {
  355. throw new NullPointerException(
  356. "Can not set visible columns to null value");
  357. }
  358. // Checks that the new visible columns contains no nulls and properties
  359. // exist
  360. final Collection properties = getContainerPropertyIds();
  361. for (int i = 0; i < visibleColumns.length; i++) {
  362. if (visibleColumns[i] == null) {
  363. throw new NullPointerException("Ids must be non-nulls");
  364. } else if (!properties.contains(visibleColumns[i])
  365. && !columnGenerators.containsKey(visibleColumns[i])) {
  366. throw new IllegalArgumentException(
  367. "Ids must exist in the Container or as a generated column , missing id: "
  368. + visibleColumns[i]);
  369. }
  370. }
  371. // If this is called before the constructor is finished, it might be
  372. // uninitialized
  373. final LinkedList<Object> newVC = new LinkedList<Object>();
  374. for (int i = 0; i < visibleColumns.length; i++) {
  375. newVC.add(visibleColumns[i]);
  376. }
  377. // Removes alignments, icons and headers from hidden columns
  378. if (this.visibleColumns != null) {
  379. boolean disabledHere = disableContentRefreshing();
  380. try {
  381. for (final Iterator<Object> i = this.visibleColumns.iterator(); i
  382. .hasNext();) {
  383. final Object col = i.next();
  384. if (!newVC.contains(col)) {
  385. setColumnHeader(col, null);
  386. setColumnAlignment(col, null);
  387. setColumnIcon(col, null);
  388. }
  389. }
  390. } finally {
  391. if (disabledHere) {
  392. enableContentRefreshing(false);
  393. }
  394. }
  395. }
  396. this.visibleColumns = newVC;
  397. // Assures visual refresh
  398. resetPageBuffer();
  399. refreshRenderedCells();
  400. }
  401. /**
  402. * Gets the headers of the columns.
  403. *
  404. * <p>
  405. * The headers match the property id:s given my the set visible column
  406. * headers. The table must be set in either
  407. * <code>COLUMN_HEADER_MODE_EXPLICIT</code> or
  408. * <code>COLUMN_HEADER_MODE_EXPLICIT_DEFAULTS_ID</code> mode to show the
  409. * headers. In the defaults mode any nulls in the headers array are replaced
  410. * with id.toString() outputs when rendering.
  411. * </p>
  412. *
  413. * @return the Array of column headers.
  414. */
  415. public String[] getColumnHeaders() {
  416. if (columnHeaders == null) {
  417. return null;
  418. }
  419. final String[] headers = new String[visibleColumns.size()];
  420. int i = 0;
  421. for (final Iterator<Object> it = visibleColumns.iterator(); it
  422. .hasNext(); i++) {
  423. headers[i] = columnHeaders.get(it.next());
  424. }
  425. return headers;
  426. }
  427. /**
  428. * Sets the headers of the columns.
  429. *
  430. * <p>
  431. * The headers match the property id:s given my the set visible column
  432. * headers. The table must be set in either
  433. * <code>COLUMN_HEADER_MODE_EXPLICIT</code> or
  434. * <code>COLUMN_HEADER_MODE_EXPLICIT_DEFAULTS_ID</code> mode to show the
  435. * headers. In the defaults mode any nulls in the headers array are replaced
  436. * with id.toString() outputs when rendering.
  437. * </p>
  438. *
  439. * @param columnHeaders
  440. * the Array of column headers that match the
  441. * <code>getVisibleColumns</code> method.
  442. */
  443. public void setColumnHeaders(String[] columnHeaders) {
  444. if (columnHeaders.length != visibleColumns.size()) {
  445. throw new IllegalArgumentException(
  446. "The length of the headers array must match the number of visible columns");
  447. }
  448. this.columnHeaders.clear();
  449. int i = 0;
  450. for (final Iterator<Object> it = visibleColumns.iterator(); it
  451. .hasNext()
  452. && i < columnHeaders.length; i++) {
  453. this.columnHeaders.put(it.next(), columnHeaders[i]);
  454. }
  455. // Assures the visual refresh
  456. // FIXME: Is this really needed? Header captions should not affect
  457. // content so requestRepaint() should be sufficient.
  458. resetPageBuffer();
  459. refreshRenderedCells();
  460. }
  461. /**
  462. * Gets the icons of the columns.
  463. *
  464. * <p>
  465. * The icons in headers match the property id:s given my the set visible
  466. * column headers. The table must be set in either
  467. * <code>COLUMN_HEADER_MODE_EXPLICIT</code> or
  468. * <code>COLUMN_HEADER_MODE_EXPLICIT_DEFAULTS_ID</code> mode to show the
  469. * headers with icons.
  470. * </p>
  471. *
  472. * @return the Array of icons that match the <code>getVisibleColumns</code>.
  473. */
  474. public Resource[] getColumnIcons() {
  475. if (columnIcons == null) {
  476. return null;
  477. }
  478. final Resource[] icons = new Resource[visibleColumns.size()];
  479. int i = 0;
  480. for (final Iterator<Object> it = visibleColumns.iterator(); it
  481. .hasNext(); i++) {
  482. icons[i] = columnIcons.get(it.next());
  483. }
  484. return icons;
  485. }
  486. /**
  487. * Sets the icons of the columns.
  488. *
  489. * <p>
  490. * The icons in headers match the property id:s given my the set visible
  491. * column headers. The table must be set in either
  492. * <code>COLUMN_HEADER_MODE_EXPLICIT</code> or
  493. * <code>COLUMN_HEADER_MODE_EXPLICIT_DEFAULTS_ID</code> mode to show the
  494. * headers with icons.
  495. * </p>
  496. *
  497. * @param columnIcons
  498. * the Array of icons that match the
  499. * <code>getVisibleColumns</code>.
  500. */
  501. public void setColumnIcons(Resource[] columnIcons) {
  502. if (columnIcons.length != visibleColumns.size()) {
  503. throw new IllegalArgumentException(
  504. "The length of the icons array must match the number of visible columns");
  505. }
  506. this.columnIcons.clear();
  507. int i = 0;
  508. for (final Iterator<Object> it = visibleColumns.iterator(); it
  509. .hasNext()
  510. && i < columnIcons.length; i++) {
  511. this.columnIcons.put(it.next(), columnIcons[i]);
  512. }
  513. // Assure visual refresh
  514. resetPageBuffer();
  515. refreshRenderedCells();
  516. }
  517. /**
  518. * Gets the array of column alignments.
  519. *
  520. * <p>
  521. * The items in the array must match the properties identified by
  522. * <code>getVisibleColumns()</code>. The possible values for the alignments
  523. * include:
  524. * <ul>
  525. * <li><code>ALIGN_LEFT</code>: Left alignment</li>
  526. * <li><code>ALIGN_CENTER</code>: Centered</li>
  527. * <li><code>ALIGN_RIGHT</code>: Right alignment</li>
  528. * </ul>
  529. * The alignments default to <code>ALIGN_LEFT</code>: any null values are
  530. * rendered as align lefts.
  531. * </p>
  532. *
  533. * @return the Column alignments array.
  534. */
  535. public String[] getColumnAlignments() {
  536. if (columnAlignments == null) {
  537. return null;
  538. }
  539. final String[] alignments = new String[visibleColumns.size()];
  540. int i = 0;
  541. for (final Iterator<Object> it = visibleColumns.iterator(); it
  542. .hasNext(); i++) {
  543. alignments[i++] = getColumnAlignment(it.next());
  544. }
  545. return alignments;
  546. }
  547. /**
  548. * Sets the column alignments.
  549. *
  550. * <p>
  551. * The items in the array must match the properties identified by
  552. * <code>getVisibleColumns()</code>. The possible values for the alignments
  553. * include:
  554. * <ul>
  555. * <li><code>ALIGN_LEFT</code>: Left alignment</li>
  556. * <li><code>ALIGN_CENTER</code>: Centered</li>
  557. * <li><code>ALIGN_RIGHT</code>: Right alignment</li>
  558. * </ul>
  559. * The alignments default to <code>ALIGN_LEFT</code>
  560. * </p>
  561. *
  562. * @param columnAlignments
  563. * the Column alignments array.
  564. */
  565. public void setColumnAlignments(String[] columnAlignments) {
  566. if (columnAlignments.length != visibleColumns.size()) {
  567. throw new IllegalArgumentException(
  568. "The length of the alignments array must match the number of visible columns");
  569. }
  570. // Checks all alignments
  571. for (int i = 0; i < columnAlignments.length; i++) {
  572. final String a = columnAlignments[i];
  573. if (a != null && !a.equals(ALIGN_LEFT) && !a.equals(ALIGN_CENTER)
  574. && !a.equals(ALIGN_RIGHT)) {
  575. throw new IllegalArgumentException("Column " + i
  576. + " aligment '" + a + "' is invalid");
  577. }
  578. }
  579. // Resets the alignments
  580. final HashMap<Object, String> newCA = new HashMap<Object, String>();
  581. int i = 0;
  582. for (final Iterator<Object> it = visibleColumns.iterator(); it
  583. .hasNext()
  584. && i < columnAlignments.length; i++) {
  585. newCA.put(it.next(), columnAlignments[i]);
  586. }
  587. this.columnAlignments = newCA;
  588. // Assures the visual refresh
  589. resetPageBuffer();
  590. refreshRenderedCells();
  591. }
  592. /**
  593. * Sets columns width (in pixels). Theme may not necessary respect very
  594. * small or very big values. Setting width to -1 (default) means that theme
  595. * will make decision of width.
  596. *
  597. *<p>
  598. * Column can either have a fixed width or expand ratio. The latter one set
  599. * is used. See @link {@link #setColumnExpandRatio(Object, float)}.
  600. *
  601. * @param columnId
  602. * colunmns property id
  603. * @param width
  604. * width to be reserved for colunmns content
  605. * @since 4.0.3
  606. */
  607. public void setColumnWidth(Object columnId, int width) {
  608. if (width < 0) {
  609. columnWidths.remove(columnId);
  610. } else {
  611. columnWidths.put(columnId, new Integer(width));
  612. }
  613. }
  614. /**
  615. * Sets the column expand ratio for given column.
  616. * <p>
  617. * Expand ratios can be defined to customize the way how excess space is
  618. * divided among columns. Table can have excess space if it has its width
  619. * defined and there is horizontally more space than columns consume
  620. * naturally. Excess space is the space that is not used by columns with
  621. * explicit width (see {@link #setColumnWidth(Object, int)}) or with natural
  622. * width (no width nor expand ratio).
  623. *
  624. * <p>
  625. * By default (without expand ratios) the excess space is divided
  626. * proportionally to columns natural widths.
  627. *
  628. * <p>
  629. * Only expand ratios of visible columns are used in final calculations.
  630. *
  631. * <p>
  632. * Column can either have a fixed width or expand ratio. The latter one set
  633. * is used.
  634. *
  635. * <p>
  636. * A column with expand ratio is considered to be minimum width by default
  637. * (if no excess space exists). The minimum width is defined by terminal
  638. * implementation.
  639. *
  640. * <p>
  641. * If terminal implementation supports re-sizeable columns the column
  642. * becomes fixed width column if users resizes the column.
  643. *
  644. * @param columnId
  645. * colunmns property id
  646. * @param expandRatio
  647. * the expandRatio used to divide excess space for this column
  648. */
  649. public void setColumnExpandRatio(Object columnId, float expandRatio) {
  650. if (expandRatio < 0) {
  651. columnWidths.remove(columnId);
  652. } else {
  653. columnWidths.put(columnId, new Float(expandRatio));
  654. }
  655. }
  656. public float getColumnExpandRatio(Object propertyId) {
  657. final Object width = columnWidths.get(propertyId);
  658. if (width == null || !(width instanceof Float)) {
  659. return -1;
  660. }
  661. final Float value = (Float) width;
  662. return value.floatValue();
  663. }
  664. /**
  665. * Gets the pixel width of column
  666. *
  667. * @param propertyId
  668. * @return width of colun or -1 when value not set
  669. */
  670. public int getColumnWidth(Object propertyId) {
  671. final Object width = columnWidths.get(propertyId);
  672. if (width == null || !(width instanceof Integer)) {
  673. return -1;
  674. }
  675. final Integer value = (Integer) width;
  676. return value.intValue();
  677. }
  678. /**
  679. * Gets the page length.
  680. *
  681. * <p>
  682. * Setting page length 0 disables paging.
  683. * </p>
  684. *
  685. * @return the Length of one page.
  686. */
  687. public int getPageLength() {
  688. return pageLength;
  689. }
  690. /**
  691. * Sets the page length.
  692. *
  693. * <p>
  694. * Setting page length 0 disables paging. The page length defaults to 15.
  695. * </p>
  696. *
  697. * <p>
  698. * If Table has width set ({@link #setWidth(float, int)} ) the client side
  699. * may update the page length automatically the correct value.
  700. * </p>
  701. *
  702. * @param pageLength
  703. * the length of one page.
  704. */
  705. public void setPageLength(int pageLength) {
  706. if (pageLength >= 0 && this.pageLength != pageLength) {
  707. this.pageLength = pageLength;
  708. // Assures the visual refresh
  709. resetPageBuffer();
  710. refreshRenderedCells();
  711. }
  712. }
  713. /**
  714. * This method adjusts a possible caching mechanism of table implementation.
  715. *
  716. * <p>
  717. * Table component may fetch and render some rows outside visible area. With
  718. * complex tables (for example containing layouts and components), the
  719. * client side may become unresponsive. Setting the value lower, UI will
  720. * become more responsive. With higher values scrolling in client will hit
  721. * server less frequently.
  722. *
  723. * <p>
  724. * The amount of cached rows will be cacheRate multiplied with pageLength (
  725. * {@link #setPageLength(int)} both below and above visible area..
  726. *
  727. * @param cacheRate
  728. * a value over 0 (fastest rendering time). Higher value will
  729. * cache more rows on server (smoother scrolling). Default value
  730. * is 2.
  731. */
  732. public void setCacheRate(double cacheRate) {
  733. if (cacheRate < 0) {
  734. throw new IllegalArgumentException(
  735. "cacheRate cannot be less than zero");
  736. }
  737. if (this.cacheRate != cacheRate) {
  738. this.cacheRate = cacheRate;
  739. requestRepaint();
  740. }
  741. }
  742. /**
  743. * @see #setCacheRate(double)
  744. *
  745. * @return the current cache rate value
  746. */
  747. public double getCacheRate() {
  748. return cacheRate;
  749. }
  750. /**
  751. * Getter for property currentPageFirstItem.
  752. *
  753. * @return the Value of property currentPageFirstItem.
  754. */
  755. public Object getCurrentPageFirstItemId() {
  756. // Priorise index over id if indexes are supported
  757. if (items instanceof Container.Indexed) {
  758. final int index = getCurrentPageFirstItemIndex();
  759. Object id = null;
  760. if (index >= 0 && index < size()) {
  761. id = ((Container.Indexed) items).getIdByIndex(index);
  762. }
  763. if (id != null && !id.equals(currentPageFirstItemId)) {
  764. currentPageFirstItemId = id;
  765. }
  766. }
  767. // If there is no item id at all, use the first one
  768. if (currentPageFirstItemId == null) {
  769. currentPageFirstItemId = ((Container.Ordered) items).firstItemId();
  770. }
  771. return currentPageFirstItemId;
  772. }
  773. /**
  774. * Setter for property currentPageFirstItemId.
  775. *
  776. * @param currentPageFirstItemId
  777. * the New value of property currentPageFirstItemId.
  778. */
  779. public void setCurrentPageFirstItemId(Object currentPageFirstItemId) {
  780. // Gets the corresponding index
  781. int index = -1;
  782. if (items instanceof Container.Indexed) {
  783. index = ((Container.Indexed) items)
  784. .indexOfId(currentPageFirstItemId);
  785. } else {
  786. // If the table item container does not have index, we have to
  787. // calculates the index by hand
  788. Object id = ((Container.Ordered) items).firstItemId();
  789. while (id != null && !id.equals(currentPageFirstItemId)) {
  790. index++;
  791. id = ((Container.Ordered) items).nextItemId(id);
  792. }
  793. if (id == null) {
  794. index = -1;
  795. }
  796. }
  797. // If the search for item index was successful
  798. if (index >= 0) {
  799. /*
  800. * The table is not capable of displaying an item in the container
  801. * as the first if there are not enough items following the selected
  802. * item so the whole table (pagelength) is filled.
  803. */
  804. int maxIndex = size() - pageLength;
  805. if (maxIndex < 0) {
  806. maxIndex = 0;
  807. }
  808. if (index > maxIndex) {
  809. setCurrentPageFirstItemIndex(maxIndex);
  810. return;
  811. }
  812. this.currentPageFirstItemId = currentPageFirstItemId;
  813. currentPageFirstItemIndex = index;
  814. }
  815. // Assures the visual refresh
  816. resetPageBuffer();
  817. refreshRenderedCells();
  818. }
  819. /**
  820. * Gets the icon Resource for the specified column.
  821. *
  822. * @param propertyId
  823. * the propertyId indentifying the column.
  824. * @return the icon for the specified column; null if the column has no icon
  825. * set, or if the column is not visible.
  826. */
  827. public Resource getColumnIcon(Object propertyId) {
  828. return columnIcons.get(propertyId);
  829. }
  830. /**
  831. * Sets the icon Resource for the specified column.
  832. * <p>
  833. * Throws IllegalArgumentException if the specified column is not visible.
  834. * </p>
  835. *
  836. * @param propertyId
  837. * the propertyId identifying the column.
  838. * @param icon
  839. * the icon Resource to set.
  840. */
  841. public void setColumnIcon(Object propertyId, Resource icon) {
  842. if (icon == null) {
  843. columnIcons.remove(propertyId);
  844. } else {
  845. columnIcons.put(propertyId, icon);
  846. }
  847. // Assures the visual refresh
  848. resetPageBuffer();
  849. refreshRenderedCells();
  850. }
  851. /**
  852. * Gets the header for the specified column.
  853. *
  854. * @param propertyId
  855. * the propertyId indentifying the column.
  856. * @return the header for the specifed column if it has one.
  857. */
  858. public String getColumnHeader(Object propertyId) {
  859. if (getColumnHeaderMode() == COLUMN_HEADER_MODE_HIDDEN) {
  860. return null;
  861. }
  862. String header = columnHeaders.get(propertyId);
  863. if ((header == null && getColumnHeaderMode() == COLUMN_HEADER_MODE_EXPLICIT_DEFAULTS_ID)
  864. || getColumnHeaderMode() == COLUMN_HEADER_MODE_ID) {
  865. header = propertyId.toString();
  866. }
  867. return header;
  868. }
  869. /**
  870. * Sets the column header for the specified column;
  871. *
  872. * @param propertyId
  873. * the propertyId indentifying the column.
  874. * @param header
  875. * the header to set.
  876. */
  877. public void setColumnHeader(Object propertyId, String header) {
  878. if (header == null) {
  879. columnHeaders.remove(propertyId);
  880. return;
  881. }
  882. columnHeaders.put(propertyId, header);
  883. // Assures the visual refresh
  884. // FIXME: Is this really needed? Header captions should not affect
  885. // content so requestRepaint() should be sufficient.
  886. refreshRenderedCells();
  887. }
  888. /**
  889. * Gets the specified column's alignment.
  890. *
  891. * @param propertyId
  892. * the propertyID identifying the column.
  893. * @return the specified column's alignment if it as one; null otherwise.
  894. */
  895. public String getColumnAlignment(Object propertyId) {
  896. final String a = columnAlignments.get(propertyId);
  897. return a == null ? ALIGN_LEFT : a;
  898. }
  899. /**
  900. * Sets the specified column's alignment.
  901. *
  902. * <p>
  903. * Throws IllegalArgumentException if the alignment is not one of the
  904. * following: ALIGN_LEFT, ALIGN_CENTER or ALIGN_RIGHT
  905. * </p>
  906. *
  907. * @param propertyId
  908. * the propertyID identifying the column.
  909. * @param alignment
  910. * the desired alignment.
  911. */
  912. public void setColumnAlignment(Object propertyId, String alignment) {
  913. // Checks for valid alignments
  914. if (alignment != null && !alignment.equals(ALIGN_LEFT)
  915. && !alignment.equals(ALIGN_CENTER)
  916. && !alignment.equals(ALIGN_RIGHT)) {
  917. throw new IllegalArgumentException("Column alignment '" + alignment
  918. + "' is not supported.");
  919. }
  920. if (alignment == null || alignment.equals(ALIGN_LEFT)) {
  921. columnAlignments.remove(propertyId);
  922. return;
  923. }
  924. columnAlignments.put(propertyId, alignment);
  925. // Assures the visual refresh
  926. refreshRenderedCells();
  927. }
  928. /**
  929. * Checks if the specified column is collapsed.
  930. *
  931. * @param propertyId
  932. * the propertyID identifying the column.
  933. * @return true if the column is collapsed; false otherwise;
  934. */
  935. public boolean isColumnCollapsed(Object propertyId) {
  936. return collapsedColumns != null
  937. && collapsedColumns.contains(propertyId);
  938. }
  939. /**
  940. * Sets whether the specified column is collapsed or not.
  941. *
  942. *
  943. * @param propertyId
  944. * the propertyID identifying the column.
  945. * @param collapsed
  946. * the desired collapsedness.
  947. * @throws IllegalAccessException
  948. */
  949. public void setColumnCollapsed(Object propertyId, boolean collapsed)
  950. throws IllegalAccessException {
  951. if (!isColumnCollapsingAllowed()) {
  952. throw new IllegalAccessException("Column collapsing not allowed!");
  953. }
  954. if (collapsed) {
  955. collapsedColumns.add(propertyId);
  956. } else {
  957. collapsedColumns.remove(propertyId);
  958. }
  959. // Assures the visual refresh
  960. resetPageBuffer();
  961. refreshRenderedCells();
  962. }
  963. /**
  964. * Checks if column collapsing is allowed.
  965. *
  966. * @return true if columns can be collapsed; false otherwise.
  967. */
  968. public boolean isColumnCollapsingAllowed() {
  969. return columnCollapsingAllowed;
  970. }
  971. /**
  972. * Sets whether column collapsing is allowed or not.
  973. *
  974. * @param collapsingAllowed
  975. * specifies whether column collapsing is allowed.
  976. */
  977. public void setColumnCollapsingAllowed(boolean collapsingAllowed) {
  978. columnCollapsingAllowed = collapsingAllowed;
  979. if (!collapsingAllowed) {
  980. collapsedColumns.clear();
  981. }
  982. // Assures the visual refresh
  983. refreshRenderedCells();
  984. }
  985. /**
  986. * Checks if column reordering is allowed.
  987. *
  988. * @return true if columns can be reordered; false otherwise.
  989. */
  990. public boolean isColumnReorderingAllowed() {
  991. return columnReorderingAllowed;
  992. }
  993. /**
  994. * Sets whether column reordering is allowed or not.
  995. *
  996. * @param reorderingAllowed
  997. * specifies whether column reordering is allowed.
  998. */
  999. public void setColumnReorderingAllowed(boolean reorderingAllowed) {
  1000. columnReorderingAllowed = reorderingAllowed;
  1001. // Assures the visual refresh
  1002. refreshRenderedCells();
  1003. }
  1004. /*
  1005. * Arranges visible columns according to given columnOrder. Silently ignores
  1006. * colimnId:s that are not visible columns, and keeps the internal order of
  1007. * visible columns left out of the ordering (trailing). Silently does
  1008. * nothing if columnReordering is not allowed.
  1009. */
  1010. private void setColumnOrder(Object[] columnOrder) {
  1011. if (columnOrder == null || !isColumnReorderingAllowed()) {
  1012. return;
  1013. }
  1014. final LinkedList<Object> newOrder = new LinkedList<Object>();
  1015. for (int i = 0; i < columnOrder.length; i++) {
  1016. if (columnOrder[i] != null
  1017. && visibleColumns.contains(columnOrder[i])) {
  1018. visibleColumns.remove(columnOrder[i]);
  1019. newOrder.add(columnOrder[i]);
  1020. }
  1021. }
  1022. for (final Iterator<Object> it = visibleColumns.iterator(); it
  1023. .hasNext();) {
  1024. final Object columnId = it.next();
  1025. if (!newOrder.contains(columnId)) {
  1026. newOrder.add(columnId);
  1027. }
  1028. }
  1029. visibleColumns = newOrder;
  1030. // Assure visual refresh
  1031. resetPageBuffer();
  1032. refreshRenderedCells();
  1033. }
  1034. /**
  1035. * Getter for property currentPageFirstItem.
  1036. *
  1037. * @return the Value of property currentPageFirstItem.
  1038. */
  1039. public int getCurrentPageFirstItemIndex() {
  1040. return currentPageFirstItemIndex;
  1041. }
  1042. private void setCurrentPageFirstItemIndex(int newIndex,
  1043. boolean needsPageBufferReset) {
  1044. if (newIndex < 0) {
  1045. newIndex = 0;
  1046. }
  1047. /*
  1048. * minimize Container.size() calls which may be expensive. For example
  1049. * it may cause sql query.
  1050. */
  1051. final int size = size();
  1052. /*
  1053. * The table is not capable of displaying an item in the container as
  1054. * the first if there are not enough items following the selected item
  1055. * so the whole table (pagelength) is filled.
  1056. */
  1057. int maxIndex = size - pageLength;
  1058. if (maxIndex < 0) {
  1059. maxIndex = 0;
  1060. }
  1061. // Ensures that the new value is valid
  1062. if (newIndex > maxIndex) {
  1063. newIndex = maxIndex;
  1064. }
  1065. // Refresh first item id
  1066. if (items instanceof Container.Indexed) {
  1067. try {
  1068. currentPageFirstItemId = ((Container.Indexed) items)
  1069. .getIdByIndex(newIndex);
  1070. } catch (final IndexOutOfBoundsException e) {
  1071. currentPageFirstItemId = null;
  1072. }
  1073. currentPageFirstItemIndex = newIndex;
  1074. } else {
  1075. // For containers not supporting indexes, we must iterate the
  1076. // container forwards / backwards
  1077. // next available item forward or backward
  1078. currentPageFirstItemId = ((Container.Ordered) items).firstItemId();
  1079. // Go forwards in the middle of the list (respect borders)
  1080. while (currentPageFirstItemIndex < newIndex
  1081. && !((Container.Ordered) items)
  1082. .isLastId(currentPageFirstItemId)) {
  1083. currentPageFirstItemIndex++;
  1084. currentPageFirstItemId = ((Container.Ordered) items)
  1085. .nextItemId(currentPageFirstItemId);
  1086. }
  1087. // If we did hit the border
  1088. if (((Container.Ordered) items).isLastId(currentPageFirstItemId)) {
  1089. currentPageFirstItemIndex = size - 1;
  1090. }
  1091. // Go backwards in the middle of the list (respect borders)
  1092. while (currentPageFirstItemIndex > newIndex
  1093. && !((Container.Ordered) items)
  1094. .isFirstId(currentPageFirstItemId)) {
  1095. currentPageFirstItemIndex--;
  1096. currentPageFirstItemId = ((Container.Ordered) items)
  1097. .prevItemId(currentPageFirstItemId);
  1098. }
  1099. // If we did hit the border
  1100. if (((Container.Ordered) items).isFirstId(currentPageFirstItemId)) {
  1101. currentPageFirstItemIndex = 0;
  1102. }
  1103. // Go forwards once more
  1104. while (currentPageFirstItemIndex < newIndex
  1105. && !((Container.Ordered) items)
  1106. .isLastId(currentPageFirstItemId)) {
  1107. currentPageFirstItemIndex++;
  1108. currentPageFirstItemId = ((Container.Ordered) items)
  1109. .nextItemId(currentPageFirstItemId);
  1110. }
  1111. // If for some reason we do hit border again, override
  1112. // the user index request
  1113. if (((Container.Ordered) items).isLastId(currentPageFirstItemId)) {
  1114. newIndex = currentPageFirstItemIndex = size - 1;
  1115. }
  1116. }
  1117. if (needsPageBufferReset) {
  1118. // Assures the visual refresh
  1119. resetPageBuffer();
  1120. refreshRenderedCells();
  1121. }
  1122. }
  1123. /**
  1124. * Setter for property currentPageFirstItem.
  1125. *
  1126. * @param newIndex
  1127. * the New value of property currentPageFirstItem.
  1128. */
  1129. public void setCurrentPageFirstItemIndex(int newIndex) {
  1130. setCurrentPageFirstItemIndex(newIndex, true);
  1131. }
  1132. /**
  1133. * Getter for property pageBuffering.
  1134. *
  1135. * @deprecated functionality is not needed in ajax rendering model
  1136. *
  1137. * @return the Value of property pageBuffering.
  1138. */
  1139. @Deprecated
  1140. public boolean isPageBufferingEnabled() {
  1141. return true;
  1142. }
  1143. /**
  1144. * Setter for property pageBuffering.
  1145. *
  1146. * @deprecated functionality is not needed in ajax rendering model
  1147. *
  1148. * @param pageBuffering
  1149. * the New value of property pageBuffering.
  1150. */
  1151. @Deprecated
  1152. public void setPageBufferingEnabled(boolean pageBuffering) {
  1153. }
  1154. /**
  1155. * Getter for property selectable.
  1156. *
  1157. * <p>
  1158. * The table is not selectable by default.
  1159. * </p>
  1160. *
  1161. * @return the Value of property selectable.
  1162. */
  1163. public boolean isSelectable() {
  1164. return selectable;
  1165. }
  1166. /**
  1167. * Setter for property selectable.
  1168. *
  1169. * <p>
  1170. * The table is not selectable by default.
  1171. * </p>
  1172. *
  1173. * @param selectable
  1174. * the New value of property selectable.
  1175. */
  1176. public void setSelectable(boolean selectable) {
  1177. if (this.selectable != selectable) {
  1178. this.selectable = selectable;
  1179. requestRepaint();
  1180. }
  1181. }
  1182. /**
  1183. * Getter for property columnHeaderMode.
  1184. *
  1185. * @return the Value of property columnHeaderMode.
  1186. */
  1187. public int getColumnHeaderMode() {
  1188. return columnHeaderMode;
  1189. }
  1190. /**
  1191. * Setter for property columnHeaderMode.
  1192. *
  1193. * @param columnHeaderMode
  1194. * the New value of property columnHeaderMode.
  1195. */
  1196. public void setColumnHeaderMode(int columnHeaderMode) {
  1197. if (columnHeaderMode >= COLUMN_HEADER_MODE_HIDDEN
  1198. && columnHeaderMode <= COLUMN_HEADER_MODE_EXPLICIT_DEFAULTS_ID) {
  1199. this.columnHeaderMode = columnHeaderMode;
  1200. }
  1201. // Assures the visual refresh
  1202. refreshRenderedCells();
  1203. }
  1204. /**
  1205. * Refreshes rendered rows
  1206. */
  1207. private void refreshRenderedCells() {
  1208. if (getParent() == null) {
  1209. return;
  1210. }
  1211. if (isContentRefreshesEnabled) {
  1212. HashSet<Property> oldListenedProperties = listenedProperties;
  1213. HashSet<Component> oldVisibleComponents = visibleComponents;
  1214. // initialize the listener collections
  1215. listenedProperties = new HashSet<Property>();
  1216. visibleComponents = new HashSet<Component>();
  1217. // Collects the basic facts about the table page
  1218. final Object[] colids = getVisibleColumns();
  1219. final int cols = colids.length;
  1220. final int pagelen = getPageLength();
  1221. int firstIndex = getCurrentPageFirstItemIndex();
  1222. int rows, totalRows;
  1223. rows = totalRows = size();
  1224. if (rows > 0 && firstIndex >= 0) {
  1225. rows -= firstIndex;
  1226. }
  1227. if (pagelen > 0 && pagelen < rows) {
  1228. rows = pagelen;
  1229. }
  1230. // If "to be painted next" variables are set, use them
  1231. if (lastToBeRenderedInClient - firstToBeRenderedInClient > 0) {
  1232. rows = lastToBeRenderedInClient - firstToBeRenderedInClient + 1;
  1233. }
  1234. Object id;
  1235. if (firstToBeRenderedInClient >= 0) {
  1236. if (firstToBeRenderedInClient < totalRows) {
  1237. firstIndex = firstToBeRenderedInClient;
  1238. } else {
  1239. firstIndex = totalRows - 1;
  1240. }
  1241. } else {
  1242. // initial load
  1243. firstToBeRenderedInClient = firstIndex;
  1244. }
  1245. if (totalRows > 0) {
  1246. if (rows + firstIndex > totalRows) {
  1247. rows = totalRows - firstIndex;
  1248. }
  1249. } else {
  1250. rows = 0;
  1251. }
  1252. Object[][] cells = new Object[cols + CELL_FIRSTCOL][rows];
  1253. if (rows == 0) {
  1254. pageBuffer = cells;
  1255. unregisterPropertiesAndComponents(oldListenedProperties,
  1256. oldVisibleComponents);
  1257. return;
  1258. }
  1259. // Gets the first item id
  1260. if (items instanceof Container.Indexed) {
  1261. id = ((Container.Indexed) items).getIdByIndex(firstIndex);
  1262. } else {
  1263. id = ((Container.Ordered) items).firstItemId();
  1264. for (int i = 0; i < firstIndex; i++) {
  1265. id = ((Container.Ordered) items).nextItemId(id);
  1266. }
  1267. }
  1268. final int headmode = getRowHeaderMode();
  1269. final boolean[] iscomponent = new boolean[cols];
  1270. for (int i = 0; i < cols; i++) {
  1271. iscomponent[i] = columnGenerators.containsKey(colids[i])
  1272. || Component.class.isAssignableFrom(getType(colids[i]));
  1273. }
  1274. int firstIndexNotInCache;
  1275. if (pageBuffer != null && pageBuffer[CELL_ITEMID].length > 0) {
  1276. firstIndexNotInCache = pageBufferFirstIndex
  1277. + pageBuffer[CELL_ITEMID].length;
  1278. } else {
  1279. firstIndexNotInCache = -1;
  1280. }
  1281. // Creates the page contents
  1282. int filledRows = 0;
  1283. for (int i = 0; i < rows && id != null; i++) {
  1284. cells[CELL_ITEMID][i] = id;
  1285. cells[CELL_KEY][i] = itemIdMapper.key(id);
  1286. if (headmode != ROW_HEADER_MODE_HIDDEN) {
  1287. switch (headmode) {
  1288. case ROW_HEADER_MODE_INDEX:
  1289. cells[CELL_HEADER][i] = String.valueOf(i + firstIndex
  1290. + 1);
  1291. break;
  1292. default:
  1293. cells[CELL_HEADER][i] = getItemCaption(id);
  1294. }
  1295. cells[CELL_ICON][i] = getItemIcon(id);
  1296. }
  1297. if (cols > 0) {
  1298. for (int j = 0; j < cols; j++) {
  1299. if (isColumnCollapsed(colids[j])) {
  1300. continue;
  1301. }
  1302. Property p = null;
  1303. Object value = "";
  1304. boolean isGenerated = columnGenerators
  1305. .containsKey(colids[j]);
  1306. if (!isGenerated) {
  1307. p = getContainerProperty(id, colids[j]);
  1308. }
  1309. // check in current pageBuffer already has row
  1310. int index = firstIndex + i;
  1311. if (p != null || isGenerated) {
  1312. if (index < firstIndexNotInCache
  1313. && index >= pageBufferFirstIndex) {
  1314. // we have data already in our cache,
  1315. // recycle it instead of fetching it via
  1316. // getValue/getPropertyValue
  1317. int indexInOldBuffer = index
  1318. - pageBufferFirstIndex;
  1319. value = pageBuffer[CELL_FIRSTCOL + j][indexInOldBuffer];
  1320. if (!isGenerated && iscomponent[j]
  1321. || !(value instanceof Component)) {
  1322. listenProperty(p, oldListenedProperties);
  1323. }
  1324. } else {
  1325. if (isGenerated) {
  1326. ColumnGenerator cg = columnGenerators
  1327. .get(colids[j]);
  1328. value = cg
  1329. .generateCell(this, id, colids[j]);
  1330. } else if (iscomponent[j]) {
  1331. value = p.getValue();
  1332. listenProperty(p, oldListenedProperties);
  1333. } else if (p != null) {
  1334. value = getPropertyValue(id, colids[j], p);
  1335. /*
  1336. * If returned value is Component (via
  1337. * fieldfactory or overridden
  1338. * getPropertyValue) we excpect it to listen
  1339. * property value changes. Otherwise if
  1340. * property emits value change events, table
  1341. * will start to listen them and refresh
  1342. * content when needed.
  1343. */
  1344. if (!(value instanceof Component)) {
  1345. listenProperty(p, oldListenedProperties);
  1346. }
  1347. } else {
  1348. value = getPropertyValue(id, colids[j],
  1349. null);
  1350. }
  1351. }
  1352. }
  1353. if (value instanceof Component) {
  1354. if (oldVisibleComponents == null
  1355. || !oldVisibleComponents.contains(value)) {
  1356. ((Component) value).setParent(this);
  1357. }
  1358. visibleComponents.add((Component) value);
  1359. }
  1360. cells[CELL_FIRSTCOL + j][i] = value;
  1361. }
  1362. }
  1363. // Gets the next item id
  1364. if (items instanceof Container.Indexed) {
  1365. Container.Indexed indexed = (Container.Indexed) items;
  1366. int index = firstIndex + i + 1;
  1367. if (index < indexed.size()) {
  1368. id = indexed.getIdByIndex(index);
  1369. } else {
  1370. id = null;
  1371. }
  1372. } else {
  1373. id = ((Container.Ordered) items).nextItemId(id);
  1374. }
  1375. filledRows++;
  1376. }
  1377. // Assures that all the rows of the cell-buffer are valid
  1378. if (filledRows != cells[0].length) {
  1379. final Object[][] temp = new Object[cells.length][filledRows];
  1380. for (int i = 0; i < cells.length; i++) {
  1381. for (int j = 0; j < filledRows; j++) {
  1382. temp[i][j] = cells[i][j];
  1383. }
  1384. }
  1385. cells = temp;
  1386. }
  1387. pageBufferFirstIndex = firstIndex;
  1388. // Saves the results to internal buffer
  1389. pageBuffer = cells;
  1390. unregisterPropertiesAndComponents(oldListenedProperties,
  1391. oldVisibleComponents);
  1392. requestRepaint();
  1393. }
  1394. }
  1395. private void listenProperty(Property p,
  1396. HashSet<Property> oldListenedProperties) {
  1397. if (p instanceof Property.ValueChangeNotifier) {
  1398. if (oldListenedProperties == null
  1399. || !oldListenedProperties.contains(p)) {
  1400. ((Property.ValueChangeNotifier) p).addListener(this);
  1401. }
  1402. /*
  1403. * register listened properties, so we can do proper cleanup to free
  1404. * memory. Essential if table has loads of data and it is used for a
  1405. * long time.
  1406. */
  1407. listenedProperties.add(p);
  1408. }
  1409. }
  1410. /**
  1411. * Helper method to remove listeners and maintain correct component
  1412. * hierarchy. Detaches properties and components if those are no more
  1413. * rendered in client.
  1414. *
  1415. * @param oldListenedProperties
  1416. * set of properties that where listened in last render
  1417. * @param oldVisibleComponents
  1418. * set of components that where attached in last render
  1419. */
  1420. private void unregisterPropertiesAndComponents(
  1421. HashSet<Property> oldListenedProperties,
  1422. HashSet<Component> oldVisibleComponents) {
  1423. if (oldVisibleComponents != null) {
  1424. for (final Iterator<Component> i = oldVisibleComponents.iterator(); i
  1425. .hasNext();) {
  1426. Component c = i.next();
  1427. if (!visibleComponents.contains(c)) {
  1428. c.setParent(null);
  1429. }
  1430. }
  1431. }
  1432. if (oldListenedProperties != null) {
  1433. for (final Iterator<Property> i = oldListenedProperties.iterator(); i
  1434. .hasNext();) {
  1435. Property.ValueChangeNotifier o = (ValueChangeNotifier) i.next();
  1436. if (!listenedProperties.contains(o)) {
  1437. o.removeListener(this);
  1438. }
  1439. }
  1440. }
  1441. }
  1442. /**
  1443. * Refreshes the current page contents.
  1444. *
  1445. * @deprecated should not need to be used
  1446. */
  1447. @Deprecated
  1448. public void refreshCurrentPage() {
  1449. }
  1450. /**
  1451. * Sets the row header mode.
  1452. * <p>
  1453. * The mode can be one of the following ones:
  1454. * <ul>
  1455. * <li><code>ROW_HEADER_MODE_HIDDEN</code>: The row captions are hidden.</li>
  1456. * <li><code>ROW_HEADER_MODE_ID</code>: Items Id-objects
  1457. * <code>toString()</code> is used as row caption.
  1458. * <li><code>ROW_HEADER_MODE_ITEM</code>: Item-objects
  1459. * <code>toString()</code> is used as row caption.
  1460. * <li><code>ROW_HEADER_MODE_PROPERTY</code>: Property set with
  1461. * <code>setItemCaptionPropertyId()</code> is used as row header.
  1462. * <li><code>ROW_HEADER_MODE_EXPLICIT_DEFAULTS_ID</code>: Items Id-objects
  1463. * <code>toString()</code> is used as row header. If caption is explicitly
  1464. * specified, it overrides the id-caption.
  1465. * <li><code>ROW_HEADER_MODE_EXPLICIT</code>: The row headers must be
  1466. * explicitly specified.</li>
  1467. * <li><code>ROW_HEADER_MODE_INDEX</code>: The index of the item is used as
  1468. * row caption. The index mode can only be used with the containers
  1469. * implementing <code>Container.Indexed</code> interface.</li>
  1470. * </ul>
  1471. * The default value is <code>ROW_HEADER_MODE_HIDDEN</code>
  1472. * </p>
  1473. *
  1474. * @param mode
  1475. * the One of the modes listed above.
  1476. */
  1477. public void setRowHeaderMode(int mode) {
  1478. if (ROW_HEADER_MODE_HIDDEN == mode) {
  1479. rowCaptionsAreHidden = true;
  1480. } else {
  1481. rowCaptionsAreHidden = false;
  1482. setItemCaptionMode(mode);
  1483. }
  1484. // Assure visual refresh
  1485. refreshRenderedCells();
  1486. }
  1487. /**
  1488. * Gets the row header mode.
  1489. *
  1490. * @return the Row header mode.
  1491. * @see #setRowHeaderMode(int)
  1492. */
  1493. public int getRowHeaderMode() {
  1494. return rowCaptionsAreHidden ? ROW_HEADER_MODE_HIDDEN
  1495. : getItemCaptionMode();
  1496. }
  1497. /**
  1498. * Adds the new row to table and fill the visible cells (except generated
  1499. * columns) with given values.
  1500. *
  1501. * @param cells
  1502. * the Object array that is used for filling the visible cells
  1503. * new row. The types must be settable to visible column property
  1504. * types.
  1505. * @param itemId
  1506. * the Id the new row. If null, a new id is automatically
  1507. * assigned. If given, the table cant already have a item with
  1508. * given id.
  1509. * @return Returns item id for the new row. Returns null if operation fails.
  1510. */
  1511. public Object addItem(Object[] cells, Object itemId)
  1512. throws UnsupportedOperationException {
  1513. // remove generated columns from the list of columns being assigned
  1514. final LinkedList<Object> availableCols = new LinkedList<Object>();
  1515. for (Iterator<Object> it = visibleColumns.iterator(); it.hasNext();) {
  1516. Object id = it.next();
  1517. if (!columnGenerators.containsKey(id)) {
  1518. availableCols.add(id);
  1519. }
  1520. }
  1521. // Checks that a correct number of cells are given
  1522. if (cells.length != availableCols.size()) {
  1523. return null;
  1524. }
  1525. // Creates new item
  1526. Item item;
  1527. if (itemId == null) {
  1528. itemId = items.addItem();
  1529. if (itemId == null) {
  1530. return null;
  1531. }
  1532. item = items.getItem(itemId);
  1533. } else {
  1534. item = items.addItem(itemId);
  1535. }
  1536. if (item == null) {
  1537. return null;
  1538. }
  1539. // Fills the item properties
  1540. for (int i = 0; i < availableCols.size(); i++) {
  1541. item.getItemProperty(availableCols.get(i)).setValue(cells[i]);
  1542. }
  1543. if (!(items instanceof Container.ItemSetChangeNotifier)) {
  1544. resetPageBuffer();
  1545. refreshRenderedCells();
  1546. }
  1547. return itemId;
  1548. }
  1549. /* Overriding select behavior */
  1550. @Override
  1551. public void setValue(Object newValue) throws ReadOnlyException,
  1552. ConversionException {
  1553. // external selection change, need to truncate pageBuffer
  1554. resetPageBuffer();
  1555. refreshRenderedCells();
  1556. super.setValue(newValue);
  1557. }
  1558. /**
  1559. * Sets the Container that serves as the data source of the viewer.
  1560. *
  1561. * As a side-effect Table's value (selection) is set to null due old
  1562. * selection not necessary exists in new Container.
  1563. *
  1564. * @see com.vaadin.data.Container.Viewer#setContainerDataSource(Container)
  1565. */
  1566. @Override
  1567. public void setContainerDataSource(Container newDataSource) {
  1568. disableContentRefreshing();
  1569. if (newDataSource == null) {
  1570. newDataSource = new IndexedContainer();
  1571. }
  1572. // Assures that the data source is ordered by making unordered
  1573. // containers ordered by wrapping them
  1574. if (newDataSource instanceof Container.Ordered) {
  1575. super.setContainerDataSource(newDataSource);
  1576. } else {
  1577. super.setContainerDataSource(new ContainerOrderedWrapper(
  1578. newDataSource));
  1579. }
  1580. // Resets page position
  1581. currentPageFirstItemId = null;
  1582. currentPageFirstItemIndex = 0;
  1583. // Resets column properties
  1584. if (collapsedColumns != null) {
  1585. collapsedColumns.clear();
  1586. }
  1587. // columnGenerators 'override' properties, don't add the same id twice
  1588. Collection<Object> col = new LinkedList<Object>();
  1589. for (Iterator it = getContainerPropertyIds().iterator(); it.hasNext();) {
  1590. Object id = it.next();
  1591. if (columnGenerators == null || !columnGenerators.containsKey(id)) {
  1592. col.add(id);
  1593. }
  1594. }
  1595. // generators added last
  1596. if (columnGenerators != null && columnGenerators.size() > 0) {
  1597. col.addAll(columnGenerators.keySet());
  1598. }
  1599. setVisibleColumns(col.toArray());
  1600. // null value as we may not be sure that currently selected identifier
  1601. // exits in new ds
  1602. setValue(null);
  1603. // Assure visual refresh
  1604. resetPageBuffer();
  1605. enableContentRefreshing(true);
  1606. }
  1607. /* Component basics */
  1608. /**
  1609. * Invoked when the value of a variable has changed.
  1610. *
  1611. * @see com.vaadin.ui.Select#changeVariables(java.lang.Object,
  1612. * java.util.Map)
  1613. */
  1614. @Override
  1615. public void changeVariables(Object source, Map<String, Object> variables) {
  1616. boolean clientNeedsContentRefresh = false;
  1617. handleClickEvent(variables);
  1618. disableContentRefreshing();
  1619. if (!isSelectable() && variables.containsKey("selected")) {
  1620. // Not-selectable is a special case, AbstractSelect does not support
  1621. // TODO could be optimized.
  1622. variables = new HashMap<String, Object>(variables);
  1623. variables.remove("selected");
  1624. }
  1625. super.changeVariables(source, variables);
  1626. // Client might update the pagelength if Table height is fixed
  1627. if (variables.containsKey("pagelength")) {
  1628. // Sets pageLength directly to avoid repaint that setter causes
  1629. pageLength = (Integer) variables.get("pagelength");
  1630. }
  1631. // Page start index
  1632. if (variables.containsKey("firstvisible")) {
  1633. final Integer value = (Integer) variables.get("firstvisible");
  1634. if (value != null) {
  1635. setCurrentPageFirstItemIndex(value.intValue(), false);
  1636. }
  1637. }
  1638. // Sets requested firstrow and rows for the next paint
  1639. if (variables.containsKey("reqfirstrow")
  1640. || variables.containsKey("reqrows")) {
  1641. try {
  1642. firstToBeRenderedInClient = ((Integer) variables
  1643. .get("firstToBeRendered")).intValue();
  1644. lastToBeRenderedInClient = ((Integer) variables
  1645. .get("lastToBeRendered")).intValue();
  1646. } catch (Exception e) {
  1647. // FIXME: Handle exception
  1648. e.printStackTrace();
  1649. }
  1650. // respect suggested rows only if table is not otherwise updated
  1651. // (row caches emptied by other event)
  1652. if (!containerChangeToBeRendered) {
  1653. Integer value = (Integer) variables.get("reqfirstrow");
  1654. if (value != null) {
  1655. reqFirstRowToPaint = value.intValue();
  1656. }
  1657. value = (Integer) variables.get("reqrows");
  1658. if (value != null) {
  1659. reqRowsToPaint = value.intValue();
  1660. // sanity check
  1661. if (reqFirstRowToPaint + reqRowsToPaint > size()) {
  1662. reqRowsToPaint = size() - reqFirstRowToPaint;
  1663. }
  1664. }
  1665. }
  1666. clientNeedsContentRefresh = true;
  1667. }
  1668. if (!sortDisabled) {
  1669. // Sorting
  1670. boolean doSort = false;
  1671. if (variables.containsKey("sortcolumn")) {
  1672. final String colId = (String) variables.get("sortcolumn");
  1673. if (colId != null && !"".equals(colId) && !"null".equals(colId)) {
  1674. final Object id = columnIdMap.get(colId);
  1675. setSortContainerPropertyId(id, false);
  1676. doSort = true;
  1677. }
  1678. }
  1679. if (variables.containsKey("sortascending")) {
  1680. final boolean state = ((Boolean) variables.get("sortascending"))
  1681. .booleanValue();
  1682. if (state != sortAscending) {
  1683. setSortAscending(state, false);
  1684. doSort = true;
  1685. }
  1686. }
  1687. if (doSort) {
  1688. this.sort();
  1689. resetPageBuffer();
  1690. }
  1691. }
  1692. // Dynamic column hide/show and order
  1693. // Update visible columns
  1694. if (isColumnCollapsingAllowed()) {
  1695. if (variables.containsKey("collapsedcolumns")) {
  1696. try {
  1697. final Object[] ids = (Object[]) variables
  1698. .get("collapsedcolumns");
  1699. for (final Iterator<Object> it = visibleColumns.iterator(); it
  1700. .hasNext();) {
  1701. setColumnCollapsed(it.next(), false);
  1702. }
  1703. for (int i = 0; i < ids.length; i++) {
  1704. setColumnCollapsed(columnIdMap.get(ids[i].toString()),
  1705. true);
  1706. }
  1707. } catch (final Exception e) {
  1708. // FIXME: Handle exception
  1709. e.printStackTrace();
  1710. }
  1711. clientNeedsContentRefresh = true;
  1712. }
  1713. }
  1714. if (isColumnReorderingAllowed()) {
  1715. if (variables.containsKey("columnorder")) {
  1716. try {
  1717. final Object[] ids = (Object[]) variables
  1718. .get("columnorder");
  1719. for (int i = 0; i < ids.length; i++) {
  1720. ids[i] = columnIdMap.get(ids[i].toString());
  1721. }
  1722. setColumnOrder(ids);
  1723. } catch (final Exception e) {
  1724. // FIXME: Handle exception
  1725. e.printStackTrace();
  1726. }
  1727. clientNeedsContentRefresh = true;
  1728. }
  1729. }
  1730. enableContentRefreshing(clientNeedsContentRefresh);
  1731. // Actions
  1732. if (variables.containsKey("action")) {
  1733. final StringTokenizer st = new StringTokenizer((String) variables
  1734. .get("action"), ",");
  1735. if (st.countTokens() == 2) {
  1736. final Object itemId = itemIdMapper.get(st.nextToken());
  1737. final Action action = (Action) actionMapper.get(st.nextToken());
  1738. if (action != null && containsId(itemId)
  1739. && actionHandlers != null) {
  1740. for (final Iterator<Handler> i = actionHandlers.iterator(); i
  1741. .hasNext();) {
  1742. (i.next()).handleAction(action, this, itemId);
  1743. }
  1744. }
  1745. }
  1746. }
  1747. }
  1748. /**
  1749. * Handles click event
  1750. *
  1751. * @param variables
  1752. */
  1753. private void handleClickEvent(Map<String, Object> variables) {
  1754. if (variables.containsKey("clickEvent")) {
  1755. String key = (String) variables.get("clickedKey");
  1756. Object itemId = itemIdMapper.get(key);
  1757. Object propertyId = null;
  1758. String colkey = (String) variables.get("clickedColKey");
  1759. // click is not necessary on a property
  1760. if (colkey != null) {
  1761. propertyId = columnIdMap.get(colkey);
  1762. }
  1763. MouseEventDetails evt = MouseEventDetails
  1764. .deSerialize((String) variables.get("clickEvent"));
  1765. Item item = getItem(itemId);
  1766. if (item != null) {
  1767. fireEvent(new ItemClickEvent(this, item, itemId, propertyId,
  1768. evt));
  1769. }
  1770. }
  1771. }
  1772. /**
  1773. * Go to mode where content updates are not done. This is due we want to
  1774. * bypass expensive content for some reason (like when we know we may have
  1775. * other content changes on their way).
  1776. *
  1777. * @return true if content refresh flag was enabled prior this call
  1778. */
  1779. protected boolean disableContentRefreshing() {
  1780. boolean wasDisabled = isContentRefreshesEnabled;
  1781. isContentRefreshesEnabled = false;
  1782. return wasDisabled;
  1783. }
  1784. /**
  1785. * Go to mode where content content refreshing has effect.
  1786. *
  1787. * @param refreshContent
  1788. * true if content refresh needs to be done
  1789. */
  1790. protected void enableContentRefreshing(boolean refreshContent) {
  1791. isContentRefreshesEnabled = true;
  1792. if (refreshContent) {
  1793. refreshRenderedCells();
  1794. // Ensure that client gets a response
  1795. requestRepaint();
  1796. }
  1797. }
  1798. /*
  1799. * (non-Javadoc)
  1800. *
  1801. * @see com.vaadin.ui.AbstractSelect#paintContent(com.vaadin.
  1802. * terminal.PaintTarget)
  1803. */
  1804. @Override
  1805. public void paintContent(PaintTarget target) throws PaintException {
  1806. // The tab ordering number
  1807. if (getTabIndex() > 0) {
  1808. target.addAttribute("tabindex", getTabIndex());
  1809. }
  1810. if (dragMode != TableDragMode.NONE) {
  1811. target.addAttribute("dragmode", dragMode.ordinal());
  1812. }
  1813. // Initialize temps
  1814. final Object[] colids = getVisibleColumns();
  1815. final int cols = colids.length;
  1816. final int first = getCurrentPageFirstItemIndex();
  1817. int total = size();
  1818. final int pagelen = getPageLength();
  1819. final int colHeadMode = getColumnHeaderMode();
  1820. final boolean colheads = colHeadMode != COLUMN_HEADER_MODE_HIDDEN;
  1821. final boolean rowheads = getRowHeaderMode() != ROW_HEADER_MODE_HIDDEN;
  1822. final Object[][] cells = getVisibleCells();
  1823. final boolean iseditable = isEditable();
  1824. int rows;
  1825. if (reqRowsToPaint >= 0) {
  1826. rows = reqRowsToPaint;
  1827. } else {
  1828. rows = cells[0].length;
  1829. if (alwaysRecalculateColumnWidths) {
  1830. // TODO experimental feature for now: tell the client to
  1831. // recalculate column widths.
  1832. // We'll only do this for paints that do not originate from
  1833. // table scroll/cache requests (i.e when reqRowsToPaint<0)
  1834. target.addAttribute("recalcWidths", true);
  1835. }
  1836. }
  1837. if (!isNullSelectionAllowed() && getNullSelectionItemId() != null
  1838. && containsId(getNullSelectionItemId())) {
  1839. total--;
  1840. rows--;
  1841. }
  1842. // selection support
  1843. LinkedList<String> selectedKeys = new LinkedList<String>();
  1844. if (isMultiSelect()) {
  1845. // only paint selections that are currently visible in the client
  1846. HashSet sel = new HashSet((Set) getValue());
  1847. Collection vids = getVisibleItemIds();
  1848. for (Iterator it = vids.iterator(); it.hasNext();) {
  1849. Object id = it.next();
  1850. if (sel.contains(id)) {
  1851. selectedKeys.add(itemIdMapper.key(id));
  1852. }
  1853. }
  1854. } else {
  1855. Object value = getValue();
  1856. if (value == null) {
  1857. value = getNullSelectionItemId();
  1858. }
  1859. if (value != null) {
  1860. selectedKeys.add(itemIdMapper.key(value));
  1861. }
  1862. }
  1863. // Table attributes
  1864. if (isSelectable()) {
  1865. target.addAttribute("selectmode", (isMultiSelect() ? "multi"
  1866. : "single"));
  1867. } else {
  1868. target.addAttribute("selectmode", "none");
  1869. }
  1870. if (cacheRate != CACHE_RATE_DEFAULT) {
  1871. target.addAttribute("cr", cacheRate);
  1872. }
  1873. target.addAttribute("cols", cols);
  1874. target.addAttribute("rows", rows);
  1875. target.addAttribute("firstrow",
  1876. (reqFirstRowToPaint >= 0 ? reqFirstRowToPaint
  1877. : firstToBeRenderedInClient));
  1878. target.addAttribute("totalrows", total);
  1879. if (pagelen != 0) {
  1880. target.addAttribute("pagelength", pagelen);
  1881. }
  1882. if (colheads) {
  1883. target.addAttribute("colheaders", true);
  1884. }
  1885. if (rowheads) {
  1886. target.addAttribute("rowheaders", true);
  1887. }
  1888. // Visible column order
  1889. final Collection sortables = getSortableContainerPropertyIds();
  1890. final ArrayList<String> visibleColOrder = new ArrayList<String>();
  1891. for (final Iterator<Object> it = visibleColumns.iterator(); it
  1892. .hasNext();) {
  1893. final Object columnId = it.next();
  1894. if (!isColumnCollapsed(columnId)) {
  1895. visibleColOrder.add(columnIdMap.key(columnId));
  1896. }
  1897. }
  1898. target.addAttribute("vcolorder", visibleColOrder.toArray());
  1899. // Rows
  1900. final Set<Action> actionSet = new LinkedHashSet<Action>();
  1901. final boolean selectable = isSelectable();
  1902. final boolean[] iscomponent = new boolean[visibleColumns.size()];
  1903. int iscomponentIndex = 0;
  1904. for (final Iterator<Object> it = visibleColumns.iterator(); it
  1905. .hasNext()
  1906. && iscomponentIndex < iscomponent.length;) {
  1907. final Object columnId = it.next();
  1908. if (columnGenerators.containsKey(columnId)) {
  1909. iscomponent[iscomponentIndex++] = true;
  1910. } else {
  1911. final Class colType = getType(columnId);
  1912. iscomponent[iscomponentIndex++] = colType != null
  1913. && Component.class.isAssignableFrom(colType);
  1914. }
  1915. }
  1916. target.startTag("rows");
  1917. // cells array contains all that are supposed to be visible on client,
  1918. // but we'll start from the one requested by client
  1919. int start = 0;
  1920. if (reqFirstRowToPaint != -1 && firstToBeRenderedInClient != -1) {
  1921. start = reqFirstRowToPaint - firstToBeRenderedInClient;
  1922. }
  1923. int end = cells[0].length;
  1924. if (reqRowsToPaint != -1) {
  1925. end = start + reqRowsToPaint;
  1926. }
  1927. // sanity check
  1928. if (lastToBeRenderedInClient != -1 && lastToBeRenderedInClient < end) {
  1929. end = lastToBeRenderedInClient + 1;
  1930. }
  1931. if (start > cells[CELL_ITEMID].length || start < 0) {
  1932. start = 0;
  1933. }
  1934. for (int i = start; i < end; i++) {
  1935. final Object itemId = cells[CELL_ITEMID][i];
  1936. if (!isNullSelectionAllowed() && getNullSelectionItemId() != null
  1937. && itemId == getNullSelectionItemId()) {
  1938. // Remove null selection item if null selection is not allowed
  1939. continue;
  1940. }
  1941. target.startTag("tr");
  1942. // tr attributes
  1943. if (rowheads) {
  1944. if (cells[CELL_ICON][i] != null) {
  1945. target.addAttribute("icon", (Resource) cells[CELL_ICON][i]);
  1946. }
  1947. if (cells[CELL_HEADER][i] != null) {
  1948. target.addAttribute("caption",
  1949. (String) cells[CELL_HEADER][i]);
  1950. }
  1951. }
  1952. target.addAttribute("key", Integer.parseInt(cells[CELL_KEY][i]
  1953. .toString()));
  1954. if (isSelected(itemId)) {
  1955. target.addAttribute("selected", true);
  1956. }
  1957. // Actions
  1958. if (actionHandlers != null) {
  1959. final ArrayList<String> keys = new ArrayList<String>();
  1960. for (final Iterator<Handler> ahi = actionHandlers.iterator(); ahi
  1961. .hasNext();) {
  1962. final Action[] aa = (ahi.next()).getActions(itemId, this);
  1963. if (aa != null) {
  1964. for (int ai = 0; ai < aa.length; ai++) {
  1965. final String key = actionMapper.key(aa[ai]);
  1966. actionSet.add(aa[ai]);
  1967. keys.add(key);
  1968. }
  1969. }
  1970. }
  1971. target.addAttribute("al", keys.toArray());
  1972. }
  1973. /*
  1974. * For each row, if a cellStyleGenerator is specified, get the
  1975. * specific style for the cell, using null as propertyId. If there
  1976. * is any, add it to the target.
  1977. */
  1978. if (cellStyleGenerator != null) {
  1979. String rowStyle = cellStyleGenerator.getStyle(itemId, null);
  1980. if (rowStyle != null && !rowStyle.equals("")) {
  1981. target.addAttribute("rowstyle", rowStyle);
  1982. }
  1983. }
  1984. // cells
  1985. int currentColumn = 0;
  1986. for (final Iterator<Object> it = visibleColumns.iterator(); it
  1987. .hasNext(); currentColumn++) {
  1988. final Object columnId = it.next();
  1989. if (columnId == null || isColumnCollapsed(columnId)) {
  1990. continue;
  1991. }
  1992. /*
  1993. * For each cell, if a cellStyleGenerator is specified, get the
  1994. * specific style for the cell. If there is any, add it to the
  1995. * target.
  1996. */
  1997. if (cellStyleGenerator != null) {
  1998. String cellStyle = cellStyleGenerator.getStyle(itemId,
  1999. columnId);
  2000. if (cellStyle != null && !cellStyle.equals("")) {
  2001. target.addAttribute("style-"
  2002. + columnIdMap.key(columnId), cellStyle);
  2003. }
  2004. }
  2005. if ((iscomponent[currentColumn] || iseditable)
  2006. && Component.class.isInstance(cells[CELL_FIRSTCOL
  2007. + currentColumn][i])) {
  2008. final Component c = (Component) cells[CELL_FIRSTCOL
  2009. + currentColumn][i];
  2010. if (c == null) {
  2011. target.addText("");
  2012. } else {
  2013. c.paint(target);
  2014. }
  2015. } else {
  2016. target
  2017. .addText((String) cells[CELL_FIRSTCOL
  2018. + currentColumn][i]);
  2019. }
  2020. }
  2021. target.endTag("tr");
  2022. }
  2023. target.endTag("rows");
  2024. // The select variable is only enabled if selectable
  2025. if (selectable) {
  2026. target.addVariable(this, "selected", selectedKeys
  2027. .toArray(new String[selectedKeys.size()]));
  2028. }
  2029. // The cursors are only shown on pageable table
  2030. if (first != 0 || getPageLength() > 0) {
  2031. target.addVariable(this, "firstvisible", first);
  2032. }
  2033. // Sorting
  2034. if (getContainerDataSource() instanceof Container.Sortable) {
  2035. target.addVariable(this, "sortcolumn", columnIdMap
  2036. .key(sortContainerPropertyId));
  2037. target.addVariable(this, "sortascending", sortAscending);
  2038. }
  2039. // Resets and paints "to be painted next" variables. Also reset
  2040. // pageBuffer
  2041. reqFirstRowToPaint = -1;
  2042. reqRowsToPaint = -1;
  2043. containerChangeToBeRendered = false;
  2044. target.addVariable(this, "reqrows", reqRowsToPaint);
  2045. target.addVariable(this, "reqfirstrow", reqFirstRowToPaint);
  2046. // Actions
  2047. if (!actionSet.isEmpty()) {
  2048. target.addVariable(this, "action", "");
  2049. target.startTag("actions");
  2050. for (final Iterator<Action> it = actionSet.iterator(); it.hasNext();) {
  2051. final Action a = it.next();
  2052. target.startTag("action");
  2053. if (a.getCaption() != null) {
  2054. target.addAttribute("caption", a.getCaption());
  2055. }
  2056. if (a.getIcon() != null) {
  2057. target.addAttribute("icon", a.getIcon());
  2058. }
  2059. target.addAttribute("key", actionMapper.key(a));
  2060. target.endTag("action");
  2061. }
  2062. target.endTag("actions");
  2063. }
  2064. if (columnReorderingAllowed) {
  2065. final String[] colorder = new String[visibleColumns.size()];
  2066. int i = 0;
  2067. for (final Iterator<Object> it = visibleColumns.iterator(); it
  2068. .hasNext()
  2069. && i < colorder.length;) {
  2070. colorder[i++] = columnIdMap.key(it.next());
  2071. }
  2072. target.addVariable(this, "columnorder", colorder);
  2073. }
  2074. // Available columns
  2075. if (columnCollapsingAllowed) {
  2076. final HashSet<Object> ccs = new HashSet<Object>();
  2077. for (final Iterator<Object> i = visibleColumns.iterator(); i
  2078. .hasNext();) {
  2079. final Object o = i.next();
  2080. if (isColumnCollapsed(o)) {
  2081. ccs.add(o);
  2082. }
  2083. }
  2084. final String[] collapsedkeys = new String[ccs.size()];
  2085. int nextColumn = 0;
  2086. for (final Iterator<Object> it = visibleColumns.iterator(); it
  2087. .hasNext()
  2088. && nextColumn < collapsedkeys.length;) {
  2089. final Object columnId = it.next();
  2090. if (isColumnCollapsed(columnId)) {
  2091. collapsedkeys[nextColumn++] = columnIdMap.key(columnId);
  2092. }
  2093. }
  2094. target.addVariable(this, "collapsedcolumns", collapsedkeys);
  2095. }
  2096. target.startTag("visiblecolumns");
  2097. int i = 0;
  2098. for (final Iterator<Object> it = visibleColumns.iterator(); it
  2099. .hasNext(); i++) {
  2100. final Object columnId = it.next();
  2101. if (columnId != null) {
  2102. target.startTag("column");
  2103. target.addAttribute("cid", columnIdMap.key(columnId));
  2104. final String head = getColumnHeader(columnId);
  2105. target.addAttribute("caption", (head != null ? head : ""));
  2106. if (isColumnCollapsed(columnId)) {
  2107. target.addAttribute("collapsed", true);
  2108. }
  2109. if (colheads) {
  2110. if (getColumnIcon(columnId) != null) {
  2111. target.addAttribute("icon", getColumnIcon(columnId));
  2112. }
  2113. if (sortables.contains(columnId)) {
  2114. target.addAttribute("sortable", true);
  2115. }
  2116. }
  2117. if (!ALIGN_LEFT.equals(getColumnAlignment(columnId))) {
  2118. target.addAttribute("align", getColumnAlignment(columnId));
  2119. }
  2120. if (columnWidths.containsKey(columnId)) {
  2121. if (getColumnWidth(columnId) > -1) {
  2122. target.addAttribute("width", String
  2123. .valueOf(getColumnWidth(columnId)));
  2124. } else {
  2125. target.addAttribute("er",
  2126. getColumnExpandRatio(columnId));
  2127. }
  2128. }
  2129. target.endTag("column");
  2130. }
  2131. }
  2132. target.endTag("visiblecolumns");
  2133. if (dropHandler != null) {
  2134. dropHandler.getAcceptCriterion().paint(target);
  2135. }
  2136. }
  2137. /**
  2138. * Gets the cached visible table contents.
  2139. *
  2140. * @return the cached visible table contents.
  2141. */
  2142. private Object[][] getVisibleCells() {
  2143. if (pageBuffer == null) {
  2144. refreshRenderedCells();
  2145. }
  2146. return pageBuffer;
  2147. }
  2148. /**
  2149. * Gets the value of property.
  2150. *
  2151. * By default if the table is editable the fieldFactory is used to create
  2152. * editors for table cells. Otherwise formatPropertyValue is used to format
  2153. * the value representation.
  2154. *
  2155. * @param rowId
  2156. * the Id of the row (same as item Id).
  2157. * @param colId
  2158. * the Id of the column.
  2159. * @param property
  2160. * the Property to be presented.
  2161. * @return Object Either formatted value or Component for field.
  2162. * @see #setTableFieldFactory(TableFieldFactory)
  2163. */
  2164. protected Object getPropertyValue(Object rowId, Object colId,
  2165. Property property) {
  2166. if (isEditable() && fieldFactory != null) {
  2167. final Field f = fieldFactory.createField(getContainerDataSource(),
  2168. rowId, colId, this);
  2169. if (f != null) {
  2170. f.setPropertyDataSource(property);
  2171. return f;
  2172. }
  2173. }
  2174. return formatPropertyValue(rowId, colId, property);
  2175. }
  2176. /**
  2177. * Formats table cell property values. By default the property.toString()
  2178. * and return a empty string for null properties.
  2179. *
  2180. * @param rowId
  2181. * the Id of the row (same as item Id).
  2182. * @param colId
  2183. * the Id of the column.
  2184. * @param property
  2185. * the Property to be formatted.
  2186. * @return the String representation of property and its value.
  2187. * @since 3.1
  2188. */
  2189. protected String formatPropertyValue(Object rowId, Object colId,
  2190. Property property) {
  2191. if (property == null) {
  2192. return "";
  2193. }
  2194. return property.toString();
  2195. }
  2196. /* Action container */
  2197. /**
  2198. * Registers a new action handler for this container
  2199. *
  2200. * @see com.vaadin.event.Action.Container#addActionHandler(Action.Handler)
  2201. */
  2202. public void addActionHandler(Action.Handler actionHandler) {
  2203. if (actionHandler != null) {
  2204. if (actionHandlers == null) {
  2205. actionHandlers = new LinkedList<Handler>();
  2206. actionMapper = new KeyMapper();
  2207. }
  2208. if (!actionHandlers.contains(actionHandler)) {
  2209. actionHandlers.add(actionHandler);
  2210. requestRepaint();
  2211. }
  2212. }
  2213. }
  2214. /**
  2215. * Removes a previously registered action handler for the contents of this
  2216. * container.
  2217. *
  2218. * @see com.vaadin.event.Action.Container#removeActionHandler(Action.Handler)
  2219. */
  2220. public void removeActionHandler(Action.Handler actionHandler) {
  2221. if (actionHandlers != null && actionHandlers.contains(actionHandler)) {
  2222. actionHandlers.remove(actionHandler);
  2223. if (actionHandlers.isEmpty()) {
  2224. actionHandlers = null;
  2225. actionMapper = null;
  2226. }
  2227. requestRepaint();
  2228. }
  2229. }
  2230. /**
  2231. * Removes all action handlers
  2232. */
  2233. public void removeAllActionHandlers() {
  2234. actionHandlers = null;
  2235. actionMapper = null;
  2236. requestRepaint();
  2237. }
  2238. /* Property value change listening support */
  2239. /**
  2240. * Notifies this listener that the Property's value has changed.
  2241. *
  2242. * Also listens changes in rendered items to refresh content area.
  2243. *
  2244. * @see com.vaadin.data.Property.ValueChangeListener#valueChange(Property.ValueChangeEvent)
  2245. */
  2246. @Override
  2247. public void valueChange(Property.ValueChangeEvent event) {
  2248. if (event.getProperty() == this) {
  2249. super.valueChange(event);
  2250. } else {
  2251. resetPageBuffer();
  2252. refreshRenderedCells();
  2253. containerChangeToBeRendered = true;
  2254. }
  2255. requestRepaint();
  2256. }
  2257. private void resetPageBuffer() {
  2258. firstToBeRenderedInClient = -1;
  2259. lastToBeRenderedInClient = -1;
  2260. reqFirstRowToPaint = -1;
  2261. reqRowsToPaint = -1;
  2262. pageBuffer = null;
  2263. }
  2264. /**
  2265. * Notifies the component that it is connected to an application.
  2266. *
  2267. * @see com.vaadin.ui.Component#attach()
  2268. */
  2269. @Override
  2270. public void attach() {
  2271. super.attach();
  2272. refreshRenderedCells();
  2273. if (visibleComponents != null) {
  2274. for (final Iterator<Component> i = visibleComponents.iterator(); i
  2275. .hasNext();) {
  2276. i.next().attach();
  2277. }
  2278. }
  2279. }
  2280. /**
  2281. * Notifies the component that it is detached from the application
  2282. *
  2283. * @see com.vaadin.ui.Component#detach()
  2284. */
  2285. @Override
  2286. public void detach() {
  2287. super.detach();
  2288. if (visibleComponents != null) {
  2289. for (final Iterator<Component> i = visibleComponents.iterator(); i
  2290. .hasNext();) {
  2291. i.next().detach();
  2292. }
  2293. }
  2294. }
  2295. /**
  2296. * Removes all Items from the Container.
  2297. *
  2298. * @see com.vaadin.data.Container#removeAllItems()
  2299. */
  2300. @Override
  2301. public boolean removeAllItems() {
  2302. currentPageFirstItemId = null;
  2303. currentPageFirstItemIndex = 0;
  2304. return super.removeAllItems();
  2305. }
  2306. /**
  2307. * Removes the Item identified by <code>ItemId</code> from the Container.
  2308. *
  2309. * @see com.vaadin.data.Container#removeItem(Object)
  2310. */
  2311. @Override
  2312. public boolean removeItem(Object itemId) {
  2313. final Object nextItemId = ((Container.Ordered) items)
  2314. .nextItemId(itemId);
  2315. final boolean ret = super.removeItem(itemId);
  2316. if (ret && (itemId != null) && (itemId.equals(currentPageFirstItemId))) {
  2317. currentPageFirstItemId = nextItemId;
  2318. }
  2319. if (!(items instanceof Container.ItemSetChangeNotifier)) {
  2320. resetPageBuffer();
  2321. refreshRenderedCells();
  2322. }
  2323. return ret;
  2324. }
  2325. /**
  2326. * Removes a Property specified by the given Property ID from the Container.
  2327. *
  2328. * @see com.vaadin.data.Container#removeContainerProperty(Object)
  2329. */
  2330. @Override
  2331. public boolean removeContainerProperty(Object propertyId)
  2332. throws UnsupportedOperationException {
  2333. // If a visible property is removed, remove the corresponding column
  2334. visibleColumns.remove(propertyId);
  2335. columnAlignments.remove(propertyId);
  2336. columnIcons.remove(propertyId);
  2337. columnHeaders.remove(propertyId);
  2338. return super.removeContainerProperty(propertyId);
  2339. }
  2340. /**
  2341. * Adds a new property to the table and show it as a visible column.
  2342. *
  2343. * @param propertyId
  2344. * the Id of the proprty.
  2345. * @param type
  2346. * the class of the property.
  2347. * @param defaultValue
  2348. * the default value given for all existing items.
  2349. * @see com.vaadin.data.Container#addContainerProperty(Object, Class,
  2350. * Object)
  2351. */
  2352. @Override
  2353. public boolean addContainerProperty(Object propertyId, Class<?> type,
  2354. Object defaultValue) throws UnsupportedOperationException {
  2355. boolean visibleColAdded = false;
  2356. if (!visibleColumns.contains(propertyId)) {
  2357. visibleColumns.add(propertyId);
  2358. visibleColAdded = true;
  2359. }
  2360. if (!super.addContainerProperty(propertyId, type, defaultValue)) {
  2361. if (visibleColAdded) {
  2362. visibleColumns.remove(propertyId);
  2363. }
  2364. return false;
  2365. }
  2366. if (!(items instanceof Container.PropertySetChangeNotifier)) {
  2367. resetPageBuffer();
  2368. refreshRenderedCells();
  2369. }
  2370. return true;
  2371. }
  2372. /**
  2373. * Adds a new property to the table and show it as a visible column.
  2374. *
  2375. * @param propertyId
  2376. * the Id of the proprty
  2377. * @param type
  2378. * the class of the property
  2379. * @param defaultValue
  2380. * the default value given for all existing items
  2381. * @param columnHeader
  2382. * the Explicit header of the column. If explicit header is not
  2383. * needed, this should be set null.
  2384. * @param columnIcon
  2385. * the Icon of the column. If icon is not needed, this should be
  2386. * set null.
  2387. * @param columnAlignment
  2388. * the Alignment of the column. Null implies align left.
  2389. * @throws UnsupportedOperationException
  2390. * if the operation is not supported.
  2391. * @see com.vaadin.data.Container#addContainerProperty(Object, Class,
  2392. * Object)
  2393. */
  2394. public boolean addContainerProperty(Object propertyId, Class<?> type,
  2395. Object defaultValue, String columnHeader, Resource columnIcon,
  2396. String columnAlignment) throws UnsupportedOperationException {
  2397. if (!this.addContainerProperty(propertyId, type, defaultValue)) {
  2398. return false;
  2399. }
  2400. setColumnAlignment(propertyId, columnAlignment);
  2401. setColumnHeader(propertyId, columnHeader);
  2402. setColumnIcon(propertyId, columnIcon);
  2403. return true;
  2404. }
  2405. /**
  2406. * Adds a generated column to the Table.
  2407. * <p>
  2408. * A generated column is a column that exists only in the Table, not as a
  2409. * property in the underlying Container. It shows up just as a regular
  2410. * column.
  2411. * </p>
  2412. * <p>
  2413. * A generated column will override a property with the same id, so that the
  2414. * generated column is shown instead of the column representing the
  2415. * property. Note that getContainerProperty() will still get the real
  2416. * property.
  2417. * </p>
  2418. * <p>
  2419. * Also note that getVisibleColumns() will return the generated columns,
  2420. * while getContainerPropertyIds() will not.
  2421. * </p>
  2422. *
  2423. * @param id
  2424. * the id of the column to be added
  2425. * @param generatedColumn
  2426. * the {@link ColumnGenerator} to use for this column
  2427. */
  2428. public void addGeneratedColumn(Object id, ColumnGenerator generatedColumn) {
  2429. if (generatedColumn == null) {
  2430. throw new IllegalArgumentException(
  2431. "Can not add null as a GeneratedColumn");
  2432. }
  2433. if (columnGenerators.containsKey(id)) {
  2434. throw new IllegalArgumentException(
  2435. "Can not add the same GeneratedColumn twice, id:" + id);
  2436. } else {
  2437. columnGenerators.put(id, generatedColumn);
  2438. /*
  2439. * add to visible column list unless already there (overriding
  2440. * column from DS)
  2441. */
  2442. if (!visibleColumns.contains(id)) {
  2443. visibleColumns.add(id);
  2444. }
  2445. resetPageBuffer();
  2446. refreshRenderedCells();
  2447. }
  2448. }
  2449. /**
  2450. * Removes a generated column previously added with addGeneratedColumn.
  2451. *
  2452. * @param columnId
  2453. * id of the generated column to remove
  2454. * @return true if the column could be removed (existed in the Table)
  2455. */
  2456. public boolean removeGeneratedColumn(Object columnId) {
  2457. if (columnGenerators.containsKey(columnId)) {
  2458. columnGenerators.remove(columnId);
  2459. // remove column from visibleColumns list unless it exists in
  2460. // container (generator previously overrode this column)
  2461. if (!items.getContainerPropertyIds().contains(columnId)) {
  2462. visibleColumns.remove(columnId);
  2463. }
  2464. resetPageBuffer();
  2465. refreshRenderedCells();
  2466. return true;
  2467. } else {
  2468. return false;
  2469. }
  2470. }
  2471. /**
  2472. * Returns the list of items on the current page
  2473. *
  2474. * @see com.vaadin.ui.Select#getVisibleItemIds()
  2475. */
  2476. @Override
  2477. public Collection getVisibleItemIds() {
  2478. final LinkedList<Object> visible = new LinkedList<Object>();
  2479. final Object[][] cells = getVisibleCells();
  2480. for (int i = 0; i < cells[CELL_ITEMID].length; i++) {
  2481. visible.add(cells[CELL_ITEMID][i]);
  2482. }
  2483. return visible;
  2484. }
  2485. /**
  2486. * Container datasource item set change. Table must flush its buffers on
  2487. * change.
  2488. *
  2489. * @see com.vaadin.data.Container.ItemSetChangeListener#containerItemSetChange(com.vaadin.data.Container.ItemSetChangeEvent)
  2490. */
  2491. @Override
  2492. public void containerItemSetChange(Container.ItemSetChangeEvent event) {
  2493. super.containerItemSetChange(event);
  2494. if (event instanceof IndexedContainer.ItemSetChangeEvent) {
  2495. IndexedContainer.ItemSetChangeEvent evt = (IndexedContainer.ItemSetChangeEvent) event;
  2496. // if the event is not a global one and the added item is outside
  2497. // the visible/buffered area, no need to do anything
  2498. if (evt.getAddedItemIndex() != -1
  2499. && (firstToBeRenderedInClient >= 0)
  2500. && (lastToBeRenderedInClient >= 0)
  2501. && (firstToBeRenderedInClient > evt.getAddedItemIndex() || lastToBeRenderedInClient < evt
  2502. .getAddedItemIndex())) {
  2503. return;
  2504. }
  2505. }
  2506. // ensure that page still has first item in page, ignore buffer refresh
  2507. // (forced in this method)
  2508. setCurrentPageFirstItemIndex(getCurrentPageFirstItemIndex(), false);
  2509. resetPageBuffer();
  2510. refreshRenderedCells();
  2511. }
  2512. /**
  2513. * Container datasource property set change. Table must flush its buffers on
  2514. * change.
  2515. *
  2516. * @see com.vaadin.data.Container.PropertySetChangeListener#containerPropertySetChange(com.vaadin.data.Container.PropertySetChangeEvent)
  2517. */
  2518. @Override
  2519. public void containerPropertySetChange(
  2520. Container.PropertySetChangeEvent event) {
  2521. super.containerPropertySetChange(event);
  2522. resetPageBuffer();
  2523. refreshRenderedCells();
  2524. }
  2525. /**
  2526. * Adding new items is not supported.
  2527. *
  2528. * @throws UnsupportedOperationException
  2529. * if set to true.
  2530. * @see com.vaadin.ui.Select#setNewItemsAllowed(boolean)
  2531. */
  2532. @Override
  2533. public void setNewItemsAllowed(boolean allowNewOptions)
  2534. throws UnsupportedOperationException {
  2535. if (allowNewOptions) {
  2536. throw new UnsupportedOperationException();
  2537. }
  2538. }
  2539. /**
  2540. * Focusing to this component is not supported.
  2541. *
  2542. * @throws UnsupportedOperationException
  2543. * if invoked.
  2544. * @see com.vaadin.ui.AbstractField#focus()
  2545. */
  2546. @Override
  2547. public void focus() throws UnsupportedOperationException {
  2548. throw new UnsupportedOperationException();
  2549. }
  2550. /**
  2551. * Gets the ID of the Item following the Item that corresponds to itemId.
  2552. *
  2553. * @see com.vaadin.data.Container.Ordered#nextItemId(java.lang.Object)
  2554. */
  2555. public Object nextItemId(Object itemId) {
  2556. return ((Container.Ordered) items).nextItemId(itemId);
  2557. }
  2558. /**
  2559. * Gets the ID of the Item preceding the Item that corresponds to the
  2560. * itemId.
  2561. *
  2562. * @see com.vaadin.data.Container.Ordered#prevItemId(java.lang.Object)
  2563. */
  2564. public Object prevItemId(Object itemId) {
  2565. return ((Container.Ordered) items).prevItemId(itemId);
  2566. }
  2567. /**
  2568. * Gets the ID of the first Item in the Container.
  2569. *
  2570. * @see com.vaadin.data.Container.Ordered#firstItemId()
  2571. */
  2572. public Object firstItemId() {
  2573. return ((Container.Ordered) items).firstItemId();
  2574. }
  2575. /**
  2576. * Gets the ID of the last Item in the Container.
  2577. *
  2578. * @see com.vaadin.data.Container.Ordered#lastItemId()
  2579. */
  2580. public Object lastItemId() {
  2581. return ((Container.Ordered) items).lastItemId();
  2582. }
  2583. /**
  2584. * Tests if the Item corresponding to the given Item ID is the first Item in
  2585. * the Container.
  2586. *
  2587. * @see com.vaadin.data.Container.Ordered#isFirstId(java.lang.Object)
  2588. */
  2589. public boolean isFirstId(Object itemId) {
  2590. return ((Container.Ordered) items).isFirstId(itemId);
  2591. }
  2592. /**
  2593. * Tests if the Item corresponding to the given Item ID is the last Item in
  2594. * the Container.
  2595. *
  2596. * @see com.vaadin.data.Container.Ordered#isLastId(java.lang.Object)
  2597. */
  2598. public boolean isLastId(Object itemId) {
  2599. return ((Container.Ordered) items).isLastId(itemId);
  2600. }
  2601. /**
  2602. * Adds new item after the given item.
  2603. *
  2604. * @see com.vaadin.data.Container.Ordered#addItemAfter(java.lang.Object)
  2605. */
  2606. public Object addItemAfter(Object previousItemId)
  2607. throws UnsupportedOperationException {
  2608. Object itemId = ((Container.Ordered) items)
  2609. .addItemAfter(previousItemId);
  2610. if (!(items instanceof Container.ItemSetChangeNotifier)) {
  2611. resetPageBuffer();
  2612. refreshRenderedCells();
  2613. }
  2614. return itemId;
  2615. }
  2616. /**
  2617. * Adds new item after the given item.
  2618. *
  2619. * @see com.vaadin.data.Container.Ordered#addItemAfter(java.lang.Object,
  2620. * java.lang.Object)
  2621. */
  2622. public Item addItemAfter(Object previousItemId, Object newItemId)
  2623. throws UnsupportedOperationException {
  2624. Item item = ((Container.Ordered) items).addItemAfter(previousItemId,
  2625. newItemId);
  2626. if (!(items instanceof Container.ItemSetChangeNotifier)) {
  2627. resetPageBuffer();
  2628. refreshRenderedCells();
  2629. }
  2630. return item;
  2631. }
  2632. /**
  2633. * Sets the TableFieldFactory that is used to create editor for table cells.
  2634. *
  2635. * The TableFieldFactory is only used if the Table is editable. By default
  2636. * the DefaultFieldFactory is used.
  2637. *
  2638. * @param fieldFactory
  2639. * the field factory to set.
  2640. * @see #isEditable
  2641. * @see DefaultFieldFactory
  2642. */
  2643. public void setTableFieldFactory(TableFieldFactory fieldFactory) {
  2644. this.fieldFactory = fieldFactory;
  2645. }
  2646. /**
  2647. * Gets the TableFieldFactory that is used to create editor for table cells.
  2648. *
  2649. * The FieldFactory is only used if the Table is editable.
  2650. *
  2651. * @return TableFieldFactory used to create the Field instances.
  2652. * @see #isEditable
  2653. */
  2654. public TableFieldFactory getTableFieldFactory() {
  2655. return fieldFactory;
  2656. }
  2657. /**
  2658. * Gets the FieldFactory that is used to create editor for table cells.
  2659. *
  2660. * The FieldFactory is only used if the Table is editable.
  2661. *
  2662. * @return FieldFactory used to create the Field instances.
  2663. * @see #isEditable
  2664. * @deprecated use {@link #getTableFieldFactory()} instead
  2665. */
  2666. @Deprecated
  2667. public FieldFactory getFieldFactory() {
  2668. if (fieldFactory instanceof FieldFactory) {
  2669. return (FieldFactory) fieldFactory;
  2670. }
  2671. return null;
  2672. }
  2673. /**
  2674. * Sets the FieldFactory that is used to create editor for table cells.
  2675. *
  2676. * The FieldFactory is only used if the Table is editable. By default the
  2677. * BaseFieldFactory is used.
  2678. *
  2679. * @param fieldFactory
  2680. * the field factory to set.
  2681. * @see #isEditable
  2682. * @see BaseFieldFactory
  2683. * @deprecated use {@link #setTableFieldFactory(TableFieldFactory)} instead
  2684. */
  2685. @Deprecated
  2686. public void setFieldFactory(FieldFactory fieldFactory) {
  2687. this.fieldFactory = fieldFactory;
  2688. // Assure visual refresh
  2689. resetPageBuffer();
  2690. refreshRenderedCells();
  2691. }
  2692. /**
  2693. * Is table editable.
  2694. *
  2695. * If table is editable a editor of type Field is created for each table
  2696. * cell. The assigned FieldFactory is used to create the instances.
  2697. *
  2698. * To provide custom editors for table cells create a class implementins the
  2699. * FieldFactory interface, and assign it to table, and set the editable
  2700. * property to true.
  2701. *
  2702. * @return true if table is editable, false oterwise.
  2703. * @see Field
  2704. * @see FieldFactory
  2705. *
  2706. */
  2707. public boolean isEditable() {
  2708. return editable;
  2709. }
  2710. /**
  2711. * Sets the editable property.
  2712. *
  2713. * If table is editable a editor of type Field is created for each table
  2714. * cell. The assigned FieldFactory is used to create the instances.
  2715. *
  2716. * To provide custom editors for table cells create a class implementins the
  2717. * FieldFactory interface, and assign it to table, and set the editable
  2718. * property to true.
  2719. *
  2720. * @param editable
  2721. * true if table should be editable by user.
  2722. * @see Field
  2723. * @see FieldFactory
  2724. *
  2725. */
  2726. public void setEditable(boolean editable) {
  2727. this.editable = editable;
  2728. // Assure visual refresh
  2729. resetPageBuffer();
  2730. refreshRenderedCells();
  2731. }
  2732. /**
  2733. * Sorts the table.
  2734. *
  2735. * @throws UnsupportedOperationException
  2736. * if the container data source does not implement
  2737. * Container.Sortable
  2738. * @see com.vaadin.data.Container.Sortable#sort(java.lang.Object[],
  2739. * boolean[])
  2740. *
  2741. */
  2742. public void sort(Object[] propertyId, boolean[] ascending)
  2743. throws UnsupportedOperationException {
  2744. final Container c = getContainerDataSource();
  2745. if (c instanceof Container.Sortable) {
  2746. final int pageIndex = getCurrentPageFirstItemIndex();
  2747. ((Container.Sortable) c).sort(propertyId, ascending);
  2748. setCurrentPageFirstItemIndex(pageIndex);
  2749. resetPageBuffer();
  2750. refreshRenderedCells();
  2751. } else if (c != null) {
  2752. throw new UnsupportedOperationException(
  2753. "Underlying Data does not allow sorting");
  2754. }
  2755. }
  2756. /**
  2757. * Sorts the table by currently selected sorting column.
  2758. *
  2759. * @throws UnsupportedOperationException
  2760. * if the container data source does not implement
  2761. * Container.Sortable
  2762. */
  2763. public void sort() {
  2764. if (getSortContainerPropertyId() == null) {
  2765. return;
  2766. }
  2767. sort(new Object[] { sortContainerPropertyId },
  2768. new boolean[] { sortAscending });
  2769. }
  2770. /**
  2771. * Gets the container property IDs, which can be used to sort the item.
  2772. *
  2773. * @see com.vaadin.data.Container.Sortable#getSortableContainerPropertyIds()
  2774. */
  2775. public Collection getSortableContainerPropertyIds() {
  2776. final Container c = getContainerDataSource();
  2777. if (c instanceof Container.Sortable && !isSortDisabled()) {
  2778. return ((Container.Sortable) c).getSortableContainerPropertyIds();
  2779. } else {
  2780. return new LinkedList();
  2781. }
  2782. }
  2783. /**
  2784. * Gets the currently sorted column property ID.
  2785. *
  2786. * @return the Container property id of the currently sorted column.
  2787. */
  2788. public Object getSortContainerPropertyId() {
  2789. return sortContainerPropertyId;
  2790. }
  2791. /**
  2792. * Sets the currently sorted column property id.
  2793. *
  2794. * @param propertyId
  2795. * the Container property id of the currently sorted column.
  2796. */
  2797. public void setSortContainerPropertyId(Object propertyId) {
  2798. setSortContainerPropertyId(propertyId, true);
  2799. }
  2800. /**
  2801. * Internal method to set currently sorted column property id. With doSort
  2802. * flag actual sorting may be bypassed.
  2803. *
  2804. * @param propertyId
  2805. * @param doSort
  2806. */
  2807. private void setSortContainerPropertyId(Object propertyId, boolean doSort) {
  2808. if ((sortContainerPropertyId != null && !sortContainerPropertyId
  2809. .equals(propertyId))
  2810. || (sortContainerPropertyId == null && propertyId != null)) {
  2811. sortContainerPropertyId = propertyId;
  2812. if (doSort) {
  2813. sort();
  2814. // Assures the visual refresh
  2815. refreshRenderedCells();
  2816. }
  2817. }
  2818. }
  2819. /**
  2820. * Is the table currently sorted in ascending order.
  2821. *
  2822. * @return <code>true</code> if ascending, <code>false</code> if descending.
  2823. */
  2824. public boolean isSortAscending() {
  2825. return sortAscending;
  2826. }
  2827. /**
  2828. * Sets the table in ascending order.
  2829. *
  2830. * @param ascending
  2831. * <code>true</code> if ascending, <code>false</code> if
  2832. * descending.
  2833. */
  2834. public void setSortAscending(boolean ascending) {
  2835. setSortAscending(ascending, true);
  2836. }
  2837. /**
  2838. * Internal method to set sort ascending. With doSort flag actual sort can
  2839. * be bypassed.
  2840. *
  2841. * @param ascending
  2842. * @param doSort
  2843. */
  2844. private void setSortAscending(boolean ascending, boolean doSort) {
  2845. if (sortAscending != ascending) {
  2846. sortAscending = ascending;
  2847. if (doSort) {
  2848. sort();
  2849. }
  2850. }
  2851. // Assures the visual refresh
  2852. refreshRenderedCells();
  2853. }
  2854. /**
  2855. * Is sorting disabled altogether.
  2856. *
  2857. * True iff no sortable columns are given even in the case where data source
  2858. * would support this.
  2859. *
  2860. * @return True iff sorting is disabled.
  2861. */
  2862. public boolean isSortDisabled() {
  2863. return sortDisabled;
  2864. }
  2865. /**
  2866. * Disables the sorting altogether.
  2867. *
  2868. * To disable sorting altogether, set to true. In this case no sortable
  2869. * columns are given even in the case where datasource would support this.
  2870. *
  2871. * @param sortDisabled
  2872. * True iff sorting is disabled.
  2873. */
  2874. public void setSortDisabled(boolean sortDisabled) {
  2875. if (this.sortDisabled != sortDisabled) {
  2876. this.sortDisabled = sortDisabled;
  2877. refreshRenderedCells();
  2878. }
  2879. }
  2880. /**
  2881. * Table does not support lazy options loading mode. Setting this true will
  2882. * throw UnsupportedOperationException.
  2883. *
  2884. * @see com.vaadin.ui.Select#setLazyLoading(boolean)
  2885. */
  2886. public void setLazyLoading(boolean useLazyLoading) {
  2887. if (useLazyLoading) {
  2888. throw new UnsupportedOperationException(
  2889. "Lazy options loading is not supported by Table.");
  2890. }
  2891. }
  2892. /**
  2893. * Used to create "generated columns"; columns that exist only in the Table,
  2894. * not in the underlying Container. Implement this interface and pass it to
  2895. * Table.addGeneratedColumn along with an id for the column to be generated.
  2896. *
  2897. */
  2898. public interface ColumnGenerator extends Serializable {
  2899. /**
  2900. * Called by Table when a cell in a generated column needs to be
  2901. * generated.
  2902. *
  2903. * @param source
  2904. * the source Table
  2905. * @param itemId
  2906. * the itemId (aka rowId) for the of the cell to be generated
  2907. * @param columnId
  2908. * the id for the generated column (as specified in
  2909. * addGeneratedColumn)
  2910. * @return
  2911. */
  2912. public abstract Component generateCell(Table source, Object itemId,
  2913. Object columnId);
  2914. }
  2915. /**
  2916. * Set cell style generator for Table.
  2917. *
  2918. * @param cellStyleGenerator
  2919. * New cell style generator or null to remove generator.
  2920. */
  2921. public void setCellStyleGenerator(CellStyleGenerator cellStyleGenerator) {
  2922. this.cellStyleGenerator = cellStyleGenerator;
  2923. requestRepaint();
  2924. }
  2925. /**
  2926. * Get the current cell style generator.
  2927. *
  2928. */
  2929. public CellStyleGenerator getCellStyleGenerator() {
  2930. return cellStyleGenerator;
  2931. }
  2932. /**
  2933. * Allow to define specific style on cells (and rows) contents. Implements
  2934. * this interface and pass it to Table.setCellStyleGenerator. Row styles are
  2935. * generated when porpertyId is null. The CSS class name that will be added
  2936. * to the cell content is <tt>v-table-cell-content-[style name]</tt>, and
  2937. * the row style will be <tt>v-table-row-[style name]</tt>.
  2938. */
  2939. public interface CellStyleGenerator extends Serializable {
  2940. /**
  2941. * Called by Table when a cell (and row) is painted.
  2942. *
  2943. * @param itemId
  2944. * The itemId of the painted cell
  2945. * @param propertyId
  2946. * The propertyId of the cell, null when getting row style
  2947. * @return The style name to add to this cell or row. (the CSS class
  2948. * name will be v-table-cell-content-[style name], or
  2949. * v-table-row-[style name] for rows)
  2950. */
  2951. public abstract String getStyle(Object itemId, Object propertyId);
  2952. }
  2953. public void addListener(ItemClickListener listener) {
  2954. addListener(VScrollTable.ITEM_CLICK_EVENT_ID, ItemClickEvent.class,
  2955. listener, ItemClickEvent.ITEM_CLICK_METHOD);
  2956. }
  2957. public void removeListener(ItemClickListener listener) {
  2958. removeListener(VScrollTable.ITEM_CLICK_EVENT_ID, ItemClickEvent.class,
  2959. listener);
  2960. }
  2961. // Identical to AbstractCompoenentContainer.setEnabled();
  2962. @Override
  2963. public void setEnabled(boolean enabled) {
  2964. super.setEnabled(enabled);
  2965. if (getParent() != null && !getParent().isEnabled()) {
  2966. // some ancestor still disabled, don't update children
  2967. return;
  2968. } else {
  2969. requestRepaintAll();
  2970. }
  2971. }
  2972. // Virtually identical to AbstractCompoenentContainer.setEnabled();
  2973. public void requestRepaintAll() {
  2974. requestRepaint();
  2975. if (visibleComponents != null) {
  2976. for (Iterator<Component> childIterator = visibleComponents
  2977. .iterator(); childIterator.hasNext();) {
  2978. Component c = childIterator.next();
  2979. if (c instanceof Form) {
  2980. // Form has children in layout, but is not
  2981. // ComponentContainer
  2982. c.requestRepaint();
  2983. ((Form) c).getLayout().requestRepaintAll();
  2984. } else if (c instanceof Table) {
  2985. ((Table) c).requestRepaintAll();
  2986. } else if (c instanceof ComponentContainer) {
  2987. ((ComponentContainer) c).requestRepaintAll();
  2988. } else {
  2989. c.requestRepaint();
  2990. }
  2991. }
  2992. }
  2993. }
  2994. /**
  2995. * Sets the drag start mode of the Table. Drag start mode controls how Table
  2996. * behaves as a drag source.
  2997. *
  2998. * @param newDragMode
  2999. */
  3000. public void setDragMode(TableDragMode newDragMode) {
  3001. dragMode = newDragMode;
  3002. requestRepaint();
  3003. }
  3004. /**
  3005. * @return the current start mode of the Table. Drag start mode controls how
  3006. * Table behaves as a drag source.
  3007. */
  3008. public TableDragMode getDragMode() {
  3009. return dragMode;
  3010. }
  3011. /**
  3012. * Concrete implementation of {@link DataBoundTransferable} for data
  3013. * transferred from a table.
  3014. *
  3015. * @see {@link DataBoundTransferable}.
  3016. *
  3017. * @since 6.3
  3018. */
  3019. public class TableTransferable extends DataBoundTransferable {
  3020. protected TableTransferable(Map<String, Object> rawVariables) {
  3021. super(Table.this, rawVariables);
  3022. Object object = rawVariables.get("itemId");
  3023. if (object != null) {
  3024. setData("itemId", itemIdMapper.get((String) object));
  3025. }
  3026. object = rawVariables.get("propertyId");
  3027. if (object != null) {
  3028. setData("propertyId", columnIdMap.get((String) object));
  3029. }
  3030. }
  3031. @Override
  3032. public Object getItemId() {
  3033. return getData("itemId");
  3034. }
  3035. @Override
  3036. public Object getPropertyId() {
  3037. return getData("propertyId");
  3038. }
  3039. @Override
  3040. public Table getSourceComponent() {
  3041. return (Table) super.getSourceComponent();
  3042. }
  3043. }
  3044. public TableTransferable getTransferable(Map<String, Object> rawVariables) {
  3045. TableTransferable transferable = new TableTransferable(rawVariables);
  3046. return transferable;
  3047. }
  3048. public DropHandler getDropHandler() {
  3049. return dropHandler;
  3050. }
  3051. public void setDropHandler(DropHandler dropHandler) {
  3052. this.dropHandler = dropHandler;
  3053. }
  3054. public AbstractSelectDropTargetDetails translateDropTargetDetails(
  3055. Map<String, Object> clientVariables) {
  3056. return new AbstractSelectDropTargetDetails(clientVariables);
  3057. }
  3058. /**
  3059. * Lazy loading accept criterion for Table. Accepted target rows are loaded
  3060. * from server once per drag and drop operation. Developer must override one
  3061. * method that decides on which rows the currently dragged data can be
  3062. * dropped.
  3063. *
  3064. * <p>
  3065. * Initially pretty much no data is sent to client. On first required
  3066. * criterion check (per drag request) the client side data structure is
  3067. * initialized from server and no subsequent requests requests are needed
  3068. * during that drag and drop operation.
  3069. */
  3070. @ClientCriterion(VLazyInitItemIdentifiers.class)
  3071. public static abstract class TableDropCriterion extends ServerSideCriterion {
  3072. private Table table;
  3073. private Set<Object> allowedItemIds;
  3074. /*
  3075. * (non-Javadoc)
  3076. *
  3077. * @see
  3078. * com.vaadin.event.dd.acceptcriteria.ServerSideCriterion#getIdentifier
  3079. * ()
  3080. */
  3081. @Override
  3082. protected String getIdentifier() {
  3083. return TableDropCriterion.class.getCanonicalName();
  3084. }
  3085. /*
  3086. * (non-Javadoc)
  3087. *
  3088. * @see
  3089. * com.vaadin.event.dd.acceptcriteria.AcceptCriterion#accepts(com.vaadin
  3090. * .event.dd.DragAndDropEvent)
  3091. */
  3092. public boolean accept(DragAndDropEvent dragEvent) {
  3093. AbstractSelectDropTargetDetails dropTargetData = (AbstractSelectDropTargetDetails) dragEvent
  3094. .getDropTargetDetails();
  3095. table = (Table) dragEvent.getDropTargetDetails().getTarget();
  3096. ArrayList<Object> visibleItemIds = new ArrayList<Object>(table
  3097. .getPageLength());
  3098. visibleItemIds.size();
  3099. Object id = table.getCurrentPageFirstItemId();
  3100. for (int i = 0; i < table.getPageLength() && id != null; i++) {
  3101. visibleItemIds.add(id);
  3102. id = table.nextItemId(id);
  3103. }
  3104. allowedItemIds = getAllowedItemIds(dragEvent, table, visibleItemIds);
  3105. return allowedItemIds.contains(dropTargetData.getItemIdOver());
  3106. }
  3107. /*
  3108. * (non-Javadoc)
  3109. *
  3110. * @see
  3111. * com.vaadin.event.dd.acceptcriteria.AcceptCriterion#paintResponse(
  3112. * com.vaadin.terminal.PaintTarget)
  3113. */
  3114. @Override
  3115. public void paintResponse(PaintTarget target) throws PaintException {
  3116. /*
  3117. * send allowed nodes to client so subsequent requests can be
  3118. * avoided
  3119. */
  3120. Object[] array = allowedItemIds.toArray();
  3121. for (int i = 0; i < array.length; i++) {
  3122. String key = table.itemIdMapper.key(array[i]);
  3123. array[i] = key;
  3124. }
  3125. target.addAttribute("allowedIds", array);
  3126. }
  3127. /**
  3128. * @param dragEvent
  3129. * @param table
  3130. * the table for which the allowed item identifiers are
  3131. * defined
  3132. * @param visibleItemIds
  3133. * the list of currently rendered item identifiers, accepted
  3134. * item id's need to be detected only for these visible items
  3135. * @return the set of identifiers for items on which the dragEvent will
  3136. * be accepted
  3137. */
  3138. protected abstract Set<Object> getAllowedItemIds(
  3139. DragAndDropEvent dragEvent, Table table,
  3140. Collection<Object> visibleItemIds);
  3141. }
  3142. }