Bladeren bron

* android: Implemented clocale. Fixed makefile.

git-svn-id: trunk@34349 -
yury 9 jaren geleden
bovenliggende
commit
4a661ea8f8
3 gewijzigde bestanden met toevoegingen van 337 en 4 verwijderingen
  1. 5 2
      packages/rtl-extra/fpmake.pp
  2. 328 2
      packages/rtl-extra/src/android/clocale.pp
  3. 4 0
      packages/rtl-extra/src/unix/clocale.pp

+ 5 - 2
packages/rtl-extra/fpmake.pp

@@ -12,8 +12,7 @@ Const
   // in workable state atm.
   UnixLikes = AllUnixOSes -[QNX]; // qnx never was active in 2.x afaik
 
-  // Android has a dummy clocale unit, while it also includes unix dir.
-  ClocaleOSes   = UnixLikes -[beos];
+  ClocaleOSes   = UnixLikes -[beos,android];
   CLocaleIncOSes= [Aix,freebsd,netbsd,openbsd,solaris,darwin,iphonesim,dragonfly];
 
   IPCOSes       = UnixLikes-[aix,android,beos,haiku];
@@ -82,6 +81,10 @@ begin
     P.IncludePath.Add('src/darwin',[iphonesim]);
     P.IncludePath.Add('src/win',AllWindowsOSes);
 
+    // Add clocale for Android first in order to compile the source file
+    // from the 'android' dir, not the 'unix' dir.
+    T:=P.Targets.AddUnit('clocale.pp',[android]);
+
     T:=P.Targets.AddUnit('ucomplex.pp',UComplexOSes);
 
     T:=P.Targets.AddUnit('objects.pp',ObjectsOSes);

+ 328 - 2
packages/rtl-extra/src/android/clocale.pp

