 doc/commands.doc      |  23 ++++++
 src/commentscan.l     |  33 +++++++-
 src/doxygen.cpp       |  19 +++++
 src/entry.cpp         |   3 +-
 src/entry.h           |   3 +
 src/groupdef.h        |   9 ++-
 src/index.cpp         | 198 ++++++++++++++++++++++++++++++++----------------
 src/libdoxygen.pro.in |   6 +-
 src/marshal.cpp       |   2 +
 src/membername.cpp    |  13 ++--
 src/sortkey.cpp       | 206 ++++++++++++++++++++++++++++++++++++++++++++++++++
 src/sortkey.h         |  33 ++++++++
 12 files changed, 471 insertions(+), 77 deletions(-)

diff --git a/doc/commands.doc b/doc/commands.doc
index b53c9d7..36bb85c 100644
--- a/doc/commands.doc
+++ b/doc/commands.doc
@@ -152,6 +152,7 @@ documentation:
 \refitem cmdpure \\pure
 \refitem cmdref \\ref
 \refitem cmdrefitem \\refitem
+\refitem cmdsortkey \\sk
 \refitem cmdrelated \\related
 \refitem cmdrelates \\relates
 \refitem cmdrelatedalso \\relatedalso
@@ -2202,6 +2203,28 @@ Commands for displaying examples
   Files or directories that doxygen should look for can be specified using the
   \ref cfg_example_path "EXAMPLE_PATH" tag of doxygen's configuration file.
 
+
+<hr>
+\section cmdsortkey \\sk <HowToReadTheEntry>
+
+  \addindex \\sk
+  This command changes where to place the entry in indices.
+  The entry can be anything the documentationable. Functions, classes, macros,
+  files, varuables or etc. 
+  
+  This command is similar to the sort-key feature in Wikipedia/MediaWiki.
+  
+  The configures \ref cfg_alphabetical_index "ALPHABETICAL_INDEX" or SORT_* commands
+  you want should be YES, otherwise \a \\sk commands makes no effect.
+  
+  For example, consider the class named "OriginalNameClass" exists,
+  but \"\a \\sk renamedClass\" command is also given. This command places the entry 
+  into header group "R" in the indices instead of "O".
+  
+  If you want just snip prefix, like "QXxxx" to "Xxxx", consider to configure
+  \ref cfg_ignore_prefix "IGNORE_PREFIX".
+
+
 <hr>
 
 \htmlonly <center> \endhtmlonly
diff --git a/src/commentscan.l b/src/commentscan.l
index 22d6444..17db373 100644
--- a/src/commentscan.l
+++ b/src/commentscan.l
@@ -47,6 +47,7 @@
 #include "markdown.h"
 #include "condparser.h"
 #include "formula.h"
+#include "sortkey.h"
 
 #define YY_NO_INPUT 1
 
@@ -120,6 +121,7 @@ static bool handleCopyBrief(const QCString &);
 static bool handleCopyDetails(const QCString &);
 static bool handleParBlock(const QCString &);
 static bool handleEndParBlock(const QCString &);
+static bool handleSortKey(const QCString& s);
 
 typedef bool (*DocCmdFunc)(const QCString &name);
 
@@ -251,6 +253,7 @@ static DocCmdMap docCmdMap[] =
   { "sa",              0,                       TRUE  },
   { "see",             0,                       TRUE  },
   { "since",           0,                       TRUE  },
+  { "sk",              &handleSortKey,          FALSE },
   { "throw",           0,                       TRUE  },
   { "throws",          0,                       TRUE  },
   { "until",           0,                       TRUE  },
@@ -854,7 +857,7 @@ DCMD5     ("latexonly"|"li"|"line"|"manonly"|"name")
 DCMD6     ("note"|"par"|"paragraph"|"param"|"post")
 DCMD7     ("pre"|"remarks"|(("relate"[sd])("also")?))
 DCMD8     ("remarks"|("return"[s]?)|"retval"|"sa"|"section")
-DCMD9     ("see"|"since"|"subsection"|"subsubsection")
+DCMD9     ("see"|"since"|"sk"|"subsection"|"subsubsection")
 DCMD10    ("throw"|"until"|"verbatim")
 DCMD11    ("verbinclude"|"version"|"warning")
 DETAILEDCMD {CMD}({DCMD1}|{DCMD2}|{DCMD3}|{DCMD4}|{DCMD5}|{DCMD6}|{DCMD7}|{DCMD8}|{DCMD9}|{DCMD10}|{DCMD11})
@@ -935,6 +938,7 @@ RCSTAG    "$"{ID}":"[^\n$]+"$"
 %x	CiteLabel
 %x	CopyDoc
 %x      GuardExpr
+%x  SortKeyArg1
 
 %%
 
@@ -2150,6 +2154,27 @@ RCSTAG    "$"{ID}":"[^\n$]+"$"
 					  addOutput(yytext);
   					}
 
