1 /*
2  *  This file is part of Poedit (http://poedit.net)
3  *
4  *  Copyright (C) 1999-2015 Vaclav Slavik
5  *  Copyright (C) 2005 Olivier Sannier
6  *
7  *  Permission is hereby granted, free of charge, to any person obtaining a
8  *  copy of this software and associated documentation files (the "Software"),
9  *  to deal in the Software without restriction, including without limitation
10  *  the rights to use, copy, modify, merge, publish, distribute, sublicense,
11  *  and/or sell copies of the Software, and to permit persons to whom the
12  *  Software is furnished to do so, subject to the following conditions:
13  *
14  *  The above copyright notice and this permission notice shall be included in
15  *  all copies or substantial portions of the Software.
16  *
17  *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19  *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20  *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21  *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22  *  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23  *  DEALINGS IN THE SOFTWARE.
24  *
25  */
26 
27 #include "cat_sorting.h"
28 
29 #include <unicode/unistr.h>
30 #include "str_helpers.h"
31 
32 #include <wx/config.h>
33 #include <wx/log.h>
34 
Default()35 /*static*/ SortOrder SortOrder::Default()
36 {
37     SortOrder order;
38 
39     wxString by = wxConfig::Get()->Read("/sort_by", "file-order");
40     long ctxt = wxConfig::Get()->Read("/sort_group_by_context", 0L);
41     long untrans = wxConfig::Get()->Read("/sort_untrans_first", 0L);
42     long errors = wxConfig::Get()->Read("/sort_errors_first", 1L);
43 
44     if ( by == "source" )
45         order.by = By_Source;
46     else if ( by == "translation" )
47         order.by = By_Translation;
48     else
49         order.by = By_FileOrder;
50 
51     order.groupByContext = (ctxt != 0);
52     order.untransFirst = (untrans != 0);
53     order.errorsFirst = (errors != 0);
54 
55     return order;
56 }
57 
58 
Save()59 void SortOrder::Save()
60 {
61     wxString bystr;
62     switch ( by )
63     {
64         case By_FileOrder:
65             bystr = "file-order";
66             break;
67         case By_Source:
68             bystr = "source";
69             break;
70         case By_Translation:
71             bystr = "translation";
72             break;
73     }
74 
75     wxConfig::Get()->Write("/sort_by", bystr);
76     wxConfig::Get()->Write("/sort_group_by_context", groupByContext);
77     wxConfig::Get()->Write("/sort_untrans_first", untransFirst);
78     wxConfig::Get()->Write("/sort_errors_first", errorsFirst);
79 }
80 
81 
CatalogItemsComparator(const Catalog & catalog,const SortOrder & order)82 CatalogItemsComparator::CatalogItemsComparator(const Catalog& catalog, const SortOrder& order)
83     : m_catalog(catalog), m_order(order)
84 {
85     UErrorCode err = U_ZERO_ERROR;
86     switch (m_order.by)
87     {
88         case SortOrder::By_Source:
89             // TODO: allow non-English source languages too
90             m_collator.reset(icu::Collator::createInstance(catalog.GetSourceLanguage().ToIcu(), err));
91             break;
92 
93         case SortOrder::By_Translation:
94             m_collator.reset(icu::Collator::createInstance(catalog.GetLanguage().ToIcu(), err));
95             break;
96 
97         case SortOrder::By_FileOrder:
98             break;
99     }
100 
101     if (!U_SUCCESS(err) || err == U_USING_FALLBACK_WARNING)
102     {
103         wxLogTrace("poedit", "warning: not using collation for %s (%s)",
104                    catalog.GetLanguage().Code(), u_errorName(err));
105     }
106 
107     if (m_collator)
108     {
109         // Case-insensitive comparison:
110         m_collator->setStrength(icu::Collator::SECONDARY);
111     }
112 }
113 
114 
operator ()(int i,int j) const115 bool CatalogItemsComparator::operator()(int i, int j) const
116 {
117     const CatalogItem& a = Item(i);
118     const CatalogItem& b = Item(j);
119 
120     if ( m_order.errorsFirst )
121     {
122         if ( a.GetValidity() == CatalogItem::Val_Invalid && b.GetValidity() != CatalogItem::Val_Invalid )
123             return true;
124         else if ( a.GetValidity() != CatalogItem::Val_Invalid && b.GetValidity() == CatalogItem::Val_Invalid )
125             return false;
126     }
127 
128     if ( m_order.untransFirst )
129     {
130         if ( !a.IsTranslated() && b.IsTranslated() )
131             return true;
132         else if ( a.IsTranslated() && !b.IsTranslated() )
133             return false;
134 
135         if ( a.IsFuzzy() && !b.IsFuzzy() )
136             return true;
137         else if ( !a.IsFuzzy() && b.IsFuzzy() )
138             return false;
139     }
140 
141     if ( m_order.groupByContext )
142     {
143         if ( a.HasContext() && !b.HasContext() )
144             return true;
145         else if ( !a.HasContext() && b.HasContext() )
146             return false;
147         else if ( a.HasContext() && b.HasContext() )
148         {
149             int r = CompareStrings(a.GetContext(), b.GetContext());
150             if ( r != 0 )
151                 return r < 0;
152         }
153     }
154 
155     switch ( m_order.by )
156     {
157         case SortOrder::By_FileOrder:
158         {
159             return i < j;
160         }
161 
162         case SortOrder::By_Source:
163         {
164             int r = CompareStrings(a.GetString(), b.GetString());
165             if ( r != 0 )
166                 return r < 0;
167             break;
168         }
169 
170         case SortOrder::By_Translation:
171         {
172             int r = CompareStrings(a.GetTranslation(), b.GetTranslation());
173             if ( r != 0 )
174                 return r < 0;
175             break;
176         }
177     }
178 
179     // As last resort, sort by position in file. Note that this means that
180     // no two items are considered equal w.r.t. sort order; this ensures stable
181     // ordering.
182     return i < j;
183 }
184 
185 
CompareStrings(wxString a,wxString b) const186 int CatalogItemsComparator::CompareStrings(wxString a, wxString b) const
187 {
188     a.Replace("&", "");
189     a.Replace("_", "");
190 
191     b.Replace("&", "");
192     b.Replace("_", "");
193 
194     if (m_collator)
195     {
196         UErrorCode err = U_ZERO_ERROR;
197 #if wxUSE_UNICODE_UTF8
198         return m_collator->compareUTF8(a.wx_str(), b.wx_str(), err);
199 #elif SIZEOF_WCHAR_T == 2
200         return m_collator->compare(a.wx_str(), a.length(), b.wx_str(), b.length(), err);
201 #else
202         return m_collator->compare(str::to_icu(a), str::to_icu(b), err);
203 #endif
204     }
205     else
206     {
207         return a.CmpNoCase(b);
208     }
209 }
210