Add support "Bidi Mode"/"Right to left" lazarus / 7-LCL ---------------------------------------------------------------------- Zaher · 2007-05-09 10:15 Hi, i started to make a arabic Win32 project, and need to add some patches to Lazarus source for support "Bidi Mode"/"Right to left" I know it not easy to trust my code, i am still new with you, but i will try make small peaces of the patches to make you under stand it. Now, what the strategy of lazarus - Make it full compatible with Delphi - Nop, just make it more flexible and logical To understand me 1 - From Delphi 4, Delphi make a property in TControl    TBiDiMode = (bdLeftToRight, bdRightToLeft, bdRightToLeftNoAlign, bdRightToLeftReadingOnly); But after more than 10 years with many projects i am used only (bdLeftToRight, bdRightToLeft) and i use Alignment property instead of other values of BidiMode 2 - If we look at VC or VB from Microsoft we will find RightToLeft:Boolean 3 - in HTML/CSS thay are useing Direction = ["rtl"|"ltr"|""] When i asked my friends/co workers thay all prefer as HTML/CSS way, that add property to TControl named "Direction" and it declare as TDirection = (dirDefault, dirLeftToRight, dirRightToLeft); dirDefault: to remove ParentBidiMode/ParentDirection property and it is mean in HTML/CSS (inherite the value from the parent). --------- I will choose the Third way to add Right To Left support, but before i start, i need to your acceptation. Thanks ---------------------------------------------------------------------- felipemdc · 2007-05-09 13:17 Quote from: "zaher"Now, what the strategy of lazarus - Make it full compatible with Delphi - Nop, just make it more flexible and logical It's both. Lot's of people want it as compatible as possible to write software that can be compiled with both. On the other hand we have already broken compatibility on some large parts. Our help system for example, is a new creation, and thus completely incompatible. And we are also moving (slowly) to UTF-8 support, which will also be incompatible. I would say that the guidelines are: Be compatible whenever possible. Only break compatibility if the delphi system is really unsuitable for us. About your proposal, well the Delphi type does look a little wierd. Maybe we could have a hybrid solution. Keeping the names similar and partially compatible, but modifying to make it better. Something like:    TBiDiMode = (dbDefault, bdLeftToRight, bdRightToLeft); ---------------------------------------------------------------------- Marc · 2007-05-09 15:18 The help integration is not something directly bound to the VCL so that we do things different there, is not a problem. In all cases we try to keep the LCL as compatible as possible to the VCL. This means that will be TBiDiMode = (bdLeftToRight, bdRightToLeft, bdRightToLeftNoAlign, bdRightToLeftReadingOnly); We are not going to invent incompatible properties here. As a start, you can implement bdLeftToRight, bdRightToLeft ---------------------------------------------------------------------- Zaher · 2007-05-11 10:52 I take the idea, i will use TBiDiMode = (bdDefault, bdLeftToRight, bdRightToLeft); bdDefault is a default value and bdLeftToRight is the default value in Delphi so it is compatible because the both of value not loaded, but if we convert a RTL application that thay use bdRightToLeft, that still compatible in both project. I will start with TEdit, TButton to understand the Widgets, becuase not all platforms support RTL, and there is a TLabel it is a simple Graphic control. You can look to "Bidi Inno setup" as sample how i am worked http://www.parmaja.com/projects/innosetup/ ---------------------------------------------------------------------- Marc · 2007-05-14 13:25 please, don't change enums. Keep the same names and the same order.  This will break hard and is difficult to trace. ---------------------------------------------------------------------- Zaher · 2007-05-14 14:54 Yes Marc, that was i am thinking about it today when i am surfing the source, and i still need some discusses. Because there is some old BidiMode properties commented. ---------------------------------------------------------------------- Zaher · 2007-05-15 08:57 1 - There is already TBiDiMode   TBiDiMode = (bdLeftToRight,bdRightToLeft,bdRightToLeftNoAlign,bdRigh tToLeftReadingOnly); in file \fpcsource\rtl\objpas\classes\classesh.inc Is that the right place? or i must ignore it. i think TBiDiMode a (GUI or visual control) must be in Controls.pas ---------------------------------------------------------------------- Marc · 2007-05-15 09:36 if there is one already defined in fpc, there is no need to redefine it. ---------------------------------------------------------------------- Zaher · 2007-05-19 22:51 Hi again, i need some advices - Is i must make patch file and send it to tracker. - or just put my code lines here and you take it after reviewing it. I worked with svn about 2 years at my work, but without making patches, so i am not sure about it Where i can put the road map for my work, i will post it temporary here (i will move it to where you say) EDIT:    http://wiki.lazarus.freepascal.org/BidiMode ---------------------------------------------------------------------- Zaher · 2007-05-19 22:57 Forgive me if i make myself as silly student but i believe any of touch the code will make it unstable. ---------------------------------------------------------------------- Paul Ishenin · 2007-05-21 01:21 1. You can use wiki to create new page with your roadmap. 2. If you are using tortoise svn, just look at menu Create Patch. ---------------------------------------------------------------------- Zaher · 2007-05-27 10:17 Hi, I created the page but i am not good writer (especially in English), and created the patch (Tortoise svn). My ask can i apply the my patch for new updated lazarus source? i want to keep update with it. ---------------------------------------------------------------------- Paul Ishenin · 2007-05-28 09:16 I applied your patch with changes. Look at TWSWinControl.SetBiDiMode and SetBiDiMode in TWin32WSButton and TWin32WSCustomEdit classes. ---------------------------------------------------------------------- Zaher · 2007-05-30 10:40 Thank to add TWSWinControl.SetBiDiMode; I replaced RecreateWnd with SetWindowLong For example Code: [Select] class procedure TWin32WSButton.SetBiDiMode(const AWinControl: TWinControl;   const ABiDiMode: TBiDiMode); begin   RecreateWnd(AWinControl); end; to Code: [Select] class procedure TWin32WSButton.SetBiDiMode(const AWinControl: TWinControl;   const ABiDiMode: TBiDiMode); var   FlagsEx: dword; begin   if not WSCheckHandleAllocated(AWinControl, 'SetBidiMode') then Exit;   FlagsEx := GetWindowLong(AWinControl.Handle, GWL_EXSTYLE) and not (WS_EX_RTLREADING);   if AWinControl.UseRightToLeftReading then     FlagsEx := FlagsEx or WS_EX_RTLREADING;   SetWindowLong(AWinControl.Handle, GWL_EXSTYLE, FlagsEx); end; Next weekend (Friday in my country) i will to make patch for Menus and other std controls ---------------------------------------------------------------------- Paul Ishenin · 2007-05-30 11:06 Ok, then add it to bug tracker as patch after. ---------------------------------------------------------------------- Zaher · 2007-06-02 09:57 I added new patch in the tracker. I need show this function This way to send notification to all components in the form for ParentBidiMode in customform.inc Code: [Select] procedure TCustomForm.CMBiDiModeChanged(var Message: TLMessage); var   i:Integer;   lMessage:TLMessage; begin   inherited;   //send CM_PARENTBIDIMODECHANGED to All Component owned by Form {   prefer use IMenu and check it then call IMenu.ParentBidiMode   This way is usefull for other TMenu components that need BidiMode of form changed   Like as TToolbar }   lMessage.msg := CM_PARENTBIDIMODECHANGED;   lMessage.wParam := 0;   lMessage.lParam := 0;   lMessage.Result := 0;   for i := 0 to ComponentCount - 1 do   begin     if not (Components[i] is TCustomControl) then//TCustomControl already has this notification       Components[i].Dispatch(lMessage); //the old way //    if Components[i] is TMenu then //      TMenu(Components[i]).ParentBiDiModeChanged;   end; end; If you not like it you can return to the old way. Some components visual in the form but derived from TComponent like as Menu some some of TToolbar like as Toolbar2000. This component need notification from the Form about visual changes like as BidiMode, i think we need a middle class between TCompnent (or TLCLComponent) and and TCustomControl and TCustomControl derived from it and TMenu also. ---------------------------------------------------------------------- Zaher · 2007-06-08 18:51 Added small patch for TLabel in same issue in bug tracker http://www.freepascal.org/mantis/view.php?id=8996 Also DrawMenuItem to be RightToLeft drawing but i can not make Patch file (already patched) so i will post here the changes, or i make patch again Code: [Select] procedure DrawMenuItemCheckMark(const aMenuItem: TMenuItem; const aHDC: HDC; const aRect: Windows.RECT; const aSelected: boolean); var checkMarkWidth: integer;     checkMarkHeight: integer;     hdcMem: HDC;     monoBitmap: HBITMAP;     oldBitmap: HBITMAP;     checkMarkShape: integer;     checkMarkRect: Windows.RECT;     x:Integer; begin   hdcMem := CreateCompatibleDC(aHDC);   checkMarkWidth := GetSystemMetrics(SM_CXMENUCHECK);   checkMarkHeight := GetSystemMetrics(SM_CYMENUCHECK);   monoBitmap := CreateBitmap(checkMarkWidth, checkMarkHeight, 1, 1, nil);   oldBitmap := SelectObject(hdcMem, monoBitmap);   checkMarkRect.left := 0;   checkMarkRect.top := 0;   checkMarkRect.right := checkMarkWidth;   checkMarkRect.bottom := checkMarkHeight;   if aMenuItem.RadioItem then checkMarkShape := DFCS_MENUBULLET   else checkMarkShape := DFCS_MENUCHECK;   DrawFrameControl(hdcMem, @checkMarkRect, DFC_MENU, checkMarkShape);   if aMenuItem.GetIsRightToLeft then     x := aRect.Right - checkMarkWidth   else     x := aRect.left;   BitBlt(aHDC, x, aRect.top + topPosition(aRect.bottom - aRect.top, checkMarkRect.bottom - checkMarkRect.top), checkMarkWidth, checkMarkHeight, hdcMem, 0, 0, SRCCOPY);   SelectObject(hdcMem, oldBitmap);   DeleteObject(monoBitmap);   DeleteDC(hdcMem); end; procedure DrawMenuItemText(const aMenuItem: TMenuItem; const aHDC: HDC; aRect: Windows.RECT; const aSelected: boolean); var crText: COLORREF;     crBkgnd: COLORREF;     TmpLength: integer;     TmpHeight: integer;     oldFont: HFONT;     newFont: HFONT;     decoration: TCaptionFlagsSet; shortCutText: string; WorkRect: Windows.RECT;   IsRightToLeft: Boolean;   etoFlags: Cardinal;   dtFlags: Word; begin   crText := TextColorMenu(aSelected, aMenuItem.Enabled);   crBkgnd := BackgroundColorMenu(aSelected, aMenuItem.IsInMenuBar);   SetTextColor(aHDC, crText);   SetBkColor(aHDC, crBkgnd);   if aMenuItem.Default then decoration := [cfBold]   else decoration := [];   newFont := getMenuItemFont(decoration);   oldFont := SelectObject(aHDC, newFont);   IsRightToLeft := aMenuItem.GetIsRightToLeft;   etoFlags := ETO_OPAQUE;   dtFlags := 0;   if IsRightToLeft then   begin     etoFlags := etoFlags or ETO_RTLREADING;     dtFlags := dtFlags or DT_RIGHT or DT_RTLREADING;   end;   ExtTextOut(aHDC, 0, 0, etoFlags, @aRect, PChar(''), 0, nil);   TmpLength := aRect.right - aRect.left;   TmpHeight := aRect.bottom - aRect.top;   DrawText(aHDC, pChar(aMenuItem.Caption), length(aMenuItem.Caption), @WorkRect,  DT_CALCRECT);   if IsRightToLeft then     Dec(aRect.Right, leftCaptionPosition(TmpLength, WorkRect.Right - WorkRect.Left, aMenuItem))   else     Inc(aRect.Left, leftCaptionPosition(TmpLength, WorkRect.Right - WorkRect.Left, aMenuItem));   Inc(aRect.Top, topPosition(TmpHeight, WorkRect.Bottom - WorkRect.Top));   DrawText(aHDC, pChar(aMenuItem.Caption), length(aMenuItem.Caption), @aRect, dtFlags);   if aMenuItem.ShortCut <> scNone then   begin     shortCutText := ShortCutToText(aMenuItem.ShortCut);     if IsRightToLeft then     begin       Inc(aRect.Left, GetSystemMetrics(SM_CXMENUCHECK));       dtFlags := DT_LEFT;     end     else     begin       Dec(aRect.Right, GetSystemMetrics(SM_CXMENUCHECK));       dtFlags := DT_RIGHT;     end;  DrawText(aHDC, pChar(shortCutText), Length(shortCutText), @aRect, dtFlags);   end;   SelectObject(aHDC, oldFont);   DeleteObject(newFont); end; procedure DrawMenuItemIcon(const aMenuItem: TMenuItem; const aHDC: HDC; const aRect: Windows.RECT; const aSelected: boolean); var hdcMem: HDC;     hbmpOld: HBITMAP;     x:Integer; begin   hdcMem := aMenuItem.Bitmap.Canvas.Handle;   hbmpOld := SelectObject(hdcMem, aMenuItem.Bitmap.Handle);   if aMenuItem.GetIsRightToLeft then     x := aRect.Right - LeftIconPosition - aMenuItem.Bitmap.Width   else     x := aRect.Left + LeftIconPosition;   TWin32WidgetSet(WidgetSet).MaskBlt(aHDC, x, aRect.top + TopPosition(aRect.bottom - aRect.top, aMenuItem.Bitmap.Height), aMenuItem.Bitmap.Width, aMenuItem.Bitmap.Height, hdcMem, 0, 0, aMenuItem.Bitmap.MaskHandle, 0, 0);   SelectObject(hdcMem, hbmpOld); end; ---------------------------------------------------------------------- Paul Ishenin · 2007-06-09 07:33 I cannot promice that your latest patches will be in 0.9.24 since we have now other problems that should be solved in short time. But I promice you that after 0.9.24 will be released I will apply your patches (or if we'll have enough time after current problems before release). ---------------------------------------------------------------------- Zaher · 2007-06-09 21:58 Yes Paul, i know how long it take (i have a long breath), and we all have job and family The first patch that you applied is enough to me to start to porting my components, but i will keep to make some patches here and in bug the tracker to save my work (i hate to save it only in my computer). I like to get round about GTK2, it is still strange to me but i wish a good luck with it. ---------------------------------------------------------------------- Paul Ishenin · 2007-07-02 07:06 Sorry for long delay. I applied your patches today with some modifications. Look at mantis issue for details and please check if it will work with that modification. ---------------------------------------------------------------------- Zaher · 2007-07-02 22:13 Thanks, do not sorry about long time, i know you are (all the team) hard working on Lazarus. SetMenuFlag worked now without buffer but need to make MIIM_TYPE instead of MIIM_FTYPE (i fixed in the patch), but i believed i needed a buffer to save the caption of first item (this is a bug or something wrong in MS Windows) but now it is worked without it. I have next patch to fix drawing the menu item in the bug tracker See this picture for the demo after it finished (http://www.parmaja.com/temp/BidiMode_RightToLeft.png) There is also another patch (not now) i will make it for TApplication.BidiMode. ---------------------------------------------------------------------- Paul Ishenin · 2007-07-03 01:32 Well done. Maybe better to place this image to Wiki page too and to update it when new parts will be ready? ---------------------------------------------------------------------- Paul Ishenin · 2007-07-03 03:36 I applied your latest patch. But I have a question why we need MIIM_TYPE instead of MIIM_FTYPE if we only need to change menu flag? from MS help: QuoteMIIM_TYPE Retrieves or sets the fType and dwTypeData members. Windows 98/Me, Windows 2000/XP: MIIM_TYPE is replaced by MIIM_BITMAP, MIIM_FTYPE, and MIIM_STRING. If we need to change only fType then we should use MIIM_FTYPE mask. ---------------------------------------------------------------------- Zaher · 2007-07-03 13:31 I think we must lookup to Windows source , in fact that problem come from Win 95 and still to WinXp http://support.microsoft.com/kb/253308 With MIIM_FTYPE (http://www.parmaja.com/temp/BidiModeBadMenu.png) With MIIM_TYPE (http://www.parmaja.com/temp/BidiModeGoodMenu.png) I make many test to menus and look in Delphi source before make menu patch.