+  /* ----- handle arguments of the sk(sortkey) command ------- */
+
+<SortKeyArg1>({ID}("::"|"."))*{ID}	{ // argument
+					  //printf("Catch \\sk command %s\n", yytext);
+					  current->sortKey = yytext;
+					  BEGIN( Comment );
+					}
+<SortKeyArg1>{LC}			{ // line continuation
+                                          yyLineNr; 
+					  addOutput('\n');
+  					}
+<SortKeyArg1>{DOCNL}			{ // missing argument
+  					  warn(yyFileName,yyLineNr,
+					      "Missing argument of \\sk command"
+					      );
+  					  if (*yytext=='\n') yyLineNr;
+					  addOutput('\n');
+					  BEGIN( Comment );
+  					}
+<SortKeyArg1>.			{ // ignore other stuff
+  					}
 
 %%
 
@@ -2764,6 +2789,12 @@ static bool handleCopyDoc(const QCString &)
   return FALSE;
 }
 
+static bool handleSortKey(const QCString& )
+{
+  BEGIN( SortKeyArg1 );
+  return FALSE;
+}
+
 //----------------------------------------------------------------------------
 
 static void checkFormula()
diff --git a/src/doxygen.cpp b/src/doxygen.cpp
index 5b7ba1d..77e7418 100644
--- a/src/doxygen.cpp
+++ b/src/doxygen.cpp
@@ -97,6 +97,7 @@
 #include "docsets.h"
 #include "formula.h"
 #include "settings.h"
+#include "sortkey.h"
 
 #define RECURSE_ENTRYTREE(func,var) \
   do { if (var->children()) { \
@@ -8813,6 +8814,19 @@ static void buildExampleList(EntryNav *rootNav)
   RECURSE_ENTRYTREE(buildExampleList,rootNav);
 }
 
+//----------------------------------------------------------------------
+static void buildSortKeyRelatedList(EntryNav *rootNav)
+{
+  QCString sortKey = rootNav->sortKey();
+  //printf("buildSortKeyRelatedList(%s, %s)\n",rootNav->name().data(), sortKey.data());
+  if (!rootNav->name().isEmpty() && !sortKey.isEmpty()) 
+  {
+    addSortKey( sortKey, rootNav->name() );
+  }
+  RECURSE_ENTRYTREE( buildSortKeyRelatedList, rootNav );
+}
+
+
 //----------------------------------------------------------------------------
 // prints the Entry tree (for debugging)
 
@@ -10858,6 +10872,11 @@ void parseInput()
    *             Gather information                                         * 
    **************************************************************************/
 
+  g_s.begin("Building sortkey related list...\n");
+  buildSortKeyRelatedList(rootNav);
+  //dumpSortKey();
+  g_s.end();
+
   g_s.begin("Building group list...\n");
   buildGroupList(rootNav);
   organizeSubGroups(rootNav);
diff --git a/src/entry.cpp b/src/entry.cpp
index 15263ab..eab6a46 100644
--- a/src/entry.cpp
+++ b/src/entry.cpp
@@ -134,6 +134,7 @@ Entry::Entry(const Entry &e)
   artificial  = e.artificial;
   groupDocType = e.groupDocType;
   id          = e.id;
+  sortKey     = e.sortKey;
 
   m_parent    = e.m_parent;
   m_sublist   = new QList<Entry>;
@@ -343,7 +344,7 @@ Entry *Entry::removeSubEntry(Entry *e)
 EntryNav::EntryNav(EntryNav *parent, Entry *e)
              : m_parent(parent), m_subList(0), m_section(e->section), m_type(e->type),
               m_name(e->name), m_fileDef(0), m_lang(e->lang), 
-              m_info(0), m_offset(-1), m_noLoad(FALSE) 
+              m_info(0), m_offset(-1), m_noLoad(FALSE), m_sortKey(e->sortKey) 
 {
   if (e->tagInfo)
   {
diff --git a/src/entry.h b/src/entry.h
index cecd095..8876573 100644
--- a/src/entry.h
+++ b/src/entry.h
@@ -283,6 +283,7 @@ class Entry
     bool        artificial;   //!< Artificially introduced item
     GroupDocType groupDocType;
     QCString    id;           //!< libclang id
+    QCString    sortKey;      //!< sortkey given with \\sk command
 
 
     static int  num;          //!< counts the total number of entries
@@ -347,6 +348,7 @@ class EntryNav
     const QList<EntryNav> *children() const { return m_subList; }
     EntryNav *parent() const { return m_parent; }
     FileDef *fileDef() const { return m_fileDef; }
+    const QCString sortKey() const { return m_sortKey; }
 
   private:
 
@@ -358,6 +360,7 @@ class EntryNav
     int          m_section;     //!< entry type (see Sections);
     QCString	 m_type;        //!< member type 
     QCString	 m_name;        //!< member name
+    QCString	 m_sortKey;     //!< sortkey given with \\sk command
     TagInfo     *m_tagInfo;      //!< tag file info
     FileDef     *m_fileDef;
     SrcLangExt   m_lang;         //!< programming language in which this entry was found
diff --git a/src/groupdef.h b/src/groupdef.h
index bd53ff5..a7e046c 100644
--- a/src/groupdef.h
+++ b/src/groupdef.h
@@ -20,6 +20,7 @@
 
 #include "sortdict.h"
 #include "definition.h"
+#include "sortkey.h"
 
 class MemberList;
 class FileList;
@@ -161,7 +162,9 @@ class GroupSDict : public SDict<GroupDef>
     virtual ~GroupSDict() {}
     int compareItems(QCollection::Item item1,QCollection::Item item2)
     {
-      return qstrcmp(((GroupDef*)item1)->groupTitle(),((GroupDef*)item2)->groupTitle());
+      return sortKeyAppliedStrcmp(
+               ((GroupDef*)item1)->groupTitle(),
+               ((GroupDef*)item2)->groupTitle());
     }
 };
 
@@ -171,7 +174,9 @@ class GroupList : public QList<GroupDef>
   public:
     int compareItems(QCollection::Item item1,QCollection::Item item2)
     {
-      return qstrcmp(((GroupDef*)item1)->groupTitle(),((GroupDef*)item2)->groupTitle());
+      return sortKeyAppliedStrcmp(
+               ((GroupDef*)item1)->groupTitle(),
+               ((GroupDef*)item2)->groupTitle());
     }
 };
 
diff --git a/src/index.cpp b/src/index.cpp
index cfa3e02..e980fb2 100644
--- a/src/index.cpp
+++ b/src/index.cpp
@@ -47,6 +47,7 @@
 #include "classlist.h"
 #include "namespacedef.h"
 #include "filename.h"
+#include "sortkey.h"
 
 #define MAX_ITEMS_BEFORE_MULTIPAGE_INDEX 200
 #define MAX_ITEMS_BEFORE_QUICK_INDEX 30
@@ -126,10 +127,12 @@ class MemberIndexList : public QList<MemberDef>
     {
       MemberDef *md1=(MemberDef *)item1;
       MemberDef *md2=(MemberDef *)item2;
-      int result = qstricmp(md1->name(),md2->name());
+      int result = prefixSkippedAndSortKeyAppliedStricmp(
+              md1->name(),md2->name());
       if (result==0)
       {
-        result = qstricmp(md1->qualifiedName(),md2->qualifiedName());
+        result = prefixSkippedAndSortKeyAppliedStricmp(
+              md1->qualifiedName(),md2->qualifiedName());
       }
       return result;
     }
@@ -1695,41 +1698,116 @@ static void writeAnnotatedClassList(OutputList &ol)
   ol.endIndexList();
 }
 
