2020-09-24 00:43:27 -04:00
// Copyright Epic Games, Inc. All Rights Reserved.
# include "Widgets/SDirectLinkStreamManager.h"
# include "DatasmithExporterManager.h"
2020-10-22 19:19:16 -04:00
# include "DirectLinkEndpoint.h"
2020-09-24 00:43:27 -04:00
# include "DesktopPlatformModule.h"
# include "Framework/Application/SlateApplication.h"
# include "IDesktopPlatform.h"
# include "Styling/CoreStyle.h"
# include "UObject/NoExportTypes.h"
# include "Widgets/Images/SImage.h"
# include "Widgets/Input/SButton.h"
# include "Widgets/Input/SEditableText.h"
# include "Widgets/Layout/SBorder.h"
# include "Widgets/Layout/SScrollBorder.h"
# include "Widgets/SBoxPanel.h"
# include "Widgets/SNullWidget.h"
# include "Widgets/Text/STextBlock.h"
# include "Widgets/Views/SHeaderRow.h"
# include "Widgets/Views/SListView.h"
# include "Widgets/Views/STableRow.h"
# include "Widgets/Views/STreeView.h"
# define LOCTEXT_NAMESPACE "SDirectLinkStreamManager"
namespace SDirectLinkStreamManagerUtils
{
const FName SourceColumnId ( " Source " ) ;
const FName DestinationColumnId ( " Destination " ) ;
}
struct FEndpointData
{
FEndpointData ( const FString & InEndpointName , const FString & InUserName , const FString & InExecutableName , const FString & InComputerName )
: EndpointName ( InEndpointName )
, UserName ( InUserName )
, ExecutableName ( InExecutableName )
, ComputerName ( InComputerName )
{
}
FString EndpointName ;
FString UserName ;
FString ExecutableName ;
FString ComputerName ;
FText GenerateTooltipText ( ) const ;
} ;
FText FEndpointData : : GenerateTooltipText ( ) const
{
FText ToolTipNonFormated = LOCTEXT ( " EndpointTooltip " , " User : {0} \n Computer Name : {1} \n Program Name: {2} \n Endpoint Name : {3} " ) ;
return FText : : FormatOrdered ( ToolTipNonFormated ,
FText : : FromString ( UserName ) ,
FText : : FromString ( ComputerName ) ,
FText : : FromString ( ExecutableName ) ,
FText : : FromString ( EndpointName )
) ;
}
struct FSourceData
{
FSourceData ( const FString & InName , const FGuid & InGuid , const TSharedRef < FEndpointData > & InEndpointData )
: Name ( InName )
, Guid ( InGuid )
, EndpointData ( InEndpointData )
{
}
FString Name ;
FGuid Guid ;
TSharedRef < FEndpointData > EndpointData ;
} ;
struct FDestinationData
{
FDestinationData ( const FString & InName , const FGuid & InGuid , const TSharedRef < FEndpointData > & InEndpointData )
: Name ( InName )
, Guid ( InGuid )
, EndpointData ( InEndpointData )
{
}
FString Name ;
FGuid Guid ;
TSharedRef < FEndpointData > EndpointData ;
} ;
struct FStreamData
{
FStreamData ( bool bInIsActive , const TSharedRef < FSourceData > & InSource , const TSharedRef < FDestinationData > & InDestination )
: bIsActive ( bInIsActive )
, SourceColumnLabel ( InSource - > Name )
, DestinationColumnLabel ( )
, Source ( InSource )
, Destination ( InDestination )
{
2020-11-24 18:42:39 -04:00
uint32 StringLenght = Destination - > EndpointData - > EndpointName . Len ( ) ;
2020-09-24 00:43:27 -04:00
// " : "
StringLenght + = 3 ;
2020-11-24 18:42:39 -04:00
StringLenght + = Destination - > Name . Len ( ) ;
2020-09-24 00:43:27 -04:00
DestinationColumnLabel . Reserve ( StringLenght ) ;
2020-11-24 18:42:39 -04:00
DestinationColumnLabel . Append ( Destination - > EndpointData - > EndpointName ) ;
2020-09-24 00:43:27 -04:00
DestinationColumnLabel . Append ( " : " ) ;
DestinationColumnLabel . Append ( Destination - > Name ) ;
}
bool bIsActive ;
FString SourceColumnLabel ;
FString DestinationColumnLabel ;
TSharedRef < FSourceData > Source ;
TSharedRef < FDestinationData > Destination ;
} ;
class SDirectLinkStreamManagerRow : public SMultiColumnTableRow < TSharedRef < FStreamData > >
{
public :
SLATE_BEGIN_ARGS ( SDirectLinkStreamManagerRow )
{ }
SLATE_END_ARGS ( )
void Construct ( const FArguments & InArgs , TSharedRef < FStreamData > InItem , const TSharedRef < STableViewBase > & InOwner ) ;
virtual TSharedRef < SWidget > GenerateWidgetForColumn ( const FName & ColumnName ) override ;
private :
TWeakPtr < FStreamData > ItemWeakPtr ;
} ;
void SDirectLinkStreamManagerRow : : Construct ( const FArguments & InArgs , TSharedRef < FStreamData > InItem , const TSharedRef < STableViewBase > & InOwner )
{
ItemWeakPtr = InItem ;
FSuperRowType : : FArguments Args = FSuperRowType : : FArguments ( ) ;
FSuperRowType : : Construct ( Args , InOwner ) ;
}
TSharedRef < SWidget > SDirectLinkStreamManagerRow : : GenerateWidgetForColumn ( const FName & ColumnName )
{
if ( TSharedPtr < FStreamData > Item = ItemWeakPtr . Pin ( ) )
{
if ( ColumnName = = SDirectLinkStreamManagerUtils : : SourceColumnId )
{
return SNew ( STextBlock )
. Text ( FText : : FromString ( Item - > Source - > Name ) )
. ToolTipText ( Item - > Source - > EndpointData - > GenerateTooltipText ( ) ) ;
}
else if ( ColumnName = = SDirectLinkStreamManagerUtils : : DestinationColumnId )
{
// We don't expect that data to change over the lifetime of the stream
return SNew ( STextBlock )
. Text ( FText : : FromString ( Item - > DestinationColumnLabel ) )
. ToolTipText ( Item - > Destination - > EndpointData - > GenerateTooltipText ( ) ) ;
}
checkNoEntry ( ) ;
}
return SNullWidget : : NullWidget ;
}
class FEndpointObserver : public DirectLink : : IEndpointObserver
{
public :
TWeakPtr < SDirectLinkStreamManager > WidgetToUpdate ;
virtual void OnStateChanged ( const DirectLink : : FRawInfo & RawInfo ) override
{
FSimpleDelegate RunOnMainThread ;
RunOnMainThread . BindLambda ( [ this , RawInfo ] ( )
{
if ( TSharedPtr < SDirectLinkStreamManager > StreamManager = WidgetToUpdate . Pin ( ) )
{
StreamManager - > UpdateData ( RawInfo ) ;
}
} ) ;
# if IS_PROGRAM
FDatasmithExporterManager : : PushCommandIntoGameThread ( MoveTemp ( RunOnMainThread ) ) ;
# else
// Unsupported for now
checkNoEntry ( ) ;
# endif
}
} ;
void SDirectLinkStreamManager : : Construct ( const FArguments & InArgs , const TSharedRef < DirectLink : : FEndpoint , ESPMode : : ThreadSafe > & InEndpoint )
{
bShowingAdavancedSetting = false ;
SortedColumn = SDirectLinkStreamManagerUtils : : SourceColumnId ;
SortMode = EColumnSortMode : : Ascending ;
DirectLinkCacheDirectory = InArgs . _DefaultCacheDirectory ;
OnCacheDirectoryChanged = InArgs . _OnCacheDirectoryChanged ;
2021-09-08 06:03:14 -04:00
OnCacheDirectoryReset = InArgs . _OnCacheDirectoryReset ;
2020-09-24 00:43:27 -04:00
TSharedRef < SHeaderRow > HeaderRow = SNew ( SHeaderRow )
// Source
+ SHeaderRow : : Column ( SDirectLinkStreamManagerUtils : : SourceColumnId )
. DefaultLabel ( LOCTEXT ( " SourceolumnLabel " , " Source " ) )
. SortMode ( this , & SDirectLinkStreamManager : : GetColumnSortMode , SDirectLinkStreamManagerUtils : : SourceColumnId )
. OnSort ( this , & SDirectLinkStreamManager : : OnColumnSortModeChanged )
// Destination
+ SHeaderRow : : Column ( SDirectLinkStreamManagerUtils : : DestinationColumnId )
. DefaultLabel ( LOCTEXT ( " DestinationColumnLabel " , " Destination " ) )
. SortMode ( this , & SDirectLinkStreamManager : : GetColumnSortMode , SDirectLinkStreamManagerUtils : : DestinationColumnId )
. OnSort ( this , & SDirectLinkStreamManager : : OnColumnSortModeChanged ) ;
2020-10-22 19:19:16 -04:00
2020-09-24 00:43:27 -04:00
ChildSlot
[
SNew ( SVerticalBox )
// No connection hint
+ SVerticalBox : : Slot ( )
. Padding ( 2.f )
. FillHeight ( TAttribute < float > ( this , & SDirectLinkStreamManager : : GetNoConnectionHintFillHeight ) )
. VAlign ( VAlign_Center )
[
SNew ( STextBlock )
. Visibility ( this , & SDirectLinkStreamManager : : GetNoConnectionHintVisibility )
2020-10-22 19:19:16 -04:00
. Text ( LOCTEXT ( " ConnectionHintText " , " No connection found. \n \n Waiting for a Datasmith Direct Link connection to be established. \n \n Please start an application supporting Datasmith Direct Link such as Twinmotion or Unreal Engine. " ) )
2020-09-24 00:43:27 -04:00
. AutoWrapText ( true )
. Justification ( ETextJustify : : Center )
. Font ( FCoreStyle : : GetDefaultFontStyle ( " Regular " , 13 ) )
]
// Connection view
+ SVerticalBox : : Slot ( )
. Padding ( 2.f )
. VAlign ( VAlign_Fill )
[
SAssignNew ( ConnectionsView , SListView < TSharedRef < FStreamData > > )
. Visibility ( this , & SDirectLinkStreamManager : : GetConnectionViewVisibility )
. ListItemsSource ( & Streams )
. OnGenerateRow ( this , & SDirectLinkStreamManager : : OnGenerateRow )
. SelectionMode ( ESelectionMode : : None )
. HeaderRow ( HeaderRow )
]
// Drop shadow
+ SVerticalBox : : Slot ( )
. AutoHeight ( )
. VAlign ( VAlign_Bottom )
[
SNew ( SImage )
. Visibility ( this , & SDirectLinkStreamManager : : GetAdavancedSettingVisibility )
// Use the drop shadow of a scroll box to add a bit of depth to the setting
. Image ( & ( FCoreStyle : : Get ( ) . GetWidgetStyle < FScrollBoxStyle > ( " ScrollBox " ) . TopShadowBrush ) )
]
// Cache directory
+ SVerticalBox : : Slot ( )
. Padding ( 2.f )
. AutoHeight ( )
. VAlign ( VAlign_Bottom )
[
SNew ( SHorizontalBox )
. Visibility ( this , & SDirectLinkStreamManager : : GetAdavancedSettingVisibility )
+ SHorizontalBox : : Slot ( )
// More padding to the right to separate the label from the content
. Padding ( 2.f , 2.f , 4.f , 2.f )
. AutoWidth ( )
. HAlign ( HAlign_Left )
[
SNew ( SVerticalBox )
+ SVerticalBox : : Slot ( )
. VAlign ( VAlign_Center )
[
SNew ( STextBlock )
. Text ( LOCTEXT ( " CacheDirectoryLabel " , " Cache Directory: " ) )
. ToolTipText ( LOCTEXT ( " CacheDirectoryLabelToolTip " , " Direct Link will use this directory to store temporary files " ) )
]
]
+ SHorizontalBox : : Slot ( )
. Padding ( 2.f )
. HAlign ( HAlign_Right )
[
SNew ( SVerticalBox )
+ SVerticalBox : : Slot ( )
. VAlign ( VAlign_Center )
[
SNew ( SEditableText )
. IsReadOnly ( true )
. Text ( this , & SDirectLinkStreamManager : : GetCacheDirectory )
. ToolTipText ( this , & SDirectLinkStreamManager : : GetCacheDirectory )
]
]
+ SHorizontalBox : : Slot ( )
. Padding ( 2.f )
. AutoWidth ( )
. HAlign ( HAlign_Right )
2021-09-08 06:03:14 -04:00
[
SNew ( SButton )
. OnClicked ( this , & SDirectLinkStreamManager : : OnResetCacheDirectoryClicked )
. ToolTipText ( LOCTEXT ( " ResetCacheDirectory_Tooltip " , " Reset cache directory " ) )
[
SNew ( SVerticalBox )
+ SVerticalBox : : Slot ( )
. VAlign ( VAlign_Center )
[
SNew ( STextBlock )
. Text ( LOCTEXT ( " ResetCacheDirectory_Label " , " Reset " ) )
]
]
]
+ SHorizontalBox : : Slot ( )
. Padding ( 2.f )
. AutoWidth ( )
. HAlign ( HAlign_Right )
2020-09-24 00:43:27 -04:00
[
SNew ( SButton )
. OnClicked ( this , & SDirectLinkStreamManager : : OnChangeCacheDirectoryClicked )
. ToolTipText ( LOCTEXT ( " ChangeCacheDirectory_Tooltip " , " Browse for another folder " ) )
[
SNew ( SVerticalBox )
+ SVerticalBox : : Slot ( )
. VAlign ( VAlign_Center )
[
SNew ( STextBlock )
. Text ( LOCTEXT ( " ... " , " ... " ) )
]
]
]
]
+ SVerticalBox : : Slot ( )
. Padding ( 2.f )
. AutoHeight ( )
. VAlign ( VAlign_Bottom )
[
SAssignNew ( ShowAdavancedSettingButton , SButton )
. ButtonStyle ( & FCoreStyle : : Get ( ) . GetWidgetStyle < FButtonStyle > ( " NoBorder " ) )
. HAlign ( HAlign_Center )
. ContentPadding ( 2.f )
. OnClicked ( this , & SDirectLinkStreamManager : : OnShowAdavancedSettingClicked )
. ToolTipText ( this , & SDirectLinkStreamManager : : GetShowAdavancedSettingToolTipText )
[
SAssignNew ( ShowAdavancedSettingImage , SImage )
. Image ( & ( FCoreStyle : : Get ( ) . GetWidgetStyle < FHeaderRowStyle > ( " TableView.Header " ) . ColumnStyle . MenuDropdownImage ) )
]
]
] ;
ShowAdavancedSettingImage - > SetRenderTransformPivot ( FVector2D ( 0.5f , 0.5f ) ) ;
Endpoint = InEndpoint ;
Observer = MakeShared < FEndpointObserver > ( ) ;
Observer - > WidgetToUpdate = StaticCastSharedRef < SDirectLinkStreamManager > ( AsShared ( ) ) ;
UpdateData ( InEndpoint - > GetRawInfoCopy ( ) ) ;
InEndpoint - > AddEndpointObserver ( Observer . Get ( ) ) ;
}
SDirectLinkStreamManager : : ~ SDirectLinkStreamManager ( )
{
Endpoint - > RemoveEndpointObserver ( Observer . Get ( ) ) ;
}
void SDirectLinkStreamManager : : UpdateData ( const DirectLink : : FRawInfo & RawInfo )
{
using namespace DirectLink ;
TMap < FGuid , TSharedRef < FDestinationData > > DestinationsMap ;
TMap < FGuid , TSharedRef < FSourceData > > SourcesMap ;
// Grab the sources
{
const FRawInfo : : FEndpointInfo & EndpointInfo = RawInfo . EndpointsInfo . FindChecked ( RawInfo . ThisEndpointAddress ) ;
TSharedRef < FEndpointData > EndpointData = MakeShared < FEndpointData > ( EndpointInfo . Name ,
EndpointInfo . UserName ,
EndpointInfo . ExecutableName ,
EndpointInfo . ComputerName
) ;
// Grab all the sources
SourcesMap . Reserve ( EndpointInfo . Sources . Num ( ) ) ;
2021-03-05 19:27:14 -04:00
for ( const FRawInfo : : FDataPointId & NamedId : EndpointInfo . Sources )
2020-09-24 00:43:27 -04:00
{
TSharedRef < FSourceData > Source = MakeShared < FSourceData > ( NamedId . Name ,
NamedId . Id ,
EndpointData
) ;
SourcesMap . Add ( NamedId . Id , MoveTemp ( Source ) ) ;
}
}
// Grab all the potential destinations
for ( const TPair < FMessageAddress , FRawInfo : : FEndpointInfo > & EnpointsInfoPair : RawInfo . EndpointsInfo )
{
const FRawInfo : : FEndpointInfo & EndpointInfo = EnpointsInfoPair . Value ;
TSharedRef < FEndpointData > EndpointData = MakeShared < FEndpointData > ( EndpointInfo . Name ,
EndpointInfo . UserName ,
EndpointInfo . ExecutableName ,
EndpointInfo . ComputerName
) ;
DestinationsMap . Reserve ( EndpointInfo . Destinations . Num ( ) ) ;
2021-03-05 19:27:14 -04:00
for ( const FRawInfo : : FDataPointId & NamedId : EndpointInfo . Destinations )
2020-09-24 00:43:27 -04:00
{
TSharedRef < FDestinationData > Destination = MakeShared < FDestinationData > ( NamedId . Name ,
NamedId . Id ,
EndpointData
) ;
DestinationsMap . Add ( NamedId . Id , MoveTemp ( Destination ) ) ;
}
}
bool bDirtyStreamList = false ;
// Remove the already connected destinations and create the connection list
for ( const FRawInfo : : FStreamInfo & StreamInfo : RawInfo . StreamsInfo )
{
TSharedPtr < FDestinationData > DestinationData ;
if ( StreamInfo . bIsActive )
{
2020-11-24 18:42:39 -04:00
if ( DestinationsMap . Contains ( StreamInfo . Destination ) )
{
DestinationData = DestinationsMap . FindAndRemoveChecked ( StreamInfo . Destination ) ;
}
2020-09-24 00:43:27 -04:00
}
FStreamID StreamId ( StreamInfo . Source , StreamInfo . Destination ) ;
uint32 StreamIdHash = GetTypeHash ( StreamId ) ;
if ( TSharedRef < FStreamData > * StreamDataPtr = StreamMap . FindByHash ( StreamIdHash , StreamId ) )
{
TSharedRef < FStreamData > & StreamData = * StreamDataPtr ;
// Made with the assumption that the source and destionation data can't change
if ( StreamData - > bIsActive ! = StreamInfo . bIsActive )
{
StreamData - > bIsActive = StreamInfo . bIsActive ;
bDirtyStreamList = true ;
}
}
else if ( StreamInfo . bIsActive )
{
2020-11-24 18:42:39 -04:00
TSharedRef < FSourceData > * SourceData = SourcesMap . Find ( StreamInfo . Source ) ;
if ( DestinationData . IsValid ( ) & & SourceData ! = nullptr )
{
TSharedRef < FStreamData > StreamData = MakeShared < FStreamData > ( StreamInfo . bIsActive , * SourceData , DestinationData . ToSharedRef ( ) ) ;
StreamMap . AddByHash ( StreamIdHash , StreamId , StreamData ) ;
bDirtyStreamList = true ;
}
2020-09-24 00:43:27 -04:00
}
}
if ( bDirtyStreamList )
{
StreamMap . GenerateValueArray ( Streams ) ;
for ( int32 Index = 0 ; Index < Streams . Num ( ) ; + + Index )
{
TSharedRef < FStreamData > & Stream = Streams [ Index ] ;
if ( ! Stream - > bIsActive )
{
Streams . RemoveAtSwap ( Index , 1 , false ) ;
- - Index ;
}
}
Streams . Shrink ( ) ;
SortStreamList ( ) ;
}
}
TSharedRef < ITableRow > SDirectLinkStreamManager : : OnGenerateRow ( TSharedRef < FStreamData > Item , const TSharedRef < STableViewBase > & Owner ) const
{
return SNew ( SDirectLinkStreamManagerRow , Item , Owner ) ;
}
EColumnSortMode : : Type SDirectLinkStreamManager : : GetColumnSortMode ( const FName InColumnId ) const
{
if ( SortedColumn = = InColumnId )
{
return SortMode ;
}
return EColumnSortMode : : None ;
}
void SDirectLinkStreamManager : : OnColumnSortModeChanged ( const EColumnSortPriority : : Type SortPriority , const FName & ColumnId , const EColumnSortMode : : Type InSortMode )
{
SortedColumn = ColumnId ;
SortMode = InSortMode ;
SortStreamList ( ) ;
}
void SDirectLinkStreamManager : : SortStreamList ( )
{
if ( SortMode ! = EColumnSortMode : : None )
{
const bool bAscending = EColumnSortMode : : Ascending = = SortMode ;
if ( SortedColumn = = SDirectLinkStreamManagerUtils : : SourceColumnId )
{
Streams . StableSort ( [ bAscending ] ( const TSharedRef < FStreamData > & First , const TSharedRef < FStreamData > & Second )
{
return First - > Source - > Name < Second - > Source - > Name = = bAscending ;
} ) ;
}
else if ( SortedColumn = = SDirectLinkStreamManagerUtils : : DestinationColumnId )
{
Streams . StableSort ( [ bAscending ] ( const TSharedRef < FStreamData > & First , const TSharedRef < FStreamData > & Second )
{
return First - > DestinationColumnLabel < Second - > DestinationColumnLabel = = bAscending ;
} ) ;
}
else
{
checkNoEntry ( ) ;
}
}
ConnectionsView - > RequestListRefresh ( ) ;
}
FText SDirectLinkStreamManager : : GetCacheDirectory ( ) const
{
return FText : : FromString ( DirectLinkCacheDirectory ) ;
}
2021-09-08 06:03:14 -04:00
FReply SDirectLinkStreamManager : : OnResetCacheDirectoryClicked ( )
{
if ( OnCacheDirectoryReset . IsBound ( ) )
{
DirectLinkCacheDirectory = OnCacheDirectoryReset . Execute ( ) ;
}
return FReply : : Handled ( ) ;
}
2020-09-24 00:43:27 -04:00
FReply SDirectLinkStreamManager : : OnChangeCacheDirectoryClicked ( )
{
IDesktopPlatform * DesktopPlatform = FDesktopPlatformModule : : Get ( ) ;
FString NewFolderSelected ;
bool bOpened = DesktopPlatform - > OpenDirectoryDialog (
FSlateApplication : : Get ( ) . FindBestParentWindowHandleForDialogs ( AsShared ( ) ) ,
LOCTEXT ( " ChoseCacheDirectoreDialog " , " Cache Directory " ) . ToString ( ) ,
DirectLinkCacheDirectory ,
NewFolderSelected
) ;
if ( bOpened )
{
DirectLinkCacheDirectory = NewFolderSelected ;
OnCacheDirectoryChanged . ExecuteIfBound ( DirectLinkCacheDirectory ) ;
}
2020-10-22 19:19:16 -04:00
2020-09-24 00:43:27 -04:00
return FReply : : Handled ( ) ;
}
EVisibility SDirectLinkStreamManager : : GetNoConnectionHintVisibility ( ) const
{
return Streams . Num ( ) = = 0 ? EVisibility : : Visible : EVisibility : : Collapsed ;
}
float SDirectLinkStreamManager : : GetNoConnectionHintFillHeight ( ) const
{
return Streams . Num ( ) = = 0 ? 1.f : 0.f ;
}
EVisibility SDirectLinkStreamManager : : GetConnectionViewVisibility ( ) const
{
return Streams . Num ( ) ! = 0 ? EVisibility : : Visible : EVisibility : : Collapsed ;
}
EVisibility SDirectLinkStreamManager : : GetAdavancedSettingVisibility ( ) const
{
if ( bShowingAdavancedSetting )
{
return EVisibility : : Visible ;
}
return EVisibility : : Collapsed ;
}
FReply SDirectLinkStreamManager : : OnShowAdavancedSettingClicked ( )
{
bShowingAdavancedSetting = ! bShowingAdavancedSetting ;
if ( bShowingAdavancedSetting )
{
ShowAdavancedSettingImage - > SetRenderTransform ( FSlateRenderTransform ( FScale2D ( 1.f , - 1.f ) ) ) ;
}
else
{
ShowAdavancedSettingImage - > SetRenderTransform ( FSlateRenderTransform ( FScale2D ( 1.f , 1.f ) ) ) ;
}
return FReply : : Handled ( ) ;
}
FText SDirectLinkStreamManager : : GetShowAdavancedSettingToolTipText ( ) const
{
if ( bShowingAdavancedSetting )
{
return LOCTEXT ( " HideAdavancedSetting " , " Hide setting " ) ;
}
return LOCTEXT ( " ShowAdavancedSetting " , " Show setting " ) ;
}
# undef LOCTEXT_NAMESPACE