// Copyright (c) Microsoft Corporation // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System; using System.Collections.ObjectModel; using System.Windows.Input; using System.Windows.Media; using Wox.Core.Plugin; using Wox.Infrastructure.Image; using Wox.Infrastructure.Logger; using Wox.Plugin; using PowerLauncher.Helper; namespace PowerLauncher.ViewModel { public class ResultViewModel : BaseModel { public enum ActivationType { Selection, Hover }; public ObservableCollection ContextMenuItems { get; } = new ObservableCollection(); public ICommand ActivateContextButtonsHoverCommand { get; set; } public ICommand ActivateContextButtonsSelectionCommand { get; set; } public ICommand DeactivateContextButtonsHoverCommand { get; set; } public ICommand DeactivateContextButtonsSelectionCommand { get; set; } public bool IsSelected { get; set; } public bool IsHovered { get; set; } public bool AreContextButtonsActive { get; set; } public int ContextMenuSelectedIndex { get; set; } public const int NoSelectionIndex = -1; public ResultViewModel(Result result) { if (result != null) { Result = result; } ContextMenuSelectedIndex = NoSelectionIndex; LoadContextMenu(); ActivateContextButtonsHoverCommand = new RelayCommand(ActivateContextButtonsHoverAction); ActivateContextButtonsSelectionCommand = new RelayCommand(ActivateContextButtonsSelectionAction); DeactivateContextButtonsHoverCommand = new RelayCommand(DeactivateContextButtonsHoverAction); DeactivateContextButtonsSelectionCommand = new RelayCommand(DeactivateContextButtonsSelectionAction); } private void ActivateContextButtonsHoverAction(object sender) { ActivateContextButtons(ActivationType.Hover); } private void ActivateContextButtonsSelectionAction(object sender) { ActivateContextButtons(ActivationType.Selection); } public void ActivateContextButtons(ActivationType activationType) { // Result does not contain any context menu items - we don't need to show the context menu ListView at all. if (ContextMenuItems.Count > 0) { AreContextButtonsActive = true; } else { AreContextButtonsActive = false; } if (activationType == ActivationType.Selection) { IsSelected = true; EnableContextMenuAcceleratorKeys(); } else if (activationType == ActivationType.Hover) { IsHovered = true; } } private void DeactivateContextButtonsHoverAction(object sender) { DeactivateContextButtons(ActivationType.Hover); } private void DeactivateContextButtonsSelectionAction(object sender) { DeactivateContextButtons(ActivationType.Selection); } public void DeactivateContextButtons(ActivationType activationType) { if (activationType == ActivationType.Selection) { IsSelected = false; DisableContextMenuAcceleratorkeys(); } else if (activationType == ActivationType.Hover) { IsHovered = false; } // Result does not contain any context menu items - we don't need to show the context menu ListView at all. if (ContextMenuItems?.Count > 0) { AreContextButtonsActive = IsSelected || IsHovered; } else { AreContextButtonsActive = false; } } public void LoadContextMenu() { var results = PluginManager.GetContextMenusForPlugin(Result); ContextMenuItems.Clear(); foreach (var r in results) { ContextMenuItems.Add(new ContextMenuItemViewModel() { PluginName = r.PluginName, Title = r.Title, Glyph = r.Glyph, FontFamily = r.FontFamily, AcceleratorKey = r.AcceleratorKey, AcceleratorModifiers = r.AcceleratorModifiers, Command = new RelayCommand(_ => { bool hideWindow = r.Action != null && r.Action(new ActionContext { SpecialKeyState = KeyboardHelper.CheckModifiers() }); if (hideWindow) { // TODO - Do we hide the window // MainWindowVisibility = Visibility.Collapsed; } }) }); } } private void EnableContextMenuAcceleratorKeys() { foreach (var i in ContextMenuItems) { i.IsAcceleratorKeyEnabled = true; } } private void DisableContextMenuAcceleratorkeys() { foreach (var i in ContextMenuItems) { i.IsAcceleratorKeyEnabled = false; } } public ImageSource Image { get { var imagePath = Result.IcoPath; if (string.IsNullOrEmpty(imagePath) && Result.Icon != null) { try { return Result.Icon(); } #pragma warning disable CA1031 // Do not catch general exception types catch (Exception e) #pragma warning restore CA1031 // Do not catch general exception types { Log.Exception($"|ResultViewModel.Image|IcoPath is empty and exception when calling Icon() for result <{Result.Title}> of plugin <{Result.PluginDirectory}>", e); imagePath = ImageLoader.ErrorIconPath; } } // will get here either when icoPath has value\icon delegate is null\when had exception in delegate return ImageLoader.Load(imagePath); } } // Returns false if we've already reached the last item. public bool SelectNextContextButton() { if (ContextMenuSelectedIndex == (ContextMenuItems.Count - 1)) { ContextMenuSelectedIndex = NoSelectionIndex; return false; } ContextMenuSelectedIndex++; return true; } // Returns false if we've already reached the first item. public bool SelectPrevContextButton() { if (ContextMenuSelectedIndex == NoSelectionIndex) { return false; } ContextMenuSelectedIndex--; return true; } public void SelectLastContextButton() { ContextMenuSelectedIndex = ContextMenuItems.Count - 1; } public bool HasSelectedContextButton() { var isContextSelected = (ContextMenuSelectedIndex != NoSelectionIndex); return isContextSelected; } /// /// Triggers the action on the selected context button /// /// False if there is nothing selected, otherwise true public bool ExecuteSelectedContextButton() { if (HasSelectedContextButton()) { ContextMenuItems[ContextMenuSelectedIndex].Command.Execute(null); return true; } return false; } public Result Result { get; } public override bool Equals(object obj) { var r = obj as ResultViewModel; if (r != null) { return Result.Equals(r.Result); } else { return false; } } public override int GetHashCode() { return Result.GetHashCode(); } public override string ToString() { var display = String.IsNullOrEmpty(Result.QueryTextDisplay) ? Result.Title : Result.QueryTextDisplay; return display; } } }