-static QCString letterToLabel(uint startLetter)
+bool isUsableOnURL(const uint codePoint)
 {
-  char s[10];
-  if (startLetter>0x20 && startLetter<=0x7f) // printable ASCII character
+  // 'unUsable' below includes reserved characters defined RFC2346
+  // and those cannot be used as a part of filename.
+  const QCString unUsable(" \\\"'|*`^><)(}{][;/?:@&=+$,");
+  if (codePoint<0x80 && unUsable.find((char)codePoint)!=-1)
+    return false;
+  return codePoint>=0x20;
+}
+
+/** \brief generate the header string as the label
+
+\sa getEscapedAnchor
+\sa getEscapedFilenameAsURL
+
+Consider carefully what we should do. The caller of this function
+should take care to where the returned value is applied.
+
+As URL, we can use non-ASCII characters, because the HTML output
+of doxygen do NOT send URLs directly to network, as the user
+agents (web browsers) will do escape URLs. But on the other hand,
+we should escape 'reserved' characters defined with RFC2396.
+
+As Filename, we should escape some characters especially NTFS,
+used by MS-Windows.
+
+As label text, we can use all printable characters. Is there 
+any probably that white-space characters are used as the head
+of IDs?
+*/
+static QCString getEscapedLabel(const uint startLetter, const bool makeUpper = true)
+{
+  if (startLetter>=0xa0 || isprint((int)startLetter))
   {
-    s[0]=(char)startLetter;
-    s[1]=0;
+    if (makeUpper)
+    {
+      return QString( QChar(startLetter) ).upper().utf8();
+    }
+    else
+    {
+      return QString( QChar(startLetter) ).lower().utf8();
+    }
   }
   else
   {
+    char s[5];
     const char hex[]="0123456789abcdef";
-    int i=0;
-    s[i++]='0';
-    s[i++]='x';
-    if (startLetter>(1<<24)) // 4 byte character
-    {
-      s[i++]=hex[(startLetter>>28)&0xf];
-      s[i++]=hex[(startLetter>>24)&0xf];
-    }
-    if (startLetter>(1<<16)) // 3 byte character
+    s[0]='0';
+    s[1]='x';
+    s[2]=hex[startLetter>>4];
+    s[3]=hex[startLetter&0xF];
+    s[4]=0;
+    return s;
+  }
+}
+
+/** \brief generate the header string as the anchor of URL
+
+\sa getEscapedLabel
+\sa getEscapedFilenameAsURL
+*/
+static QCString getEscapedAnchor(const uint startLetter)
+{
+  // reserved as the separators in RFC2396 and add '#'
+  const QCString unUsable(";/?:@&=+$,#");
+  if (startLetter < 0x80 && unUsable.find((char)startLetter) != -1)
+  {
+    QCString s;
+    s.sprintf("0x%02x",startLetter);
+    return s;
+  }
+  else
+  {
+    return getEscapedLabel(startLetter);
+  }
+}
+
+/** \brief generate the header string commonly usable as the filename and the URL
+
+\sa getEscapedLabel
+\sa getEscapedAnchor
+*/
+static QCString getEscapedFilenameAsURL(uint startLetter)
+{
+  const QCString unUsablesWithFilename("\\/:*?\"<>|");
+  if (startLetter < 0x80)
+  {
+    if (unUsablesWithFilename.find((char)startLetter) != -1)
     {
-      s[i++]=hex[(startLetter>>20)&0xf];
-      s[i++]=hex[(startLetter>>16)&0xf];
+      QCString s;
+      s.sprintf("0x%02x",startLetter);
+      return s;
     }
-    if (startLetter>(1<<8)) // 2 byte character
+    else
     {
-      s[i++]=hex[(startLetter>>12)&0xf];
-      s[i++]=hex[(startLetter>>8)&0xf];
+      char s[2];
+      s[0]=(char)startLetter; s[1]=0;
+      return s;
     }
-    // one byte character
-    s[i++]=hex[(startLetter>>4)&0xf];
-    s[i++]=hex[(startLetter>>0)&0xf];
-    s[i++]=0;
   }
-  return s;
+  else
+  {
+    QCString s;
+    const char* format;
+    if (startLetter>(1<<16)) format = "0x%06x";
+    else if (startLetter>(1<<8)) format = "0x%04x";
+    else format = "0x%02x";
+    s.sprintf(format,startLetter);
+    return s;
+  }
 }
 
 //----------------------------------------------------------------------------
