From c6447e2ce289188493590ec0d5449fa3692eed08 Mon Sep 17 00:00:00 2001
From: Aleksander Machniak <alec@alec.pl>
Date: Wed, 02 Jul 2014 07:03:22 -0400
Subject: [PATCH] Use treelist widget for folders list in Settings/Folders (#1489648)

---
 program/js/treelist.js                                |   67 ++++++++-
 skins/classic/templates/folders.html                  |    4 
 program/steps/settings/folders.inc                    |   72 +++++----
 skins/larry/settings.css                              |   23 +-
 skins/classic/common.css                              |    1 
 skins/larry/templates/folders.html                    |    2 
 skins/classic/mail.css                                |    8 -
 skins/classic/functions.js                            |    6 
 program/steps/settings/func.inc                       |    2 
 skins/classic/settings.css                            |   61 +++-----
 program/js/app.js                                     |  129 +++++++-----------
 plugins/subscriptions_option/subscriptions_option.php |    4 
 skins/larry/ui.js                                     |    4 
 13 files changed, 197 insertions(+), 186 deletions(-)

diff --git a/plugins/subscriptions_option/subscriptions_option.php b/plugins/subscriptions_option/subscriptions_option.php
index 130f16a..5b926f2 100644
--- a/plugins/subscriptions_option/subscriptions_option.php
+++ b/plugins/subscriptions_option/subscriptions_option.php
@@ -86,7 +86,9 @@
     {
         $rcmail = rcmail::get_instance();
         if (!$rcmail->config->get('use_subscriptions', true)) {
-            $args['table']->remove_column('subscribed');
+            foreach ($args['list'] as $idx => $data) {
+                $args['list'][$idx]['content'] = preg_replace('/<input [^>]+>/', '', $data['content']);
+            }
         }
         return $args;
     }
diff --git a/program/js/app.js b/program/js/app.js
index db9a2ff..2b9c3f0 100644
--- a/program/js/app.js
+++ b/program/js/app.js
@@ -1856,9 +1856,6 @@
             && !this.env.mailboxes[id].virtual
             && (this.env.mailboxes[id].id != this.env.mailbox || this.is_multifolder_listing())) ? 1 : 0;
 
