using System; using System.Collections.Generic; using System.Reflection; using System.Threading; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Input; using System.Windows.Threading; using FasdDesktopUi.Basics.Models; using C4IT.FASD.Cockpit.Communication; using C4IT.Logging; using static C4IT.Logging.cLogManager; using C4IT.MultiLanguage; using C4IT.FASD.Base; namespace FasdDesktopUi.Basics.UserControls { public partial class SearchBar : UserControl, IBlurInvoker { private const int constTimeBetweenKeystrokes = 250; #region Propeties public enum eSearchStatus { message, active, fixedResult }; public eSearchStatus SearchStatus { get; private set; } = eSearchStatus.message; #region Search Value Property private readonly DispatcherTimer _searchTimer = new DispatcherTimer(); private static CancellationTokenSource _currentSearchTaskToken = null; private static Guid? _currentSearchTaskId = null; private static readonly object _currentSearchTaskLock = new object(); private string _searchValue = null; public string SearchValue { get => _searchValue; set { if (value != _searchValue) { _searchValue = value; if (SearchStatus == eSearchStatus.active) HandleSearchValueChanged(); } } } private readonly Dictionary _searchIcons = null; #endregion public event EventHandler SearchValueChanged; public delegate Task ChangedSearchValueDelegate(cFilteredResults results); public ChangedSearchValueDelegate ChangedSearchValue { get; set; } = null; public Action CancledSearchAction { get; set; } public bool BlurInvoker_IsActive => IsVisible; #endregion #region Dependency Propertys public double SearchButtonSize { get { return (double)GetValue(SearchButtonSizeProperty); } set { SetValue(SearchButtonSizeProperty, value); } } public static readonly DependencyProperty SearchButtonSizeProperty = DependencyProperty.Register("SearchButtonSize", typeof(double), typeof(SearchBar), new PropertyMetadata(30.0)); public double CloseButtonSize { get { return (double)GetValue(CloseButtonSizeProperty); } set { SetValue(CloseButtonSizeProperty, value); } } public static readonly DependencyProperty CloseButtonSizeProperty = DependencyProperty.Register("CloseButtonSize", typeof(double), typeof(SearchBar), new PropertyMetadata(30.0)); #endregion public SearchBar() { InitializeComponent(); _searchIcons = new Dictionary() { { enumF4sdSearchResultClass.Phone, PhoneCallIndicator }, { enumF4sdSearchResultClass.User, UserCallIndicator }, { enumF4sdSearchResultClass.Computer, ComputerCallIndicator } }; } public void BlurInvoker_IsActiveChanged(object sender, DependencyPropertyChangedEventArgs e) { BlurInvoker.InvokeVisibilityChanged(this, new EventArgs()); if (e.NewValue is bool newVisible) { if (newVisible == false) { SearchTextBox.IsEnabled = true; SearchButton.Visibility = Visibility.Visible; WarningIndicator.Visibility = Visibility.Collapsed; TicketOverview.Instance?.ResetSelection(); foreach (var Entry in _searchIcons.Values) Entry.Visibility = Visibility.Collapsed; } SetApiStatus(); } if (Visibility == Visibility.Visible) { var _h = Dispatcher.BeginInvoke((Action)delegate { Keyboard.Focus(SearchTextBox); }, DispatcherPriority.Render); } } public void Clear() { SearchStatus = eSearchStatus.message; SearchTextBox.Text = null; } public void ActivateManualSearch() { SearchStatus = eSearchStatus.active; SearchButton.Visibility = Visibility.Visible; WarningIndicator.Visibility = Visibility.Collapsed; foreach (var Entry in _searchIcons.Values) Entry.Visibility = Visibility.Collapsed; SearchTextBox.Text = null; SearchTextBox.IsEnabled = true; SearchTextBox.Focus(); Keyboard.Focus(SearchTextBox); } public void SetSearchText(string search) { SearchStatus = eSearchStatus.active; SearchTextBox.Text = search; SearchTextBox.CaretIndex = search.Length; SearchTextBox.Focus(); Keyboard.Focus(SearchTextBox); } internal void SetSpinnerVisibility(Visibility visibility) { Dispatcher.Invoke(() => SearchSpinner.Visibility = visibility); } public async Task SetFixedSearchResultAsync(enumF4sdSearchResultClass Class, string search, cFilteredResults result) { SearchStatus = eSearchStatus.fixedResult; SearchTextBox.IsEnabled = false; SearchButton.Visibility = Visibility.Collapsed; WarningIndicator.Visibility = Visibility.Collapsed; foreach (var entryIcon in _searchIcons) { if (Class == entryIcon.Key) entryIcon.Value.Visibility = Visibility.Visible; else entryIcon.Value.Visibility = Visibility.Collapsed; } SearchTextBox.Text = search; if (ChangedSearchValue != null) await ChangedSearchValue.Invoke(result); } public void SetApiStatus() { try { var Status = cConnectionStatusHelper.Instance?.ApiConnectionStatus; if (Status == null) return; bool IsEnabled = false; string txt = null; switch (Status) { case cConnectionStatusHelper.enumOnlineStatus.online: IsEnabled = true; break; case cConnectionStatusHelper.enumOnlineStatus.connectionError: txt = cMultiLanguageSupport.GetItem("Searchbar.Status.ConnectError"); break; case cConnectionStatusHelper.enumOnlineStatus.incompatibleServerVersion: txt = cMultiLanguageSupport.GetItem("Searchbar.Status.IncompatibleVersion"); break; case cConnectionStatusHelper.enumOnlineStatus.serverStarting: txt = cMultiLanguageSupport.GetItem("Searchbar.Status.ServerStarting"); break; case cConnectionStatusHelper.enumOnlineStatus.serverNotConfigured: txt = cMultiLanguageSupport.GetItem("Searchbar.Status.ServerNotConfigured"); break; case cConnectionStatusHelper.enumOnlineStatus.illegalConfig: txt = cMultiLanguageSupport.GetItem("Searchbar.Status.IllegalConfig"); break; case cConnectionStatusHelper.enumOnlineStatus.unauthorized: txt = cMultiLanguageSupport.GetItem("Searchbar.Status.Unauthorized"); break; default: txt = cMultiLanguageSupport.GetItem("Searchbar.Status.Offline"); break; } if (!IsEnabled) { SearchStatus = eSearchStatus.message; SearchTextBox.Text = txt; SearchTextBox.IsEnabled = false; WarningIndicator.Visibility = Visibility.Visible; SearchButton.Visibility = Visibility.Collapsed; foreach (var Entry in _searchIcons.Values) Entry.Visibility = Visibility.Collapsed; } } catch (Exception E) { LogException(E); } finally { } } private async Task CancelRunningSearchTaskAsync() { // cancel the search task, in case of a running search task lock (_currentSearchTaskLock) { if (_currentSearchTaskToken != null) { LogEntry("Canceling current running search..."); _currentSearchTaskToken.Cancel(true); _currentSearchTaskToken = null; } } // send a stop to the server, in case of a running search task Guid? currentTaskId = null; lock (_currentSearchTaskLock) { if (_currentSearchTaskId != null) { currentTaskId = _currentSearchTaskId; _currentSearchTaskId = null; } } if (currentTaskId != null) { LogEntry("Sending a search stop..."); await cFasdCockpitCommunicationBase.Instance.GetSearchResultsStop((Guid)currentTaskId, CancellationToken.None); } } private async void UpdateSearchResultsTick(object sender, EventArgs e) { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } try { _searchTimer.Stop(); await CancelRunningSearchTaskAsync(); lock (_currentSearchTaskLock) { LogEntry("Start running new search..."); SearchValueChanged?.Invoke(this, SearchValue); } } catch (Exception E) { LogException(E); } finally { if (CM != null) LogMethodEnd(CM); } } private void HandleSearchValueChanged() { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } try { if (!SearchTextBox.IsEnabled) return; if (_searchTimer.IsEnabled) _searchTimer.Stop(); _searchTimer.Start(); _ = Task.Run(async () => { await CancelRunningSearchTaskAsync(); }); } catch (Exception E) { LogException(E); } finally { if (CM != null) LogMethodEnd(CM); } } private void SearchButton_Click(object sender, InputEventArgs e) { SearchTextBox.Focus(); } #region CancledSearch Event public static readonly RoutedEvent CancledSearchEvent = EventManager.RegisterRoutedEvent("CancledSearch", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(SearchBar)); public event RoutedEventHandler CancledSearch { add { AddHandler(CancledSearchEvent, value); } remove { RemoveHandler(CancledSearchEvent, value); } } private void RaiseCancledSearchEvent() { RoutedEventArgs newEventArgs = new RoutedEventArgs(CancledSearchEvent); RaiseEvent(newEventArgs); } #endregion private void CloseButton_Click(object sender, InputEventArgs e) { RaiseCancledSearchEvent(); if (cConnectionStatusHelper.Instance?.ApiConnectionStatus == cConnectionStatusHelper.enumOnlineStatus.online) SearchTextBox.Text = null; CancledSearchAction?.Invoke(); } private void SearchBar_Loaded(object sender, RoutedEventArgs e) { _searchTimer.Stop(); _searchTimer.Interval = TimeSpan.FromMilliseconds(constTimeBetweenKeystrokes); _searchTimer.Tick += UpdateSearchResultsTick; BackgroundBorder.CornerRadius = new CornerRadius(BackgroundBorder.ActualHeight / 2.0); } } }