@@ -1835,10 +1913,7 @@ static void writeAlphabeticalClassList(OutputList &ol)
     {
       if (cd->getLanguage()==SrcLangExt_VHDL && !((VhdlDocGen::VhdlClasses)cd->protection()==VhdlDocGen::ENTITYCLASS ))// no architecture
         continue;
-	     
-      int index = getPrefixIndex(cd->className());
-      //printf("name=%s index=%d %d\n",cd->className().data(),index,cd->protection());
-      startLetter=getUtf8CodeToUpper(cd->className(),index);
+      startLetter=prefixSkippedAndSortKeyAppliedStartLetter(cd->className(),true);
       indexLettersUsed.add(startLetter);
     }
   }
@@ -1852,11 +1927,11 @@ static void writeAlphabeticalClassList(OutputList &ol)
   {
     if (headerItems) alphaLinks += "&#160;|&#160;";
     headerItems++;
-    QCString li = letterToLabel(*pLetter);
-    QCString ls = QString(QChar(*pLetter)).utf8();
+    QCString anchor = getEscapedAnchor(*pLetter);
+    QCString label = getEscapedLabel(*pLetter);
     alphaLinks += (QCString)"<a class=\"qindex\" href=\"#letter_" + 
-                  li + "\">" + 
-                  ls + "</a>";
+                  anchor + "\">" + 
+                  label + "</a>";
   }
   alphaLinks += "</div>\n";
   ol.writeString(alphaLinks);
