diff --git a/bootstrap.php b/bootstrap.php
index df723ae7af7c9e2da93e1ae81ca7fcb8b652dbf2..6d7acf23f8152980feabb54d00ffdf3cb0d53b6a 100644
--- a/bootstrap.php
+++ b/bootstrap.php
@@ -133,7 +133,8 @@ class Bootstrap {
         define('SEQUENCE_TABLE', $prefix.'sequence');
         define('TRANSLATION_TABLE', $prefix.'translation');
         define('QUEUE_TABLE', $prefix.'queue');
-        define('QUEUE_COLUMN_TABLE', $prefix.'queue_column');
+        define('COLUMN_TABLE', $prefix.'queue_column');
+        define('QUEUE_COLUMN_TABLE', $prefix.'queue_columns');
 
         define('API_KEY_TABLE',$prefix.'api_key');
         define('TIMEZONE_TABLE',$prefix.'timezone');
diff --git a/include/ajax.admin.php b/include/ajax.admin.php
index ddeb7c5db090d30078925e202db7de47e36bd475..b1f910c2a08d7464a4c3b47c391a884664206789 100644
--- a/include/ajax.admin.php
+++ b/include/ajax.admin.php
@@ -190,4 +190,29 @@ class AdminAjaxAPI extends AjaxController {
 
         include STAFFINC_DIR . 'templates/quick-add.tmpl.php';
     }
+
+    function addQueueColumn($root='Ticket') {
+        global $ost, $thisstaff;
+
+        if (!$thisstaff)
+            Http::response(403, 'Agent login required');
+        if (!$thisstaff->isAdmin())
+            Http::response(403, 'Access denied');
+
+        $column = QueueColumn::create();
+        if ($_POST) {
+            $data_form = $column->getDataConfigForm($_POST);
+            if ($data_form->isValid()) {
+                $column->update($_POST, $root);
+                if ($column->save())
+                    Http::response(201, $this->encode(array(
+                        'id' => $column->getId(),
+                        'name' => (string) $column->getName(),
+                    ), 'application/json'));
+            }
+        }
+
+        include STAFFINC_DIR . 'templates/queue-column-add.tmpl.php';
+
+    }
 }
diff --git a/include/ajax.search.php b/include/ajax.search.php
index 5ef597a5538b51acb77d724ba09da84e46d435e2..d11a347576474364ffbcf973e0e65a030660e959 100644
--- a/include/ajax.search.php
+++ b/include/ajax.search.php
@@ -212,19 +212,27 @@ class SearchAjaxAPI extends AjaxController {
         )));
     }
 