@@ -1,8 +1,8 @@
 {
     This file is part of the Free Pascal run time library.
-    Copyright (c) 2013 by the Free Pascal development team.
+    Copyright (c) 2016 by the Free Pascal development team.
 
-    Android locale support. !!! NOT IMPLEMENTED !!!
+    Android locale support.
 
     See the file COPYING.FPC, included in this distribution,
     for details about the copyright.
@@ -18,6 +18,332 @@ unit clocale;
 
 interface
 
+uses
+  cwstring, SysUtils;
+
+procedure GetAndroidFormatSettings(var ASettings: TFormatSettings; ALocale: utf8string = '');
+
 implementation
 
+type
+  UErrorCode = SizeInt;
+  int32_t = longint;
+  uint32_t = longword;
+  UBool = LongBool;
+
+  UDateFormat = pointer;
+  UDateFormatStyle = longint;
+  UDate = double;
+  UNumberFormat = pointer;
+  UNumberFormatStyle = longint;
+  UNumberFormatSymbol = longint;
+
+const
+  UDAT_NONE   = -1;
+  UDAT_FULL   = 0;
+  UDAT_LONG   = 1;
+  UDAT_MEDIUM = 2;
+  UDAT_SHORT  = 3;
+
+  UNUM_DECIMAL_SEPARATOR_SYMBOL = 0;
+  UNUM_GROUPING_SEPARATOR_SYMBOL = 1;
+  UNUM_CURRENCY_SYMBOL = 8;
+
+var
+  FunctionsInited: boolean;
+
+  udat_open: function (timeStyle: UDateFormatStyle; dateStyle: UDateFormatStyle; locale: PAnsiChar; tzID: PUnicodeChar; tzIDLength: int32_t;
+                       pattern: PUnicodeChar; patternLength: int32_t; var status: UErrorCode): UDateFormat; cdecl;
+  udat_close: procedure (format: UDateFormat); cdecl;
+  udat_toPattern: function (format: UDateFormat; localized: UBool; result: PUnicodeChar; resultLength: int32_t; var status: UErrorCode): int32_t; cdecl;
+  udat_applyPattern: procedure (format: UDateFormat; localized: UBool; pattern: PUnicodeChar; patternLength: int32_t); cdecl;
+  udat_format: function (format: UDateFormat; dateToFormat: UDate; result: PUnicodeChar; resultLength: int32_t; position: pointer; var status: UErrorCode): int32_t; cdecl;
+
+  unum_open: function (style: UNumberFormatStyle; pattern: PUnicodeChar; patternLength: int32_t; locale: PAnsiChar; parseErr: pointer;
+                       var status: UErrorCode): UNumberFormat; cdecl;
+  unum_close: procedure (fmt: UNumberFormat); cdecl;
+  unum_getSymbol: function (fmt: UNumberFormat; symbol: UNumberFormatSymbol; result: PUnicodeChar; resultLength: int32_t; var status: UErrorCode): int32_t; cdecl;
+
+function GetIcuProc(const Name: AnsiString; out ProcPtr; libId: longint = 0): boolean; external name 'CWSTRING_GET_ICU_PROC';
+
+procedure InitIcuFunctions;
+begin
+  if FunctionsInited then exit;
+  if not GetIcuProc('udat_open', udat_open, 1) then exit;
+  if not GetIcuProc('udat_close', udat_close, 1) then exit;
+  if not GetIcuProc('udat_toPattern', udat_toPattern, 1) then exit;
+  if not GetIcuProc('udat_applyPattern', udat_applyPattern, 1) then exit;
+  if not GetIcuProc('udat_format', udat_format, 1) then exit;
+  if not GetIcuProc('unum_open', unum_open, 1) then exit;
+  if not GetIcuProc('unum_close', unum_close, 1) then exit;
+  if not GetIcuProc('unum_getSymbol', unum_getSymbol, 1) then exit;
+
+  FunctionsInited:=True;
+end;
+
+{$ifdef android}
+
+function GetCurrentLocaleStr: utf8string;
+var
+  s: utf8string;
+begin
+  Result:=GetSystemProperty('persist.sys.language');
+  if Result = '' then
+    exit;
+  s:=GetSystemProperty('persist.sys.country');
+  if s = '' then
+    exit;
+  Result:=Result + '_' + s;
+end;
+
+{$endif android}
+
+function StrOfChar(c: AnsiChar; Count: integer): utf8string;
+begin
+  SetLength(Result, Count);
+  FillChar(PAnsiChar(Result)^, Count, c);
+end;
+
+function ConvertFormatStr(const fmt: utf8string): utf8string;
+var
+  cnt: integer;
+  c, q: AnsiChar;
+  p: PAnsiChar;
+  s: utf8string;
+begin
+  Result:='';
+  q:=#0;
+  cnt:=1;
+  p:=PAnsiChar(fmt);
+  while p^<>#0 do
+    begin
+      s:='';
+      c:=p^;
+      if c in ['''', '"'] then
+        begin
+          if q=#0 then
+            q:=c
+          else
+            if c=q then
+              begin
+                q:=#0;
+                cnt:=1;
+              end;
+          s:=c;
+        end
+      else if q <> #0 then
+        s:=c
+      else
+        begin
+          if (p+1)^=c then
+            Inc(cnt)
+          else
+            begin
+              case c of
+                'y', 'Y':
+                  begin
+                    c:='y';
+                    if cnt > 2 then
+                      cnt:=4
+                    else
+                      cnt:=2;
+                  end;
+                'M', 'L':
+                  begin
+                    c:='m';
+                    if cnt > 4 then
+                      cnt:=3;
+                  end;
+                'd':
+                  if cnt > 2 then
+                    cnt:=2;
+                'E', 'e', 'c':
+                  begin
+                    c:='d';
+                    if (cnt < 3) or (cnt > 4) then
+                      cnt:=3;
+                  end;
+                'a':
+                  begin
+                    cnt:=0;
+                    s:='ampm';
+                  end;
+                'h', 'H', 'k', 'K':
+                  begin
+                    c:='h';
+                    if cnt > 2 then
+                      cnt:=2;
+                  end;
+                'm':
+                  begin
+                    c:='n';
+                    if cnt>2 then
+                      cnt:=2;
+                  end;
+                's':
+                  if cnt>2 then
+                    cnt:=2;
+                'S':
+                  begin
+                    c:='z';
+                    cnt:=1;
+                  end;
+                'G','u','Q','q','w','W','D','F','g','A','z','Z','v':
+                  cnt:=0;
+              end;
+              if cnt>0 then
+                s:=StrOfChar(c, cnt);
+              cnt:=1;
+            end;
+        end;
+      Inc(p);
+      if s<>'' then
+        Result:=Result+s;
+    end;
+end;
+
+procedure GetAndroidFormatSettings(var ASettings: TFormatSettings; ALocale: utf8string);
+const
+  SGMT = 'GMT';
+
+var
+  locale: ansistring;
+
+  function _GetFormat(dateStyle: UDateFormatStyle; timeStyle: UDateFormatStyle; const DefFormat: utf8string): utf8string;
+  var
+    fmt: UDateFormat;
+    err: UErrorCode;
+    res: unicodestring;
+  begin
+    Result:='';
+    err:=0;
+    fmt:=udat_open(timeStyle, dateStyle, PAnsiChar(locale), SGMT, Length(SGMT), nil, 0, err);
+    if fmt <> nil then
+      begin
+        SetLength(res, 200);
+        SetLength(res, udat_toPattern(fmt, False, PUnicodeChar(res), Length(res), err));
+        udat_close(fmt);
+        Result:=ConvertFormatStr(utf8string(res));
+      end;
+    if Result = '' then
+      Result:=DefFormat;
+  end;
+
+  function _DateToStr(fmt: UDateFormat; const AFormat: unicodestring; AYear: integer; AMonth, ADay, AHour: byte): utf8string;
+  var
+    d: double;
+    err: UErrorCode;
+    res: unicodestring;
+  begin
+    d:=EncodeDate(AYear, AMonth, ADay) + EncodeTime(AHour, 0, 0, 0) - UnixDateDelta;
+    d:=MSecsPerDay*d;
+    udat_applyPattern(fmt, False, PUnicodeChar(AFormat), Length(AFormat));
+    err:=0;
+    SetLength(res, 200);
+    SetLength(res, udat_format(fmt, d, PUnicodeChar(res), Length(res), nil, err));
+    Result:=utf8string(res);
+  end;
+
+  function _GetSeparator(dateStyle: UDateFormatStyle; timeStyle: UDateFormatStyle; DefSep: char): char;
+  var
+    fmt: UDateFormat;
+    err: UErrorCode;
+    s: utf8string;
+    p: PAnsiChar;
+    res: unicodestring;
+  begin
+    Result:=DefSep;
+    err:=0;
+    fmt:=udat_open(timeStyle, dateStyle, PAnsiChar(locale), SGMT, Length(SGMT), nil, 0, err);
+    if fmt <> nil then
+      begin
+        SetLength(res, 200);
+        SetLength(res, udat_toPattern(fmt, False, PUnicodeChar(res), Length(res), err));
+        s:=_DateToStr(fmt, res, 2000, 1, 1, 0);
+        udat_close(fmt);
+        s:=Trim(s);
+        p:=PAnsiChar(s);
+        while p^<>#0 do
+          if (p^>' ') and (p^<'A') and not (p^ in ['0'..'9']) then
+            begin
+              Result:=p^;
+              break;
+            end
+          else
+            Inc(p);
+      end;
+  end;
+
+var
+  fmt: UDateFormat;
+  nfmt: UNumberFormat;
+  err: UErrorCode;
+  i: integer;
+  res: unicodestring;
+begin
+  if not FunctionsInited then
+    exit;
+  locale:=ALocale;
+{$ifdef android}
+  if locale = '' then
+    locale:=GetCurrentLocaleStr;
+{$endif android}
+  if locale = '' then
+    locale:='en_US';
+  err:=0;
+  with ASettings do
+    begin
+      nfmt:=unum_open(2, nil, 0, PAnsiChar(locale), nil, err);
+      if nfmt <> nil then
+        begin
+          SetLength(res, 200);
+          SetLength(res, unum_getSymbol(nfmt, UNUM_DECIMAL_SEPARATOR_SYMBOL, PUnicodeChar(res), Length(res), err));
+          if res <> '' then
+            DecimalSeparator:=ansichar(res[1]);
+          SetLength(res, 200);
+          SetLength(res, unum_getSymbol(nfmt, UNUM_GROUPING_SEPARATOR_SYMBOL, PUnicodeChar(res), Length(res), err));
+          if res <> '' then
+            if Ord(res[1]) < 128 then
+              ThousandSeparator:=ansichar(res[1])
+            else
+              ThousandSeparator:=' ';
+          SetLength(res, 200);
+          SetLength(res, unum_getSymbol(nfmt, 8, PUnicodeChar(res), Length(res), err));
+          CurrencyString:=utf8string(res);
+          unum_close(nfmt);
+        end;
+
+      DateSeparator:=_GetSeparator(UDAT_SHORT, UDAT_NONE, DateSeparator);
+      TimeSeparator:=_GetSeparator(UDAT_NONE, UDAT_SHORT, TimeSeparator);
+
+      LongDateFormat:=_GetFormat(UDAT_LONG, UDAT_NONE, LongDateFormat);
+      ShortDateFormat:=_GetFormat(UDAT_SHORT, UDAT_NONE, ShortDateFormat);
+      LongTimeFormat:=_GetFormat(UDAT_NONE, UDAT_MEDIUM, LongTimeFormat);
+      ShortTimeFormat:=_GetFormat(UDAT_NONE, UDAT_SHORT, ShortTimeFormat);
+
+      fmt:=udat_open(UDAT_NONE, UDAT_NONE, PAnsiChar(locale), SGMT, Length(SGMT), nil, 0, err);
+      if fmt <> nil then
+        begin
+          for i:=1 to 12 do
+            begin
+              LongMonthNames[i]:=_DateToStr(fmt, 'LLLL', 2006, i, 1, 0);
+              ShortMonthNames[i]:=_DateToStr(fmt, 'LLL', 2006, i, 1, 0);
+            end;
+          for i:=1 to 7 do
+            begin
+              LongDayNames[i]:=_DateToStr(fmt, 'cccc', 2006, 1, i, 0);
+              ShortDayNames[i]:=_DateToStr(fmt, 'ccc', 2006, 1, i, 0);
+            end;
+          TimeAMString:=_DateToStr(fmt, 'a', 2006, 1, 1, 1);
+          TimePMString:=_DateToStr(fmt, 'a', 2006, 1, 1, 13);
+          udat_close(fmt);
+        end;
+    end;
+end;
+
+initialization
+  InitIcuFunctions;
+  GetAndroidFormatSettings(DefaultFormatSettings);
+
 end.
+

+ 4 - 0
packages/rtl-extra/src/unix/clocale.pp

@@ -16,6 +16,10 @@
 
 unit clocale;
 
+{$ifdef android}
+  {$error This unit is not intended for Android. Something wrong with the make file. }
+{$endif android}
+
 {$mode objfpc}
 
 interface