Invert Pan-Azimuth (up means left)
[ardour.git] / libs / pbd / convert.cc
1 /*
2  * Copyright (C) 2006-2017 Paul Davis <paul@linuxaudiosystems.com>
3  * Copyright (C) 2006 Taybin Rutkin <taybin@taybin.com>
4  * Copyright (C) 2009-2012 Carl Hetherington <carl@carlh.net>
5  * Copyright (C) 2015-2019 Robin Gareus <robin@gareus.org>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License along
18  * with this program; if not, write to the Free Software Foundation, Inc.,
19  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20  */
21
22 #include <cmath>
23 #include <algorithm>
24 #include <string>
25
26 #include <stdint.h>
27 #include <stdlib.h>
28 #include <cstdio>
29 #include <ctype.h>
30 #include <cstring>
31 #ifndef __STDC_FORMAT_MACROS
32 #define __STDC_FORMAT_MACROS
33 #endif
34 #include <inttypes.h>
35
36 #include <glib.h>
37
38 #include "pbd/convert.h"
39
40 #include "pbd/i18n.h"
41
42 using std::string;
43 using std::vector;
44 using Glib::ustring;
45
46 namespace PBD {
47
48 string
49 capitalize (const string& str)
50 {
51         string ret = str;
52         if (!str.empty()) {
53                 /* XXX not unicode safe */
54                 ret[0] = toupper (str[0]);
55         }
56         return ret;
57 }
58
59 string
60 downcase (const string& str)
61 {
62         string copy (str);
63         std::transform (copy.begin(), copy.end(), copy.begin(), ::tolower);
64         return copy;
65 }
66
67 const char*
68 downcase (const char* str)
69 {
70         char *copy = strdup (str);
71         for (char* p = copy; *p; ++p) {
72                 *p = tolower (*p);
73         }
74         return copy;
75 }
76
77 string
78 short_version (string orig, string::size_type target_length)
79 {
80         /* this tries to create a recognizable abbreviation
81            of "orig" by removing characters until we meet
82            a certain target length.
83
84            note that we deliberately leave digits in the result
85            without modification.
86         */
87
88
89         string::size_type pos;
90
91         /* remove white-space and punctuation, starting at end */
92
93         while (orig.length() > target_length) {
94                 if ((pos = orig.find_last_of (_("\"\n\t ,<.>/?:;'[{}]~`!@#$%^&*()_-+="))) == string::npos) {
95                         break;
96                 }
97                 orig.replace (pos, 1, "");
98         }
99
100         /* remove lower-case vowels, starting at end */
101
102         while (orig.length() > target_length) {
103                 if ((pos = orig.find_last_of (_("aeiou"))) == string::npos) {
104                         break;
105                 }
106                 orig.replace (pos, 1, "");
107         }
108
109         /* remove upper-case vowels, starting at end */
110
111         while (orig.length() > target_length) {
112                 if ((pos = orig.find_last_of (_("AEIOU"))) == string::npos) {
113                         break;
114                 }
115                 orig.replace (pos, 1, "");
116         }
117
118         /* remove lower-case consonants, starting at end */
119
120         while (orig.length() > target_length) {
121                 if ((pos = orig.find_last_of (_("bcdfghjklmnpqrtvwxyz"))) == string::npos) {
122                         break;
123                 }
124                 orig.replace (pos, 1, "");
125         }
126
127         /* remove upper-case consonants, starting at end */
128
129         while (orig.length() > target_length) {
130                 if ((pos = orig.find_last_of (_("BCDFGHJKLMNPQRTVWXYZ"))) == string::npos) {
131                         break;
132                 }
133                 orig.replace (pos, 1, "");
134         }
135
136         /* whatever the length is now, use it */
137
138         return orig;
139 }
140
141 int
142 atoi (const string& s)
143 {
144         return ::atoi (s.c_str());
145 }
146
147 int32_t
148 atol (const string& s)
149 {
150         return (int32_t) ::atol (s.c_str());
151 }
152
153 int64_t
154 atoll (const string& s)
155 {
156         return (int64_t) ::atoll (s.c_str());
157 }
158
159 double
160 atof (const string& s)
161 {
162         return ::atof (s.c_str());
163 }
164
165 vector<string>
166 internationalize (const char *package_name, const char **array)
167 {
168         vector<string> v;
169
170         for (uint32_t i = 0; array[i]; ++i) {
171                 v.push_back (dgettext(package_name, array[i]));
172         }
173
174         return v;
175 }
176
177 static int32_t
178 int_from_hex (char hic, char loc)
179 {
180         int hi; /* hi byte */
181         int lo; /* low byte */
182
183         hi = (int) hic;
184
185         if( ('0'<=hi) && (hi<='9') ) {
186                 hi -= '0';
187         } else if( ('a'<= hi) && (hi<= 'f') ) {
188                 hi -= ('a'-10);
189         } else if( ('A'<=hi) && (hi<='F') ) {
190                 hi -= ('A'-10);
191         }
192
193         lo = (int) loc;
194
195         if( ('0'<=lo) && (lo<='9') ) {
196                 lo -= '0';
197         } else if( ('a'<=lo) && (lo<='f') ) {
198                 lo -= ('a'-10);
199         } else if( ('A'<=lo) && (lo<='F') ) {
200                 lo -= ('A'-10);
201         }
202
203         return lo + (16 * hi);
204 }
205
206 string
207 url_decode (string const & url)
208 {
209         string decoded;
210
211         for (string::size_type i = 0; i < url.length(); ++i) {
212                 if (url[i] == '+') {
213                         decoded += ' ';
214                 } else if (url[i] == '%' && i <= url.length() - 3) {
215                         decoded += char (int_from_hex (url[i + 1], url[i + 2]));
216                         i += 2;
217                 } else {
218                         decoded += url[i];
219                 }
220         }
221
222         return decoded;
223 }
224
225 #if 0
226 string
227 length2string (const int32_t samples, const float sample_rate)
228 {
229         int32_t secs = (int32_t) (samples / sample_rate);
230         int32_t hrs =  secs / 3600;
231         secs -= (hrs * 3600);
232         int32_t mins = secs / 60;
233         secs -= (mins * 60);
234
235         int32_t total_secs = (hrs * 3600) + (mins * 60) + secs;
236         int32_t samples_remaining = (int) floor (samples - (total_secs * sample_rate));
237         float fractional_secs = (float) samples_remaining / sample_rate;
238
239         char duration_str[32];
240         sprintf (duration_str, "%02" PRIi32 ":%02" PRIi32 ":%05.2f", hrs, mins, (float) secs + fractional_secs);
241
242         return duration_str;
243 }
244 #endif
245
246 string
247 length2string (const int64_t samples, const double sample_rate)
248 {
249         int64_t secs = (int64_t) floor (samples / sample_rate);
250         int64_t hrs =  secs / 3600LL;
251         secs -= (hrs * 3600LL);
252         int64_t mins = secs / 60LL;
253         secs -= (mins * 60LL);
254
255         int64_t total_secs = (hrs * 3600LL) + (mins * 60LL) + secs;
256         int64_t samples_remaining = (int64_t) floor (samples - (total_secs * sample_rate));
257         float fractional_secs = (float) samples_remaining / sample_rate;
258
259         char duration_str[64];
260         sprintf (duration_str, "%02" PRIi64 ":%02" PRIi64 ":%05.2f", hrs, mins, (float) secs + fractional_secs);
261
262         return duration_str;
263 }
264
265 static bool
266 chars_equal_ignore_case(char x, char y)
267 {
268         /* app should have called setlocale() if its wants this comparison to be
269            locale sensitive.
270         */
271         return toupper (x) == toupper (y);
272 }
273
274 bool
275 strings_equal_ignore_case (const string& a, const string& b)
276 {
277         if (a.length() == b.length()) {
278                 return std::equal (a.begin(), a.end(), b.begin(), chars_equal_ignore_case);
279         }
280         return false;
281 }
282
283 /** A wrapper for dgettext that takes a msgid of the form Context|Text.
284  *  If Context|Text is translated, the translation is returned, otherwise
285  *  just Text is returned.  Useful for getting translations of words or phrases
286  *  that have different meanings in different contexts.
287  */
288 const char *
289 sgettext (const char* domain_name, const char* msgid)
290 {
291         const char * msgval = dgettext (domain_name, msgid);
292         if (msgval == msgid) {
293                 const char * p = strrchr (msgid, '|');
294                 if (p) {
295                         msgval = p + 1;
296                 }
297         }
298         return msgval;
299 }
300
301 } // namespace PBD