-      case 'settings':
-        return id != this.env.mailbox ? 1 : 0;
-
       case 'addressbook':
         var target;
         if (id != this.env.source && (target = this.env.contactfolders[id])) {
@@ -5765,62 +5762,38 @@
 
     this.last_sub_rx = RegExp('['+delim+']?[^'+delim+']+$');
 
-    this.subscription_list = new rcube_list_widget(this.gui_objects.subscriptionlist,
-      {multiselect:false, draggable:true, keyboard:true, toggleselect:true});
+    this.subscription_list = new rcube_treelist_widget(this.gui_objects.subscriptionlist, {
+        selectable: true
+    });
+
     this.subscription_list
-      .addEventListener('select', function(o){ ref.subscription_select(o); })
-      .addEventListener('dragstart', function(o){ ref.drag_active = true; })
-      .addEventListener('dragend', function(o){ ref.subscription_move_folder(o); })
-      .addEventListener('initrow', function (row) {
-        row.obj.onmouseover = function() { ref.focus_subscription(row.id); };
-        row.obj.onmouseout = function() { ref.unfocus_subscription(row.id); };
-      })
-      .init()
-      .focus();
+      .addEventListener('select', function(node) { ref.subscription_select(node.id); })
+      .draggable({cancel: '#mailboxroot'})
+      .droppable({
+        // @todo: find better way, accept callback is executed for every folder
+        // on the list when dragging starts (and stops), this is slow, but
+        // I didn't find a method to check droptarget on over event
+        accept: function(node) {
+          var source = ref.env.subscriptionrows[$(node).attr('id')],
+            dest = ref.env.subscriptionrows[this.id],
+            source_name = source[0],
+            dest_name = dest[0];
 
-    $('#mailboxroot')
-      .mouseover(function(){ ref.focus_subscription(this.id); })
-      .mouseout(function(){ ref.unfocus_subscription(this.id); })
-  };
-
-  this.focus_subscription = function(id)
-  {
-    var row, folder;
-
-    if (this.drag_active && this.env.mailbox && (row = document.getElementById(id)))
-      if (this.env.subscriptionrows[id] &&
-          (folder = this.env.subscriptionrows[id][0]) !== null
-      ) {
-        if (this.check_droptarget(folder) &&
-            !this.env.subscriptionrows[this.get_folder_row_id(this.env.mailbox)][2] &&
-            folder != this.env.mailbox.replace(this.last_sub_rx, '') &&
-            !folder.startsWith(this.env.mailbox + this.env.delimiter)
-        ) {
-          this.env.dstfolder = folder;
-          $(row).addClass('droptarget');
+          return !source[2]
+            && dest_name != source_name.replace(ref.last_sub_rx, '')
+            && !dest_name.startsWith(source_name + ref.env.delimiter);
+        },
+        drop: function(e, ui) {
+          ref.subscription_move_folder(ui.draggable.attr('id'), this.id);
         }
-      }
+      });
   };
 
-  this.unfocus_subscription = function(id)
+  this.subscription_select = function(id)
   {
-    var row = $('#'+id);
+    var folder;
 
-    this.env.dstfolder = null;
-
-    if (row.length && this.env.subscriptionrows[id])
-      row.removeClass('droptarget');
-    else
-      $(this.subscription_list.frame).removeClass('droptarget');
-  };
-
-  this.subscription_select = function(list)
-  {
-    var id, folder;
-
-    if (list && (id = list.get_single_selection()) &&
-        (folder = this.env.subscriptionrows['rcmrow'+id])
-    ) {
+    if (id && id != 'mailboxroot' && (folder = this.env.subscriptionrows[id])) {
       this.env.mailbox = folder[0];
       this.show_folder(folder[0]);
       this.enable_command('delete-folder', !folder[2]);
@@ -5832,24 +5805,21 @@
     }
   };
 
-  this.subscription_move_folder = function(list)
+  this.subscription_move_folder = function(from, to)
   {
-    if (this.env.mailbox && this.env.dstfolder !== null &&
-        this.env.dstfolder != this.env.mailbox &&
-        this.env.dstfolder != this.env.mailbox.replace(this.last_sub_rx, '')
-    ) {
-      var path = this.env.mailbox.split(this.env.delimiter),
-        basename = path.pop(),
-        newname = this.env.dstfolder === '' ? basename : this.env.dstfolder + this.env.delimiter + basename;
+    var source = this.env.subscriptionrows[from][0];
+      dest = this.env.subscriptionrows[to][0];
 
-      if (newname != this.env.mailbox) {
-        this.http_post('rename-folder', {_folder_oldname: this.env.mailbox, _folder_newname: newname}, this.set_busy(true, 'foldermoving'));
-        this.subscription_list.draglayer.hide();
+    if (source && dest !== null && source != dest && dest != source.replace(this.last_sub_rx, '')) {
+      var path = source.split(this.env.delimiter),
+        basename = path.pop(),
+        newname = dest === '' ? basename : dest + this.env.delimiter + basename;
+
+      if (newname != source) {
+        this.http_post('rename-folder', {_folder_oldname: source, _folder_newname: newname},
+          this.set_busy(true, 'foldermoving'));
       }
     }
-
-    this.drag_active = false;
-    this.unfocus_subscription(this.get_folder_row_id(this.env.dstfolder));
   };
 
   // tell server to create and subscribe a new mailbox
@@ -5865,8 +5835,7 @@
       folder = this.env.subscriptionrows[id][0];
 
     if (folder && confirm(this.get_label('deletefolderconfirm'))) {
-      var lock = this.set_busy(true, 'folderdeleting');
-      this.http_post('delete-folder', {_mbox: folder}, lock);
+      this.http_post('delete-folder', {_mbox: folder}, this.set_busy(true, 'folderdeleting'));
     }
   };
 
@@ -5878,9 +5847,9 @@
 
     var row, n, tmp, tmp_name, rowid, collator,
       folders = [], list = [], slist = [],
-      tbody = this.gui_objects.subscriptionlist.tBodies[0],
-      refrow = $('tr', tbody).get(1),
-      id = 'rcmrow'+((new Date).getTime());
+      list_element = $(this.gui_objects.subscriptionlist),
+      refrow = $('li', list_element).get(1),
+      id = 'rcmli'+((new Date).getTime());
 
     if (!refrow) {
       // Refresh page if we don't have a table row to clone
@@ -5895,7 +5864,7 @@
     row.attr({id: id, 'class': class_name});
 
     // set folder name
-    row.find('td:first').html(display_name);
+    $('.name', row).html(display_name);
 
     // update subscription checkbox
     $('input[name="_subscribed[]"]', row).val(name)
@@ -5966,12 +5935,13 @@
 
     // add row to the table
     if (rowid)
-      $('#'+rowid).after(row);
+      $('#' + rowid).after(row);
     else
-      row.appendTo(tbody);
+      list_element.append(row);
 
     // update list widget
-    this.subscription_list.clear_selection();
+    this.subscription_list.select();
+
     if (!skip_init)
       this.init_subscription_list();
 
@@ -5988,11 +5958,11 @@
     if (!this.gui_objects.subscriptionlist) {
       if (this.is_framed)
         return parent.rcmail.replace_folder_row(oldfolder, newfolder, display_name, is_protected, class_name);
+
       return false;
     }
 
     var i, n, len, name, dispname, oldrow, tmprow, row, level,
-      tbody = this.gui_objects.subscriptionlist.tBodies[0],
       folders = this.env.subscriptionrows,
       id = this.get_folder_row_id(oldfolder),
       prefix_len = oldfolder.length,
@@ -6003,7 +5973,6 @@
     // no renaming, only update class_name
     if (oldfolder == newfolder) {
       $('#'+id).attr('class', class_name || '');
-      this.subscription_list.focus();
       return;
     }
 
@@ -6040,7 +6009,7 @@
           for (i=level; i<0; i++)
             dispname = '&nbsp;&nbsp;&nbsp;&nbsp;' + dispname;
         }
-        row.find('td:first').html(dispname);
+        $('.name', row).html(dispname);
         this.env.subscriptionrows[id][1] = dispname;
       }
     }
@@ -6068,8 +6037,8 @@
 
   this._remove_folder_row = function(id)
   {
-    this.subscription_list.remove_row(id.replace(/^rcmrow/, ''));
-    $('#'+id).remove();
+    this.subscription_list.remove(id.replace(/^rcmli/, ''));
+    $('#' + id).remove();
     delete this.env.subscriptionrows[id];
   };
 
diff --git a/program/js/treelist.js b/program/js/treelist.js
index b2d838e..1e60617 100644
--- a/program/js/treelist.js
+++ b/program/js/treelist.js
@@ -46,7 +46,7 @@
     scroll_speed: 20,
     save_state: false,
     keyboard: true,
-    check_droptarget: function(node){ return !node.virtual }
+    check_droptarget: function(node) { return !node.virtual; }
   }, p || {});
 
   var container = $(node),