@@ -1888,8 +1963,8 @@ static void writeAlphabeticalClassList(OutputList &ol)
     
     if (cd->isLinkableInProject() && cd->templateMaster()==0)
     {
-      int index = getPrefixIndex(cd->className());
-      startLetter=getUtf8Code(cd->className(),index);
+      startLetter=prefixSkippedAndSortKeyAppliedStartLetter(cd->className(),false);
+
       // Do some sorting again, since the classes are sorted by name with 
       // prefix, which should be ignored really.
       if (cd->getLanguage()==SrcLangExt_VHDL)
@@ -1984,15 +2059,14 @@ static void writeAlphabeticalClassList(OutputList &ol)
           {
             if (cell->letter()!=0)
             {
-              QCString s = letterToLabel(cell->letter());
               ol.writeString("<td rowspan=\"2\" valign=\"bottom\">");
-              ol.writeString("<a name=\"letter_");
-              ol.writeString(s);
+              ol.writeString("<a id=\"letter_");
+              ol.writeString(getEscapedAnchor(cell->letter()));
               ol.writeString("\"></a>");
               ol.writeString("<table border=\"0\" cellspacing=\"0\" cellpadding=\"0\">"
                   "<tr>"
                   "<td><div class=\"ah\">&#160;&#160;"); 
-              ol.writeString(QString(QChar(cell->letter())).utf8());
+              ol.writeString(getEscapedLabel(cell->letter()));
               ol.writeString(         "&#160;&#160;</div>"
                   "</td>"
                   "</tr>"
@@ -2237,6 +2311,7 @@ static void writeMemberList(OutputList &ol,bool useSections,int page,
   };
   QCString prevName;
   QCString prevDefName;
+  uint currentSectionChar = 0;
   bool first=TRUE;
   bool firstSection=TRUE;
   bool firstItem=TRUE;
@@ -2259,25 +2334,23 @@ static void writeMemberList(OutputList &ol,bool useSections,int page,
       bool isFunc=!md->isObjCMethod() && 
         (md->isFunction() || md->isSlot() || md->isSignal()); 
       QCString name=md->name();
-      int startIndex = getPrefixIndex(name);
-      if (QCString(name.data()+startIndex)!=prevName) // new entry
+      QCString idxName = getPrefixSkippedAndSortKeyApplied(name);
+      if (idxName!=prevName) // new entry
       {
-        if ((prevName.isEmpty() || 
-            tolower(name.at(startIndex))!=tolower(prevName.at(0))) && 
-            useSections) // new section
+        const uint newSectionChar = getUtf8CodeToLower(idxName, 0);
+        if (useSections && newSectionChar!=currentSectionChar) // new section
         {
           if (!firstItem)    ol.endItemListItem();
           if (!firstSection) ol.endItemList();
-          QCString cs = letterToLabel(ml->letter());
-          QCString cl = QString(QChar(ml->letter())).utf8();
-          QCString anchor=(QCString)"index_"+cs;
-          QCString title=(QCString)"- "+cl+" -";
+          QCString anchor=(QCString)"index_"+getEscapedAnchor(newSectionChar);
+          QCString title=(QCString)"- "+getEscapedLabel(newSectionChar,false)+" -";
           ol.startSection(anchor,title,SectionInfo::Subsection);
           ol.docify(title);
           ol.endSection(anchor,SectionInfo::Subsection);
           ol.startItemList();
           firstSection=FALSE;
           firstItem=TRUE;
+          currentSectionChar = newSectionChar;
         }
         else if (!useSections && first)
         {
@@ -2296,7 +2369,7 @@ static void writeMemberList(OutputList &ol,bool useSections,int page,
         // link to class
         prevDefName="";
         sep = ": ";
-        prevName = name.data()+startIndex;
+        prevName = idxName;
       }
       else // same entry
       {
@@ -2336,8 +2409,7 @@ void addClassMemberNameToIndex(MemberDef *md)
       cd->templateMaster()==0)
   {
     QCString n = md->name();
-    int index = getPrefixIndex(n);
-    uint letter = getUtf8CodeToLower(n,index);
+    uint letter = prefixSkippedAndSortKeyAppliedStartLetter(n,false);
     if (!n.isEmpty()) 
     {
       bool isFriendToHide = hideFriendCompounds &&
@@ -2349,7 +2421,7 @@ void addClassMemberNameToIndex(MemberDef *md)
         g_memberIndexLetterUsed[CMHL_All].append(letter,md);
         documentedClassMembers[CMHL_All]++;
       }
-      if (md->isFunction()  || md->isSlot() || md->isSignal())
+      if (md->isFunction() || md->isSlot() || md->isSignal())
       {
         g_memberIndexLetterUsed[CMHL_Functions].append(letter,md);
         documentedClassMembers[CMHL_Functions]++;
@@ -2412,8 +2484,7 @@ void addNamespaceMemberNameToIndex(MemberDef *md)
   if (nd && nd->isLinkableInProject() && md->isLinkableInProject())
   {
     QCString n = md->name();
-    int index = getPrefixIndex(n);
-    uint letter = getUtf8CodeToLower(n,index);
+    uint letter = prefixSkippedAndSortKeyAppliedStartLetter(n,false);
     if (!n.isEmpty()) 
     {
       g_namespaceIndexLetterUsed[NMHL_All].append(letter,md);
@@ -2466,8 +2537,7 @@ void addFileMemberNameToIndex(MemberDef *md)
   if (fd && fd->isLinkableInProject() && md->isLinkableInProject())
   {
     QCString n = md->name();
-    int index = getPrefixIndex(n);
-    uint letter = getUtf8CodeToLower(n,index);
+    uint letter = prefixSkippedAndSortKeyAppliedStartLetter(n,false);
     if (!n.isEmpty()) 
     {
       g_fileIndexLetterUsed[FMHL_All].append(letter,md);
@@ -2520,8 +2590,8 @@ static void writeQuickMemberIndex(OutputList &ol,
   for (it.toFirst();(ml=it.current());++it)
   {
     uint i = ml->letter();
-    QCString is = letterToLabel(i);
-    QCString ci = QString(QChar(i)).utf8();
+    QCString ci = getEscapedAnchor(i);
+    QCString cl = getEscapedLabel(i,false);
     QCString anchor;
     QCString extension=Doxygen::htmlFileExtension;
     if (!multiPage)
@@ -2529,9 +2599,9 @@ static void writeQuickMemberIndex(OutputList &ol,
     else if (first) 
       anchor=fullName+extension+"#index_";
     else 
-      anchor=fullName+"_"+letterToLabel(i)+extension+"#index_";
+      anchor=fullName+"_"+getEscapedFilenameAsURL(i)+extension+"#index_";
     startQuickIndexItem(ol,anchor+ci,i==page,TRUE,first);
-    ol.writeString(is);
+    ol.writeString(cl);
     endQuickIndexItem(ol);
     first=FALSE;
   }
@@ -2609,7 +2679,7 @@ static void writeClassMemberIndexFiltered(OutputList &ol, ClassMemberHighlight h
     { 
       if (!first)
       {
-        fileName+="_"+letterToLabel(page);
+        fileName+="_"+getEscapedFilenameAsURL(page);
       }
       QCString cs = QString(QChar(page)).utf8();
       if (addToIndex)
@@ -2781,7 +2851,7 @@ static void writeFileMemberIndexFiltered(OutputList &ol, FileMemberHighlight hl)
     {
       if (!first)
       {
-        fileName+="_"+letterToLabel(page);
+        fileName+="_"+getEscapedFilenameAsURL(page);
       }
       QCString cs = QString(QChar(page)).utf8();
       if (addToIndex)
@@ -2949,7 +3019,7 @@ static void writeNamespaceMemberIndexFiltered(OutputList &ol,
     {
       if (!first)
       {
-        fileName+="_"+letterToLabel(page);
+        fileName+="_"+getEscapedFilenameAsURL(page);
       }
       QCString cs = QString(QChar(page)).utf8();
       if (addToIndex)
diff --git a/src/libdoxygen.pro.in b/src/libdoxygen.pro.in
index 96b7b9e..ff9bf2a 100644
--- a/src/libdoxygen.pro.in
+++ b/src/libdoxygen.pro.in
@@ -141,7 +141,8 @@ HEADERS      =	arguments.h \
 		xmldocvisitor.h \
                 xmlgen.h \
                 docbookvisitor.h \
-                docbookgen.h
+                docbookgen.h \
+                sortkey.h
 
 SOURCES      =	arguments.cpp \
                 constexp.cpp \
@@ -235,7 +236,8 @@ SOURCES      =	arguments.cpp \
 		xmldocvisitor.cpp \
                 xmlgen.cpp \
                 docbookvisitor.cpp \
-                docbookgen.cpp
+                docbookgen.cpp \
+                sortkey.cpp
 
 win32:TMAKE_CXXFLAGS       += -DQT_NODLL 
 win32-msvc:TMAKE_CXXFLAGS  += -Zm200
diff --git a/src/marshal.cpp b/src/marshal.cpp
index a6b3e75..e42dcd2 100644
--- a/src/marshal.cpp
+++ b/src/marshal.cpp
@@ -398,6 +398,7 @@ void marshalEntry(StorageIntf *s,Entry *e)
   marshalBool(s,e->artificial);
   marshalInt(s,(int)e->groupDocType);
   marshalQCString(s,e->id);
+  marshalQCString(s,e->sortKey);
 }
 
 void marshalEntryTree(StorageIntf *s,Entry *e)
@@ -783,6 +784,7 @@ Entry * unmarshalEntry(StorageIntf *s)
   e->artificial       = unmarshalBool(s);
   e->groupDocType     = (Entry::GroupDocType)unmarshalInt(s);
   e->id               = unmarshalQCString(s);
+  e->sortKey          = unmarshalQCString(s);
   return e;
 }
 
diff --git a/src/membername.cpp b/src/membername.cpp
index 58eaf8c..c75e1ac 100644
--- a/src/membername.cpp
+++ b/src/membername.cpp
@@ -19,6 +19,7 @@
 #include "classdef.h"
 #include "util.h"
 #include "filedef.h"
+#include "sortkey.h"
 
 MemberName::MemberName(const char *n) : QList<MemberDef>()
 {
@@ -39,9 +40,9 @@ int MemberName::compareItems(QCollection::Item item1, QCollection::Item item2)
   FileDef  *f1=m1->getFileDef();
   FileDef  *f2=m2->getFileDef();
   if (c1 && c2)
-    return qstrcmp(c1->name(),c2->name());
+    return sortKeyAppliedStrcmp(c1->name(),c2->name());
   else if (f1 && f2)
-    return qstrcmp(f1->name(),f2->name());
+    return sortKeyAppliedStrcmp(f1->name(),f2->name());
   else 
     return 0;
 }
@@ -61,9 +62,9 @@ int MemberNameInfo::compareItems(QCollection::Item item1, QCollection::Item item
   FileDef  *f1=m1->memberDef->getFileDef();
   FileDef  *f2=m2->memberDef->getFileDef();
   if (c1 && c2)
-    return qstrcmp(c1->name(),c2->name());
+    return sortKeyAppliedStrcmp(c1->name(),c2->name());
   else if (f1 && f2)
-    return qstrcmp(f1->name(),f2->name());
+    return sortKeyAppliedStrcmp(f1->name(),f2->name());
   else 
     return 0;
 }
@@ -76,8 +77,6 @@ int MemberNameSDict::compareItems(QCollection::Item item1, QCollection::Item ite
 {
   MemberName *n1=(MemberName *)item1;
   MemberName *n2=(MemberName *)item2;
-  return qstricmp(n1->memberName()+getPrefixIndex(n1->memberName()),
-                 n2->memberName()+getPrefixIndex(n2->memberName())
-                );
+  return sortKeyAppliedStrcmp(n1->memberName(), n2->memberName());
 }
 
diff --git a/src/sortkey.cpp b/src/sortkey.cpp
new file mode 100644
index 0000000..4cd1cab
--- /dev/null
+++ b/src/sortkey.cpp
@@ -0,0 +1,206 @@
+/******************************************************************************
+ *
+ *
+ *
+ * Copyright (C) 2013 by Suzumizaki-Kimitaka
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation under the terms of the GNU General Public License is hereby 
+ * granted. No representations are made about the suitability of this software 
+ * for any purpose. It is provided "as is" without express or implied warranty.
+ * See the GNU General Public License for more details.
+ *
+ * Documents produced by Doxygen are derivative works derived from the
+ * input used in their production; they are not affected by this license.
+ *
+ */
+
+#include <stdio.h>
+#include "qmap.h"
+#include "qregexp.h"
+#include "sortkey.h"
+#include "util.h"
+
+static const QRegExp sc_lastComponentFinder("[A-Z_a-z\\x80-\\xFF][0-9A-Z_a-z\\x80-\\xFF]*$");
+static const QRegExp sc_lastSeparatorFinder("[^0-9A-Z_a-z\\x80-\\xFF]+$");
+
+/*! @file sortkey.cpp
+@brief defines sortkey related functions
+
+The sortkeys are used to notify how to read another string.
+
+When you use kanji characters to name your functions or any entries,
+the doxygen will generate headers to indices by each kanji characters.
+As you know we have thousands of kanji characters, the headers can be
+nonsense to find entries you want.
+
+With the doxygen, you can use \\sortKey &lt;TheStringHowToRead&gt; command.
+Given string with the command is used to sort within indices instead of 
+the name of the functions, classes, files or so.
+
+The behavior is similar to \ref cfg_ignore_prefix "IGNORE_PREFIX" configure.
+But you can give any character to reorder within indices.
+*/
+
+typedef QMap<QCString, QCString> SortKeyFromBaseMap;
+
+static SortKeyFromBaseMap g_sortKeyFromBase;
+
+/*! @brief get internal recorded sortkey from given identifier
+
+@param i_base the identifier string used to sort in indices.
+@return the string used to sort instead of \a i_base. if not exist, returns \a i_base.
+*/
+static QCString getSortKeyApplied(const QCString& i_base)
+{
+  if ( g_sortKeyFromBase.count() == 0 )
+  {
+    return i_base;
+  }
+  QCString base = i_base;
+  QCString retVal;
+  QRegExp componentFinder( sc_lastComponentFinder );
+  QRegExp separatorFinder( sc_lastSeparatorFinder );
+  while ( true )
+  {
+    const int componentStartsAt = componentFinder.find( base, 0 );
+    if ( componentStartsAt == -1 )
+    {
+      break;
+    }
+    const QCString component = base.mid( componentStartsAt );
+    SortKeyFromBaseMap::ConstIterator it = g_sortKeyFromBase.find( component );
+    if ( it != g_sortKeyFromBase.end() )
+    {
+      retVal.prepend( it.data() );
+    }
+    else
+    {
+      retVal.prepend( component );
+    }
+    base = base.mid( 0, componentStartsAt );
+    const int separatorStartsAt = separatorFinder.find( base, 0 );
+    if ( separatorStartsAt == -1 )
+    {
+      break;
+    }
+    retVal.prepend( base.mid( separatorStartsAt ) );
+    base = base.mid( 0, separatorStartsAt );
+  }
+  retVal.prepend( base );
+  return retVal;
+}
+
+QCString getPrefixSkippedAndSortKeyApplied(const QCString& base)
+{
+  const int idx = getPrefixIndex( base );
+  if ( idx == 0 && g_sortKeyFromBase.count() == 0 )
+  {
+    return base;
+  }
+  return getSortKeyApplied( base.mid( idx ) );
+}
+
+int prefixSkippedAndSortKeyAppliedStricmp(const QCString& left, const QCString& right)
+{
+  QCString l1 = getPrefixSkippedAndSortKeyApplied( left );
+  QCString r1 = getPrefixSkippedAndSortKeyApplied( right );
+  return qstricmp( l1, r1 );
+}
+
+int sortKeyAppliedStrcmp(const QCString& left, const QCString& right)
+{
+  QCString l1 = getSortKeyApplied( left );
+  QCString r1 = getSortKeyApplied( right );
+  return qstrcmp( l1, r1 );
+}
+
+uint prefixSkippedAndSortKeyAppliedStartLetter(const QCString& given, const bool makeUpper)
+{
+  QCString s = getPrefixSkippedAndSortKeyApplied( given );
+  if (makeUpper)
+  {
+    return getUtf8CodeToUpper( s, 0 );
+  }
+  else
+  {
+    return getUtf8CodeToLower( s, 0 );
+  }
+}
+
+/*! @brief Register the relationship between sortkey and original strings
+
+The pair of the base name and its sortkey is registered by each component.
+Both \a i_sortKey and \a i_base are splitted as the components from their tails.
+
+The users can give only last part of the sortkey, but note the component
+can be the extension of the file. When \a i_base have the extension, 
+\a i_sortKey must have one. Otherwise the sortkey isn't registered correctly.
+
+When the component and its sortkey is completely same, they are not registered.
+
+When the base name of the component is already exists, simply overwrited.
+
+@param i_sortKey the string used to sort in indices instead of i_base.
+@param i_base the original name of entry like functions, classes or files.
+*/
+void addSortKey(const QCString& i_sortKey, const QCString& i_base)
+{
+  QCString sortKey = i_sortKey;
+  QCString base = i_base;
+  QRegExp componentFinder( sc_lastComponentFinder );
+  QRegExp separatorFinder( sc_lastSeparatorFinder );
+  while ( true )
+  {
+    const int lastComponentOfSortKeyStartsAt = componentFinder.match( sortKey );
+    if ( lastComponentOfSortKeyStartsAt == -1 )
+    {
+      break;
+    }
+    const int lastComponentOfBaseStartsAt = componentFinder.match( base );
+    if ( lastComponentOfBaseStartsAt == -1 )
+    {
+      break;
+    }
+    const QCString sortKeyPart = sortKey.mid( lastComponentOfSortKeyStartsAt );
+    const QCString basePart = base.mid( lastComponentOfBaseStartsAt );
+    if ( sortKeyPart != basePart && sortKeyPart.length() && basePart.length() )
+    {
+      SortKeyFromBaseMap::ConstIterator existed = g_sortKeyFromBase.find( basePart );
+      if ( existed != g_sortKeyFromBase.end() && *existed != i_sortKey)
+      {
+        printf("The sortkey of '%s' already defined as '%s',('%s' -> '%s')\n",
+               basePart.data(), (*existed).data(), i_base.data(), i_sortKey.data());
+      }
+      g_sortKeyFromBase.insert( basePart, sortKeyPart );
+    }
+    sortKey = sortKey.mid( 0, lastComponentOfSortKeyStartsAt );
+    base = base.mid( 0, lastComponentOfBaseStartsAt );
+    int lastSeparatorStartsAt = separatorFinder.find( sortKey, 0 );
+    if ( lastSeparatorStartsAt == -1 )
+    {
+      break;
+    }
+    sortKey = sortKey.mid( 0, lastSeparatorStartsAt );
+    lastSeparatorStartsAt = separatorFinder.find( base, 0 );
+    if ( lastSeparatorStartsAt == -1 )
+    {
+      break;
+    }
+    base = base.mid( 0, lastSeparatorStartsAt );
+  }
+}
+
+/*! @brief (for debug purpose)
+
+*/
+void dumpSortKey()
+{
+  printf("Enter dumpSortKey function.\n");
+  SortKeyFromBaseMap::Iterator it = g_sortKeyFromBase.begin();
+  for (; it != g_sortKeyFromBase.end(); it++ )
+  {
+    printf("%s is sortkey of %s\n", it.data().data(), it.key().data() );
+  }
+  printf("Leave dumpSortKey function.\n");
+}
diff --git a/src/sortkey.h b/src/sortkey.h
new file mode 100644
index 0000000..1eb858c
--- /dev/null
+++ b/src/sortkey.h
@@ -0,0 +1,33 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2013 by Suzumizaki-Kimitaka
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation under the terms of the GNU General Public License is hereby 
+ * granted. No representations are made about the suitability of this software 
+ * for any purpose. It is provided "as is" without express or implied warranty.
+ * See the GNU General Public License for more details.
+ *
+ * Documents produced by Doxygen are derivative works derived from the
+ * input used in their production; they are not affected by this license.
+ *
+ */
+
+#ifndef SORTKEY_H
+#define SORTKEY_H
+
+/*! @file sortkey.h
+*   @brief \ref sortkey.cpp to see detail
+*
+*/
+
+#include <qcstring.h>
+
+void addSortKey(const QCString& sortkey, const QCString& base);
+void dumpSortKey();
+QCString getPrefixSkippedAndSortKeyApplied(const QCString& base);
+int prefixSkippedAndSortKeyAppliedStricmp(const QCString& left, const QCString& right);
+int sortKeyAppliedStrcmp(const QCString& left, const QCString& right);
+uint prefixSkippedAndSortKeyAppliedStartLetter(const QCString& s, const bool makeUpper = true);
+
+#endif
