Revert "Work in progress. Do not rely on this commit"
[pithos-web-client] / src / gr / grnet / pithos / web / client / FileList.java
1 /*
2  * Copyright 2011-2012 GRNET S.A. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or
5  * without modification, are permitted provided that the following
6  * conditions are met:
7  *
8  *   1. Redistributions of source code must retain the above
9  *      copyright notice, this list of conditions and the following
10  *      disclaimer.
11  *
12  *   2. Redistributions in binary form must reproduce the above
13  *      copyright notice, this list of conditions and the following
14  *      disclaimer in the documentation and/or other materials
15  *      provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
18  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
21  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
24  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
25  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
27  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28  * POSSIBILITY OF SUCH DAMAGE.
29  *
30  * The views and conclusions contained in the software and
31  * documentation are those of the authors and should not be
32  * interpreted as representing official policies, either expressed
33  * or implied, of GRNET S.A.
34  */
35
36 package gr.grnet.pithos.web.client;
37
38 import gr.grnet.pithos.web.client.foldertree.File;
39 import gr.grnet.pithos.web.client.foldertree.Folder;
40 import gr.grnet.pithos.web.client.foldertree.FolderTreeView;
41
42 import java.util.ArrayList;
43 import java.util.Collections;
44 import java.util.Comparator;
45 import java.util.Iterator;
46 import java.util.List;
47 import java.util.Set;
48
49 import com.google.gwt.cell.client.Cell.Context;
50 import com.google.gwt.cell.client.ImageResourceCell;
51 import com.google.gwt.cell.client.SafeHtmlCell;
52 import com.google.gwt.cell.client.TextCell;
53 import com.google.gwt.cell.client.ValueUpdater;
54 import com.google.gwt.core.client.GWT;
55 import com.google.gwt.event.dom.client.ContextMenuEvent;
56 import com.google.gwt.event.dom.client.ContextMenuHandler;
57 import com.google.gwt.i18n.client.DateTimeFormat;
58 import com.google.gwt.resources.client.ImageResource;
59 import com.google.gwt.resources.client.ImageResource.ImageOptions;
60 import com.google.gwt.resources.client.ImageResource.RepeatStyle;
61 import com.google.gwt.safehtml.client.SafeHtmlTemplates;
62 import com.google.gwt.safehtml.shared.SafeHtml;
63 import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
64 import com.google.gwt.user.cellview.client.CellTable;
65 import com.google.gwt.user.cellview.client.Column;
66 import com.google.gwt.user.client.Command;
67 import com.google.gwt.user.client.DOM;
68 import com.google.gwt.user.client.Event;
69 import com.google.gwt.user.client.Window;
70 import com.google.gwt.user.client.ui.Composite;
71 import com.google.gwt.user.client.ui.VerticalPanel;
72 import com.google.gwt.view.client.ListDataProvider;
73 import com.google.gwt.view.client.MultiSelectionModel;
74 import com.google.gwt.view.client.ProvidesKey;
75 import com.google.gwt.view.client.SelectionChangeEvent;
76
77 /**
78  * A composite that displays the list of files in a particular folder.
79  */
80 public class FileList extends Composite {
81
82         ListDataProvider<File> provider = new ListDataProvider<File>();
83
84     /**
85        * The styles applied to the table.
86        */
87     interface TableStyle extends CellTable.Style {
88         String cellTableFirstColumnShared();
89     }
90
91         interface TableResources extends CellTable.Resources {
92             @Override
93                 @Source({CellTable.Style.DEFAULT_CSS, "PithosCellTable.css"})
94             TableStyle cellTableStyle();
95             
96             @Source("share.png")
97             @ImageOptions(repeatStyle = RepeatStyle.None)
98             ImageResource cellTableSharedIcon();
99         }
100         
101         static interface Templates extends SafeHtmlTemplates {
102             Templates INSTANCE = GWT.create(Templates.class);
103
104             @Template("<div id='dragHelper' style='border:1px solid black; background-color:#ffffff; color:black; width:150px;z-index:100'></div>")
105             SafeHtml outerHelper();
106
107         @Template("<span id='{0}'>{0}</span>")
108         public SafeHtml filenameSpan(String filename);
109
110         @Template("<a href='{0}' title='{1}' rel='lytebox[mnf]' onclick='myLytebox.start(this, false, false); return false;'>(view)</a>")
111         public SafeHtml viewLink(String link, String title);
112
113         @Template("<table><tr><td rowspan='3'>{0}</td><td style='font-size:95%;' id='{1}'>{1}</td></tr><tr><td>{2}</td></tr></table>")
114         public SafeHtml rendelContactCell(String imageHtml, String name, String fileSize);
115
116         @Template("<span id='{0}' class='{1}'>{2}</span>")
117         public SafeHtml spanWithIdAndClass(String id, String cssClass, String content);
118         }
119
120         protected final DateTimeFormat formatter = DateTimeFormat.getFormat("d/M/yyyy h:mm a");
121
122         /**
123          * Specifies that the images available for this composite will be the ones
124          * available in FileContextMenu.
125          */
126         public interface Images extends FolderTreeView.Images {
127
128                 @Source("gr/grnet/pithos/resources/blank.gif")
129                 ImageResource blank();
130
131                 @Source("gr/grnet/pithos/resources/asc.png")
132                 ImageResource asc();
133
134                 @Source("gr/grnet/pithos/resources/desc.png")
135                 ImageResource desc();
136
137                 @Source("gr/grnet/pithos/resources/mimetypes/document_shared.png")
138                 ImageResource documentShared();
139
140                 @Source("gr/grnet/pithos/resources/mimetypes/kcmfontinst.png")
141                 ImageResource wordprocessor();
142
143                 @Source("gr/grnet/pithos/resources/mimetypes/log.png")
144                 ImageResource spreadsheet();
145
146                 @Source("gr/grnet/pithos/resources/mimetypes/kpresenter_kpr.png")
147                 ImageResource presentation();
148
149                 @Source("gr/grnet/pithos/resources/mimetypes/acroread.png")
150                 ImageResource pdf();
151
152                 @Source("gr/grnet/pithos/resources/mimetypes/image.png")
153                 ImageResource image();
154
155                 @Source("gr/grnet/pithos/resources/mimetypes/video2.png")
156                 ImageResource video();
157
158                 @Source("gr/grnet/pithos/resources/mimetypes/knotify.png")
159                 ImageResource audio();
160
161                 @Source("gr/grnet/pithos/resources/mimetypes/html.png")
162                 ImageResource html();
163
164                 @Source("gr/grnet/pithos/resources/mimetypes/txt.png")
165                 ImageResource txt();
166
167                 @Source("gr/grnet/pithos/resources/mimetypes/ark2.png")
168                 ImageResource zip();
169
170                 @Source("gr/grnet/pithos/resources/mimetypes/kcmfontinst_shared.png")
171                 ImageResource wordprocessorShared();
172
173                 @Source("gr/grnet/pithos/resources/mimetypes/log_shared.png")
174                 ImageResource spreadsheetShared();
175
176                 @Source("gr/grnet/pithos/resources/mimetypes/kpresenter_kpr_shared.png")
177                 ImageResource presentationShared();
178
179                 @Source("gr/grnet/pithos/resources/mimetypes/acroread_shared.png")
180                 ImageResource pdfShared();
181
182                 @Source("gr/grnet/pithos/resources/mimetypes/image_shared.png")
183                 ImageResource imageShared();
184
185                 @Source("gr/grnet/pithos/resources/mimetypes/video2_shared.png")
186                 ImageResource videoShared();
187
188                 @Source("gr/grnet/pithos/resources/mimetypes/knotify_shared.png")
189                 ImageResource audioShared();
190
191                 @Source("gr/grnet/pithos/resources/mimetypes/html_shared.png")
192                 ImageResource htmlShared();
193
194                 @Source("gr/grnet/pithos/resources/mimetypes/txt_shared.png")
195                 ImageResource txtShared();
196
197                 @Source("gr/grnet/pithos/resources/mimetypes/ark2_shared.png")
198                 ImageResource zipShared();
199
200         }
201         
202         /**
203          * The number of files in this folder.
204          */
205         int folderFileCount;
206
207         /**
208          * Total folder size
209          */
210         long folderTotalSize;
211
212         /**
213          * A cache of the files in the list.
214          */
215         private List<File> files;
216
217         /**
218          * The widget's image bundle.
219          */
220         protected final Images images;
221         
222         protected CellTable<File> celltable;
223
224         private final MultiSelectionModel<File> selectionModel;
225
226         Column<File, String> pathColumn;
227
228         protected final List<SortableHeader> allHeaders = new ArrayList<SortableHeader>();
229
230         SortableHeader nameHeader;
231
232         SortableHeader pathHeader;
233
234     protected Pithos app;
235
236     /**
237          * Construct the file list widget. This entails setting up the widget
238          * layout, fetching the number of files in the current folder from the
239          * server and filling the local file cache of displayed files with data from
240          * the server, as well.
241          *
242          * @param _images
243          */
244         public FileList(final Pithos _app, Images _images) {
245         app = _app;
246                 images = _images;
247
248         final CellTable.Resources resources = GWT.create(TableResources.class);
249
250         ProvidesKey<File> keyProvider = new ProvidesKey<File>(){
251
252                         @Override
253                         public Object getKey(File item) {
254                                 return item.getUri();
255                         }
256                 };
257
258                 celltable = new CellTable<File>(10, resources, keyProvider);
259         celltable.setWidth("100%");
260         celltable.setStyleName("pithos-List");
261
262                 Column<File, ImageResource> status = new Column<File, ImageResource>(new ImageResourceCell() {
263                     @Override
264                 public boolean handlesSelection() {
265                     return false;
266                 }
267                 })
268         {
269                  @Override
270                  public ImageResource getValue(File entity) {
271                      return getFileIcon(entity);
272                  }
273
274                         @Override
275                         public String getCellStyleNames(Context context, File object) {
276                                 if (!object.getPermissions().isEmpty() && !object.isPublished())
277                                         return ((TableStyle) resources.cellTableStyle()).cellTableFirstColumnShared();
278                                 return super.getCellStyleNames(context, object);
279                         }
280             };
281             celltable.addColumn(status,"");
282
283         final Column<File,SafeHtml> nameColumn = new Column<File,SafeHtml>(new SafeHtmlCell()) {
284
285                         @Override
286                         public SafeHtml getValue(File object) {
287                                 SafeHtmlBuilder sb = new SafeHtmlBuilder();
288                                 sb.append(Templates.INSTANCE.filenameSpan(object.getName()));
289                                 if (object.getContentType() != null && (object.getContentType().endsWith("png") || object.getContentType().endsWith("gif") || object.getContentType().endsWith("jpeg"))) {
290                                 sb.appendHtmlConstant("&nbsp;")
291                       .append(Templates.INSTANCE.viewLink(app.getApiPath() + object.getOwnerID() + object.getUri(), object.getName()));
292                                 }
293                                 
294                                 return sb.toSafeHtml();
295                         }
296                         
297                 };
298         celltable.addColumn(nameColumn, nameHeader = new SortableHeader("Name", "name"));
299                 allHeaders.add(nameHeader);
300                 nameHeader.setUpdater(new FileValueUpdater(nameHeader));
301                 nameHeader.setSorted(true);
302                 nameHeader.setReverseSort(true);
303
304                 celltable.redrawHeaders();
305                 
306                 pathColumn = new Column<File, String>(new TextCell()) {
307
308                         @Override
309                         public String getValue(File f) {
310                                 String path;
311                                 if (!app.getSelectedTree().equals(app.mysharedTreeView)) {
312                                         path = f.getParent().getPrefix();
313                                         if (path.length() == 0)
314                                                 path = "/";
315                                 }
316                                 else {
317                                         path = f.getPath();
318                                         if (path.lastIndexOf("/") != -1)
319                                                 path = path.substring(0, path.lastIndexOf("/"));
320                                         else
321                                                 path = "/";
322                                 }
323                                 return path;
324                         }
325                 };
326                 pathHeader = new SortableHeader("Path", "path");
327                 celltable.addColumn(pathColumn, pathHeader);
328                 allHeaders.add(pathHeader);
329                 pathHeader.setUpdater(new FileValueUpdater(pathHeader));
330                 
331         Column<File,String> aColumn = new Column<File,String>(new TextCell()) {
332                         @Override
333                         public String getValue(File object) {
334                                 return object.getSizeAsString();
335                         }
336                 };
337         SortableHeader aheader = new SortableHeader("Size", "size");
338         celltable.addColumn(aColumn, aheader);
339                 allHeaders.add(aheader);
340                 aheader.setUpdater(new FileValueUpdater(aheader));
341
342         aColumn = new Column<File,String>(new TextCell()) {
343                         @Override
344                         public String getValue(File object) {
345                                 return object.getLastModified() != null ? formatter.format(object.getLastModified()) : "";
346                         }
347                 };
348         aheader = new SortableHeader("Last Modified", "date");
349                 celltable.addColumn(aColumn, aheader);
350                 allHeaders.add(aheader);
351                 aheader.setUpdater(new FileValueUpdater(aheader));
352                
353                 provider.addDataDisplay(celltable);
354
355                 VerticalPanel vp = new VerticalPanel();
356                 vp.setWidth("100%");
357                 vp.addStyleName("pithos-FileListContainer");
358
359         vp.add(celltable);
360
361                 vp.setCellWidth(celltable, "100%");
362         vp.addHandler(new ContextMenuHandler() {
363             @Override
364             public void onContextMenu(final ContextMenuEvent event) {
365                 final TreeView tree = app.getSelectedTree();
366                 if (tree != null) {
367                         final int x = event.getNativeEvent().getClientX();
368                         final int y = event.getNativeEvent().getClientY();
369                         final Folder selectedFolder = app.getSelection();
370                         app.scheduleFolderHeadCommand(selectedFolder, new Command() {
371                                                 
372                                                 @Override
373                                                 public void execute() {
374                                                         final List<File> selectedFiles = getSelectedFiles();
375                                                         Iterator<File> iter = selectedFiles.iterator();
376                                                         iterateFilesHeadCommand(iter, new Command() {
377                                                                 
378                                                                 @Override
379                                                                 public void execute() {
380                                                         FileContextMenu contextMenu = new FileContextMenu(app, images, tree, selectedFolder, selectedFiles);
381                                                         contextMenu.setPopupPosition(x, y);
382                                                         contextMenu.show();
383                                                                 }
384                                                         });
385                                                 }
386                                         });
387                 }
388             }
389         }, ContextMenuEvent.getType());
390                 initWidget(vp);
391
392                 selectionModel = new MultiSelectionModel<File>(keyProvider);
393                 selectionModel.addSelectionChangeHandler(new SelectionChangeEvent.Handler() {
394                         
395                         @Override
396                         public void onSelectionChange(SelectionChangeEvent event) {
397                                 app.showRelevantToolbarButtons();
398                         }
399                 });
400                 
401                 celltable.setSelectionModel(selectionModel, PithosSelectionEventManager.<File> createDefaultManager());
402                 
403                 sinkEvents(Event.ONCONTEXTMENU);
404                 sinkEvents(Event.ONDBLCLICK);
405                 Pithos.preventIESelection();
406         }
407
408         public List<File> getSelectedFiles() {
409         return new ArrayList<File>(selectionModel.getSelectedSet());
410         }
411         
412         @Override
413         public void onBrowserEvent(Event event) {
414
415                 if (DOM.eventGetType(event) == Event.ONDBLCLICK)
416                         if (getSelectedFiles().size() == 1) {
417                                 File file = getSelectedFiles().get(0);
418                                 Window.open(app.getApiPath() + file.getOwnerID() + file.getUri(), "_blank", "");
419                                 event.preventDefault();
420                                 return;
421                         }
422                 super.onBrowserEvent(event);
423         }
424
425         /**
426          * Update the display of the file list.
427          */
428         void update() {
429                 showCellTable();
430         }
431
432         /**
433          * Return the proper icon based on the MIME type of the file.
434          *
435          * @param file
436          * @return the icon
437          */
438         protected ImageResource getFileIcon(File file) {
439                 String mimetype = file.getContentType();
440                 boolean published = file.isPublished();
441                 if (mimetype == null)
442                         return published ? images.documentShared() : images.document();
443                 mimetype = mimetype.toLowerCase();
444                 if (mimetype.startsWith("application/pdf"))
445                         return published ? images.pdfShared() : images.pdf();
446                 else if (mimetype.endsWith("excel"))
447                         return published ? images.spreadsheetShared() : images.spreadsheet();
448                 else if (mimetype.endsWith("msword"))
449                         return published ? images.wordprocessorShared() : images.wordprocessor();
450                 else if (mimetype.endsWith("powerpoint"))
451                         return published ? images.presentationShared() : images.presentation();
452                 else if (mimetype.startsWith("application/zip") ||
453                                         mimetype.startsWith("application/gzip") ||
454                                         mimetype.startsWith("application/x-gzip") ||
455                                         mimetype.startsWith("application/x-tar") ||
456                                         mimetype.startsWith("application/x-gtar"))
457                         return published ? images.zipShared() : images.zip();
458                 else if (mimetype.startsWith("text/html"))
459                         return published ? images.htmlShared() : images.html();
460                 else if (mimetype.startsWith("text/plain"))
461                         return published ? images.txtShared() : images.txt();
462                 else if (mimetype.startsWith("image/"))
463                         return published ? images.imageShared() : images.image();
464                 else if (mimetype.startsWith("video/"))
465                         return published ? images.videoShared() : images.video();
466                 else if (mimetype.startsWith("audio/"))
467                         return published ? images.audioShared() : images.audio();
468                 return published ? images.documentShared() : images.document();
469         }
470
471         /**
472          * Fill the file cache with data.
473          */
474         public void setFiles(final List<File> _files) {
475                 if (!app.getSelectedTree().equals(app.mysharedTreeView)) {
476                         if (celltable.getColumnIndex(pathColumn) != -1)
477                                 celltable.removeColumn(pathColumn);
478                 }
479                 else {
480                         if (celltable.getColumnIndex(pathColumn) == -1)
481                                 celltable.insertColumn(2, pathColumn, pathHeader);
482                 }
483                 files = new ArrayList<File>();
484         for (File fres : _files) {
485                         files.add(fres);
486         }
487         
488                 folderFileCount = files.size();
489                 
490                 for (SortableHeader header : allHeaders) {
491                         if (header.isSorted())
492                                 sortFiles(header.getProperty(), header.getReverseSort());
493                 }
494                 
495                 List<File> previousSelection = getSelectedFiles(); //Keep the previous selection
496
497                 provider.getList().clear();
498         provider.setList(files);
499         selectionModel.clear();
500         for (File f : files) {
501                 if (previousSelection.contains(f))
502                         selectionModel.setSelected(f, true);
503         }
504         
505         app.showFolderStatistics(folderFileCount);
506         celltable.setPageSize(folderFileCount);
507         }
508
509         /**
510          * Does the list contains the requested filename
511          *
512          * @param fileName
513          * @return true/false
514          */
515         public boolean contains(String fileName) {
516                 for (int i = 0; i < files.size(); i++)
517                         if (files.get(i).getName().equals(fileName))
518                                 return true;
519                 return false;
520         }
521
522         public void clearSelectedRows() {
523                 Iterator<File> it = selectionModel.getSelectedSet().iterator();
524                 while(it.hasNext()){
525                         selectionModel.setSelected(it.next(),false);
526                 }
527         }
528         
529         /**
530          *
531          */
532         public void selectAllRows() {
533                 Iterator<File> it = provider.getList().iterator();
534                 while(it.hasNext()){
535                         selectionModel.setSelected(it.next(),true);
536                 }
537         }
538
539         protected void sortFiles(final String sortingProperty, final boolean sortingType){
540                 Collections.sort(files, new Comparator<File>() {
541
542             @Override
543             public int compare(File arg0, File arg1) {
544                     if (sortingType){
545                             if (sortingProperty.equals("version")) {
546                                     return arg0.getVersion() - arg1.getVersion();
547                             } else if (sortingProperty.equals("owner")) {
548                                     return arg0.getOwnerID().compareTo(arg1.getOwnerID());
549                             } else if (sortingProperty.equals("date")) {
550                                         if (arg0.getLastModified() != null && arg1.getLastModified() != null)
551                                                 return arg0.getLastModified().compareTo(arg1.getLastModified());
552                                         return 0;
553                             } else if (sortingProperty.equals("size")) {
554                                     return (int) (arg0.getBytes() - arg1.getBytes());
555                             } else if (sortingProperty.equals("name")) {
556                                     return arg0.getName().compareTo(arg1.getName());
557                             } else if (sortingProperty.equals("path")) {
558                                     return arg0.getUri().compareTo(arg1.getUri());
559                             } else {
560                                     return arg0.getName().compareTo(arg1.getName());
561                             }
562                     }
563                     else if (sortingProperty.equals("version")) {
564                             
565                             return arg1.getVersion() - arg0.getVersion();
566                     } else if (sortingProperty.equals("owner")) {
567                             
568                             return arg1.getOwnerID().compareTo(arg0.getOwnerID());
569                     } else if (sortingProperty.equals("date")) {
570                             
571                             return arg1.getLastModified().compareTo(arg0.getLastModified());
572                     } else if (sortingProperty.equals("size")) {
573                             return (int) (arg1.getBytes() - arg0.getBytes());
574                     } else if (sortingProperty.equals("name")) {
575                             
576                             return arg1.getName().compareTo(arg0.getName());
577                     } else if (sortingProperty.equals("path")) {
578                             
579                             return arg1.getUri().compareTo(arg0.getUri());
580                     } else {
581                             
582                             return arg1.getName().compareTo(arg0.getName());
583                     }
584             }
585
586                 });
587         }
588         
589         final class FileValueUpdater implements ValueUpdater<String>{
590                 private SortableHeader header;
591                 /**
592                  * 
593                  */
594                 public FileValueUpdater(SortableHeader header) {
595                         this.header=header;
596                 }
597                 @Override
598                 public void update(String value) {
599                         header.setSorted(true);
600                         header.toggleReverseSort();
601
602                 for (SortableHeader otherHeader : allHeaders) {
603                   if (otherHeader != header) {
604                     otherHeader.setSorted(false);
605                     otherHeader.setReverseSort(true);
606                   }
607                 }
608                 celltable.redrawHeaders();
609                 sortFiles(header.getProperty(), header.getReverseSort());
610                 FileList.this.update();                 
611                 }
612                 
613         }
614
615         /**
616          * Shows the files in the cellTable 
617      */
618         private void showCellTable(){
619                 provider.setList(files);
620                 
621                 provider.refresh();
622                 
623                 //celltable.redraw();
624                 celltable.redrawHeaders();              
625         }
626         
627         void iterateFilesHeadCommand(final Iterator<File> iter, final Command callback) {
628                 if (iter.hasNext()) {
629                         File f = iter.next();
630                         app.scheduleFileHeadCommand(f, new Command() {
631                                 
632                                 @Override
633                                 public void execute() {
634                                         iterateFilesHeadCommand(iter, callback);
635                                 }
636                         });
637                 }
638                 else if (callback != null)
639                         callback.execute();
640         }
641
642         public void selectByUrl(List<String> selectedUrls) {
643                 Set<File> previous = selectionModel.getSelectedSet();
644                 for (File f : previous)
645                         selectionModel.setSelected(f, false);
646                 
647                 int i = 0;
648                 boolean scrolled = false;
649                 for (File f : files) {
650                         if (selectedUrls.contains(app.getApiPath() + f.getOwnerID() + f.getUri())) {
651                                 selectionModel.setSelected(f, true);
652                                 if (!scrolled) {
653                                         celltable.getRowElement(i).getCells().getItem(0).scrollIntoView();
654                                         scrolled = true;
655                                 }
656                         }
657                         i++;
658                 }
659         }
660 }