diff --git a/lcl/include/menuitem.inc b/lcl/include/menuitem.inc index 85689f0627..c8bf27ea69 100644 --- a/lcl/include/menuitem.inc +++ b/lcl/include/menuitem.inc @@ -1309,9 +1309,12 @@ begin // Only one item in a menu or submenu can be default. for i:=0 to FParent.Count-1 do if FParent[i].Default then - FParent[i].FDefault := False; + FParent[i].Default := False; FDefault:= AValue; - MenuChanged(True); + if HandleAllocated and not (csReading in ComponentState) and ((FParent <> nil) or (FMenu = nil)) then + TWSMenuItemClass(WidgetSetClass).SetDefault(Self, AValue); + MenuChanged(False); + OwnerFormDesignerModified(Self); end; {------------------------------------------------------------------------------ diff --git a/lcl/interfaces/gtk2/gtk2int.pas b/lcl/interfaces/gtk2/gtk2int.pas index 5f965490ff..b548c87b1e 100644 --- a/lcl/interfaces/gtk2/gtk2int.pas +++ b/lcl/interfaces/gtk2/gtk2int.pas @@ -302,7 +302,9 @@ type procedure _SetCallbackEx(const AMsg: LongInt; const AGTKObject: PGTKObject; const ALCLObject: TObject; Direct: Boolean); procedure SetCallbackEx(const AMsg: LongInt; const AGTKObject: PGTKObject; const ALCLObject: TObject; Direct: Boolean); procedure SetCommonCallbacks(const AGTKObject: PGTKObject; const ALCLObject: TObject); - procedure SetLabelCaption(const ALabel: PGtkLabel; const ACaption: String); + function SetLabelCaption(const ALabel: PGtkLabel; const ACaption: String): String; + function SetLabelCaptionMarkup(const ALabel: PGtkLabel; const ACaption: String; + AmpersandsEscape: Boolean=True; MarkupsEscape: Boolean=True): String; procedure SetSelectionMode(Sender: TObject; Widget: PGtkWidget; MultiSelect, {%H-}ExtendedSelect: Boolean); function ForceLineBreaks(DC : hDC; Src: PChar; MaxWidthInPixels : Longint; diff --git a/lcl/interfaces/gtk2/gtk2proc.inc b/lcl/interfaces/gtk2/gtk2proc.inc index a9178f0516..1e2a6385df 100644 --- a/lcl/interfaces/gtk2/gtk2proc.inc +++ b/lcl/interfaces/gtk2/gtk2proc.inc @@ -6023,11 +6023,17 @@ var MenuItemWidget: PGtkWidget); var LabelWidget: PGtkLabel; + s: String; begin if (MenuItemWidget = nil) or (LCLMenuItem = nil) then Exit; LabelWidget := g_object_get_data(PGObject(MenuItemWidget), 'LCLLabel'); - Gtk2Widgetset.SetLabelCaption(LabelWidget, LCLMenuItem.Caption); + if LCLMenuItem.Default then + Gtk2Widgetset.SetLabelCaptionMarkup(LabelWidget, + ''+EscapeMarkups(Ampersands2Underscore(LCLMenuItem.Caption))+'', + False, False) + else Gtk2Widgetset.SetLabelCaption(LabelWidget, LCLMenuItem.Caption); + gtk_widget_set_direction(PGtkWidget(LabelWidget), WidgetDirection[UseRTL]); end; @@ -6057,15 +6063,24 @@ var if LabelWidget = nil then begin // create a label for the ShortCut - LabelWidget := PGtkLabel(gtk_label_new(PChar(Pointer(s)))); + LabelWidget := PGtkLabel(gtk_label_new('')); g_object_set_data(PGObject(MenuItemWidget), 'LCLShortCutLabel', LabelWidget); + gtk_label_set_use_markup(LabelWidget, True); gtk_container_add(GTK_CONTAINER(HBoxWidget), PGtkWidget(LabelWidget)); + + if LCLMenuItem.Default then + gtk_label_set_markup(LabelWidget, PChar(''+EscapeMarkups(s)+'')) + else gtk_label_set_text(LabelWidget, PChar(s)); + gtk_widget_show(PGtkWidget(LabelWidget)); end else begin - gtk_label_set_text(LabelWidget, PChar(Pointer(s))); + if LCLMenuItem.Default then + gtk_label_set_markup(LabelWidget, PChar(''+EscapeMarkups(s)+'')) + else gtk_label_set_text(LabelWidget, PChar(s)); end; + gtk_widget_set_direction(PGtkWidget(LabelWidget), GTK_TEXT_DIR_LTR); //Shortcut always LTR if UseRTL then gtk_misc_set_alignment(GTK_MISC(LabelWidget), 0.0, 0.5) @@ -6114,6 +6129,8 @@ var gtk_container_add(GTK_CONTAINER(HBoxWidget), PGtkWidget(LabelWidget)); SetMenuItemLabelText(LCLMenuItem, MenuItemWidget); //gtk_accel_label_set_accel_widget(GTK_ACCEL_LABEL(LabelWidget), MenuItemWidget); + gtk_label_set_use_underline(LabelWidget, True); + gtk_label_set_use_markup(LabelWidget, True); gtk_widget_show(PGtkWidget(LabelWidget)); end; @@ -9162,6 +9179,19 @@ begin end; end; +function EscapeMarkups(const Str: String): String; +var + s_escaped: Pgchar; + +begin + s_escaped:= g_markup_escape_text(PChar(Str), Length(Str)); + + //MaxM: maybe #95 but even with the escape it is seen as _ and treated as mnemonic + Result:= StringReplace(s_escaped, '__', ' ', [rfReplaceAll]); + + g_free(s_escaped); +end; + {------------------------------------------------------------------------------- Creates a new PChar. Deletes escaping ampersands, replaces the first single ampersand with an underscore and deletes all other single ampersands. @@ -9182,30 +9212,32 @@ end; -------------------------------------------------------------------------------} function Ampersands2Underscore(const ASource: String): String; var - n: Integer; + n, len: Integer; FirstFound: Boolean; + s: String; begin //TODO: escape underscores FirstFound := False; - Result := ASource; n := 1; - while n <= Length(Result) do + Result:= ''; + len:= Length(ASource); + while n <= len do begin - if Result[n] = '&' then - begin - if FirstFound - or ( (n < Length(Result)) and (Result[n+1] = '&') ) // got && - then begin - Delete(Result, n, 1); - if not FirstFound then - Inc(n); // Skip the second & of && - end - else begin - FirstFound := True; - Result[n] := '_'; - end; + case ASource[n] of + '_': Result := Result + '__'; + '&': if (n < len) and (ASource[n+1] = '&') then // got && + begin + Result:= Result + '&'; + Inc(n); // Skip the second & of && + end + else if not(FirstFound) then + begin + Result:= Result+ '_'; + FirstFound:= True; + end; + else Result:= Result+ ASource[n]; end; - Inc(n); + inc(n); end; end; diff --git a/lcl/interfaces/gtk2/gtk2proc.pp b/lcl/interfaces/gtk2/gtk2proc.pp index 26919d85bd..b7b8cb8901 100644 --- a/lcl/interfaces/gtk2/gtk2proc.pp +++ b/lcl/interfaces/gtk2/gtk2proc.pp @@ -591,6 +591,9 @@ procedure ConnectInternalWidgetsSignals(AWidget: PGtkWidget; //-- +// markups +function EscapeMarkups(const Str: String): String; + // accelerators function Ampersands2Underscore(Src: PChar): PChar; function Ampersands2Underscore(const ASource: String): String; diff --git a/lcl/interfaces/gtk2/gtk2widgetset.inc b/lcl/interfaces/gtk2/gtk2widgetset.inc index 4956b73290..447627bde8 100644 --- a/lcl/interfaces/gtk2/gtk2widgetset.inc +++ b/lcl/interfaces/gtk2/gtk2widgetset.inc @@ -950,32 +950,22 @@ begin ConnectSignal(AGTKObject,'grab-notify',@gtk2GrabNotify, ALCLObject); end; -procedure TGtk2WidgetSet.SetLabelCaption(const ALabel: PGtkLabel; - const ACaption: String); -var - s: String; - i: Integer; +function TGtk2WidgetSet.SetLabelCaption(const ALabel: PGtkLabel; + const ACaption: String): String; begin - s := ''; - i := 1; - while i <= Length(ACaption) do - begin - case ACaption[i] of - '_': s := s + '__'; - '&': - if (i < Length(ACaption)) and (ACaption[i + 1] = '&') then - begin - s := s + '&'; - inc(i); - end - else - s := s + '_'; - else - s := s + ACaption[i]; - end; - inc(i); - end; - gtk_label_set_text_with_mnemonic(ALabel, PChar(s)); + Result:= Ampersands2Underscore(ACaption); + gtk_label_set_text_with_mnemonic(ALabel, PChar(Result)); +end; + +function TGtk2WidgetSet.SetLabelCaptionMarkup(const ALabel: PGtkLabel; + const ACaption: String; AmpersandsEscape: Boolean; MarkupsEscape: Boolean): String; +begin + Result:= ACaption; + if AmpersandsEscape then Result:= Ampersands2Underscore(Result); + if MarkupsEscape then Result:= EscapeMarkups(Result); + + gtk_label_set_use_markup(ALabel, True); + gtk_label_set_markup_with_mnemonic(ALabel, PChar(Result)); end; {------------------------------------------------------------------------------ diff --git a/lcl/interfaces/gtk2/gtk2wsmenus.pp b/lcl/interfaces/gtk2/gtk2wsmenus.pp index 50413de00c..81fe0afa5f 100644 --- a/lcl/interfaces/gtk2/gtk2wsmenus.pp +++ b/lcl/interfaces/gtk2/gtk2wsmenus.pp @@ -44,6 +44,7 @@ type class procedure SetShortCut(const AMenuItem: TMenuItem; const ShortCutK1, ShortCutK2: TShortCut); override; class procedure SetVisible(const AMenuItem: TMenuItem; const Visible: boolean); override; class function SetCheck(const AMenuItem: TMenuItem; const Checked: boolean): boolean; override; + class function SetDefault(const AMenuItem: TMenuItem; const ADefault: boolean): boolean; override; class function SetEnable(const AMenuItem: TMenuItem; const Enabled: boolean): boolean; override; class function SetRadioItem(const AMenuItem: TMenuItem; const {%H-}RadioItem: boolean): boolean; override; class function SetRightJustify(const AMenuItem: TMenuItem; const Justified: boolean): boolean; override; @@ -452,6 +453,23 @@ begin end; end; +class function TGtk2WSMenuItem.SetDefault(const AMenuItem: TMenuItem; const ADefault: boolean): boolean; +var + MenuItemWidget: PGtkWidget; + +begin + Result:= False; + + if not WSCheckMenuItem(AMenuItem, 'SetDefault') then + Exit; + + MenuItemWidget:={%H-}PGtkWidget(AMenuItem.Handle); + UpdateInnerMenuItem(AMenuItem,MenuItemWidget); +// gtk_widget_set_sensitive({%H-}PGtkWidget(AMenuItem.Handle), AMenuItem.Enabled); + + Result:= True; +end; + class function TGtk2WSMenuItem.SetEnable(const AMenuItem: TMenuItem; const Enabled: boolean): boolean; begin diff --git a/lcl/widgetset/wsmenus.pp b/lcl/widgetset/wsmenus.pp index 467b6fe365..635402d04e 100644 --- a/lcl/widgetset/wsmenus.pp +++ b/lcl/widgetset/wsmenus.pp @@ -59,6 +59,7 @@ type class procedure SetShortCut(const AMenuItem: TMenuItem; const ShortCutK1, ShortCutK2: TShortCut); virtual; class procedure SetVisible(const AMenuItem: TMenuItem; const Visible: boolean); virtual; class function SetCheck(const AMenuItem: TMenuItem; const Checked: boolean): boolean; virtual; + class function SetDefault(const AMenuItem: TMenuItem; const ADefault: boolean): boolean; virtual; class function SetEnable(const AMenuItem: TMenuItem; const Enabled: boolean): boolean; virtual; class function SetRadioItem(const AMenuItem: TMenuItem; const RadioItem: boolean): boolean; virtual; class function SetRightJustify(const AMenuItem: TMenuItem; const Justified: boolean): boolean; virtual; @@ -157,6 +158,11 @@ begin Result := false; end; +class function TWSMenuItem.SetDefault(const AMenuItem: TMenuItem; const ADefault: boolean): boolean; +begin + Result := false; +end; + class function TWSMenuItem.SetEnable(const AMenuItem: TMenuItem; const Enabled: boolean): boolean; begin Result := false;