@@ -67,6 +67,7 @@
     searchfield,
     tree_state,
     ui_droppable,
+    ui_draggable,
     list_id = (container.attr('id') || p.id_prefix || '0'),
     me = this;
 
@@ -83,6 +84,7 @@
   this.drag_end = drag_end;
   this.intersects = intersects;
   this.droppable = droppable;
+  this.draggable = draggable;
   this.update = update_node;
   this.insert = insert;
   this.remove = remove;
@@ -108,7 +110,11 @@
     e.stopPropagation();
   });
 
-  container.on('click', 'li', function(e){
+  container.on('click', 'li', function(e) {
+    // do not select record on checkbox/input click
+    if ($(e.target).is('input'))
+      return true;
+
     var node = p.selectable ? indexbyid[dom2id($(this))] : null;
     if (node && !node.virtual) {
       select(node.id);
@@ -231,6 +237,9 @@
           id2dom(selection).removeClass('selected').removeAttr('aria-selected');
       selection = null;
     }
+
+    if (!id)
+      return;
 
     var li = id2dom(id, true);
     if (li.length) {
@@ -708,6 +717,7 @@
   {
     var domid = p.id_encode ? p.id_encode(id) : id,
       suffix = search_active && !real ? '--xsR' : '';
+
     return $('#' + p.id_prefix + domid + suffix, container);
   }
 
@@ -850,6 +860,11 @@
    */
   function drag_start()
   {
+    if (drag_active)
+      return;
+
+    drag_active = true;
+
     var li, item, height,
       pos = container.offset();
 
@@ -857,7 +872,6 @@
     list_scroll_top = container.parent().scrollTop();
     pos.top += list_scroll_top;
 
-    drag_active = true;
     box_coords = {
       x1: pos.left,
       y1: pos.top,
@@ -920,6 +934,9 @@
    */
   function drag_end()
   {
+    if (!drag_active)
+      return;
+
     drag_active = false;
     scroll_timer = null;
 
@@ -950,7 +967,7 @@
   }
 
   /**
-   * Determine if the given mouse coords intersect the list and one if its items
+   * Determine if the given mouse coords intersect the list and one of its items
    */
   function intersects(mouse, highlight)
   {
@@ -970,8 +987,8 @@
     }
 
     // check intersection with visible list items
-    var pos, node;
-    for (var id in item_coords) {
+    var id, pos, node;
+    for (id in item_coords) {
       pos = item_coords[id];
       if (mouse.x >= pos.x1 && mouse.x < pos.x2 && mouse.top >= pos.y1 && mouse.top < pos.y2) {
         node = indexbyid[id];
@@ -1024,7 +1041,14 @@
    */
   function droppable(opts)
   {
-    var my_opts = $.extend({ greedy: true, hoverClass: 'droptarget', addClasses:false }, opts);
+    if (!opts) opts = {};
+
+    var my_opts = $.extend({
+        greedy: true,
+        tolerance: 'pointer',
+        hoverClass: 'droptarget',
+        addClasses: false
+      }, opts);
 
     my_opts.activate = function(e, ui) {
       drag_start();
@@ -1046,7 +1070,34 @@
         opts.over(e, ui);
     };
 
-    $('li:not(.virtual)', container).droppable(my_opts);
+    $(selector ? selector : 'li:not(.virtual)', container).droppable(my_opts);
+
+    return this;
+  }
+
+  /**
+   * Wrapper for jQuery.UI.draggable() activation on this widget
+   *
+   * @param object Options as passed to regular .draggable() function
+   */
+  function draggable(opts)
+  {
+    if (!opts) opts = {};
+
+    var my_opts = $.extend({
+        appendTo: 'body',
+        iframeFix: true,
+        addClasses: false,
+        cursorAt: {left: -20, top: 5},
+        helper: function(e) {
+          return $('<div>').attr('id', 'rcmdraglayer')
+            .text($.trim($(e.target).first().text()));
+        }
+      }, opts);
+
+    $('li:not(.virtual)', container).draggable(my_opts);
+
+    return this;
   }
 }
 
diff --git a/program/steps/settings/folders.inc b/program/steps/settings/folders.inc
index 33b2b06..ad5f37d 100644
--- a/program/steps/settings/folders.inc
+++ b/program/steps/settings/folders.inc
@@ -177,11 +177,9 @@
 }
 
 $OUTPUT->set_pagetitle($RCMAIL->gettext('folders'));
-$OUTPUT->include_script('list.js');
 $OUTPUT->set_env('prefix_ns', $STORAGE->get_namespace('prefix'));
-if ($STORAGE->get_capability('QUOTA')) {
-    $OUTPUT->set_env('quota', true);
-}
+$OUTPUT->set_env('quota', (bool) $STORAGE->get_capability('QUOTA'));
+$OUTPUT->include_script('treelist.js');
 
 // add some labels to client
 $OUTPUT->add_label('deletefolderconfirm', 'purgefolderconfirm', 'folderdeleting',
@@ -205,15 +203,8 @@
     list($form_start, $form_end) = get_form_tags($attrib, 'folders');
     unset($attrib['form']);
 
-    if (!$attrib['id'])
+    if (!$attrib['id']) {
         $attrib['id'] = 'rcmSubscriptionlist';
-
-    $table = new html_table();
-
-    if ($attrib['noheader'] !== true && $attrib['noheader'] != "true") {
-        // add table header
-        $table->add_header('name', $RCMAIL->gettext('foldername'));
-        $table->add_header('subscribed', '');
     }
 
     $STORAGE = $RCMAIL->get_storage();
@@ -227,7 +218,6 @@
     $namespace       = $STORAGE->get_namespace();
     $special_folders = array_flip(array_merge(array('inbox' => 'INBOX'), $STORAGE->get_special_folders()));
     $protect_default = $RCMAIL->config->get('protect_default_folders');
-    $a_js_folders    = array();
     $seen            = array();
     $list_folders    = array();
 
@@ -272,18 +262,14 @@
 
     unset($seen);
 
-    // add drop-target representing 'root'
-    $table->add_row(array('id' => 'mailboxroot', 'class' => 'virtual root'));
-    $table->add('name', '&nbsp;');
-    $table->add(null, '&nbsp;');
-
-    $a_js_folders['mailboxroot'] = array('', '', true);
-
     $checkbox_subscribe = new html_checkbox(array(
         'name'    => '_subscribed[]',
         'title'   => $RCMAIL->gettext('changesubscription'),
         'onclick' => rcmail_output::JS_OBJECT_NAME.".command(this.checked?'subscribe':'unsubscribe',this.value)",
     ));
+
+    $js_folders = array();
+    $folders    = array();
 
     // create list of available folders
     foreach ($list_folders as $i => $folder) {
@@ -292,7 +278,7 @@
         $subscribed = $sub_key !== false;
         $protected  = $protect_default && isset($special_folders[$folder['id']]);
         $noselect   = false;
-        $classes    = array($i%2 ? 'even' : 'odd');
+        $classes    = array('listitem');
 
         $folder_utf8    = rcube_charset::convert($folder['id'], 'UTF7-IMAP');
         $display_folder = str_repeat('&nbsp;&nbsp;&nbsp;&nbsp;', $folder['level'])
@@ -352,25 +338,45 @@
             }
         }
 
-        $table->add_row(array('id' => 'rcmrow'.$idx, 'class' => join(' ', $classes),
-            'foldername' => $folder['id']));
-
-        $table->add('name', $display_folder);
-        $table->add('subscribed', $checkbox_subscribe->show(($subscribed ? $folder_utf8 : ''),
-            array('value' => $folder_utf8, 'disabled' => $disabled ? 'disabled' : '')));
-
-        $a_js_folders['rcmrow'.$idx] = array($folder_utf8,
-            $display_folder, $protected || $folder['virtual']);
+        $row_id = 'rcmli' . $idx;
+        $folders[$row_id] = array(
+            'folder'      => $folder_utf8,
+            'display'     => $display_folder,
+            'class'       => join(' ', $classes),
+            'folder_imap' => $folder['id'],
+            'subscribed'  => $subscribed,
+            'protected'   => $protected || $folder['virtual'],
+            'content'     => html::a(array('class' => 'name', 'href' => '#_' . $row_id), $display_folder)
+                . $checkbox_subscribe->show(($subscribed ? $folder_utf8 : ''),
+                    array('value' => $folder_utf8, 'disabled' => $disabled ? 'disabled' : ''))
+        );
     }
 
-    $RCMAIL->plugins->exec_hook('folders_list', array('table' => $table));
+    $plugin = $RCMAIL->plugins->exec_hook('folders_list', array('list' => $folders));
+
+    // add drop-target representing 'root'
+    $roots = array(
+        'mailboxroot' => array(
+            'folder'    => '',
+            'display'   => '',
+            'protected' => true,
+            'class'     => 'root',
+            'content'   => html::span('name', '&nbsp;')
+        )
+    );
+    $folders = array_merge($roots, $plugin['list']);
+
+    while (list($key, $data) = each($folders)) {
+        $js_folders[$key] = array($data['folder'], $data['display'], $data['protected']);
+        $folders[$key]    = html::tag('li', array('id'  => $key, 'class' => $data['class']), $data['content']);
+    }
 
     $OUTPUT->add_gui_object('subscriptionlist', $attrib['id']);
-    $OUTPUT->set_env('subscriptionrows', $a_js_folders);
+    $OUTPUT->set_env('subscriptionrows', $js_folders);
     $OUTPUT->set_env('defaultfolders', array_keys($special_folders));
     $OUTPUT->set_env('delimiter', $delimiter);
 
-    return $form_start . $table->show($attrib) . $form_end;
+    return $form_start . html::tag('ul', $attrib, implode("\n", $folders)) . $form_end;
 }
 
 function rcmail_folder_frame($attrib)
diff --git a/program/steps/settings/func.inc b/program/steps/settings/func.inc
index 7ccbfa4..40b70b1 100644
--- a/program/steps/settings/func.inc
+++ b/program/steps/settings/func.inc
@@ -1309,6 +1309,8 @@
     $display_name = str_repeat('&nbsp;&nbsp;&nbsp;&nbsp;', $level)
         . rcube::Q($protected ? $RCMAIL->localize_foldername($name) : rcube_charset::convert($foldersplit[$level], 'UTF7-IMAP'));
 
+    $class_name = trim($class_name . ' listitem');
+
     if ($oldname === null) {
         $OUTPUT->command('add_folder_row', $name_utf8, $display_name, $protected, $subscribe,
             false, $class_name);
diff --git a/skins/classic/common.css b/skins/classic/common.css
index d28f287..13f4e64 100644
--- a/skins/classic/common.css
+++ b/skins/classic/common.css
@@ -723,6 +723,7 @@
   font-size: 11px;
   border-bottom: 1px solid #EBEBEB;
   white-space: nowrap;
+  overflow: hidden;
 }
 
 ul.treelist li a
diff --git a/skins/classic/functions.js b/skins/classic/functions.js
index 7f2b8b4..b521be3 100644
--- a/skins/classic/functions.js
+++ b/skins/classic/functions.js
@@ -751,6 +751,8 @@
 
 /**
  * Scroller
+ *
+ * @deprecated Use treelist widget
  */
 function rcmail_scroller(list, top, bottom)
 {
@@ -1009,10 +1011,6 @@
     else if (rcmail.env.task == 'addressbook') {
       rcmail.addEventListener('afterupload-photo', function(){ rcmail_ui.show_popup('uploadform', false); })
         .gui_object('dragmenu', 'dragmenu');
-    }
-    else if (rcmail.env.task == 'settings') {
-      if (rcmail.gui_objects.subscriptionlist)
-        new rcmail_scroller('#folderlist-content', '#folderlist-title', '#folderlist-footer');
     }
   });
 }
diff --git a/skins/classic/mail.css b/skins/classic/mail.css
index f9e5e4b..58db795 100644
--- a/skins/classic/mail.css
+++ b/skins/classic/mail.css
@@ -409,14 +409,6 @@
   background-color: #FFF;
 }
 
-#mailboxlist li
-{
-  display: block;
-  position: relative;
-  font-size: 11px;
-  border-bottom: 1px solid #EBEBEB;
-}
-
 #mailboxlist li ul li:last-child
 {
   border-bottom: 0 none;
diff --git a/skins/classic/settings.css b/skins/classic/settings.css
index acd0b9f..3b084de 100644
--- a/skins/classic/settings.css
+++ b/skins/classic/settings.css
@@ -12,26 +12,10 @@
 }
 
 #identities-table,
-#subscription-table,
 #sections-table
 {
   width: 100%;
   table-layout: fixed;
-}
-
-#subscription-table input
-{
-  font: inherit;
-}
-
-#subscription-table tbody td,
-#identities-table tbody td,
-#sections-table tbody td
-{
-  cursor: default;
-  text-overflow: ellipsis;
-  -o-text-overflow: ellipsis;
-  height: 18px;
 }
 
 #identities-table tbody tr.readonly td
@@ -39,37 +23,42 @@
   font-style: italic;
 }
 
-#subscription-table tr.virtual td
+#subscription-table li.selected a
 {
-  color: #666;
-}
-
-#subscription-table tr.root td
-{
-  font-size: 10%;
-  height: 5px;
-}
-
-#subscription-table tr.selected td
-{
-  color: #FFFFFF;
+  color: #FFF;
   background-color: #CC3333;
 }
 
-#subscription-table tr.droptarget td
+#subscription-table li.root
 {
-  background-color: #FFFFA6;
+  font-size: 5%;
+  line-height: 5px;
+  height: 5px;
+  padding: 2px;
 }
 
-#subscription-table td.name
+#subscription-table li a.name
 {
-  width: auto;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  width: 100%;
+  display: block;
+  float: left;
+  padding: 0 0 0 5px;
+  height: 24px;
+  line-height: 24px;
 }
 
-#subscription-table td.subscribed
+#subscription-table li input
 {
-  text-align: right;
-  padding-right: 12px;
+  position: absolute;
+  right: 0;
+}
+
+html.chrome #subscription-table li input,
+html.opera #subscription-table li input
+{
+  margin-top: 6px;
 }
 
 #folder-box,
diff --git a/skins/classic/templates/folders.html b/skins/classic/templates/folders.html
index f00c23b..66bec62 100644
--- a/skins/classic/templates/folders.html
+++ b/skins/classic/templates/folders.html
@@ -21,8 +21,8 @@
 <div id="folder-manager">
 <div id="folderlist-title" class="boxtitle"><span class="rightalign"><roundcube:label name="subscribed" /></span><roundcube:label name="folders" /></div>
 <div id="folderlist-content" class="boxlistcontent">
-    <roundcube:object name="foldersubscription" form="subscriptionform" id="subscription-table" noheader="true"
-        cellpadding="1" cellspacing="0" summary="Folder subscription table" class="records-table" />
+    <roundcube:object name="foldersubscription" form="subscriptionform" id="subscription-table"
+        summary="Folder subscription table" class="treelist" />
 </div>
 <div id="folderlist-footer" class="boxfooter">
     <roundcube:button command="create-folder" type="link" title="createfolder" class="buttonPas addgroup" classAct="button addgroup" content=" " />
diff --git a/skins/larry/settings.css b/skins/larry/settings.css
index 518c027..0493e30 100644
--- a/skins/larry/settings.css
+++ b/skins/larry/settings.css
@@ -244,27 +244,28 @@
 	bottom: 0;
 }
 
-#subscription-table {
-	table-layout: fixed;
-}
-
-#subscription-table tr.root td {
+#subscription-table li.root {
 	font-size: 5%;
 	line-height: 5px;
 	height: 5px;
 	padding: 2px;
 }
 