-
-    function editColumn($queue_id, $column) {
+    function editColumn($column_id) {
         global $thisstaff;
 
         if (!$thisstaff) {
             Http::response(403, 'Agent login is required');
         }
-        elseif (!($queue = CustomQueue::lookup($queue_id))) {
+        elseif (!($column = QueueColumn::lookup($column_id))) {
             Http::response(404, 'No such queue');
         }
 
-        $data_form = new QueueDataConfigForm($_POST);
-        include STAFFINC_DIR . 'templates/queue-column.tmpl.php';
+        if ($_POST) {
+            $data_form = $column->getDataConfigForm($_POST);
+            if ($data_form->isValid()) {
+                $column->update($_POST, 'Ticket');
+                if ($column->save())
+                    Http::response(201, 'Successfully updated');
+            }
+        }
+
+        $root = 'Ticket';
+        include STAFFINC_DIR . 'templates/queue-column-edit.tmpl.php';
     }
 
     function previewQueue($id=false) {
@@ -288,43 +296,4 @@ class SearchAjaxAPI extends AjaxController {
         $id = $_GET['condition'];
         include STAFFINC_DIR . 'templates/queue-column-condition-prop.tmpl.php';
     }
-
-    function addColumn() {
-        global $thisstaff;
-
-        if (!$thisstaff) {
-            Http::response(403, 'Agent login is required');
-        }
-        elseif (!isset($_GET['field'])) {
-            Http::response(400, '`field` parameter is required');
-        }
-
-        $field = $_GET['field'];
-        // XXX: This method should receive a queue ID or queue root so that
-        //      $field can be properly checked
-        $fields = SavedSearch::getSearchableFields('Ticket');
-        if (!isset($fields[$field])) {
-            Http::response(400, 'Not a supported field for this queue');
-        }
-
-        // Get the tabbed column configuration
-        list($label, $F) = $fields[$field];
-        $column = QueueColumn::create(array(
-            "id"        => (int) $_GET['id'],
-            "heading"   => _S($F->getLabel()),
-            "primary"   => $field,
-            "width"     => 100,
-        ));
-        ob_start();
-        include STAFFINC_DIR .  'templates/queue-column.tmpl.php';
-        $config = ob_get_clean();
-
-        // Send back the goodies
-        Http::response(200, $this->encode(array(
-            'config' => $config,
-            'id' => $column->id,
-            'heading' => _S($F->getLabel()),
-            'width' => $column->getWidth(),
-        )), 'application/json');
-    }
 }
diff --git a/include/class.i18n.php b/include/class.i18n.php
index d7aaa71ed98e7a296b0efd09cb7097602b3e3962..9d8575c1379a14fe823dfe5780d2ff443d1b7f04 100644
--- a/include/class.i18n.php
+++ b/include/class.i18n.php
@@ -66,6 +66,7 @@ class Internationalization {
             'role.yaml' =>          'Role::__create',
             'file.yaml' =>          'AttachmentFile::__create',
             'sequence.yaml' =>      'Sequence::__create',
+            'queue_column.yaml' =>  'QueueColumn::__create',
             'queue.yaml' =>         'CustomQueue::__create',
         );
 
diff --git a/include/class.queue.php b/include/class.queue.php
index 77077e13824d41957a369681e109fc5e3e1ea4f2..2f1aa3f20dfbe46539a11d9f41f017f4dfdee88f 100644
--- a/include/class.queue.php
+++ b/include/class.queue.php
@@ -21,7 +21,8 @@ class CustomQueue extends SavedSearch {
         'select_related' => array('parent'),
         'joins' => array(
             'columns' => array(
-                'reverse' => 'QueueColumn.queue',
+                'reverse' => 'QueueColumnGlue.queue',
+                'broker' => 'QueueColumnListBroker',
             ),
             'children' => array(
                 'reverse' => 'CustomQueue.parent',
@@ -113,7 +114,7 @@ class CustomQueue extends SavedSearch {
 
         // Apply column, annotations and conditions additions
         foreach ($this->getColumns() as $C) {
-            $query = $C->mangleQuery($query);
+            $query = $C->mangleQuery($query, $this->getRoot());
         }
         return $query;
     }
@@ -145,21 +146,30 @@ class CustomQueue extends SavedSearch {
         // Update queue columns (but without save)
         if (isset($vars['columns'])) {
             $new = $vars['columns'];
+            $order = array_keys($new);
             foreach ($this->columns as $col) {
-                if (false === ($sort = array_search($col->id, $vars['columns']))) {
+                $key = $col->column_id;
+                if (!isset($vars['columns'][$key])) {
                     $this->columns->remove($col);
                     continue;
                 }
-                $col->set('sort', $sort+1);
-                $col->update($vars, $errors);
-                unset($new[$sort]);
+                $info = $vars['columns'][$key];
+                $col->set('sort', array_search($key, $order));
+                $col->set('heading', $info['heading']);
+                $col->set('width', $info['width']);
+                unset($new[$key]);
             }
             // Add new columns
-            foreach ($new as $sort=>$colid) {
-                $col = QueueColumn::create(array("id" => $colid, "queue" => $this));
-                $col->set('sort', $sort+1);
-                $col->update($vars, $errors);
-                $this->addColumn($col);
+            foreach ($new as $info) {
+                $glue = QueueColumnGlue::create(array(
+                    'column_id' => $info['column_id'], 
+                    'sort' => array_search($info['column_id'], $order),
+                    'heading' => $info['heading'],
+                    'width' => $info['width'] ?: 100
+                ));
+                $glue->queue = $this;
+                $this->columns->add(
+                    QueueColumn::lookup($info['column_id']), $glue);
             }
             // Re-sort the in-memory columns array
             $this->columns->sort(function($c) { return $c->sort; });
@@ -193,6 +203,11 @@ class CustomQueue extends SavedSearch {
     static function __create($vars) {
         $q = static::create($vars);
         $q->save();
+        foreach ($vars['columns'] as $info) {
+            $glue = QueueColumnGlue::create($info);
+            $glue->queue_id = $q->getId();
+            $glue->save();
+        }
         return $q;
     }
 }
@@ -628,14 +643,8 @@ extends ChoiceField {
 class QueueColumn
 extends VerySimpleModel {
     static $meta = array(
-        'table' => QUEUE_COLUMN_TABLE,
+        'table' => COLUMN_TABLE,
         'pk' => array('id'),
-        'ordering' => array('sort'),
-        'joins' => array(
-            'queue' => array(
-                'constraint' => array('queue_id' => 'SavedSearch.id'),
-            ),
-        ),
     );
 
     var $_annotations;
@@ -645,22 +654,40 @@ extends VerySimpleModel {
         return $this->id;
     }
 
+    function getFilter() {
+         if ($this->filter)
+             return QueueColumnFilter::getInstance($this->filter);
+     }
+
+    function getName() {
+        return $this->name;
+    }
+
+    // These getters fetch data from the annotated overlay from the
+    // queue_column table
     function getQueue() {
         return $this->queue;
     }
 
+    function getWidth() {
+        return $this->width ?: 100;
+    }
+
     function getHeading() {
         return $this->heading;
     }
 
-    function getWidth() {
-        return $this->width ?: 100;
+    function getTranslateTag($subtag) {
+        return _H(sprintf('column.%s.%s.%s', $subtag, $this->queue_id, $this->id));
+    }
+    function getLocal($subtag) {
+        $tag = $this->getTranslateTag($subtag);
+        $T = CustomDataTranslation::translate($tag);
+        return $T != $tag ? $T : $this->get($subtag);
+    }
+    function getLocalHeading() {
+        return $this->getLocal('heading');
     }
-
-    function getFilter() {
-         if ($this->filter)
-             return QueueColumnFilter::getInstance($this->filter);
-     }
 
     function render($row) {
         // Basic data
@@ -732,9 +759,9 @@ extends VerySimpleModel {
         return $field->addToQuery($query, $path);
     }
 
-    function mangleQuery($query) {
+    function mangleQuery($query, $root=null) {
         // Basic data
-        $fields = SavedSearch::getSearchableFields($this->getQueue()->getRoot());
+        $fields = SavedSearch::getSearchableFields($root ?: $this->getQueue()->getRoot());
         if ($primary = $fields[$this->primary]) {
             list(,$field) = $primary;
             $query = $this->addToQuery($query, $field,
@@ -763,7 +790,7 @@ extends VerySimpleModel {
     }
 
     function getDataConfigForm($source=false) {
-        return new QueueColDataConfigForm($source ?: $this->ht,
+        return new QueueColDataConfigForm($source ?: $this->__getDbFields(),
             array('id' => $this->id));
     }
 
@@ -805,7 +832,13 @@ extends VerySimpleModel {
         return $inst;
     }
 
-    function update($vars) {
+    static function __create($vars) {
+        $c = static::create($vars);
+        $c->save();
+        return $c;
+    }
+
+    function update($vars, $root='Ticket') {
         $form = $this->getDataConfigForm($vars);
         foreach ($form->getClean() as $k=>$v)
             $this->set($k, $v);
@@ -833,7 +866,7 @@ extends VerySimpleModel {
                     continue;
                 // Determine the criteria
                 $name = $vars['condition_field'][$i];
-                $fields = SavedSearch::getSearchableFields($this->getQueue()->getRoot());
+                $fields = SavedSearch::getSearchableFields($root);
                 if (!isset($fields[$name]))
                     // No such field exists for this queue root type
                     continue;
@@ -884,6 +917,48 @@ extends VerySimpleModel {
     }
 }
 
+class QueueColumnGlue
+extends VerySimpleModel {
+    static $meta = array(
+        'table' => QUEUE_COLUMN_TABLE,
+        'pk' => array('queue_id', 'column_id'),
+        'joins' => array(
+            'column' => array(
+                'constraint' => array('column_id' => 'QueueColumn.id'),
+            ),
+            'queue' => array(
+                'constraint' => array('queue_id' => 'CustomQueue.id'),
+            ),
+        ),
+        'select_related' => array('column', 'queue'),
+        'ordering' => array('sort'),
+    );
+}
+
+class QueueColumnListBroker
+extends InstrumentedList {
+    function __construct($fkey, $queryset=false) {
+        parent::__construct($fkey, $queryset);
+        $this->queryset->select_related('column');
+    }
+
+    function getOrBuild($modelClass, $fields, $cache=true) {
+        $m = parent::getOrBuild($modelClass, $fields, $cache);
+        if ($m && $modelClass === 'QueueColumnGlue') {
+            // Instead, yield the QueueColumn instance with the local fields
+            // inthe association table as annotations
+            $m = AnnotatedModel::wrap($m->column, $m, 'QueueColumn');
+        }
+        return $m;
+    }
+
+    function add($column, $glue=null) {
+        $glue = $glue ?: QueueColumnGlue::create();
+        $glue->column = $column;
+        parent::add(AnnotatedModel::wrap($column, $glue));
+    }
+}
+
 abstract class QueueColumnFilter {
     static $registry;
 
@@ -987,41 +1062,33 @@ QueueColumnFilter::register('TicketLinkWithPreviewFilter');
 
 class QueueColDataConfigForm
 extends AbstractForm {
-function buildFields() {
-    return array(
-        'primary' => new DataSourceField(array(
-            'label' => __('Primary Data Source'),
-            'required' => true,
-            'configuration' => array(
-                'root' => 'Ticket',
-            ),
-            'layout' => new GridFluidCell(6),
-        )),
-        'secondary' => new DataSourceField(array(
-            'label' => __('Secondary Data Source'),
-            'configuration' => array(
-                'root' => 'Ticket',
-            ),
-            'layout' => new GridFluidCell(6),
-        )),
-            'heading' => new TextboxField(array(
-                'label' => __('Heading'),
+    function buildFields() {
+        return array(
+            'primary' => new DataSourceField(array(
+                'label' => __('Primary Data Source'),
                 'required' => true,
-                'layout' => new GridFluidCell(3),
+                'configuration' => array(
+                    'root' => 'Ticket',
+                ),
+                'layout' => new GridFluidCell(6),
+            )),
+            'secondary' => new DataSourceField(array(
+                'label' => __('Secondary Data Source'),
+                'configuration' => array(
+                    'root' => 'Ticket',
+                ),
+                'layout' => new GridFluidCell(6),
+            )),
+            'name' => new TextboxField(array(
+                'label' => __('Name'),
+                'required' => true,
+                'layout' => new GridFluidCell(4),
             )),
             'filter' => new ChoiceField(array(
                 'label' => __('Filter'),
                 'required' => false,
                 'choices' => QueueColumnFilter::getFilters(),
-                'layout' => new GridFluidCell(3),
-            )),
-            'width' => new TextboxField(array(
-                'label' => __('Width'),
-                'default' => 75,
-                'configuration' => array(
-                    'validator' => 'number',
-                ),
-                'layout' => new GridFluidCell(3),
+                'layout' => new GridFluidCell(4),
             )),
             'truncate' => new ChoiceField(array(
                 'label' => __('Text Overflow'),
@@ -1031,7 +1098,7 @@ function buildFields() {
                     'clip' => __("Clip Text"),
                 ),
                 'default' => 'wrap',
-                'layout' => new GridFluidCell(3),
+                'layout' => new GridFluidCell(4),
             )),
         );
     }
diff --git a/include/i18n/en_US/queue.yaml b/include/i18n/en_US/queue.yaml
index ce7b6a344d2eb6933bd3fe00e3b4391d564e1511..19c096d2031a9973f85d08b6f847d6ac1944f289 100644
--- a/include/i18n/en_US/queue.yaml
+++ b/include/i18n/en_US/queue.yaml
@@ -26,37 +26,6 @@
 #   'T':    Tickets
 #   'A':    Tasks
 #
-# Columns are not necessary and a default list is used if no columns are
-# specified.
-#
-# columns:  Array of column instances with these fields
-#   flags:      (unused)
-#   sort:       Manual sort order of the queue
-#   heading:    Display name of the column header
-#   primary:    Data source for the field
-#   secondary:  Backup data source / default text
-#   width:      Width weight of the column
-#   filter:     What the field should link to
-#     'link:ticket':    Ticket
-#     'link:user':      User
-#     'link:org':       Organization
-#     'link:ticketP':   Ticket with hover preview
-#   truncate:
-#     'wrap':   Fold words on multiple lines
-#   annotations:
-#     c:        Annotation class name
-#     p:        Placement
-#       'a':    After column text
-#       'b':    Before column text
-#       '<':    Float to start (left)
-#       '>':    Float to end (right)
-#   conditions:
-#     crit:     Criteria for the condiditon, in the form of [field, method, value]
-#     prop:     Array of CSS properties to apply to the field
-#       'font-weight':
-#       'font-style':
-#       ...
-#   extra:      (future use and for plugins)
 ---
 - id: 1
   title: Open
@@ -64,6 +33,31 @@
   sort: 1
   root: T 
   config: '[["status__state","includes",{"open":"Open"}]]'
+  columns:
+    - column_id: 1
+      sort: 1
+      width: 75
+      heading: Ticket
+    - column_id: 10
+      sort: 2
+      width: 150
+      heading: Last Updated
+    - column_id: 3
+      sort: 3
+      width: 300
+      heading: Subject
+    - column_id: 4
+      sort: 4
+      width: 185
+      heading: From
+    - column_id: 5
+      sort: 5
+      width: 85
+      heading: Priority
+    - column_id: 8
+      sort: 6
+      width: 160
+      heading: Assigned To
 
 - id: 2
   title: Closed
@@ -71,6 +65,31 @@
   sort: 2
   root: T
   config: '[["status__state","includes",{"closed":"Closed"}]]'
+  columns:
+    - column_id: 1
+      sort: 1
+      width: 100
+      heading: Ticket
+    - column_id: 7
+      sort: 2
+      width: 150
+      heading: Date Closed
+    - column_id: 3
+      sort: 3
+      width: 300
+      heading: Subject
+    - column_id: 4
+      sort: 4
+      width: 185
+      heading: From
+    - column_id: 5
+      sort: 5
+      width: 85
+      heading: Priority
+    - column_id: 8
+      sort: 6
+      width: 160
+      heading: Closed By
 
 - title: Unanswered
   parent_id: 1
@@ -78,13 +97,63 @@
   root: T
   sort: 1
   config: '[["status__state","includes",{"open":"Open"}],["answered","nset",null]]'
+  columns:
+    - column_id: 1
+      sort: 1
+      width: 100
+      heading: Ticket
+    - column_id: 10
+      sort: 2
+      width: 150
+      heading: Last Update
+    - column_id: 3
+      sort: 3
+      width: 300
+      heading: Subject
+    - column_id: 4
+      sort: 4
+      width: 185
+      heading: From
+    - column_id: 5
+      sort: 5
+      width: 85
+      heading: Priority
+    - column_id: 8
+      sort: 6
+      width: 160
+      heading: Assigned To
 
 - title: Unassigned
   parent_id: 1
   flags: 0x0b
   root: T
   sort: 2
-  config: '[["assignee","unassigned",null]]
+  config: '[["assignee","unassigned",null]]'
+  columns:
+    - column_id: 1
+      sort: 1
+      width: 100
+      heading: Ticket
+    - column_id: 10
+      sort: 2
+      width: 150
+      heading: Last Update
+    - column_id: 3
+      sort: 3
+      width: 300
+      heading: Subject
+    - column_id: 4
+      sort: 4
+      width: 185
+      heading: From
+    - column_id: 5
+      sort: 5
+      width: 85
+      heading: Priority
+    - column_id: 11
+      sort: 6
+      width: 160
+      heading: Department
 
 - title: My Tickets
   parent_id: 1
@@ -92,3 +161,28 @@
   root: T
   sort: 4
   config: '[["assignee","includes",{"M":"Me", "T":"One of my teams"}]]'
+  columns:
+    - column_id: 1
+      sort: 1
+      width: 100
+      heading: Ticket
+    - column_id: 10
+      sort: 2
+      width: 150
+      heading: Last Update
+    - column_id: 3
+      sort: 3
+      width: 300
+      heading: Subject
+    - column_id: 4
+      sort: 4
+      width: 185
+      heading: From
+    - column_id: 5
+      sort: 5
+      width: 85
+      heading: Priority
+    - column_id: 11
+      sort: 6
+      width: 160
+      heading: Department
diff --git a/include/staff/queue.inc.php b/include/staff/queue.inc.php
index d873ccba35cc5b46f60ea8ac2713b9d1a130ea38..36eff7912446675921e48369a8f968e945fffc50 100644
--- a/include/staff/queue.inc.php
+++ b/include/staff/queue.inc.php
@@ -115,123 +115,68 @@ else {
   </div>
 
   <div class="hidden tab_content" id="columns">
-    <h2><?php echo __("Manage columns in this queue"); ?></h2>
-    <p><?php echo __("Add, remove, and customize the content of the columns in this queue using the options below. Click a column header to manage or resize it"); ?></p>
-
-    <div>
-      <i class="icon-plus-sign"></i>
-      <select id="add-column" data-next-id="0" onchange="javascript:
-        var $this = $(this),
-            selected = $this.find(':selected'),
-            nextId = $this.data('nextId'),
-            columns = $('#resizable-columns');
-        $.ajax({
-          url: 'ajax.php/queue/addColumn',
-          data: { field: selected.val(), id: nextId },
-          dataType: 'json',
-          success: function(json) {
-            var div = $('<div></div>')
-                .addClass('column-header ui-resizable')
-                .text(json.heading)
-                .attr({'data-id': nextId})
-                .data({colId: 'colconfig-'+nextId, width: json.width})
-                .append($('<i>')
-                  .addClass('icon-ellipsis-vertical ui-resizable-handle ui-resizable-handle-e')
-                )
-                .append($('<input />')
-                  .attr({type:'hidden', name:'columns[]'})
-                  .val(nextId)
-                );
-              config = $('<div></div>')
-                .addClass('hidden column-configuration')
-                .attr('id', 'colconfig-' + nextId);
-            config.append($(json.config)).insertAfter(columns.append(div));
-            $this.data('nextId', nextId+1);
-          }
-        });
-      ">
-        <option value="">— <?php echo __('Add a column'); ?> —</option>
-<?php foreach (SavedSearch::getSearchableFields('Ticket') as $path=>$f) {
-        list($label,) = $f; ?>
-        <option value="<?php echo $path; ?>"><?php echo $label; ?></option>
+    <table class="table two-column">
+      <tbody>
+        <tr class="header">
+          <th colspan="2">
+            <?php echo __("Manage columns in this queue"); ?>
+            <div><small><?php echo __(
+            "Add, remove, and customize the content of the columns in this queue using the options below. Click a column header to manage or resize it"); ?>
+            </small></div>
+          </th>
+        </tr>
+        <tr class="header">
+          <td><small><b><?php echo __('Column Name'); ?></b></small></td>
+          <td><small><b><?php echo __('Heading and Width'); ?></b></small></td>
+        </tr>
+      </tbody>
+      <tbody class="sortable-rows">
+        <tr id="column-template" class="hidden">
+          <td>
+            <i class="faded-more icon-sort"></i>
+            <input type="hidden" data-name="queue_id"
+              value="<?php echo $queue->getId(); ?>"/>
+            <input type="hidden" data-name="column_id" />
+            <span></span>
+          </td>
+          <td>
+            <input type="text" size="25" data-name="heading"
+              data-translate-tag="" />
+            <input type="text" size="5" data-name="width" />
+            <a class="action-button"
+                href="#" onclick="javascript:
+                var colid = $(this).closest('tr').find('[data-name=column_id]').val();
+                $.dialog('ajax.php/tickets/search/column/edit/' + colid, 201);
+                return false;
+                "><i class="icon-edit"></i> <?php echo __('Edit'); ?></a>
+            <a href="#" class="pull-right drop-column" title="<?php echo __('Delete');
+              ?>"><i class="icon-trash"></i></a>
+          </td>
+        </tr>
+      </tbody>
+      <tbody>
+        <tr class="header">
+          <td colspan="2"></td>
+        </tr>
+        <tr>
+          <td colspan="2" id="append-column">
+            <i class="icon-plus-sign"></i>
+            <select id="add-column" data-quick-add="queue-column">
+              <option value="">— <?php echo __('Add a column'); ?> —</option>
+<?php foreach (QueueColumn::objects() as $C) { ?>
+              <option value="<?php echo $C->id; ?>"><?php echo
+                  Format::htmlchars($C->name); ?></option>
 <?php } ?>
-      </select>
-
-      <div id="resizable-columns">
-<?php foreach ($queue->getColumns() as $column) {
-        $colid = $column->getId();
-        $maxcolid = max(@$maxcolid ?: 0, $colid);
-        echo sprintf('<div data-id="%1$s" data-col-id="colconfig-%1$s" class="column-header" '
-          .'data-width="%2$s">%3$s'
-          .'<i class="icon-ellipsis-vertical ui-resizable-handle ui-resizable-handle-e"></i>'
-          .'<input type="hidden" name="columns[]" value="%1$s"/>'
-          .'</div>',
-          $colid, $column->getWidth(), $column->getHeading(),
-          $column->sort ?: 1);
-} ?>
-      </div>
-      <script>
-        $(function() {
-          $('#add-column').data('nextId', <?php echo $maxcolid+1; ?>);
-          var qq = setInterval(function() {
-            var total = 0,
-                container = $('#resizable-columns'),
-                width = container.width(),
-                w2px = 1.25,
-                columns = $('.column-header', container);
-            // Await computation of the <div>'s width
-            if (width)
-              clearInterval(qq);
-            columns.each(function() {
-              total += $(this).data('width') || 100;
-            });
-            container.data('w2px', w2px);
-            columns.each(function() {
-              // FIXME: jQuery will compensate for padding (40px)
-              $(this).width(w2px * ($(this).data('width') || 100) - 42);
-            });
-          }, 20);
-        });
-      </script>
-
-<?php foreach ($queue->getColumns() as $column) {
-        $colid = $column->getId();
-        echo sprintf('<div class="hidden column-configuration" id="colconfig-%s">',
-            $colid);
-        include STAFFINC_DIR . 'templates/queue-column.tmpl.php';
-        echo '</div>';
-} ?>
-    </div>
+              <option value="0" data-quick-add>&mdash; <?php echo __('Add New');?> &mdash;</option>
+            </select>
+            <button type="button" class="green button">
+              <?php echo __('Add'); ?>
+            </button>
+          </td>
+        </tr>
+      </tbody>
+    </table>
 
-    <script>
-      var aa = setInterval(function() {
-        var cols = $('#resizable-columns');
-        if (cols.length && cols.sortable)
-          clearInterval(aa);
-        cols.sortable({
-          containment: 'parent'
-        });
-        $('.column-header', cols).resizable({
-          handles: {'e' : '.ui-resizable-handle'},
-          grid: [ 20, 0 ],
-          maxHeight: 16,
-          minHeight: 16,
-          stop: function(event, ui) {
-            var w2px = ui.element.parent().data('w2px'),
-                width = ui.element.width() - 42;
-            ui.element.data('width', width / w2px);
-            // TODO: Update WIDTH text box in the data form
-          }
-        });
-        cols.click('.column-header', function(e) {
-          var $this = $(event.target);
-          $this.parent().children().removeClass('active');
-          $this.addClass('active');
-          $('.column-configuration', $this.closest('.tab_content')).hide();
-          $('#'+$this.data('colId')).fadeIn('fast');
-        });
-      }, 20);
-    </script>
   </div>
 
   <div class="hidden tab_content" id="preview-tab">
@@ -263,3 +208,58 @@ else {
   </p>
 
 </form>
+
+<script>
+var addColumn = function(colid, info) {
+  if (!colid) return;
+  var copy = $('#column-template').clone(),
+      name_prefix = 'columns[' + colid + ']';
+  info['column_id'] = colid;
+  copy.find('input[data-name]').each(function() {
+    var $this = $(this),
+        name = $this.data('name');
+
+    if (info[name] !== undefined)
+      $this.val(info[name]);
+    $this.attr('name', name_prefix + '[' + name + ']');
+  });
+  copy.find('span').text(info['name']);
+  copy.attr('id', '').show().insertBefore($('#column-template'));
+  copy.removeClass('hidden');
+  if (info['trans'] !== undefined) {
+    var input = copy.find('input[data-translate-tag]')
+      .attr('data-translate-tag', info['trans']);
+    if ($.fn.translatable)
+      input.translatable();
+    // Else it will be made translatable when the JS library is loaded
+  }
+  copy.find('a.drop-column').click(function() {
+    $('<option>')
+      .attr('value', copy.find('input[data-name=column_id]').val())
+      .text(info.name)
+      .insertBefore($('#add-column')
+        .find('[data-quick-add]')
+      );
+    copy.fadeOut(function() { $(this).remove(); });
+    return false;
+  });
+  var selected = $('#add-column').find('option[value=' + colid + ']');
+  selected.remove();
+};
+
+$('#append-column').find('button').on('click', function() {
+  var selected = $('#add-column').find(':selected'),
+      id = parseInt(selected.val());
+  if (!id)
+      return;
+  addColumn(id, {name: selected.text(), heading: selected.text(), width: 100});
+  return false;
+});
+
+<?php foreach ($queue->columns as $C) {
+  echo sprintf('addColumn(%d, {name: %s, heading: %s, width: %d, trans: %s});',
+    $C->column_id, JsonDataEncoder::encode($C->name),
+    JsonDataEncoder::encode($C->heading), $C->width,
+    JsonDataEncoder::encode($C->getTranslateTag('heading')));
+} ?>
+</script>
diff --git a/include/staff/templates/queue-column-add.tmpl.php b/include/staff/templates/queue-column-add.tmpl.php
new file mode 100644
index 0000000000000000000000000000000000000000..f10e303d13bdbf64d1515c04ecbae71bd3f51b16
--- /dev/null
+++ b/include/staff/templates/queue-column-add.tmpl.php
@@ -0,0 +1,30 @@
+<?php
+/**
+ * Calling conventions
+ *
+ * $column - <QueueColumn> instance for this column
+ */
+$colid = 0;
+?>
+<h3 class="drag-handle"><?php echo __('Add Queue Column'); ?></h3>
+<a class="close" href=""><i class="icon-remove-circle"></i></a>
+<hr/>
+
+<form method="post" action="#admin/quick-add/queue-column">
+
+<?php
+include 'queue-column.tmpl.php';
+?>
+
+<hr>
+<p class="full-width">
+    <span class="buttons pull-left">
+        <input type="reset" value="<?php echo __('Reset'); ?>">
+        <input type="button" value="<?php echo __('Cancel'); ?>" class="close">
+    </span>
+    <span class="buttons pull-right">
+        <input type="submit" value="<?php echo __('Save'); ?>">
+    </span>
+ </p>
+
+ </form>
diff --git a/include/staff/templates/queue-column-edit.tmpl.php b/include/staff/templates/queue-column-edit.tmpl.php
new file mode 100644
index 0000000000000000000000000000000000000000..c186335852e51ada91bbcb8357f08924c9f458f4
--- /dev/null
+++ b/include/staff/templates/queue-column-edit.tmpl.php
@@ -0,0 +1,32 @@
+<?php
+/**
+ * Calling conventions
+ *
+ * $column - <QueueColumn> instance for this column
+ */
+$colid = $column->getId();
+?>
+<h3 class="drag-handle"><?php echo __('Manage Queue Column'); ?> &mdash;
+    <?php echo $column->get('name') ?></h3>
+<a class="close" href=""><i class="icon-remove-circle"></i></a>
+<hr/>
+
+<form method="post" action="#tickets/search/column/edit/<?php
+    echo $colid; ?>">
+
+<?php
+include 'queue-column.tmpl.php';
+?>
+
+<hr>
+<p class="full-width">
+    <span class="buttons pull-left">
+        <input type="reset" value="<?php echo __('Reset'); ?>">
+        <input type="button" value="<?php echo __('Cancel'); ?>" class="close">
+    </span>
+    <span class="buttons pull-right">
+        <input type="submit" value="<?php echo __('Save'); ?>">
+    </span>
+ </p>
+
+ </form>
diff --git a/include/staff/templates/queue-column.tmpl.php b/include/staff/templates/queue-column.tmpl.php
index 9890c73c6b25d00390aefac6d968970df2450d91..65867c95ff82a4e0eb2d1668bf30aa30bf4ae62c 100644
--- a/include/staff/templates/queue-column.tmpl.php
+++ b/include/staff/templates/queue-column.tmpl.php
@@ -3,29 +3,26 @@
  * Calling conventions
  *
  * $column - <QueueColumn> instance for this column
+ * $root - <Class> name of queue root ('Ticket')
+ * $data_form - <QueueColDataConfigForm> instance, optional
  */
 $colid = $column->getId();
-$data_form = $column->getDataConfigForm($_POST);
+$data_form = $data_form ?: $column->getDataConfigForm($_POST);
 ?>
-<ul class="alt tabs">
-  <li class="active"><a href="#<?php echo $colid; ?>-data"><?php echo __('Data'); ?></a></li>
-  <li><a href="#<?php echo $colid; ?>-annotations"><?php echo __('Annotations'); ?></a></li>
-  <li><a href="#<?php echo $colid; ?>-conditions"><?php echo __('Conditions'); ?></a></li>
-  <a onclick="javascript:
-  $(this).closest('.column-configuration').hide();
-  $('#resizable-columns').find('div[data-id=<?php echo $colid; ?>]').hide()
-    .find('input[name^=columns]').remove();
-  " class="button red pull-right"><?php echo __("Delete Column"); ?></a>
+<ul class="tabs">
+  <li class="active"><a href="#data"><?php echo __('Data'); ?></a></li>
+  <li><a href="#annotations"><?php echo __('Annotations'); ?></a></li>
+  <li><a href="#conditions"><?php echo __('Conditions'); ?></a></li>
 </ul>
 
-<div class="tab_content" id="<?php echo $colid; ?>-data">
+<div class="tab_content" id="data">
 <?php
   print $data_form->asTable();
 ?>
 </div>
 
 <div class="hidden tab_content" data-col-id="<?php echo $colid; ?>"
-  id="<?php echo $colid; ?>-annotations" style="max-width: 400px">
+  id="annotations" style="max-width: 400px">
   <div class="empty placeholder" style="margin-left: 20px">
     <em><?php echo __('No annotations for this field'); ?></em>
   </div>
@@ -69,7 +66,7 @@ $data_form = $column->getDataConfigForm($_POST);
     <script>
       $(function() {
         var addAnnotation = function(type, desc, icon, pos) {
-          var template = $('.annotation.template', '#<?php echo $colid; ?>-annotations'),
+          var template = $('.annotation.template', '#annotations'),
               clone = template.clone().show().removeClass('template').insertBefore(template),
               input = clone.find('[data-field=input]'),
               colid = clone.closest('.tab_content').data('colId'),
@@ -86,14 +83,14 @@ $data_form = $column->getDataConfigForm($_POST);
             position.val(pos);
           template.closest('.tab_content').find('.empty').hide();
         };
-        $('select.add-annotation', '#<?php echo $colid; ?>-annotations').change(function() {
+        $('select.add-annotation', '#annotations').change(function() {
           var selected = $(this).find(':selected');
           addAnnotation(selected.val(), selected.text(), selected.data('icon'));
           selected.prop('disabled', true);
         });
-        $('#<?php echo $colid; ?>-annotations').click('a[data-field=delete]',
+        $('#annotations').click('a[data-field=delete]',
         function() {
-          var tab = $('#<?php echo $colid; ?>-annotations');
+          var tab = $('#annotations');
           if ($('.annotation', tab).length === 0)
             tab.find('.empty').show();
         });
@@ -110,13 +107,13 @@ $data_form = $column->getDataConfigForm($_POST);
   </div>
 </div>
 
-<div class="hidden tab_content" id="<?php echo $colid; ?>-conditions">
+<div class="hidden tab_content" id="conditions">
   <div style="margin: 0 20px"><?php echo __("Conditions are used to change the view of the data in a row based on some conditions of the data. For instance, a column might be shown bold if some condition is met.");
   ?></div>
   <div class="conditions" style="margin: 20px; max-width: 400px">
 <?php
 if ($column->getConditions()) {
-  $fields = SavedSearch::getSearchableFields($column->getQueue()->getRoot());
+  $fields = SavedSearch::getSearchableFields($root);
   foreach ($column->getConditions() as $i=>$condition) {
      $id = QueueColumnCondition::getUid();
      list($label, $field) = $condition->getField();
diff --git a/include/staff/templates/queue-tickets.tmpl.php b/include/staff/templates/queue-tickets.tmpl.php
index 0d999886a6b307e9923047107ceb59252aafb417..49a8125755f97eb5c8c9b461c30249c9b1926fa2 100644
--- a/include/staff/templates/queue-tickets.tmpl.php
+++ b/include/staff/templates/queue-tickets.tmpl.php
@@ -122,7 +122,7 @@ if ($canManageTickets) { ?>
 }
 foreach ($columns as $C) {
     echo sprintf('<th width="%s">%s</th>', $C->getWidth(),
-        Format::htmlchars($C->getHeading()));
+        Format::htmlchars($C->getLocalHeading()));
 } ?>
     </tr>
   </thead>
@@ -145,8 +145,8 @@ foreach ($tickets as $T) {
   </tbody>
   <tfoot>
     <tr>
-      <td colspan="7">
-        <?php if ($total && $canManageTickets) {
+      <td colspan="<?php echo count($columns)+1; ?>">
+        <?php if ($count && $canManageTickets) {
         echo __('Select');?>:&nbsp;
         <a id="selectAll" href="#ckb"><?php echo __('All');?></a>&nbsp;&nbsp;
         <a id="selectNone" href="#ckb"><?php echo __('None');?></a>&nbsp;&nbsp;
diff --git a/include/upgrader/streams/core/98ad7d55-00000000.patch.sql b/include/upgrader/streams/core/98ad7d55-00000000.patch.sql
index b253c9efd9cafd9b498e8b217a02e7f1e19f2244..4993af2fdca44fe614f4efb5eccdaf7e135852f3 100644
--- a/include/upgrader/streams/core/98ad7d55-00000000.patch.sql
+++ b/include/upgrader/streams/core/98ad7d55-00000000.patch.sql
@@ -16,13 +16,10 @@ ALTER TABLE `%TABLE_PREFIX%queue`
 DROP TABLE IF EXISTS `%TABLE_PREFIX%queue_column`;
 CREATE TABLE `%TABLE_PREFIX%queue_column` (
   `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
-  `queue_id` int(10) unsigned NOT NULL,
   `flags` int(10) unsigned NOT NULL DEFAULT '0',
-  `sort` int(10) unsigned NOT NULL DEFAULT '0',
-  `heading` varchar(64) NOT NULL DEFAULT '',
+  `name` varchar(64) NOT NULL DEFAULT '',
   `primary` varchar(64) NOT NULL DEFAULT '',
   `secondary` varchar(64) DEFAULT NULL,
-  `width` int(10) unsigned DEFAULT NULL,
   `filter` varchar(32) DEFAULT NULL,
   `truncate` varchar(16) DEFAULT NULL,
   `annotations` text,
@@ -31,3 +28,12 @@ CREATE TABLE `%TABLE_PREFIX%queue_column` (
   PRIMARY KEY (`id`)
 ) DEFAULT CHARSET=utf8;
 
+DROP TABLE IF EXISTS `%TABLE_PREFIX%queue_columns`;
+CREATE TABLE `%TABLE_PREFIX%queue_columns` (
+  `queue_id` int(11) unsigned NOT NULL,
+  `column_id` int(11) unsigned NOT NULL,
+  `sort` int(10) unsigned NOT NULL DEFAULT '1',
+  `heading` varchar(64) DEFAULT NULL,
+  `width` int(10) unsigned NOT NULL DEFAULT '100',
+  PRIMARY KEY (`queue_id`, `column_id`)
+) DEFAULT CHARSET=utf8;
diff --git a/scp/ajax.php b/scp/ajax.php
index 1069c3b078044a2bd3acffa8b57e626cc993cde5..610e9f5ca302892a99bd9dcfcff20e834f70f6ba 100644
--- a/scp/ajax.php
+++ b/scp/ajax.php
@@ -174,7 +174,11 @@ $dispatcher = patterns('',
             url_post('^/(?P<id>\d+)$', 'saveSearch'),
             url_delete('^/(?P<id>\d+)$', 'deleteSearch'),
             url_post('^/create$', 'createSearch'),
-            url_get('^/field/(?P<id>[\w_!:]+)$', 'addField')
+            url_get('^/field/(?P<id>[\w_!:]+)$', 'addField'),
+            url('^/column/edit/(?P<id>\d+)$', 'editColumn'),
+            url_post('^(?P<id>\d+)/delete$', 'deleteQueues'),
+            url_post('^(?P<id>\d+)/disable$', 'disableQueues'),
+            url_post('^(?P<id>\d+)/enable$', 'undisableQueues')
         ))
     )),
     url('^/tasks/', patterns('ajax.tasks.php:TasksAjaxAPI',
@@ -242,7 +246,8 @@ $dispatcher = patterns('',
             url('^/department$', 'addDepartment'),
             url('^/team$', 'addTeam'),
             url('^/role$', 'addRole'),
-            url('^/staff$', 'addStaff')
+            url('^/staff$', 'addStaff'),
+            url('^/queue-column$', 'addQueueColumn')
         )),
         url_get('^/role/(?P<id>\d+)/perms', 'getRolePerms')
     )),
diff --git a/setup/inc/streams/core/install-mysql.sql b/setup/inc/streams/core/install-mysql.sql
index 643fe925ece4fbe6e4d87139f00b0fd058f138d4..1a39a4a34d778bb8627648fe9e5c5697667aeabc 100644
--- a/setup/inc/streams/core/install-mysql.sql
+++ b/setup/inc/streams/core/install-mysql.sql
@@ -827,7 +827,7 @@ DROP TABLE IF EXISTS `%TABLE_PREFIX%queue`;
 CREATE TABLE `%TABLE_PREFIX%queue` (
   `id` int(11) unsigned not null auto_increment,
   `parent_id` int(11) unsigned not null default 0,
-  `columns_id` int(11) unsigned,
+  `columns_id` int(11) unsigned default null,
   `flags` int(11) unsigned not null default 0,
   `staff_id` int(11) unsigned not null default 0,
   `sort` int(11) unsigned not null default 0,
@@ -844,13 +844,10 @@ CREATE TABLE `%TABLE_PREFIX%queue` (
 DROP TABLE IF EXISTS `%TABLE_PREFIX%queue_column`;
 CREATE TABLE `%TABLE_PREFIX%queue_column` (
   `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
-  `queue_id` int(10) unsigned NOT NULL,
   `flags` int(10) unsigned NOT NULL DEFAULT '0',
-  `sort` int(10) unsigned NOT NULL DEFAULT '0',
-  `heading` varchar(64) NOT NULL DEFAULT '',
+  `name` varchar(64) NOT NULL DEFAULT '',
   `primary` varchar(64) NOT NULL DEFAULT '',
   `secondary` varchar(64) DEFAULT NULL,
-  `width` int(10) unsigned DEFAULT NULL,
   `filter` varchar(32) DEFAULT NULL,
   `truncate` varchar(16) DEFAULT NULL,
   `annotations` text,
@@ -859,6 +856,16 @@ CREATE TABLE `%TABLE_PREFIX%queue_column` (
   PRIMARY KEY (`id`)
 ) DEFAULT CHARSET=utf8;
 
+DROP TABLE IF EXISTS `%TABLE_PREFIX%queue_columns`;
+CREATE TABLE `%TABLE_PREFIX%queue_columns` (
+  `queue_id` int(11) unsigned NOT NULL,
+  `column_id` int(11) unsigned NOT NULL,
+  `sort` int(10) unsigned NOT NULL DEFAULT '1',
+  `heading` varchar(64) DEFAULT NULL,
+  `width` int(10) unsigned NOT NULL DEFAULT '100',
+  PRIMARY KEY (`queue_id`, `column_id`)
+) DEFAULT CHARSET=utf8;
+
 DROP TABLE IF EXISTS `%TABLE_PREFIX%translation`;
 CREATE TABLE `%TABLE_PREFIX%translation` (
   `id` int(11) unsigned NOT NULL AUTO_INCREMENT,