Binary files django_trunk/django/conf/global_settings.pyc and django_adminsortable/conf/global_settings.pyc differ
Binary files django_trunk/django/conf/__init__.pyc and django_adminsortable/conf/__init__.pyc differ
diff -x .svn -Nur django_trunk/django/contrib/admin/media/css/base.css django_adminsortable/contrib/admin/media/css/base.css
--- django_trunk/django/contrib/admin/media/css/base.css	2009-09-03 09:30:12.000000000 -0500
+++ django_adminsortable/contrib/admin/media/css/base.css	2009-09-11 23:38:57.000000000 -0500
@@ -326,6 +326,20 @@
     background: url(../img/admin/arrow-up.gif) right .4em no-repeat;
 }
 
+table thead th.sorted a span.text {
+	display: block;
+	float: left;
+}
+table thead th.sorted a span.sort_pos {
+	display: block;
+	float: right;
+	font-size: .6em;
+}
+table thead th.sorted a span.clear{
+	display: block;
+	clear: both;
+}
+
 /* ORDERABLE TABLES */
 
 table.orderable tbody tr td:hover {
diff -x .svn -Nur django_trunk/django/contrib/admin/templates/admin/change_list_results.html django_adminsortable/contrib/admin/templates/admin/change_list_results.html
--- django_trunk/django/contrib/admin/templates/admin/change_list_results.html	2009-09-03 09:30:13.000000000 -0500
+++ django_adminsortable/contrib/admin/templates/admin/change_list_results.html	2009-09-11 23:38:57.000000000 -0500
@@ -2,10 +2,14 @@
 <table cellspacing="0">
 <thead>
 <tr>
-{% for header in result_headers %}<th{{ header.class_attrib }}>
-{% if header.sortable %}<a href="{{ header.url }}">{% endif %}
-{{ header.text|capfirst }}
-{% if header.sortable %}</a>{% endif %}</th>{% endfor %}
+{% for header in result_headers %}
+<th{{ header.class_attrib }}>
+  {% if header.sortable %}<a href="{{ header.url }}">{% endif %}
+  <span class="text">{{ header.text|capfirst }}</span>
+  {% if header.sortable %}<span class="sort_pos">{{ header.sort_pos }}</span>
+  <span class="clear"></span>
+  </a>{% endif %}
+</th>{% endfor %}
 </tr>
 </thead>
 <tbody>
diff -x .svn -Nur django_trunk/django/contrib/admin/templatetags/admin_list.py django_adminsortable/contrib/admin/templatetags/admin_list.py
--- django_trunk/django/contrib/admin/templatetags/admin_list.py	2009-09-03 09:30:12.000000000 -0500
+++ django_adminsortable/contrib/admin/templatetags/admin_list.py	2009-09-12 00:17:55.000000000 -0500
@@ -120,13 +120,43 @@
 
         th_classes = []
         new_order_type = 'asc'
-        if field_name == cl.order_field or admin_order_field == cl.order_field:
-            th_classes.append('sorted %sending' % cl.order_type.lower())
-            new_order_type = {'asc': 'desc', 'desc': 'asc'}[cl.order_type.lower()]
+        ordering_fields = cl.get_ordering_fields()
+        sort_pos = ''
+        if field_name in ordering_fields.keys() or admin_order_field in ordering_fields.keys():
+            if not field_name in ordering_fields.keys():
+                field_name = admin_order_field
+            order_type = ordering_fields.get(field_name).lower()
+            sort_pos = ordering_fields.keys().index(field_name) + 1
+            th_classes.append('sorted %sending' % order_type)
+            new_order_type = {'':'asc', 'asc': 'desc', 'desc': ''}[order_type]
+        
+        # build new ordering param
+        o_list = []
+        for f in ordering_fields.keys():
+            try:
+                n = cl.list_display.index(f)
+            except ValueError:
+                continue
+
+            if f == field_name:
+                if new_order_type == '': 
+                    continue
+                t = new_order_type
+            else:
+                t = ordering_fields.get(f)
+
+            o_list.append((t=='desc' and '-' or '') + str(n))
+        
+        if field_name not in ordering_fields.keys() and new_order_type:
+            n = cl.list_display.index(field_name)
+            o_list.append((new_order_type=='desc' and '-' or '') + str(n))
+
+        o_list = ','.join(o_list)
 
         yield {"text": header,
                "sortable": True,
-               "url": cl.get_query_string({ORDER_VAR: i, ORDER_TYPE_VAR: new_order_type}),
+               "sort_pos": sort_pos > 0 and len(ordering_fields) > 0 and unicode(sort_pos) or '',
+               "url": cl.get_query_string({ORDER_VAR: o_list}),
                "class_attrib": mark_safe(th_classes and ' class="%s"' % ' '.join(th_classes) or '')}
 
 def _boolean_icon(field_val):
diff -x .svn -Nur django_trunk/django/contrib/admin/views/main.py django_adminsortable/contrib/admin/views/main.py
--- django_trunk/django/contrib/admin/views/main.py	2009-09-03 09:30:12.000000000 -0500
+++ django_adminsortable/contrib/admin/views/main.py	2009-09-11 23:23:12.000000000 -0500
@@ -4,6 +4,7 @@
 from django.core.paginator import Paginator, InvalidPage
 from django.db import models
 from django.db.models.query import QuerySet
+from django.utils.datastructures import SortedDict
 from django.utils.encoding import force_unicode, smart_str
 from django.utils.translation import ugettext
 from django.utils.http import urlencode
@@ -63,7 +64,7 @@
         if ERROR_FLAG in self.params:
             del self.params[ERROR_FLAG]
 
-        self.order_field, self.order_type = self.get_ordering()
+        self.ordering = self.get_ordering()
         self.query = request.GET.get(SEARCH_VAR, '')
         self.query_set = self.get_query_set()
         self.get_results(request)
@@ -137,36 +138,47 @@
         # those exist, order descending by ID by default. Finally, look for
         # manually-specified ordering from the query string.
         ordering = self.model_admin.ordering or lookup_opts.ordering or ['-' + lookup_opts.pk.name]
-
-        if ordering[0].startswith('-'):
-            order_field, order_type = ordering[0][1:], 'desc'
-        else:
-            order_field, order_type = ordering[0], 'asc'
+        
         if ORDER_VAR in params:
-            try:
-                field_name = self.list_display[int(params[ORDER_VAR])]
+            # Clear ordering and used params
+            ordering = []
+            order_params = params[ORDER_VAR].split(',')
+            for p in order_params:
                 try:
-                    f = lookup_opts.get_field(field_name)
-                except models.FieldDoesNotExist:
-                    # See whether field_name is a name of a non-field
-                    # that allows sorting.
+                    none, pfx, idx = p.rpartition('-')
+                    field_name = self.list_display[int(idx)]
                     try:
-                        if callable(field_name):
-                            attr = field_name
-                        elif hasattr(self.model_admin, field_name):
-                            attr = getattr(self.model_admin, field_name)
-                        else:
-                            attr = getattr(self.model, field_name)
-                        order_field = attr.admin_order_field
-                    except AttributeError:
-                        pass
-                else:
-                    order_field = f.name
-            except (IndexError, ValueError):
-                pass # Invalid ordering specified. Just use the default.
-        if ORDER_TYPE_VAR in params and params[ORDER_TYPE_VAR] in ('asc', 'desc'):
-            order_type = params[ORDER_TYPE_VAR]
-        return order_field, order_type
+                        f = lookup_opts.get_field(field_name)
+                    except models.FieldDoesNotExist:
+                        # See whether field_name is a name of a non-field
+                        # that allows sorting.
+                        try:
+                            if callable(field_name):
+                                attr = field_name
+                            elif hasattr(self.model_admin, field_name):
+                                attr = getattr(self.model_admin, field_name)
+                            else:
+                                attr = getattr(self.model, field_name)
+                            field_name = attr.admin_order_field
+                        except AttributeError:
+                            pass
+                    else:
+                        field_name = f.name
+
+                    ordering.append(pfx + field_name)
+
+                except (IndexError, ValueError):
+                    pass # Invalid ordering specified. Skip it.
+
+        return ordering
+    
+    def get_ordering_fields(self):
+        # Returns a SortedDict of ordering fields and asc/desc
+        ordering_fields = SortedDict()
+        for o in self.ordering:
+            none, t, f = o.rpartition('-')
+            ordering_fields[f] = t == '-' and 'desc' or 'asc'
+        return ordering_fields
 
     def get_query_set(self):
         qs = self.root_query_set
@@ -214,8 +226,8 @@
                             break
 
         # Set ordering.
-        if self.order_field:
-            qs = qs.order_by('%s%s' % ((self.order_type == 'desc' and '-' or ''), self.order_field))
+        if self.ordering:
+            qs = qs.order_by(*self.ordering)
 
         # Apply keyword searches.
         def construct_search(field_name):