-#subscription-table td.name {
-	width: 85%;
+#subscription-table li a.name {
 	overflow: hidden;
 	text-overflow: ellipsis;
+	width: 100%;
+	float: left;
 }
 
-#subscription-table td.subscribed {
-	min-width: 30px;
-	padding: 0 14px 0 2px;
-	text-align: right;
+#subscription-table li input {
+	position: absolute;
+	right: 0;
+}
+
+html.chrome #subscription-table li input,
+html.opera #subscription-table li input {
+	margin-top: 6px;
 }
 
 .skinselection {
diff --git a/skins/larry/templates/folders.html b/skins/larry/templates/folders.html
index eed5631..f364792 100644
--- a/skins/larry/templates/folders.html
+++ b/skins/larry/templates/folders.html
@@ -19,7 +19,7 @@
 <div id="folderslist" class="uibox listbox">
 <h2 id="folderslist-header" class="boxtitle"><span style="float:right"><roundcube:label name="subscribed" /></span><roundcube:label name="folders" /></h2>
 <div id="folderslist-content" class="scroller withfooter">
-<roundcube:object name="foldersubscription" form="subscriptionform" id="subscription-table" class="listing" noheader="true" />
+	<roundcube:object name="foldersubscription" form="subscriptionform" id="subscription-table" class="listing" />
 </div>
 <div id="folderslist-footer" class="boxfooter">
 	<roundcube:button command="create-folder" type="link" title="createfolder" class="listbutton add disabled" classAct="listbutton add" innerClass="inner" label="createfolder" /><roundcube:button name="mailboxmenulink" id="mailboxmenulink" type="link" title="folderactions" class="listbutton groupactions" onclick="return UI.toggle_popup('mailboxmenu',event)" innerClass="inner" content="&#9881;" aria-haspopup="true" aria-expanded="false" aria-owns="mailboxoptionsmenu" />
diff --git a/skins/larry/ui.js b/skins/larry/ui.js
index f8c06f8..786e354 100644
--- a/skins/larry/ui.js
+++ b/skins/larry/ui.js
@@ -258,8 +258,6 @@
         new rcube_splitter({ id:'folderviewsplitter', p1:'#folderslist', p2:'#folder-details',
           orientation:'v', relative:true, start:266, min:180, size:12 }).init();
 
-        new rcube_scroller('#folderslist-content', '#folderslist-header', '#folderslist-footer');
-
         rcmail.addEventListener('setquota', update_quota);
       }
       else if (rcmail.env.action == 'identities') {
@@ -1136,6 +1134,8 @@
 
 /**
  * Roundcube Scroller class
+ *
+ * @deprecated Use treelist widget
  */
 function rcube_scroller(list, top, bottom)
 {

--
Gitblit v1.9.1