.
1 /*
2 Copyright (C) 2012 2014 2015 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 public static string? sandbox_directory = null;
98
99 public BirdFont () {
100 set_defaul_drawing_callbacks ();
101 }
102
103 void set_defaul_drawing_callbacks () {
104 if (drawing == null) {
105 drawing = new Drawing ();
106 }
107 }
108
109 /**
110 * @param arg command line arguments
111 * @param program path
112 * @param setting subdirectory
113 */
114 public void init (string[] arg, string? program_path, string? settings_subdir, string? sandbox_path) {
115 int i;
116 File font_file;
117 string exec_path;
118 string theme;
119 int default_theme_version;
120 string theme_version;
121 CharDatabaseParser parser;
122 CodePageBits codepage_bits;
123
124 set_settings_subdir (settings_subdir);
125 sandbox_directory = sandbox_path;
126
127 args = new Argument.command_line (arg);
128 Font.empty = new Font ();
129
130 #if ANDROID
131 BirdFont.logging = true;
132
133 __android_log_print (ANDROID_LOG_WARN, "BirdFont", @"libbirdfont version $VERSION");
134 LogLevelFlags log_levels = LogLevelFlags.LEVEL_ERROR | LogLevelFlags.LEVEL_CRITICAL | LogLevelFlags.LEVEL_WARNING;
135 Log.set_handler (null, log_levels, android_warning);
136
137 android = true;
138 #else
139 stdout.printf ("birdfont version %s\n", VERSION);
140
141 android = args.has_argument ("--android");
142
143 if (!BirdFont.logging) {
144 BirdFont.logging = args.has_argument ("--log");
145 }
146 #endif
147
148 if (BirdFont.logging) {
149 init_logfile ();
150 }
151
152 if (!args.has_argument ("--no-translation")) {
153 init_gettext ();
154 }
155
156 if (args.has_argument ("--help")) {
157 args.print_help ();
158 Process.exit (0);
159 }
160
161 #if !MAC
162 int err_arg = args.validate ();
163 if (err_arg != 0) {
164 stdout.printf (@"Unknown parameter $(arg [err_arg])\n\n");
165 args.print_help ();
166 Process.exit (0);
167 }
168 #endif
169 Preferences.load ();
170
171 // always load default theme when names in theme does change
172 default_theme_version = 1;
173 theme = Preferences.get ("theme");
174 theme_version = Preferences.get ("theme_version");
175
176 Theme.set_default_colors ();
177
178 if (theme_version == "" || int.parse (theme_version) < default_theme_version) {
179 Theme.load_theme ("dark.theme");
180 Preferences.set ("theme", "dark.theme");
181 } else {
182 if (theme != "") {
183 Theme.load_theme (theme);
184 } else {
185 Theme.load_theme ("dark.theme");
186 }
187 }
188
189 Preferences.set ("theme_version", @"$default_theme_version");
190
191 current_font = new Font ();
192 current_font.set_name ("");
193 current_font.initialised = false;
194 current_glyph_collection = new GlyphCollection.with_glyph ('\0', "");
195
196 experimental = args.has_argument ("--test");
197 show_coordinates = args.has_argument ("--show-coordinates") || experimental;
198 fatal_wanings = args.has_argument ("--fatal-warning");
199 win32 = (arg[0].index_of (".exe") > -1)
200 || arg[0] == "wine"
201 || args.has_argument ("--windows");
202
203 #if MAC
204 mac = true;
205 #else
206 mac = args.has_argument ("--mac");
207 #endif
208
209 if (program_path == null) {
210 exec_path = "";
211
212 if (win32) {
213 // wine hack to get "." folder in win32 environment
214 i = arg[0].last_index_of ("\\");
215
216 if (i != -1) {
217 exec_path = arg[0];
218 exec_path = exec_path.substring (0, i);
219 exec_path = wine_to_unix_path (exec_path);
220 }
221 } else {
222 exec_path = "./";
223 }
224 } else {
225 exec_path = (!) program_path;
226 }
227
228 if (args.get_file () != "") {
229 font_file = File.new_for_path (args.get_file ());
230
231 if (!font_file.query_exists ()) {
232 stderr.printf (@"The file \"$(args.get_file ())\" was not found.\n");
233 Process.exit (-1);
234 }
235 }
236
237 if (fatal_wanings) {
238 LogLevelFlags levels = LogLevelFlags.LEVEL_ERROR | LogLevelFlags.LEVEL_CRITICAL | LogLevelFlags.LEVEL_WARNING;
239 Log.set_handler (null, levels, fatal_warning);
240 }
241
242 Preferences.set_last_file (get_current_font ().get_path ());
243
244 DefaultCharacterSet.create_default_character_sets ();
245 DefaultCharacterSet.get_characters_for_prefered_language ();
246
247 HeadTable.init (1024);
248
249 if (TestBirdFont.get_singleton ().test_cases_to_run != "All") {
250 TestBirdFont.run_tests ();
251 }
252
253 if (has_argument ("--parse-ucd")) {
254 parser = new CharDatabaseParser ();
255 parser.regenerate_database ();
256 }
257
258 if (has_argument ("--codepages")) {
259 codepage_bits = new CodePageBits ();
260 codepage_bits.generate_codepage_database ();
261 }
262 }
263
264 public static bool has_logging () {
265 bool log;
266
267 lock (BirdFont.logging) {
268 log = BirdFont.logging;
269 }
270
271 return log;
272 }
273
274 public static Argument get_arguments () {
275 return args;
276 }
277
278 public static void set_bundle_path (string path) {
279 bundle_path = path;
280 }
281
282 public static void init_gettext () {
283 // FIXME: android, this should be OK now
284 #if !ANDROID
285 string locale_directory = SearchPaths.get_locale_directory ();
286 Intl.setlocale (LocaleCategory.MESSAGES, "");
287 Intl.bind_textdomain_codeset (GETTEXT_PACKAGE, "utf-8");
288 Intl.bindtextdomain (GETTEXT_PACKAGE, locale_directory);
289 #endif
290 }
291
292 public static void load_font_from_command_line () {
293 string file = args.get_file ();
294 if (file != "") {
295 RecentFiles.load_font (file);
296 }
297 }
298
299 public static Font get_current_font () {
300 return current_font;
301 }
302
303 internal static void fatal_warning (string? log_domain, LogLevelFlags log_levels, string message) {
304 bool fatal = true;
305
306 if (log_domain != null) {
307 stderr.printf ("%s: \n", (!) log_domain);
308 }
309
310 stderr.printf ("\n%s\n\n", message);
311 assert (!fatal);
312 }
313
314 #if ANDROID
315 internal static void android_warning (string? log_domain, LogLevelFlags log_levels, string message) {
316 __android_log_print (ANDROID_LOG_WARN, "BirdFont", message);
317 }
318 #endif
319
320 public static Font new_font () {
321 current_font = new Font ();
322
323 if (!is_null (MainWindow.tools)) {
324 MainWindow.get_drawing_tools ().remove_all_grid_buttons ();
325 DrawingTools.add_new_grid (1, false);
326 DrawingTools.add_new_grid (2, false);
327 DrawingTools.add_new_grid (4, false);
328 }
329
330 if (!is_null (Toolbox.background_tools)) {
331 Toolbox.background_tools.remove_images ();
332 }
333
334 KerningTools.update_kerning_classes ();
335
336 return current_font;
337 }
338
339 public static string? get_sandbox_directory () {
340 return sandbox_directory;
341 }
342
343 public static void set_settings_directory (string directory) {
344 settings_subdirectory = directory;
345 }
346
347 public static File get_preview_directory () {
348 string? export = BirdFont.get_current_font ().get_export_directory ();
349
350 if (export == null) {
351 warning ("No export directory is set.");
352 export = "";
353 }
354
355 File e = File.new_for_path ((!) export);
356 File p = get_child(e, "preview");
357
358 return p;
359 }
360
361 public static void set_settings_subdir (string? subdir) {
362 settings_subdirectory = subdir;
363 }
364
365 internal static File get_settings_directory () {
366 string home_path;
367 File home;
368 File settings;
369
370 #if ANDROID
371 home_path = "/data/data/org.birdfont.sefyr/files";
372 home = File.new_for_path (home_path);
373
374 if (!home.query_exists ()) {
375 printd ("Create settings directory.");
376 DirUtils.create ((!) home.get_path (),0755);
377 }
378 #else
379 if (sandbox_directory != null) {
380 home = File.new_for_path ((!) sandbox_directory);
381 } else {
382 home_path = (settings_directory != null)
383 ? (!) settings_directory : Environment.get_user_config_dir ();
384
385 if (is_null (home_path)) {
386 warning ("No home directory set.");
387 home_path = ".";
388 }
389
390 home = File.new_for_path (home_path);
391 }
392 #endif
393
394 if (settings_subdirectory != null) {
395 settings = get_child(home, (!) settings_subdirectory);
396 } else {
397 settings = get_child(home, "birdfont");
398 }
399
400 if (!settings.query_exists ()) {
401 DirUtils.create ((!) settings.get_path (), 0755);
402 }
403
404 return settings;
405 }
406
407 internal static File get_backup_directory () {
408 File settings = get_settings_directory ();
409 File backup = get_child (settings, "backup");
410
411 if (!backup.query_exists ()) {
412 DirUtils.create ((!) backup.get_path (), 0755);
413 }
414
415 return backup;
416 }
417
418 public static bool has_argument (string param) {
419 if (is_null (args)) {
420 return false;
421 }
422
423 return args.has_argument (param);
424 }
425
426 internal static string? get_argument (string param) {
427 return args.get_argument (param);
428 }
429
430 public static void debug_message (string s) {
431 if (unlikely (has_logging ())) {
432 try {
433 if (BirdFont.logstream != null) {
434 ((!)BirdFont.logstream).put_string (s);
435 ((!)BirdFont.logstream).flush ();
436 } else {
437 warning ("No logstream.");
438 }
439
440 stderr.printf (s);
441 } catch (GLib.Error e) {
442 warning (e.message);
443 }
444 }
445 }
446 }
447
448 void init_logfile () {
449 DateTime t;
450 File settings;
451 string s;
452 File log;
453
454 try {
455 t = new DateTime.now_local ();
456 settings = BirdFont.get_settings_directory ();
457 s = t.to_string ().replace (":", "_");
458 log = get_child (settings, @"birdfont_$s.log");
459
460 BirdFont.logstream = new DataOutputStream (log.create (FileCreateFlags.REPLACE_DESTINATION));
461 ((!)BirdFont.logstream).put_string ((!) log.get_path ());
462 ((!)BirdFont.logstream).put_string ("\n");
463
464 warning ("Logging to " + (!) log.get_path ());
465 } catch (GLib.Error e) {
466 warning (e.message);
467 warning ((!) log.get_path ());
468 }
469
470 LogLevelFlags levels = LogLevelFlags.LEVEL_ERROR | LogLevelFlags.LEVEL_CRITICAL | LogLevelFlags.LEVEL_WARNING | LogLevelFlags.LEVEL_DEBUG;
471 Log.set_handler (null, levels, log_warning);
472
473 BirdFont.logging = true;
474
475 printd (@"Program version: $(VERSION)\n");
476 }
477
478 internal static void log_warning (string? log_domain, LogLevelFlags log_levels, string message) {
479 if (log_domain != null) {
480 printd ((!) log_domain);
481 }
482
483 printd ("\n");
484 printd (message);
485 printd ("\n");
486 printd ("\n");
487 }
488
489 /** Write debug output to logfile. */
490 public static void printd (string s) {
491 #if ANDROID
492 __android_log_print (ANDROID_LOG_WARN, "BirdFont", s);
493 #else
494 BirdFont.debug_message (s);
495 #endif
496 }
497
498 /** Translate string */
499 public string t_ (string t) {
500 #if ANDROID
501 return t;
502 #else
503 string translate = Preferences.get ("translate");
504
505 if (translate == "" || translate == "true") {
506 return _(t);
507 } else {
508 return t;
509 }
510 #endif
511 }
512
513 /** Translate mac menu items */
514 public static string translate_mac (string t) {
515 string s = t_(t);
516 return s.replace ("_", "");
517 }
518
519 /** Print a warning if Birdfont was started with the --test argument. */
520 public static void warn_if_test (string message) {
521 if (BirdFont.has_argument ("--test")) {
522 warning (message);
523 }
524 }
525
526 /** Obtain a handle to a file in a folder. */
527 public static File get_child (File folder, string file_name) {
528 string f;
529 string s;
530 string n;
531
532 // avoid drive letter problems on windows
533
534 f = (!) folder.get_path ();
535
536 #if LINUX
537 s = "/";
538 #else
539 s = (BirdFont.win32) ? "\\" : "/";
540 #endif
541
542 n = file_name;
543 if (unlikely (BirdFont.win32 && file_name.index_of ("\\") != -1)) {
544 warning (@"File name contains path separator: $file_name, Directory: $f");
545 n = n.substring (n.last_index_of ("\\")).replace ("\\", "");
546 }
547
548 if (!f.has_suffix (s)) {
549 f += s;
550 }
551
552 printd (@"File in Directory: $f Name: $n\n");
553
554 return File.new_for_path (f + n);
555 }
556
557 public static void set_drawing_callbacks (Drawing callbacks) {
558 BirdFont.drawing = callbacks;
559 }
560
561 }
562