Index: trac/ticket/query.py
===================================================================
--- trac/ticket/query.py	(revision 6737)
+++ trac/ticket/query.py	(working copy)
@@ -41,7 +41,7 @@
                             INavigationContributor, Chrome
 from trac.wiki.api import IWikiSyntaxProvider, parse_args
 from trac.wiki.macros import WikiMacroBase # TODO: should be moved in .api
-from trac.config import Option 
+from trac.config import Option
 
 class QuerySyntaxError(Exception):
     """Exception raised when a ticket query cannot be parsed from a string."""
@@ -94,7 +94,7 @@
         for filter_ in filters:
             filter_ = filter_.split('=')
             if len(filter_) != 2:
-                raise QuerySyntaxError('Query filter requires field and ' 
+                raise QuerySyntaxError('Query filter requires field and '
                                        'constraints separated by a "="')
             field,values = filter_
             if not field:
@@ -254,6 +254,7 @@
         # shorter in the common case where we just want the default columns.
         if cols == self.get_default_columns():
             cols = None
+                                for idx, col in enumerate(cols)))
         return href.query(report=id,
                           order=order, desc=desc and 1 or None,
                           group=self.group or None,
@@ -637,6 +638,17 @@
                       rows,
                       req.args.get('limit'))
 
+        if cols:
+            for idx, col in enumerate(cols):
+                if 'up_' + col in req.args:
+                    query.cols[idx] = cols[idx-1]
+                    query.cols[idx-1] = col
+                    req.redirect(query.get_href(req.href))
+                elif 'down_' + col in req.args:
+                    query.cols[idx] = cols[idx+1]
+                    query.cols[idx+1] = col
+                    req.redirect(query.get_href(req.href))
+
         if 'update' in req.args:
             # Reset session vars
             for var in ('query_constraints', 'query_time', 'query_tickets'):
@@ -779,9 +791,13 @@
         data.setdefault('description', None)
         data['title'] = title
 
-        data['all_columns'] = query.get_all_columns()
+        all_columns = query.get_all_columns()
+        all_columns.sort(key=lambda x: (not x in query.cols,
+                                        x in query.cols and 
+                                        query_cols.index(x)))
         # Don't allow the user to remove the id column        
-        data['all_columns'].remove('id')
+        all_columns.remove('id')
+        data['all_columns'] = all_columns
         data['all_textareas'] = query.get_all_textareas()
 
         add_stylesheet(req, 'common/css/report.css')
Index: trac/ticket/templates/query.html
===================================================================
--- trac/ticket/templates/query.html	(revision 6737)
+++ trac/ticket/templates/query.html	(working copy)
@@ -136,14 +136,24 @@
         <fieldset id="columns">
           <legend>Columns</legend>
           <div>
-            <py:for each="column in all_columns">
-              <label>
-                <input type="checkbox" name="col" value="$column"
-                       checked="${any([(value == column) for value in col])
-                                  and 'checked' or None}" />
-                ${labels.get(column, column or 'none')}
-              </label>
-            </py:for>
+            <table>
+              <tbody>
+                <tr py:for="idx, column in enumerate(all_columns)">
+                  <td>
+                    <input type="checkbox" name="col" value="$column"
+                           checked="${any([(value == column) for value in col])
+                                      and 'selected' or None}" />
+                  </td>
+                  <td>${labels.get(column, column or 'none')}</td>
+                  <td>
+                    <input type="submit" name="up_${column}" value="Up"
+                           disabled="${not idx or None}" />&nbsp;
+                    <input type="submit" name="down_${column}" value="Down"
+                           disabled="${idx == len(all_columns)-1 or None}" />
+                  </td>
+                </tr>
+              </tbody>
+            </table>
           </div>
         </fieldset>
 
Index: trac/htdocs/js/query.js
===================================================================
--- trac/htdocs/js/query.js	(revision 6737)
+++ trac/htdocs/js/query.js	(working copy)
@@ -273,6 +273,38 @@
       }
       select.selectedIndex = 0;
     }
+
+    $("input[@name^='up_']").click(function() {
+      var row = $(this).parents('tr');
+      if (row.prev().length) {
+        var prev = row.prev();
+        if (!row.next().length) {
+          prev.find("input[@name^='down_']").attr("disabled", true);
+        }
+        prev.find("input[@name^='up_']").attr("disabled", false);
+        row.insertBefore(row.prev());
+        if (!row.prev().length) {
+          $(this).attr("disabled", true);
+        }
+        row.find("input[@name^='down_']").attr("disabled", false);
+      }
+      return false;
+    });
+    $("input[@name^='down_']").click(function() {
+      var row = $(this).parents('tr');
+      if (row.next().length) {
+        var next = row.next();
+        if (!row.prev().length) {
+          next.find("input[@name^='up_']").attr("disabled", true);
+        }
+        next.find("input[@name^='down_']").attr("disabled", false);
+        row.insertAfter(row.next());
+        if (!row.next().length) {
+          $(this).attr("disabled", true);
+        }
+        row.find("input[@name^='up_']").attr("disabled", false);
+      }
+      return false;
+    });
   }
-
-})(jQuery);
\ No newline at end of file
+})(jQuery);
Index: trac/htdocs/css/report.css
===================================================================
--- trac/htdocs/css/report.css	(revision 6737)
+++ trac/htdocs/css/report.css	(working copy)
@@ -41,6 +41,13 @@
 #filters td.filter label { padding-right: 1em }
 #filters td.actions { text-align: right; white-space: nowrap }
 
+#columns div {
+ height: 15em;
+ overflow: -moz-scrollbars-vertical;
+ overflow-x: hidden;
+ overflow-y: scroll;
+}
+
 #columns div label { 
  display: block;
  float: left;
