.
1 /*
2 Copyright (C) 2012 2014 Johan Mattsson
3
4 This library is free software; you can redistribute it and/or modify
5 it under the terms of the GNU Lesser General Public License as
6 published by the Free Software Foundation; either version 3 of the
7 License, or (at your option) any later version.
8
9 This library is distributed in the hope that it will be useful, but
10 WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Lesser General Public License for more details.
13 */
14 using BirdFont;
15
16 public const string GETTEXT_PACKAGE = "birdfont";
17
18 namespace BirdFont {
19
20 public static string? settings_directory = null;
21
22 internal static string build_absoulute_path (string file_name) {
23 File f = File.new_for_path (file_name);
24 return (!) f.get_path ();
25 }
26
27 public static string get_version () {
28 return VERSION;
29 }
30
31 public static void set_logging (bool log) {
32 BirdFont.logging = log;
33 }
34
35 public static string wine_to_unix_path (string exec_path) {
36 bool drive_c, drive_z;
37 int i;
38 string p, q;
39
40 p = exec_path;
41 p = p.replace ("\\", "/");
42
43 drive_c = exec_path.index_of ("C:") == 0;
44 drive_z = exec_path.index_of ("Z:") == 0;
45
46 i = p.index_of (":");
47
48 if (i != -1) {
49 p = p.substring (i + 2);
50 }
51
52 if (drive_c) {
53 q = @"/home/$(Environment.get_user_name ())/.wine/drive_c/" + p;
54
55 if (File.new_for_path (q).query_exists ()) {
56 return q;
57 } else {
58 return p;
59 }
60 }
61
62 if (drive_z) {
63 return ("/" + p).dup ();
64 }
65
66 return exec_path.dup ();
67 }
68
69 public bool is_null (void* n) {
70 return n == null;
71 }
72
73 public bool has_flag (uint32 flag, uint32 mask) {
74 return (flag & mask) > 0;
75 }
76
77 public class BirdFont {
78 public static Argument args;
79 public static bool experimental = false;
80 public static bool show_coordinates = false;
81 public static bool fatal_wanings = false;
82 public static bool win32 = false;
83 public static bool mac = false;
84 public static bool android = false;
85 public static string exec_path = "";
86 public static string? bundle_path = null;
87
88 internal static bool logging = false;
89 public static DataOutputStream? logstream = null;
90
91 public static Font current_font;
92 public static GlyphCollection current_glyph_collection;
93
94 public static Drawing? drawing = null;
95
96 public static string? settings_subdirectory = null;
97
98 public BirdFont () {
99 set_defaul_drawing_callbacks ();
100 }
101
102 void set_defaul_drawing_callbacks () {
103 if (drawing == null) {
104 drawing = new Drawing ();
105 }
106 }
107
108 /**
109 * @param arg command line arguments
110 * @param program path
111 * @param setting subdirectory
112 */
113 public void init (string[] arg, string? program_path, string? settings_subdir) {
114 int i;
115 File font_file;
116 string exec_path;
117 string theme;
118 int default_theme_version;
119 string theme_version;
120 CharDatabaseParser parser;
121 CodePageBits codepage_bits;
122
123 set_settings_subdir (settings_subdir);
124
125 args = new Argument.command_line (arg);
126 Font.empty = new Font ();
127
128 #if ANDROID
129 BirdFont.logging = true;
130
131 __android_log_print (ANDROID_LOG_WARN, "BirdFont", @"libbirdfont version $VERSION");
132 LogLevelFlags log_levels = LogLevelFlags.LEVEL_ERROR | LogLevelFlags.LEVEL_CRITICAL | LogLevelFlags.LEVEL_WARNING;
133 Log.set_handler (null, log_levels, android_warning);
134
135 android = true;
136 #else
137 stdout.printf ("birdfont version %s\n", VERSION);
138
139 android = args.has_argument ("--android");
140
141 if (!BirdFont.logging) {
142 BirdFont.logging = args.has_argument ("--log");
143 }
144 #endif
145
146 if (BirdFont.logging) {
147 init_logfile ();
148 }
149
150 if (!args.has_argument ("--no-translation")) {
151 init_gettext ();
152 }
153
154 if (args.has_argument ("--help")) {
155 args.print_help ();
156 Process.exit (0);
157 }
158
159 #if !MAC
160 int err_arg = args.validate ();
161 if (err_arg != 0) {
162 stdout.printf (@"Unknown parameter $(arg [err_arg])\n\n");
163 args.print_help ();
164 Process.exit (0);
165 }
166 #endif
167 Preferences.load ();
168
169 // always load default theme when names in theme does change
170 default_theme_version = 1;
171 theme = Preferences.get ("theme");
172 theme_version = Preferences.get ("theme_version");
173
174 Theme.set_default_colors ();
175
176 if (theme_version == "" || int.parse (theme_version) < default_theme_version) {
177
178 Theme.load_theme ("dark.theme");
179 Preferences.set ("theme", "dark.theme");
180 } else {
181 if (theme != "") {
182 Theme.load_theme (theme);
183 } else {
184 Theme.load_theme ("dark.theme");
185 }
186 }
187
188 Preferences.set ("theme_version", @"$default_theme_version");
189
190 current_font = new Font ();
191 current_font.set_name ("");
192 current_font.initialised = false;
193 current_glyph_collection = new GlyphCollection.with_glyph ('\0', "");
194
195 experimental = args.has_argument ("--test");
196 show_coordinates = args.has_argument ("--show-coordinates") || experimental;
197 fatal_wanings = args.has_argument ("--fatal-warning");
198 win32 = (arg[0].index_of (".exe") > -1)
199 || arg[0] == "wine"
200 || args.has_argument ("--windows");
201
202 #if MAC
203 mac = true;
204 #else
205 mac = args.has_argument ("--mac");
206 #endif
207
208 if (program_path == null) {
209 exec_path = "";
210
211 if (win32) {
212 // wine hack to get "." folder in win32 environment
213 i = arg[0].last_index_of ("\\");
214
215 if (i != -1) {
216 exec_path = arg[0];
217 exec_path = exec_path.substring (0, i);
218 exec_path = wine_to_unix_path (exec_path);
219 }
220 } else {
221 exec_path = "./";
222 }
223 } else {
224 exec_path = (!) program_path;
225 }
226
227 if (args.get_file () != "") {
228 font_file = File.new_for_path (args.get_file ());
229
230 if (!font_file.query_exists ()) {
231 stderr.printf (@"The file \"$(args.get_file ())\" was not found.\n");
232 Process.exit (-1);
233 }
234 }
235
236 if (fatal_wanings) {
237 LogLevelFlags levels = LogLevelFlags.LEVEL_ERROR | LogLevelFlags.LEVEL_CRITICAL | LogLevelFlags.LEVEL_WARNING;
238 Log.set_handler (null, levels, fatal_warning);
239 }
240
241 Preferences.set_last_file (get_current_font ().get_path ());
242
243 DefaultCharacterSet.create_default_character_sets ();
244 DefaultCharacterSet.get_characters_for_prefered_language ();
245
246 HeadTable.init (1024);
247
248 if (TestBirdFont.get_singleton ().test_cases_to_run != "All") {
249 TestBirdFont.run_tests ();
250 }
251
252 if (has_argument ("--parse-ucd")) {
253 parser = new CharDatabaseParser ();
254 parser.regenerate_database ();
255 }
256
257 if (has_argument ("--codepages")) {
258 codepage_bits = new CodePageBits ();
259 codepage_bits.generate_codepage_database ();
260 }
261 }
262
263 public static bool has_logging () {
264 bool log;
265
266 lock (BirdFont.logging) {
267 log = BirdFont.logging;
268 }
269
270 return log;
271 }
272
273 public static Argument get_arguments () {
274 return args;
275 }
276
277 public static void set_bundle_path (string path) {
278 bundle_path = path;
279 }
280
281 public static void init_gettext () {
282 // FIXME: android, this should be OK now
283 #if !ANDROID
284 string locale_directory = SearchPaths.get_locale_directory ();
285 Intl.setlocale (LocaleCategory.MESSAGES, "");
286 Intl.bind_textdomain_codeset (GETTEXT_PACKAGE, "utf-8");
287 Intl.bindtextdomain (GETTEXT_PACKAGE, locale_directory);
288 #endif
289 }
290
291 public static void load_font_from_command_line () {
292 string file = args.get_file ();
293 if (file != "") {
294 RecentFiles.load_font (file);
295 }
296 }
297
298 public static Font get_current_font () {
299 return current_font;
300 }
301
302 internal static void fatal_warning (string? log_domain, LogLevelFlags log_levels, string message) {
303 bool fatal = true;
304
305 if (log_domain != null) {
306 stderr.printf ("%s: \n", (!) log_domain);
307 }
308
309 stderr.printf ("\n%s\n\n", message);
310 assert (!fatal);
311 }
312
313 #if ANDROID
314 internal static void android_warning (string? log_domain, LogLevelFlags log_levels, string message) {
315 __android_log_print (ANDROID_LOG_WARN, "BirdFont", message);
316 }
317 #endif
318
319 public static Font new_font () {
320 current_font = new Font ();
321
322 if (!is_null (MainWindow.tools)) {
323 MainWindow.get_drawing_tools ().remove_all_grid_buttons ();
324 DrawingTools.add_new_grid (1, false);
325 DrawingTools.add_new_grid (2, false);
326 DrawingTools.add_new_grid (4, false);
327 }
328
329 if (!is_null (Toolbox.background_tools)) {
330 Toolbox.background_tools.remove_images ();
331 }
332
333 KerningTools.update_kerning_classes ();
334
335 return current_font;
336 }
337
338 public static void set_settings_directory (string directory) {
339 settings_subdirectory = directory;
340 }
341
342 public static File get_preview_directory () {
343 File settings = get_settings_directory ();
344 File backup = get_child(settings, "preview");
345
346 if (!backup.query_exists ()) {
347 DirUtils.create ((!) backup.get_path (), 0755);
348 }
349
350 return backup;
351 }
352
353 public static void set_settings_subdir (string? subdir) {
354 settings_subdirectory = subdir;
355 }
356
357 internal static File get_settings_directory () {
358 string home_path;
359 File home;
360 File settings;
361
362 #if ANDROID
363 home_path = "/data/data/org.birdfont.sefyr/files";
364 home = File.new_for_path (home_path);
365
366 if (!home.query_exists ()) {
367 printd ("Create settings directory.");
368 DirUtils.create ((!) home.get_path (),0755);
369 }
370 #else
371 home_path = (settings_directory != null)
372 ? (!) settings_directory : Environment.get_user_config_dir ();
373
374 if (is_null (home_path)) {
375 warning ("No home directory set.");
376 home_path = ".";
377 }
378
379 home = File.new_for_path (home_path);
380 #endif
381
382 if (settings_subdirectory != null) {
383 settings = get_child(home, (!) settings_subdirectory);
384 } else {
385 settings = get_child(home, "birdfont");
386 }
387
388 if (!settings.query_exists ()) {
389 DirUtils.create ((!) settings.get_path (), 0755);
390 }
391
392 return settings;
393 }
394
395 internal static File get_backup_directory () {
396 File settings = get_settings_directory ();
397 File backup = get_child (settings, "backup");
398
399 if (!backup.query_exists ()) {
400 DirUtils.create ((!) backup.get_path (), 0755);
401 }
402
403 return backup;
404 }
405
406 public static bool has_argument (string param) {
407 if (is_null (args)) {
408 return false;
409 }
410
411 return args.has_argument (param);
412 }
413
414 internal static string? get_argument (string param) {
415 return args.get_argument (param);
416 }
417
418 public static void debug_message (string s) {
419 if (unlikely (has_logging ())) {
420 try {
421 if (BirdFont.logstream != null) {
422 ((!)BirdFont.logstream).put_string (s);
423 ((!)BirdFont.logstream).flush ();
424 } else {
425 warning ("No logstream.");
426 }
427
428 stderr.printf (s);
429 } catch (GLib.Error e) {
430 warning (e.message);
431 }
432 }
433 }
434 }
435
436 void init_logfile () {
437 DateTime t;
438 File settings;
439 string s;
440 File log;
441
442 try {
443 t = new DateTime.now_local ();
444 settings = BirdFont.get_settings_directory ();
445 s = t.to_string ().replace (":", "_");
446 log = get_child (settings, @"birdfont_$s.log");
447
448 BirdFont.logstream = new DataOutputStream (log.create (FileCreateFlags.REPLACE_DESTINATION));
449 ((!)BirdFont.logstream).put_string ((!) log.get_path ());
450 ((!)BirdFont.logstream).put_string ("\n");
451
452 warning ("Logging to " + (!) log.get_path ());
453 } catch (GLib.Error e) {
454 warning (e.message);
455 warning ((!) log.get_path ());
456 }
457
458 LogLevelFlags levels = LogLevelFlags.LEVEL_ERROR | LogLevelFlags.LEVEL_CRITICAL | LogLevelFlags.LEVEL_WARNING | LogLevelFlags.LEVEL_DEBUG;
459 Log.set_handler (null, levels, log_warning);
460
461 BirdFont.logging = true;
462
463 printd (@"Program version: $(VERSION)\n");
464 }
465
466 internal static void log_warning (string? log_domain, LogLevelFlags log_levels, string message) {
467 if (log_domain != null) {
468 printd ((!) log_domain);
469 }
470
471 printd ("\n");
472 printd (message);
473 printd ("\n");
474 printd ("\n");
475 }
476
477 /** Write debug output to logfile. */
478 public static void printd (string s) {
479 #if ANDROID
480 __android_log_print (ANDROID_LOG_WARN, "BirdFont", s);
481 #else
482 BirdFont.debug_message (s);
483 #endif
484 }
485
486 /** Translate string */
487 public string t_ (string t) {
488 #if ANDROID
489 return t;
490 #else
491 return _(t);
492 #endif
493 }
494
495 /** Translate mac menu items */
496 public static string translate_mac (string t) {
497 string s = t_(t);
498 return s.replace ("_", "");
499 }
500
501 /** Print a warning if Birdfont was started with the --test argument. */
502 public static void warn_if_test (string message) {
503 if (BirdFont.has_argument ("--test")) {
504 warning (message);
505 }
506 }
507
508 /** Obtain a handle to a file in a folder. */
509 public static File get_child (File folder, string file_name) {
510 string f;
511 string s;
512 string n;
513
514 // avoid drive letter problems on windows
515
516 f = (!) folder.get_path ();
517
518 #if LINUX
519 s = "/";
520 #else
521 s = (BirdFont.win32) ? "\\" : "/";
522 #endif
523
524 n = file_name;
525 if (unlikely (BirdFont.win32 && file_name.index_of ("\\") != -1)) {
526 warning (@"File name contains path separator: $file_name, Directory: $f");
527 n = n.substring (n.last_index_of ("\\")).replace ("\\", "");
528 }
529
530 if (!f.has_suffix (s)) {
531 f += s;
532 }
533
534 printd (@"File in Directory: $f Name: $n\n");
535
536 return File.new_for_path (f + n);
537 }
538
539 public static void set_drawing_callbacks (Drawing callbacks) {
540 BirdFont.drawing = callbacks;
541 }
542
543 }
544