2020-09-27 01:54:24 +12:00
// <copyright file="Menus.cs" company="PlaceholderCompany">
// Copyright (c) PlaceholderCompany. All rights reserved.
// </copyright>
namespace SystemTrayMenu.Business
{
using System ;
using System.Collections.Generic ;
using System.ComponentModel ;
using System.Data ;
using System.Diagnostics ;
using System.Drawing ;
using System.IO ;
using System.Linq ;
using System.Windows.Forms ;
using SystemTrayMenu.DataClasses ;
using SystemTrayMenu.Handler ;
using SystemTrayMenu.Helper ;
2022-02-28 04:44:08 +13:00
using SystemTrayMenu.UserInterface ;
2020-09-27 01:54:24 +12:00
using SystemTrayMenu.Utilities ;
2021-06-27 21:35:07 +12:00
using Menu = SystemTrayMenu . UserInterface . Menu ;
2020-09-27 01:54:24 +12:00
using Timer = System . Windows . Forms . Timer ;
internal class Menus : IDisposable
{
private readonly Menu [ ] menus = new Menu [ MenuDefines . MenusMax ] ;
2021-11-17 12:13:46 +13:00
private readonly BackgroundWorker workerMainMenu = new ( ) ;
private readonly List < BackgroundWorker > workersSubMenu = new ( ) ;
2020-09-27 01:54:24 +12:00
2021-11-17 12:13:46 +13:00
private readonly DgvMouseRow dgvMouseRow = new ( ) ;
private readonly WaitToLoadMenu waitToOpenMenu = new ( ) ;
2021-04-17 12:39:48 +12:00
private readonly KeyboardInput keyboardInput ;
2022-06-18 00:38:09 +12:00
private readonly List < FileSystemWatcher > watchers = new ( ) ;
2022-06-21 03:39:39 +12:00
private readonly List < EventArgs > watcherHistory = new ( ) ;
2021-11-17 12:13:46 +13:00
private readonly Timer timerShowProcessStartedAsLoadingIcon = new ( ) ;
private readonly Timer timerStillActiveCheck = new ( ) ;
private readonly WaitLeave waitLeave = new ( Properties . Settings . Default . TimeUntilCloses ) ;
2020-09-27 01:54:24 +12:00
private DateTime deactivatedTime = DateTime . MinValue ;
private OpenCloseState openCloseState = OpenCloseState . Default ;
private TaskbarPosition taskbarPosition = new WindowsTaskbar ( ) . Position ;
2021-04-17 12:39:48 +12:00
private bool searchTextChanging ;
2021-10-29 03:29:04 +13:00
private bool waitingForReactivate ;
2021-12-31 00:36:00 +13:00
private int lastMouseDownRowIndex = - 1 ;
2021-11-26 11:49:50 +13:00
private bool showMenuAfterMainPreload ;
2021-12-31 00:36:00 +13:00
private int dragSwipeScrollingStartRowIndex = - 1 ;
private bool isDraggingSwipeScrolling ;
private bool isDragSwipeScrolled ;
2022-06-18 00:38:09 +12:00
private bool hideSubmenuDuringRefreshSearch ;
2020-09-27 01:54:24 +12:00
public Menus ( )
{
workerMainMenu . WorkerSupportsCancellation = true ;
workerMainMenu . DoWork + = LoadMenu ;
workerMainMenu . RunWorkerCompleted + = LoadMainMenuCompleted ;
void LoadMainMenuCompleted ( object sender , RunWorkerCompletedEventArgs e )
{
keyboardInput . ResetSelectedByKey ( ) ;
LoadStopped ( ) ;
2021-11-11 11:39:52 +13:00
if ( e . Result = = null )
2020-09-27 01:54:24 +12:00
{
2021-11-11 11:39:52 +13:00
// Clean up menu status IsMenuOpen for previous one
DataGridView dgvMainMenu = menus [ 0 ] . GetDataGridView ( ) ;
foreach ( DataRow row in ( ( DataTable ) dgvMainMenu . DataSource ) . Rows )
{
RowData rowDataToClear = ( RowData ) row [ 2 ] ;
rowDataToClear . IsMenuOpen = false ;
2022-02-05 22:32:28 +13:00
rowDataToClear . IsClicking = false ;
2021-11-11 11:39:52 +13:00
rowDataToClear . IsSelected = false ;
rowDataToClear . IsContextMenuOpen = false ;
}
2020-09-27 01:54:24 +12:00
2021-11-11 11:39:52 +13:00
RefreshSelection ( dgvMainMenu ) ;
2020-09-27 01:54:24 +12:00
2022-07-10 21:40:48 +12:00
if ( Properties . Settings . Default . AppearAtMouseLocation )
2021-11-21 07:45:41 +13:00
{
menus [ 0 ] . Tag = null ;
}
2021-11-11 11:39:52 +13:00
AsEnumerable . ToList ( ) . ForEach ( m = > { m . ShowWithFade ( ) ; } ) ;
}
else
{
MenuData menuData = ( MenuData ) e . Result ;
switch ( menuData . Validity )
{
case MenuDataValidity . Valid :
2022-07-10 21:40:48 +12:00
if ( IconReader . MainPreload )
2021-11-13 20:18:33 +13:00
{
2022-07-10 21:40:48 +12:00
workerMainMenu . DoWork - = LoadMenu ;
menus [ 0 ] = Create ( menuData , Path . GetFileName ( Config . Path ) ) ;
menus [ 0 ] . HandleCreated + = ( s , e ) = > ExecuteWatcherHistory ( ) ;
2022-07-28 07:35:09 +12:00
Scaling . CalculateFactorByDpi ( menus [ 0 ] . GetDataGridView ( ) . CreateGraphics ( ) ) ;
2022-07-10 21:40:48 +12:00
IconReader . MainPreload = false ;
if ( showMenuAfterMainPreload )
2021-11-13 20:18:33 +13:00
{
AsEnumerable . ToList ( ) . ForEach ( m = > { m . ShowWithFade ( ) ; } ) ;
}
}
else
2021-11-11 11:39:52 +13:00
{
2022-07-10 21:40:48 +12:00
AsEnumerable . ToList ( ) . ForEach ( m = > { m . ShowWithFade ( ) ; } ) ;
2021-11-11 11:39:52 +13:00
}
break ;
case MenuDataValidity . Empty :
2022-05-30 02:41:23 +12:00
MessageBox . Show ( Translator . GetText ( "Your root directory for the app does not exist or is empty! Change the root directory or put some files, directories or shortcuts into the root directory." ) ) ;
2022-02-26 22:39:00 +13:00
OpenFolder ( ) ;
Config . SetFolderByUser ( ) ;
AppRestart . ByConfigChange ( ) ;
2021-11-11 11:39:52 +13:00
break ;
case MenuDataValidity . NoAccess :
2022-05-30 02:41:23 +12:00
MessageBox . Show ( Translator . GetText ( "You have no access to the root directory of the app. Grant access to the directory or change the root directory." ) ) ;
2022-02-26 22:39:00 +13:00
OpenFolder ( ) ;
Config . SetFolderByUser ( ) ;
AppRestart . ByConfigChange ( ) ;
2021-11-11 11:39:52 +13:00
break ;
2022-06-18 00:38:09 +12:00
case MenuDataValidity . Undefined :
Log . Info ( $"{nameof(MenuDataValidity)}.{nameof(MenuDataValidity.Undefined)}" ) ;
2021-11-11 11:39:52 +13:00
break ;
default :
break ;
}
2020-09-27 01:54:24 +12:00
}
2021-11-10 03:12:52 +13:00
openCloseState = OpenCloseState . Default ;
2020-09-27 01:54:24 +12:00
}
waitToOpenMenu . StopLoadMenu + = WaitToOpenMenu_StopLoadMenu ;
void WaitToOpenMenu_StopLoadMenu ( )
{
foreach ( BackgroundWorker workerSubMenu in workersSubMenu .
Where ( w = > w . IsBusy ) )
{
workerSubMenu . CancelAsync ( ) ;
}
LoadStopped ( ) ;
}
waitToOpenMenu . StartLoadMenu + = StartLoadMenu ;
void StartLoadMenu ( RowData rowData )
{
if ( menus [ 0 ] . IsUsable & &
2022-06-18 00:38:09 +12:00
( menus [ rowData . Level + 1 ] = = null | |
menus [ rowData . Level + 1 ] . Tag as RowData ! = rowData ) )
2020-09-27 01:54:24 +12:00
{
2021-10-04 07:24:22 +13:00
CreateAndShowLoadingMenu ( rowData ) ;
void CreateAndShowLoadingMenu ( RowData rowData )
{
2022-06-18 00:38:09 +12:00
MenuData menuDataLoading = new ( rowData . Level + 1 )
2021-10-04 07:24:22 +13:00
{
Validity = MenuDataValidity . Valid ,
} ;
2022-06-18 00:38:09 +12:00
Menu menuLoading = Create ( menuDataLoading , Path . GetFileName ( rowData . Path ) ) ;
2021-10-04 07:24:22 +13:00
menuLoading . IsLoadingMenu = true ;
AdjustMenusSizeAndLocation ( ) ;
2022-06-18 00:38:09 +12:00
menus [ rowData . Level + 1 ] = menuLoading ;
2021-10-04 07:24:22 +13:00
menuLoading . Tag = menuDataLoading . RowDataParent = rowData ;
menuDataLoading . RowDataParent . SubMenu = menuLoading ;
menuLoading . SetTypeLoading ( ) ;
ShowSubMenu ( menuLoading ) ;
}
2020-09-27 01:54:24 +12:00
BackgroundWorker workerSubMenu = workersSubMenu .
Where ( w = > ! w . IsBusy ) . FirstOrDefault ( ) ;
if ( workerSubMenu = = null )
{
workerSubMenu = new BackgroundWorker
{
WorkerSupportsCancellation = true ,
} ;
workerSubMenu . DoWork + = LoadMenu ;
workerSubMenu . RunWorkerCompleted + = LoadSubMenuCompleted ;
workersSubMenu . Add ( workerSubMenu ) ;
}
workerSubMenu . RunWorkerAsync ( rowData ) ;
}
void LoadSubMenuCompleted ( object senderCompleted , RunWorkerCompletedEventArgs e )
{
MenuData menuData = ( MenuData ) e . Result ;
2021-10-05 04:47:39 +13:00
Menu menuLoading = menus [ menuData . Level ] ;
2021-12-10 08:00:33 +13:00
string userSearchText = string . Empty ;
2022-02-05 03:49:45 +13:00
bool closedLoadingMenu = false ;
2021-10-05 04:47:39 +13:00
if ( menuLoading ! = null & & menuLoading . IsLoadingMenu )
2021-10-04 07:24:22 +13:00
{
2021-10-07 10:22:58 +13:00
menuLoading . HideWithFade ( ) ;
2022-02-05 03:49:45 +13:00
userSearchText = menuLoading . GetSearchText ( ) ;
2021-10-07 10:22:58 +13:00
menus [ menuLoading . Level ] = null ;
2022-02-05 03:49:45 +13:00
closedLoadingMenu = true ;
2021-10-04 07:24:22 +13:00
}
2021-10-05 04:47:39 +13:00
2022-06-18 00:38:09 +12:00
if ( menuData . Validity ! = MenuDataValidity . Undefined & &
2021-10-05 04:47:39 +13:00
menus [ 0 ] . IsUsable )
2020-09-27 01:54:24 +12:00
{
Menu menu = Create ( menuData ) ;
switch ( menuData . Validity )
{
case MenuDataValidity . Valid :
menu . SetTypeSub ( ) ;
break ;
case MenuDataValidity . Empty :
menu . SetTypeEmpty ( ) ;
break ;
case MenuDataValidity . NoAccess :
menu . SetTypeNoAccess ( ) ;
break ;
}
menu . Tag = menuData . RowDataParent ;
menuData . RowDataParent . SubMenu = menu ;
if ( menus [ 0 ] . IsUsable )
{
2022-02-05 03:49:45 +13:00
ShowSubMenu ( menu ) ;
2022-02-21 06:00:04 +13:00
menu . SetSearchText ( userSearchText ) ;
2020-09-27 01:54:24 +12:00
}
}
2022-02-05 03:49:45 +13:00
else if ( closedLoadingMenu & & menus [ 0 ] . IsUsable )
{
menuData . RowDataParent . IsMenuOpen = false ;
2022-02-05 22:32:28 +13:00
menuData . RowDataParent . IsClicking = false ;
menuData . RowDataParent . IsSelected = false ;
2022-02-19 04:02:16 +13:00
Menu menuPrevious = menus [ menuData . Level - 1 ] ;
if ( menuPrevious ! = null )
{
RefreshSelection ( menuPrevious . GetDataGridView ( ) ) ;
}
2022-02-05 03:49:45 +13:00
}
2020-09-27 01:54:24 +12:00
}
}
waitToOpenMenu . CloseMenu + = CloseMenu ;
void CloseMenu ( int level )
{
2021-10-11 04:33:54 +13:00
if ( level < menus . Length & & menus [ level ] ! = null )
2020-09-27 01:54:24 +12:00
{
HideOldMenu ( menus [ level ] ) ;
}
2021-05-14 22:22:15 +12:00
2021-10-11 04:33:54 +13:00
if ( level - 1 < menus . Length & & menus [ level - 1 ] ! = null )
2021-05-14 22:22:15 +12:00
{
menus [ level - 1 ] . FocusTextBox ( ) ;
}
2020-09-27 01:54:24 +12:00
}
waitToOpenMenu . MouseEnterOk + = MouseEnterOk ;
dgvMouseRow . RowMouseEnter + = waitToOpenMenu . MouseEnter ;
dgvMouseRow . RowMouseLeave + = waitToOpenMenu . MouseLeave ;
2021-11-24 11:08:19 +13:00
dgvMouseRow . RowMouseLeave + = Dgv_RowMouseLeave ;
2020-09-27 01:54:24 +12:00
keyboardInput = new KeyboardInput ( menus ) ;
keyboardInput . RegisterHotKey ( ) ;
2021-10-26 05:55:34 +13:00
keyboardInput . HotKeyPressed + = ( ) = > SwitchOpenClose ( false ) ;
2020-09-27 01:54:24 +12:00
keyboardInput . ClosePressed + = MenusFadeOut ;
keyboardInput . RowDeselected + = waitToOpenMenu . RowDeselected ;
keyboardInput . EnterPressed + = waitToOpenMenu . EnterOpensInstantly ;
2021-06-03 22:52:47 +12:00
keyboardInput . RowSelected + = waitToOpenMenu . RowSelected ;
keyboardInput . RowSelected + = AdjustScrollbarToDisplayedRow ;
void AdjustScrollbarToDisplayedRow ( DataGridView dgv , int index )
{
2021-06-26 23:24:56 +12:00
Menu menu = ( Menu ) dgv . FindForm ( ) ;
2021-06-03 22:52:47 +12:00
menu . AdjustScrollbar ( ) ;
}
2020-09-27 01:54:24 +12:00
2021-10-27 09:09:40 +13:00
timerShowProcessStartedAsLoadingIcon . Interval = Properties . Settings . Default . TimeUntilClosesAfterEnterPressed ;
2021-10-29 03:29:04 +13:00
timerStillActiveCheck . Interval = Properties . Settings . Default . TimeUntilClosesAfterEnterPressed + 20 ;
2021-10-27 09:09:40 +13:00
timerStillActiveCheck . Tick + = ( sender , e ) = > StillActiveTick ( ) ;
void StillActiveTick ( )
2020-09-27 01:54:24 +12:00
{
if ( ! IsActive ( ) )
{
FadeHalfOrOutIfNeeded ( ) ;
}
2021-10-29 03:29:04 +13:00
timerStillActiveCheck . Stop ( ) ;
2020-09-27 01:54:24 +12:00
}
2021-10-26 05:55:34 +13:00
waitLeave . LeaveTriggered + = FadeHalfOrOutIfNeeded ;
2022-06-18 00:38:09 +12:00
CreateWatcher ( Config . Path , false ) ;
foreach ( var pathAndFlags in MenusHelpers . GetAddionalPathsForMainMenu ( ) )
{
CreateWatcher ( pathAndFlags . Path , pathAndFlags . Recursive ) ;
}
void CreateWatcher ( string path , bool recursiv )
{
try
{
2022-06-21 03:39:39 +12:00
FileSystemWatcher watcher = new ( ) ;
2022-06-18 00:38:09 +12:00
watcher . Path = path ;
watcher . NotifyFilter = NotifyFilters . Attributes |
NotifyFilters . DirectoryName |
NotifyFilters . FileName |
NotifyFilters . LastWrite ;
watcher . Filter = "*.*" ;
watcher . Created + = WatcherProcessItem ;
watcher . Deleted + = WatcherProcessItem ;
2022-06-21 03:39:39 +12:00
watcher . Renamed + = WatcherProcessItem ;
watcher . Changed + = WatcherProcessItem ;
2022-06-18 00:38:09 +12:00
watcher . IncludeSubdirectories = recursiv ;
watcher . EnableRaisingEvents = true ;
watchers . Add ( watcher ) ;
}
catch ( Exception ex )
{
Log . Warn ( $"Failed to {nameof(CreateWatcher)}: {path}" , ex ) ;
}
}
2020-09-27 01:54:24 +12:00
}
internal event EventHandlerEmpty LoadStarted ;
internal event EventHandlerEmpty LoadStopped ;
private enum OpenCloseState
{
Default ,
Opening ,
Closing ,
}
private IEnumerable < Menu > AsEnumerable = > menus . Where ( m = > m ! = null & & ! m . IsDisposed ) ;
private List < Menu > AsList = > AsEnumerable . ToList ( ) ;
public void Dispose ( )
{
workerMainMenu . Dispose ( ) ;
foreach ( BackgroundWorker worker in workersSubMenu )
{
worker . Dispose ( ) ;
}
waitToOpenMenu . Dispose ( ) ;
keyboardInput . Dispose ( ) ;
2021-10-26 08:45:49 +13:00
timerShowProcessStartedAsLoadingIcon . Dispose ( ) ;
2020-09-27 01:54:24 +12:00
timerStillActiveCheck . Dispose ( ) ;
waitLeave . Dispose ( ) ;
IconReader . Dispose ( ) ;
DisposeMenu ( menus [ 0 ] ) ;
dgvMouseRow . Dispose ( ) ;
2022-06-18 00:38:09 +12:00
foreach ( FileSystemWatcher watcher in watchers )
{
watcher . Created - = WatcherProcessItem ;
watcher . Deleted - = WatcherProcessItem ;
2022-06-21 03:39:39 +12:00
watcher . Renamed - = WatcherProcessItem ;
watcher . Changed - = WatcherProcessItem ;
2022-06-18 00:38:09 +12:00
watcher . Dispose ( ) ;
}
2020-09-27 01:54:24 +12:00
}
internal static MenuData GetData ( BackgroundWorker worker , string path , int level )
{
2022-06-18 00:38:09 +12:00
MenuData menuData = new ( level ) ;
if ( worker ? . CancellationPending = = true | | string . IsNullOrEmpty ( path ) )
2021-11-11 11:39:52 +13:00
{
2022-06-18 00:38:09 +12:00
return menuData ;
2021-11-11 11:39:52 +13:00
}
2022-06-18 00:38:09 +12:00
MenusHelpers . GetItemsForMainMenu ( worker , path , ref menuData ) ;
if ( worker ? . CancellationPending = = true )
2020-09-27 01:54:24 +12:00
{
2022-06-18 00:38:09 +12:00
return menuData ;
2020-09-27 01:54:24 +12:00
}
2022-06-18 00:38:09 +12:00
MenusHelpers . GetAddionalItemsForMainMenu ( ref menuData ) ;
if ( worker ? . CancellationPending = = true )
2020-09-27 01:54:24 +12:00
{
2022-06-18 00:38:09 +12:00
return menuData ;
2020-09-27 01:54:24 +12:00
}
2022-06-18 00:38:09 +12:00
MenusHelpers . ReadHiddenAndReadIcons ( worker , ref menuData ) ;
if ( worker ? . CancellationPending = = true )
2020-09-27 01:54:24 +12:00
{
2022-06-18 00:38:09 +12:00
return menuData ;
2020-09-27 01:54:24 +12:00
}
2022-06-18 00:38:09 +12:00
MenusHelpers . CheckIfValid ( ref menuData ) ;
MenusHelpers . SortItemsWhenValid ( ref menuData ) ;
2020-09-27 01:54:24 +12:00
return menuData ;
}
2021-11-10 05:41:39 +13:00
internal void SwitchOpenCloseByTaskbarItem ( )
{
SwitchOpenClose ( true ) ;
timerStillActiveCheck . Start ( ) ;
}
2021-10-26 05:55:34 +13:00
internal bool IsOpenCloseStateOpening ( )
{
2022-02-05 02:34:32 +13:00
return openCloseState = = OpenCloseState . Opening ;
2021-10-26 05:55:34 +13:00
}
2021-11-24 11:48:59 +13:00
internal void SwitchOpenClose ( bool byClick , bool isMainPreload = false )
2020-09-27 01:54:24 +12:00
{
2021-11-24 11:48:59 +13:00
// Ignore open close events during main preload #248
if ( IconReader . MainPreload & & ! isMainPreload )
{
2021-11-26 11:49:50 +13:00
showMenuAfterMainPreload = true ;
2021-11-24 11:48:59 +13:00
return ;
}
2020-09-27 01:54:24 +12:00
waitToOpenMenu . MouseActive = byClick ;
2021-06-26 23:24:56 +12:00
if ( byClick & &
! Config . AlwaysOpenByPin & &
( DateTime . Now - deactivatedTime ) . TotalMilliseconds < 200 )
2020-09-27 01:54:24 +12:00
{
// By click on notifyicon the menu gets deactivated and closed
}
else if ( string . IsNullOrEmpty ( Config . Path ) )
{
// Case when Folder Dialog open
}
else if ( openCloseState = = OpenCloseState . Opening | |
( menus [ 0 ] ! = null & & menus [ 0 ] . Visible & & openCloseState = = OpenCloseState . Default ) )
{
openCloseState = OpenCloseState . Closing ;
MenusFadeOut ( ) ;
StopWorker ( ) ;
if ( ! AsEnumerable . Any ( m = > m . Visible ) )
{
openCloseState = OpenCloseState . Default ;
}
}
else
{
openCloseState = OpenCloseState . Opening ;
StartWorker ( ) ;
}
deactivatedTime = DateTime . MinValue ;
}
internal void DisposeMenu ( Menu menuToDispose )
{
if ( menuToDispose ! = null )
{
menuToDispose . MouseWheel - = AdjustMenusSizeAndLocation ;
menuToDispose . MouseLeave - = waitLeave . Start ;
menuToDispose . MouseEnter - = waitLeave . Stop ;
menuToDispose . CmdKeyProcessed - = keyboardInput . CmdKeyProcessed ;
menuToDispose . SearchTextChanging - = keyboardInput . SearchTextChanging ;
2022-02-21 09:35:38 +13:00
menuToDispose . KeyPressCheck - = Menu_KeyPressCheck ;
2020-09-27 01:54:24 +12:00
menuToDispose . SearchTextChanged - = Menu_SearchTextChanged ;
2021-04-17 12:39:48 +12:00
DataGridView dgv = menuToDispose . GetDataGridView ( ) ;
2020-10-10 20:47:30 +13:00
if ( dgv ! = null )
{
dgv . CellMouseEnter - = dgvMouseRow . CellMouseEnter ;
dgv . CellMouseLeave - = dgvMouseRow . CellMouseLeave ;
dgv . MouseLeave - = dgvMouseRow . MouseLeave ;
2021-12-30 23:56:34 +13:00
dgv . MouseLeave - = Dgv_MouseLeave ;
2020-10-10 20:47:30 +13:00
dgv . MouseMove - = waitToOpenMenu . MouseMove ;
2021-12-30 23:56:34 +13:00
dgv . MouseMove - = Dgv_MouseMove ;
2020-10-10 20:47:30 +13:00
dgv . MouseDown - = Dgv_MouseDown ;
2021-11-24 11:08:19 +13:00
dgv . MouseUp - = Dgv_MouseUp ;
dgv . MouseClick - = Dgv_MouseClick ;
2020-10-10 20:47:30 +13:00
dgv . MouseDoubleClick - = Dgv_MouseDoubleClick ;
dgv . SelectionChanged - = Dgv_SelectionChanged ;
dgv . RowPostPaint - = Dgv_RowPostPaint ;
dgv . ClearSelection ( ) ;
foreach ( DataGridViewRow row in dgv . Rows )
{
RowData rowData = ( RowData ) row . Cells [ 2 ] . Value ;
DisposeMenu ( rowData . SubMenu ) ;
}
2020-09-27 01:54:24 +12:00
}
2021-04-17 12:39:48 +12:00
menuToDispose . Dispose ( ) ;
2020-09-27 01:54:24 +12:00
}
}
2021-12-28 00:22:52 +13:00
internal void ReAdjustSizeAndLocation ( )
{
if ( menus [ 0 ] . IsUsable )
{
menus [ 0 ] . Tag = null ;
}
}
2020-09-27 01:54:24 +12:00
internal void MainPreload ( )
{
2021-11-13 20:18:33 +13:00
IconReader . MainPreload = true ;
timerShowProcessStartedAsLoadingIcon . Tick + = Tick ;
2021-11-29 07:42:35 +13:00
timerShowProcessStartedAsLoadingIcon . Interval = 5 ;
2021-11-13 20:18:33 +13:00
timerShowProcessStartedAsLoadingIcon . Start ( ) ;
void Tick ( object sender , EventArgs e )
2021-11-11 11:39:52 +13:00
{
2021-11-13 20:18:33 +13:00
timerShowProcessStartedAsLoadingIcon . Tick - = Tick ;
2021-11-24 11:48:59 +13:00
timerShowProcessStartedAsLoadingIcon . Interval = Properties . Settings . Default . TimeUntilClosesAfterEnterPressed ;
SwitchOpenClose ( false , true ) ;
2021-11-11 11:39:52 +13:00
}
2020-09-27 01:54:24 +12:00
}
internal void StartWorker ( )
{
2022-07-10 21:40:48 +12:00
if ( Properties . Settings . Default . GenerateShortcutsToDrives )
{
GenerateDriveShortcuts . Start ( ) ;
}
2020-09-27 01:54:24 +12:00
if ( ! workerMainMenu . IsBusy )
{
LoadStarted ( ) ;
workerMainMenu . RunWorkerAsync (
new object [ ] { Config . Path , 0 } ) ;
}
}
internal void StopWorker ( )
{
if ( workerMainMenu . IsBusy )
{
workerMainMenu . CancelAsync ( ) ;
}
}
2021-11-11 11:39:52 +13:00
private static void LoadMenu ( object senderDoWork , DoWorkEventArgs eDoWork )
{
2022-07-10 21:40:48 +12:00
string path ;
2021-11-11 11:39:52 +13:00
int level = 0 ;
RowData rowData = eDoWork . Argument as RowData ;
if ( rowData ! = null )
{
2022-06-18 00:38:09 +12:00
path = rowData . ResolvedPath ;
level = rowData . Level + 1 ;
2021-11-11 11:39:52 +13:00
}
2022-07-10 21:40:48 +12:00
else
2021-12-11 03:20:24 +13:00
{
2022-07-10 21:40:48 +12:00
path = Config . Path ;
2021-12-11 03:20:24 +13:00
}
2021-11-11 11:39:52 +13:00
MenuData menuData = GetData ( ( BackgroundWorker ) senderDoWork , path , level ) ;
menuData . RowDataParent = rowData ;
eDoWork . Result = menuData ;
}
2022-06-18 00:38:09 +12:00
private static void OpenFolder ( string pathToFolder = "" )
2021-11-11 11:39:52 +13:00
{
2022-06-18 00:38:09 +12:00
string path = pathToFolder ;
if ( string . IsNullOrEmpty ( path ) )
2021-11-11 11:39:52 +13:00
{
2022-06-18 00:38:09 +12:00
path = Config . Path ;
2021-11-11 11:39:52 +13:00
}
2022-06-18 00:38:09 +12:00
Log . ProcessStart ( path ) ;
2021-11-11 11:39:52 +13:00
}
2022-06-18 00:38:09 +12:00
private static int GetRowUnderCursor ( DataGridView dgv , Point location )
2021-11-11 11:39:52 +13:00
{
2022-06-18 00:38:09 +12:00
DataGridView . HitTestInfo myHitTest = dgv . HitTest ( location . X , location . Y ) ;
return myHitTest . RowIndex ;
2021-11-11 11:39:52 +13:00
}
2022-06-18 00:38:09 +12:00
private static void InvalidateRowIfIndexInRange ( DataGridView dgv , int rowIndex )
2020-09-27 01:54:24 +12:00
{
2022-06-18 00:38:09 +12:00
if ( rowIndex > - 1 & & rowIndex < dgv . Rows . Count )
2020-09-27 01:54:24 +12:00
{
2022-06-18 00:38:09 +12:00
dgv . InvalidateRow ( rowIndex ) ;
2020-09-27 01:54:24 +12:00
}
2022-06-18 00:38:09 +12:00
}
2020-09-27 01:54:24 +12:00
2022-06-18 00:38:09 +12:00
private static void AddItemsToMenu ( List < RowData > data , Menu menu , out int foldersCount , out int filesCount )
{
foldersCount = 0 ;
filesCount = 0 ;
DataGridView dgv = menu . GetDataGridView ( ) ;
DataTable dataTable = new ( ) ;
dataTable . Columns . Add ( dgv . Columns [ 0 ] . Name , typeof ( Icon ) ) ;
dataTable . Columns . Add ( dgv . Columns [ 1 ] . Name , typeof ( string ) ) ;
dataTable . Columns . Add ( "data" , typeof ( RowData ) ) ;
dataTable . Columns . Add ( "SortIndex" ) ;
foreach ( RowData rowData in data )
{
if ( ! ( rowData . IsAddionalItem & & Properties . Settings . Default . ShowOnlyAsSearchResult ) )
2020-09-27 01:54:24 +12:00
{
2022-06-18 00:38:09 +12:00
if ( rowData . ContainsMenu )
2022-01-02 00:37:33 +13:00
{
2022-06-18 00:38:09 +12:00
foldersCount + + ;
2022-01-02 00:37:33 +13:00
}
2020-09-27 01:54:24 +12:00
else
{
2022-06-18 00:38:09 +12:00
filesCount + + ;
2020-09-27 01:54:24 +12:00
}
}
2022-06-18 00:38:09 +12:00
rowData . SetData ( rowData , dataTable ) ;
2021-06-26 23:24:56 +12:00
}
2022-06-18 00:38:09 +12:00
dgv . DataSource = dataTable ;
dgv . Columns [ "data" ] . Visible = false ;
dgv . Columns [ "SortIndex" ] . Visible = false ;
2022-02-06 00:47:27 +13:00
2022-06-18 00:38:09 +12:00
string columnSortIndex = "SortIndex" ;
foreach ( DataRow row in dataTable . Rows )
2022-02-08 10:45:10 +13:00
{
2022-06-18 00:38:09 +12:00
RowData rowData = ( RowData ) row [ 2 ] ;
if ( rowData . IsAddionalItem & & Properties . Settings . Default . ShowOnlyAsSearchResult )
{
row [ columnSortIndex ] = 99 ;
}
else
{
row [ columnSortIndex ] = 0 ;
}
2022-02-08 10:45:10 +13:00
}
}
2022-02-28 04:44:08 +13:00
private bool IsActive ( )
{
bool IsShellContextMenuOpen ( )
{
bool isShellContextMenuOpen = false ;
foreach ( Menu menu in menus . Where ( m = > m ! = null ) )
{
DataGridView dgv = menu . GetDataGridView ( ) ;
foreach ( DataGridViewRow row in dgv . Rows )
{
RowData rowData = ( RowData ) row . Cells [ 2 ] . Value ;
if ( rowData ! = null & & rowData . IsContextMenuOpen )
{
isShellContextMenuOpen = true ;
break ;
}
}
if ( isShellContextMenuOpen )
{
break ;
}
}
return isShellContextMenuOpen ;
}
return Form . ActiveForm is Menu or TaskbarForm | | IsShellContextMenuOpen ( ) ;
}
2020-09-27 01:54:24 +12:00
private Menu Create ( MenuData menuData , string title = null )
{
2021-11-17 12:13:46 +13:00
Menu menu = new ( ) ;
2020-09-27 01:54:24 +12:00
2021-06-28 00:44:12 +12:00
string path = Config . Path ;
if ( title = = null )
2020-09-27 01:54:24 +12:00
{
2022-06-18 00:38:09 +12:00
title = Path . GetFileName ( menuData . RowDataParent . ResolvedPath ) ;
path = menuData . RowDataParent . ResolvedPath ;
2021-06-28 00:44:12 +12:00
}
2020-09-27 01:54:24 +12:00
2021-06-28 00:44:12 +12:00
if ( string . IsNullOrEmpty ( title ) )
{
title = Path . GetPathRoot ( path ) ;
2021-06-26 23:24:56 +12:00
}
2021-06-28 00:44:12 +12:00
2022-06-01 09:23:26 +12:00
menu . AdjustControls ( title , menuData . Validity ) ;
2021-10-26 05:55:34 +13:00
menu . UserClickedOpenFolder + = ( ) = > OpenFolder ( path ) ;
2020-09-27 01:54:24 +12:00
menu . Level = menuData . Level ;
menu . MouseWheel + = AdjustMenusSizeAndLocation ;
menu . MouseLeave + = waitLeave . Start ;
menu . MouseEnter + = waitLeave . Stop ;
menu . CmdKeyProcessed + = keyboardInput . CmdKeyProcessed ;
2022-02-21 09:35:38 +13:00
menu . KeyPressCheck + = Menu_KeyPressCheck ;
2020-10-10 20:47:30 +13:00
menu . SearchTextChanging + = Menu_SearchTextChanging ;
2020-09-27 01:54:24 +12:00
menu . SearchTextChanged + = Menu_SearchTextChanged ;
2021-11-25 07:01:59 +13:00
menu . UserDragsMenu + = Menu_UserDragsMenu ;
void Menu_UserDragsMenu ( )
{
if ( menus [ 1 ] ! = null )
{
HideOldMenu ( menus [ 1 ] ) ;
}
}
2020-09-27 01:54:24 +12:00
menu . Deactivate + = Deactivate ;
void Deactivate ( object sender , EventArgs e )
{
2021-11-10 03:12:52 +13:00
if ( IsOpenCloseStateOpening ( ) )
{
Log . Info ( "Ignored Deactivate, because openCloseState == OpenCloseState.Opening" ) ;
}
else if ( ! Properties . Settings . Default . StaysOpenWhenFocusLostAfterEnterPressed | |
2021-10-29 03:29:04 +13:00
! waitingForReactivate )
2020-09-27 01:54:24 +12:00
{
2021-10-27 09:09:40 +13:00
FadeHalfOrOutIfNeeded ( ) ;
if ( ! IsActive ( ) )
{
deactivatedTime = DateTime . Now ;
}
2020-09-27 01:54:24 +12:00
}
}
2021-10-27 09:09:40 +13:00
menu . Activated + = ( sender , e ) = > Activated ( ) ;
void Activated ( )
2020-09-27 01:54:24 +12:00
{
2021-10-27 09:09:40 +13:00
if ( IsActive ( ) & & menus [ 0 ] . IsUsable )
2020-09-27 01:54:24 +12:00
{
AsList . ForEach ( m = > m . ShowWithFade ( ) ) ;
2021-10-27 09:09:40 +13:00
timerStillActiveCheck . Stop ( ) ;
2020-09-27 01:54:24 +12:00
timerStillActiveCheck . Start ( ) ;
}
}
menu . VisibleChanged + = MenuVisibleChanged ;
2021-06-28 00:44:12 +12:00
AddItemsToMenu ( menuData . RowDatas , menu , out int foldersCount , out int filesCount ) ;
2020-09-27 01:54:24 +12:00
DataGridView dgv = menu . GetDataGridView ( ) ;
dgv . CellMouseEnter + = dgvMouseRow . CellMouseEnter ;
dgv . CellMouseLeave + = dgvMouseRow . CellMouseLeave ;
dgv . MouseLeave + = dgvMouseRow . MouseLeave ;
2021-12-30 23:56:34 +13:00
dgv . MouseLeave + = Dgv_MouseLeave ;
2020-09-27 01:54:24 +12:00
dgv . MouseMove + = waitToOpenMenu . MouseMove ;
2021-12-30 23:56:34 +13:00
dgv . MouseMove + = Dgv_MouseMove ;
2020-09-27 01:54:24 +12:00
dgv . MouseDown + = Dgv_MouseDown ;
2021-11-24 11:08:19 +13:00
dgv . MouseUp + = Dgv_MouseUp ;
dgv . MouseClick + = Dgv_MouseClick ;
2020-09-27 01:54:24 +12:00
dgv . MouseDoubleClick + = Dgv_MouseDoubleClick ;
dgv . SelectionChanged + = Dgv_SelectionChanged ;
dgv . RowPostPaint + = Dgv_RowPostPaint ;
2021-06-04 01:00:53 +12:00
dgv . DataError + = Dgv_DataError ;
void Dgv_DataError ( object sender , DataGridViewDataErrorEventArgs e )
{
// WARN Dgv_DataError occured System.ObjectDisposedException: Cannot access a disposed object. Object name: 'Icon'
// => Rare times occured (e.g. when focused an close other application => closed and activated at same time)
Log . Warn ( "Dgv_DataError occured" , e . Exception ) ;
}
2020-09-27 01:54:24 +12:00
2021-06-28 00:44:12 +12:00
menu . SetCounts ( foldersCount , filesCount ) ;
2020-09-27 01:54:24 +12:00
return menu ;
}
private void MenuVisibleChanged ( object sender , EventArgs e )
{
Menu menu = ( Menu ) sender ;
if ( menu . IsUsable )
{
AdjustMenusSizeAndLocation ( ) ;
2022-01-08 23:39:23 +13:00
if ( menu . Level = = 0 )
{
2022-06-11 22:45:46 +12:00
menu . ResetSearchText ( ) ;
2022-01-10 01:33:44 +13:00
AdjustMenusSizeAndLocation ( ) ;
2022-01-08 23:39:23 +13:00
}
2020-09-27 01:54:24 +12:00
}
2021-11-11 11:39:52 +13:00
if ( ! menu . Visible & & menu . Level ! = 0 )
2020-09-27 01:54:24 +12:00
{
DisposeMenu ( menu ) ;
}
if ( ! AsEnumerable . Any ( m = > m . Visible ) )
{
2021-10-07 10:22:58 +13:00
if ( IconReader . ClearIfCacheTooBig ( ) )
{
GC . Collect ( ) ;
}
2021-10-14 04:13:11 +13:00
openCloseState = OpenCloseState . Default ;
2020-09-27 01:54:24 +12:00
}
}
2021-12-30 23:56:34 +13:00
private void Dgv_MouseMove ( object sender , MouseEventArgs e )
{
2021-12-31 00:36:00 +13:00
if ( isDraggingSwipeScrolling )
2021-12-30 23:56:34 +13:00
{
DataGridView dgv = ( DataGridView ) sender ;
int newRow = GetRowUnderCursor ( dgv , e . Location ) ;
if ( newRow > - 1 )
{
2021-12-31 00:36:00 +13:00
int delta = dragSwipeScrollingStartRowIndex - newRow ;
2022-01-10 05:13:10 +13:00
if ( DoScroll ( dgv , ref delta ) )
{
dragSwipeScrollingStartRowIndex + = delta ;
}
2021-12-30 23:56:34 +13:00
}
}
}
2022-01-10 05:13:10 +13:00
private bool DoScroll ( DataGridView dgv , ref int delta )
2021-12-30 23:56:34 +13:00
{
2022-01-10 05:13:10 +13:00
bool scrolled = false ;
2021-12-30 23:56:34 +13:00
if ( delta ! = 0 )
{
if ( delta < 0 & & dgv . FirstDisplayedScrollingRowIndex = = 0 )
{
delta = 0 ;
}
2022-01-10 05:13:10 +13:00
int newFirstDisplayedScrollingRowIndex = dgv . FirstDisplayedScrollingRowIndex + ( delta * 2 ) ;
if ( newFirstDisplayedScrollingRowIndex < 0 | | newFirstDisplayedScrollingRowIndex > = dgv . RowCount )
{
newFirstDisplayedScrollingRowIndex = dgv . FirstDisplayedScrollingRowIndex + delta ;
}
2021-12-30 23:56:34 +13:00
if ( newFirstDisplayedScrollingRowIndex > - 1 & & newFirstDisplayedScrollingRowIndex < dgv . RowCount )
{
2021-12-31 00:36:00 +13:00
isDragSwipeScrolled = true ;
2021-12-30 23:56:34 +13:00
dgv . FirstDisplayedScrollingRowIndex = newFirstDisplayedScrollingRowIndex ;
Menu menu = ( Menu ) dgv . FindForm ( ) ;
menu . AdjustScrollbar ( ) ;
2022-01-10 05:13:10 +13:00
scrolled = dgv . FirstDisplayedScrollingRowIndex = = newFirstDisplayedScrollingRowIndex ;
2021-12-30 23:56:34 +13:00
}
}
2022-01-10 05:13:10 +13:00
return scrolled ;
2021-12-30 23:56:34 +13:00
}
2020-09-27 01:54:24 +12:00
private void Dgv_MouseDown ( object sender , MouseEventArgs e )
{
DataGridView dgv = ( DataGridView ) sender ;
DataGridView . HitTestInfo hitTestInfo ;
hitTestInfo = dgv . HitTest ( e . X , e . Y ) ;
2021-11-24 12:22:38 +13:00
if ( hitTestInfo . RowIndex > - 1 & &
hitTestInfo . RowIndex < dgv . Rows . Count )
{
2022-06-18 00:38:09 +12:00
MouseEnterOk ( dgv , hitTestInfo . RowIndex , true ) ;
2021-11-24 12:22:38 +13:00
RowData rowData = ( RowData ) dgv . Rows [ hitTestInfo . RowIndex ] . Cells [ 2 ] . Value ;
rowData . MouseDown ( dgv , e ) ;
2022-02-08 07:22:16 +13:00
InvalidateRowIfIndexInRange ( dgv , hitTestInfo . RowIndex ) ;
2021-11-24 12:22:38 +13:00
}
2021-11-24 11:08:19 +13:00
if ( e . Button = = MouseButtons . Left )
{
2021-12-31 00:36:00 +13:00
lastMouseDownRowIndex = hitTestInfo . RowIndex ;
2021-11-24 11:08:19 +13:00
}
2021-12-30 23:56:34 +13:00
2021-12-31 00:36:00 +13:00
Menu menu = ( Menu ) ( ( DataGridView ) sender ) . FindForm ( ) ;
2022-01-09 23:51:21 +13:00
if ( menu ! = null & & menu . ScrollbarVisible )
2021-12-31 00:36:00 +13:00
{
2022-05-03 05:55:15 +12:00
bool isTouchEnabled = DllImports . NativeMethods . IsTouchEnabled ( ) ;
if ( ( isTouchEnabled & & Properties . Settings . Default . SwipeScrollingEnabledTouch ) | |
( ! isTouchEnabled & & Properties . Settings . Default . SwipeScrollingEnabled ) )
{
isDraggingSwipeScrolling = true ;
}
2021-12-31 00:36:00 +13:00
dragSwipeScrollingStartRowIndex = GetRowUnderCursor ( dgv , e . Location ) ;
}
2021-12-30 23:56:34 +13:00
}
2021-11-24 11:08:19 +13:00
private void Dgv_MouseUp ( object sender , MouseEventArgs e )
{
2021-12-31 00:36:00 +13:00
lastMouseDownRowIndex = - 1 ;
isDraggingSwipeScrolling = false ;
isDragSwipeScrolled = false ;
2021-12-30 23:56:34 +13:00
// In case during mouse down move mouse out of dgv (it has own scrollbehavior) which we need to refresh
Menu menu = ( Menu ) ( ( DataGridView ) sender ) . FindForm ( ) ;
menu . AdjustScrollbar ( ) ;
}
private void Dgv_MouseLeave ( object sender , EventArgs e )
{
2021-12-31 00:36:00 +13:00
isDraggingSwipeScrolling = false ;
isDragSwipeScrolled = false ;
2021-11-24 11:08:19 +13:00
}
2022-06-18 00:38:09 +12:00
private void MouseEnterOk ( DataGridView dgv , int rowIndex )
{
MouseEnterOk ( dgv , rowIndex , false ) ;
}
private void MouseEnterOk ( DataGridView dgv , int rowIndex , bool refreshView )
{
if ( menus [ 0 ] . IsUsable )
{
if ( keyboardInput . InUse )
{
keyboardInput . ClearIsSelectedByKey ( ) ;
keyboardInput . InUse = false ;
}
keyboardInput . Select ( dgv , rowIndex , refreshView ) ;
}
}
2021-11-24 11:08:19 +13:00
private void Dgv_RowMouseLeave ( object sender , DataGridViewCellEventArgs e )
{
DataGridView dgv = ( DataGridView ) sender ;
2021-12-31 00:36:00 +13:00
if ( ! isDragSwipeScrolled & &
e . RowIndex = = lastMouseDownRowIndex & &
2021-11-24 11:08:19 +13:00
e . RowIndex > - 1 & &
e . RowIndex < dgv . Rows . Count )
{
2021-12-31 00:36:00 +13:00
lastMouseDownRowIndex = - 1 ;
2021-11-24 11:08:19 +13:00
RowData rowData = ( RowData ) dgv . Rows [ e . RowIndex ] . Cells [ 2 ] . Value ;
2022-06-18 00:38:09 +12:00
string [ ] files = new string [ ] { rowData . Path } ;
2021-11-24 11:08:19 +13:00
// Update position raises move event which prevent DoDragDrop blocking UI when mouse not moved
Cursor . Position = new Point ( Cursor . Position . X , Cursor . Position . Y ) ;
dgv . DoDragDrop ( new DataObject ( DataFormats . FileDrop , files ) , DragDropEffects . Copy ) ;
}
}
private void Dgv_MouseClick ( object sender , MouseEventArgs e )
{
DataGridView dgv = ( DataGridView ) sender ;
DataGridView . HitTestInfo hitTestInfo ;
hitTestInfo = dgv . HitTest ( e . X , e . Y ) ;
2022-01-09 11:23:14 +13:00
if ( ! isDragSwipeScrolled & &
2021-12-31 00:36:00 +13:00
hitTestInfo . RowIndex = = lastMouseDownRowIndex & &
2021-11-24 11:08:19 +13:00
hitTestInfo . RowIndex > - 1 & &
hitTestInfo . RowIndex < dgv . Rows . Count )
2020-09-27 01:54:24 +12:00
{
2022-06-26 20:56:47 +12:00
lastMouseDownRowIndex = - 1 ;
2020-09-27 01:54:24 +12:00
RowData rowData = ( RowData ) dgv . Rows [ hitTestInfo . RowIndex ] . Cells [ 2 ] . Value ;
2021-11-24 12:22:38 +13:00
rowData . MouseClick ( e , out bool toCloseByClick ) ;
2020-09-27 01:54:24 +12:00
waitToOpenMenu . ClickOpensInstantly ( dgv , hitTestInfo . RowIndex ) ;
2021-05-02 23:12:18 +12:00
if ( toCloseByClick )
{
MenusFadeOut ( ) ;
}
2020-09-27 01:54:24 +12:00
}
2021-11-24 11:08:19 +13:00
2021-12-31 00:36:00 +13:00
lastMouseDownRowIndex = - 1 ;
2020-09-27 01:54:24 +12:00
}
private void Dgv_MouseDoubleClick ( object sender , MouseEventArgs e )
{
DataGridView dgv = ( DataGridView ) sender ;
DataGridView . HitTestInfo hitTestInfo ;
hitTestInfo = dgv . HitTest ( e . X , e . Y ) ;
if ( hitTestInfo . RowIndex > - 1 & &
dgv . Rows . Count > hitTestInfo . RowIndex )
{
2022-06-26 20:56:47 +12:00
lastMouseDownRowIndex = - 1 ;
RowData rowData = ( RowData ) dgv . Rows [ hitTestInfo . RowIndex ] . Cells [ 2 ] . Value ;
rowData . DoubleClick ( e , out bool toCloseByDoubleClick ) ;
2022-02-08 07:22:16 +13:00
InvalidateRowIfIndexInRange ( dgv , hitTestInfo . RowIndex ) ;
2021-05-02 23:12:18 +12:00
if ( toCloseByDoubleClick )
{
MenusFadeOut ( ) ;
}
2020-09-27 01:54:24 +12:00
}
2021-11-24 11:08:19 +13:00
2021-12-31 00:36:00 +13:00
lastMouseDownRowIndex = - 1 ;
2020-09-27 01:54:24 +12:00
}
private void Dgv_SelectionChanged ( object sender , EventArgs e )
{
RefreshSelection ( ( DataGridView ) sender ) ;
}
private void RefreshSelection ( DataGridView dgv )
{
2020-10-10 20:47:30 +13:00
dgv . SelectionChanged - = Dgv_SelectionChanged ;
2020-09-27 01:54:24 +12:00
foreach ( DataGridViewRow row in dgv . Rows )
{
RowData rowData = ( RowData ) row . Cells [ 2 ] . Value ;
if ( rowData = = null )
{
// Case when filtering a previous menu
}
else if ( ! menus [ 0 ] . IsUsable )
{
row . DefaultCellStyle . SelectionBackColor = Color . White ;
row . Selected = false ;
}
2022-02-05 22:32:28 +13:00
else if ( rowData . IsClicking )
{
row . DefaultCellStyle . SelectionBackColor = MenuDefines . ColorIcons ;
row . Selected = true ;
}
2020-09-27 01:54:24 +12:00
else if ( rowData . IsContextMenuOpen | | ( rowData . IsMenuOpen & & rowData . IsSelected ) )
{
row . Selected = true ;
}
else if ( rowData . IsMenuOpen )
{
row . Selected = true ;
}
else if ( rowData . IsSelected )
{
row . DefaultCellStyle . SelectionBackColor = MenuDefines . ColorSelectedItem ;
row . Selected = true ;
}
else
{
row . DefaultCellStyle . SelectionBackColor = Color . White ;
row . Selected = false ;
}
}
2020-10-10 20:47:30 +13:00
dgv . SelectionChanged + = Dgv_SelectionChanged ;
if ( ! searchTextChanging )
{
2021-06-03 22:52:47 +12:00
dgv . Invalidate ( ) ;
2020-10-10 20:47:30 +13:00
}
2020-09-27 01:54:24 +12:00
}
private void Dgv_RowPostPaint ( object sender , DataGridViewRowPostPaintEventArgs e )
{
DataGridView dgv = ( DataGridView ) sender ;
DataGridViewRow row = dgv . Rows [ e . RowIndex ] ;
if ( row . Selected )
{
RowData rowData = ( RowData ) row . Cells [ 2 ] . Value ;
int width = dgv . Columns [ 0 ] . Width + dgv . Columns [ 1 ] . Width ;
2021-11-17 12:13:46 +13:00
Rectangle rowBounds = new ( 0 , e . RowBounds . Top , width , e . RowBounds . Height ) ;
2020-09-27 01:54:24 +12:00
2022-02-05 22:32:28 +13:00
if ( rowData . IsClicking )
{
ControlPaint . DrawBorder ( e . Graphics , rowBounds , MenuDefines . ColorIcons , ButtonBorderStyle . Solid ) ;
row . DefaultCellStyle . SelectionBackColor = MenuDefines . ColorSelectedItem ;
}
else if ( rowData . IsContextMenuOpen | | ( rowData . IsMenuOpen & & rowData . IsSelected ) )
2020-09-27 01:54:24 +12:00
{
ControlPaint . DrawBorder ( e . Graphics , rowBounds , MenuDefines . ColorSelectedItemBorder , ButtonBorderStyle . Solid ) ;
row . DefaultCellStyle . SelectionBackColor = MenuDefines . ColorSelectedItem ;
}
else if ( rowData . IsMenuOpen )
{
ControlPaint . DrawBorder ( e . Graphics , rowBounds , MenuDefines . ColorOpenFolderBorder , ButtonBorderStyle . Solid ) ;
row . DefaultCellStyle . SelectionBackColor = MenuDefines . ColorOpenFolder ;
}
2021-10-26 08:45:49 +13:00
if ( rowData . ProcessStarted )
{
2021-10-29 03:29:04 +13:00
waitingForReactivate = true ;
2021-10-26 08:45:49 +13:00
rowData . ProcessStarted = false ;
row . Cells [ 0 ] . Value = Resources . StaticResources . LoadingIcon ;
timerShowProcessStartedAsLoadingIcon . Tick + = Tick ;
void Tick ( object sender , EventArgs e )
{
timerShowProcessStartedAsLoadingIcon . Tick - = Tick ;
timerShowProcessStartedAsLoadingIcon . Stop ( ) ;
2022-06-18 00:38:09 +12:00
row . Cells [ 0 ] . Value = rowData . ReadIcon ( false ) ;
2021-10-29 03:29:04 +13:00
waitingForReactivate = false ;
2021-10-26 08:45:49 +13:00
}
2021-10-27 09:09:40 +13:00
timerShowProcessStartedAsLoadingIcon . Stop ( ) ;
2021-10-26 08:45:49 +13:00
timerShowProcessStartedAsLoadingIcon . Start ( ) ;
2021-10-27 09:09:40 +13:00
timerStillActiveCheck . Stop ( ) ;
timerStillActiveCheck . Start ( ) ;
2021-10-26 08:45:49 +13:00
}
2020-09-27 01:54:24 +12:00
}
}
private void ShowSubMenu ( Menu menuToShow )
{
HideOldMenu ( menuToShow , true ) ;
menus [ menuToShow . Level ] = menuToShow ;
AdjustMenusSizeAndLocation ( ) ;
menus [ menuToShow . Level ] . ShowWithFadeOrTransparent ( IsActive ( ) ) ;
}
private void HideOldMenu ( Menu menuToShow , bool keepOrSetIsMenuOpen = false )
{
Menu menuPrevious = menus [ menuToShow . Level - 1 ] ;
2021-05-14 01:20:40 +12:00
if ( menuPrevious ! = null )
2020-09-27 01:54:24 +12:00
{
2021-05-14 01:20:40 +12:00
// Clean up menu status IsMenuOpen for previous one
DataGridView dgvPrevious = menuPrevious . GetDataGridView ( ) ;
foreach ( DataRow row in ( ( DataTable ) dgvPrevious . DataSource ) . Rows )
2020-09-27 01:54:24 +12:00
{
2021-05-14 01:20:40 +12:00
RowData rowDataToClear = ( RowData ) row [ 2 ] ;
if ( rowDataToClear = = ( RowData ) menuToShow . Tag )
{
rowDataToClear . IsMenuOpen = keepOrSetIsMenuOpen ;
}
else
{
rowDataToClear . IsMenuOpen = false ;
}
2020-09-27 01:54:24 +12:00
}
2021-05-14 01:20:40 +12:00
RefreshSelection ( dgvPrevious ) ;
2020-09-27 01:54:24 +12:00
2021-05-14 01:20:40 +12:00
// Hide old menu
foreach ( Menu menuToClose in menus . Where (
m = > m ! = null & & m . Level > menuPrevious . Level ) )
{
menuToClose . HideWithFade ( ) ;
menus [ menuToClose . Level ] = null ;
}
2020-09-27 01:54:24 +12:00
}
}
private void FadeHalfOrOutIfNeeded ( )
{
2021-11-13 20:18:33 +13:00
if ( menus [ 0 ] ! = null & & menus [ 0 ] . IsUsable )
2020-09-27 01:54:24 +12:00
{
if ( ! IsActive ( ) )
{
Point position = Control . MousePosition ;
2021-05-03 01:21:07 +12:00
if ( Properties . Settings . Default . StaysOpenWhenFocusLost & &
AsList . Any ( m = > m . IsMouseOn ( position ) ) )
2020-09-27 01:54:24 +12:00
{
if ( ! keyboardInput . InUse )
{
AsList . ForEach ( menu = > menu . ShowTransparent ( ) ) ;
}
}
2021-06-26 23:24:56 +12:00
else if ( Config . AlwaysOpenByPin )
{
AsList . ForEach ( menu = > menu . ShowTransparent ( ) ) ;
}
2020-09-27 01:54:24 +12:00
else
{
MenusFadeOut ( ) ;
}
}
}
}
private void MenusFadeOut ( )
{
openCloseState = OpenCloseState . Closing ;
AsList . ForEach ( menu = >
{
if ( menu . Level > 0 )
{
menus [ menu . Level ] = null ;
}
menu . HideWithFade ( ) ;
} ) ;
2021-06-26 23:24:56 +12:00
Config . AlwaysOpenByPin = false ;
2020-09-27 01:54:24 +12:00
}
private void AdjustMenusSizeAndLocation ( )
{
2021-11-10 06:48:30 +13:00
Rectangle screenBounds ;
2021-12-28 00:22:52 +13:00
bool isCustomLocationOutsideOfScreen = false ;
2021-11-10 06:48:30 +13:00
if ( Properties . Settings . Default . AppearAtMouseLocation )
{
screenBounds = Screen . FromPoint ( Cursor . Position ) . Bounds ;
}
2021-12-10 08:00:33 +13:00
else if ( Properties . Settings . Default . UseCustomLocation )
{
screenBounds = Screen . FromPoint ( new Point (
Properties . Settings . Default . CustomLocationX ,
Properties . Settings . Default . CustomLocationY ) ) . Bounds ;
2021-12-28 00:22:52 +13:00
isCustomLocationOutsideOfScreen = ! screenBounds . Contains (
new Point ( Properties . Settings . Default . CustomLocationX , Properties . Settings . Default . CustomLocationY ) ) ;
2021-12-10 08:00:33 +13:00
}
2021-11-10 06:48:30 +13:00
else
{
screenBounds = Screen . PrimaryScreen . Bounds ;
}
2020-09-27 01:54:24 +12:00
// Only apply taskbar position change when no menu is currently open
2021-04-29 08:58:33 +12:00
List < Menu > list = AsList ;
2021-11-17 12:13:46 +13:00
WindowsTaskbar taskbar = new ( ) ;
2020-09-21 03:26:45 +12:00
if ( list . Count = = 1 )
{
taskbarPosition = taskbar . Position ;
2020-09-27 01:54:24 +12:00
}
// Shrink the usable space depending on taskbar location
2021-04-29 08:58:33 +12:00
Menu . StartLocation startLocation ;
2020-09-21 03:26:45 +12:00
switch ( taskbarPosition )
{
case TaskbarPosition . Left :
screenBounds . X + = taskbar . Size . Width ;
screenBounds . Width - = taskbar . Size . Width ;
2020-09-21 06:45:12 +12:00
startLocation = Menu . StartLocation . BottomLeft ;
2020-09-21 03:26:45 +12:00
break ;
case TaskbarPosition . Right :
screenBounds . Width - = taskbar . Size . Width ;
2020-09-21 06:45:12 +12:00
startLocation = Menu . StartLocation . BottomRight ;
2020-09-21 03:26:45 +12:00
break ;
case TaskbarPosition . Top :
screenBounds . Y + = taskbar . Size . Height ;
screenBounds . Height - = taskbar . Size . Height ;
2020-09-21 06:45:12 +12:00
startLocation = Menu . StartLocation . TopRight ;
2020-09-21 03:26:45 +12:00
break ;
case TaskbarPosition . Bottom :
default :
screenBounds . Height - = taskbar . Size . Height ;
2020-09-21 06:45:12 +12:00
startLocation = Menu . StartLocation . BottomRight ;
2020-09-21 03:26:45 +12:00
break ;
2020-09-27 01:54:24 +12:00
}
2021-12-28 00:22:52 +13:00
if ( Properties . Settings . Default . AppearAtTheBottomLeft | |
isCustomLocationOutsideOfScreen )
2021-11-10 06:31:29 +13:00
{
startLocation = Menu . StartLocation . BottomLeft ;
}
2021-04-29 08:58:33 +12:00
Menu menu ;
Menu menuPredecessor = null ;
2020-09-27 01:54:24 +12:00
for ( int i = 0 ; i < list . Count ; i + + )
{
menu = list [ i ] ;
// Only last one has to be updated as all previous one were already updated in the past
2020-09-20 21:14:20 +12:00
if ( list . Count - 1 = = i )
2020-09-27 01:54:24 +12:00
{
2021-12-28 00:22:52 +13:00
menu . AdjustSizeAndLocation ( screenBounds , menuPredecessor , startLocation , isCustomLocationOutsideOfScreen ) ;
2021-06-03 22:52:47 +12:00
}
else
{
2021-06-27 21:35:07 +12:00
// workaround added also as else, because need adjust scrollbar after search
2021-12-28 00:22:52 +13:00
menu . AdjustSizeAndLocation ( screenBounds , menuPredecessor , startLocation , isCustomLocationOutsideOfScreen ) ;
2020-09-27 01:54:24 +12:00
}
2021-12-10 08:00:33 +13:00
if ( ! Properties . Settings . Default . AppearAtTheBottomLeft & &
! Properties . Settings . Default . AppearAtMouseLocation & &
! Properties . Settings . Default . UseCustomLocation & &
i = = 0 )
2020-09-21 03:26:45 +12:00
{
2020-09-21 06:45:12 +12:00
const int overlapTolerance = 4 ;
2020-09-21 03:26:45 +12:00
// Remember width of the initial menu as we don't want to overlap with it
2020-09-21 06:45:12 +12:00
if ( taskbarPosition = = TaskbarPosition . Left )
{
screenBounds . X + = menu . Width - overlapTolerance ;
}
screenBounds . Width - = menu . Width - overlapTolerance ;
2020-09-27 01:54:24 +12:00
}
menuPredecessor = menu ;
}
}
2022-02-21 09:35:38 +13:00
private void Menu_KeyPressCheck ( object sender , KeyPressEventArgs e )
{
if ( isDraggingSwipeScrolling )
{
e . Handled = true ;
}
}
2020-10-10 20:47:30 +13:00
private void Menu_SearchTextChanging ( )
{
searchTextChanging = true ;
keyboardInput . SearchTextChanging ( ) ;
2022-06-18 00:38:09 +12:00
waitToOpenMenu . MouseActive = false ;
2020-10-10 20:47:30 +13:00
}
2022-06-11 23:04:21 +12:00
private void Menu_SearchTextChanged ( object sender , bool isSearchStringEmpty )
2020-09-27 01:54:24 +12:00
{
2021-06-28 00:44:12 +12:00
Menu menu = ( Menu ) sender ;
2022-06-11 23:04:21 +12:00
keyboardInput . SearchTextChanged ( menu , isSearchStringEmpty ) ;
2020-09-27 01:54:24 +12:00
AdjustMenusSizeAndLocation ( ) ;
2020-10-10 20:47:30 +13:00
searchTextChanging = false ;
2021-06-28 00:44:12 +12:00
// if any open menu close
2021-10-11 04:33:54 +13:00
if ( menu . Level + 1 < menus . Length )
2021-06-28 00:44:12 +12:00
{
2021-10-05 08:36:52 +13:00
Menu menuToClose = menus [ menu . Level + 1 ] ;
2022-06-18 00:38:09 +12:00
if ( menuToClose ! = null & & hideSubmenuDuringRefreshSearch )
2021-10-05 08:36:52 +13:00
{
HideOldMenu ( menuToClose ) ;
}
2021-06-28 00:44:12 +12:00
}
2020-09-27 01:54:24 +12:00
}
2022-06-18 00:38:09 +12:00
private void ExecuteWatcherHistory ( )
{
foreach ( var fileSystemEventArgs in watcherHistory )
{
WatcherProcessItem ( watchers , fileSystemEventArgs ) ;
}
watcherHistory . Clear ( ) ;
}
2022-06-21 03:39:39 +12:00
private void WatcherProcessItem ( object sender , EventArgs e )
2022-06-18 00:38:09 +12:00
{
if ( menus [ 0 ] = = null | | ! menus [ 0 ] . IsHandleCreated )
{
watcherHistory . Add ( e ) ;
return ;
}
2022-06-21 03:39:39 +12:00
if ( e is RenamedEventArgs renamedEventArgs )
{
menus [ 0 ] . Invoke ( ( ) = > RenameItem ( renamedEventArgs ) ) ;
}
else
{
FileSystemEventArgs fileSystemEventArgs = ( FileSystemEventArgs ) e ;
if ( fileSystemEventArgs . ChangeType = = WatcherChangeTypes . Deleted )
{
menus [ 0 ] . Invoke ( ( ) = > DeleteItem ( e as FileSystemEventArgs ) ) ;
}
else if ( fileSystemEventArgs . ChangeType = = WatcherChangeTypes . Created )
{
menus [ 0 ] . Invoke ( ( ) = > CreateItem ( e as FileSystemEventArgs ) ) ;
}
}
}
private void RenameItem ( RenamedEventArgs e )
{
try
2022-06-18 00:38:09 +12:00
{
2022-06-21 03:39:39 +12:00
List < RowData > rowDatas = new ( ) ;
DataTable dataTable = ( DataTable ) menus [ 0 ] . GetDataGridView ( ) . DataSource ;
foreach ( DataRow row in dataTable . Rows )
{
RowData rowData = ( RowData ) row [ 2 ] ;
if ( rowData . Path . StartsWith ( $"{e.OldFullPath}" ) )
{
bool isAddionalPathRenamed = false ;
string oldPath = rowData . Path ;
string path = rowData . Path . Replace ( e . OldFullPath , e . FullPath ) ;
foreach ( var pathAndFlags in MenusHelpers . GetAddionalPathsForMainMenu ( ) )
{
if ( oldPath . StartsWith ( $"{pathAndFlags.Path}\\" ) & &
! path . StartsWith ( $"{pathAndFlags.Path}\\" ) )
{
isAddionalPathRenamed = true ;
break ;
}
}
if ( isAddionalPathRenamed )
{
continue ;
}
FileAttributes attr = File . GetAttributes ( path ) ;
bool isFolder = ( attr & FileAttributes . Directory ) = = FileAttributes . Directory ;
bool isAddionalItem = Path . GetDirectoryName ( path ) ! = Config . Path ;
RowData rowDataRenamed = new ( isFolder , isAddionalItem , false , 0 , path ) ;
if ( FolderOptions . IsHidden ( rowDataRenamed ) )
{
continue ;
}
IconReader . RemoveIconFromCache ( rowData . Path ) ;
rowDataRenamed . ReadIcon ( true ) ;
rowDatas . Add ( rowDataRenamed ) ;
}
else
{
rowDatas . Add ( rowData ) ;
}
}
rowDatas = MenusHelpers . SortItems ( rowDatas ) ;
keyboardInput . ClearIsSelectedByKey ( ) ;
AddItemsToMenu ( rowDatas , menus [ 0 ] , out _ , out _ ) ;
hideSubmenuDuringRefreshSearch = false ;
menus [ 0 ] . RefreshSearchText ( ) ;
hideSubmenuDuringRefreshSearch = true ;
menus [ 0 ] . TimerUpdateIconsStart ( ) ;
2022-06-18 00:38:09 +12:00
}
2022-06-21 03:39:39 +12:00
catch ( Exception ex )
2022-06-18 00:38:09 +12:00
{
2022-06-21 03:39:39 +12:00
Log . Warn ( $"Failed to {nameof(RenameItem)}: {e.OldFullPath} {e.FullPath}" , ex ) ;
2022-06-18 00:38:09 +12:00
}
}
private void DeleteItem ( FileSystemEventArgs e )
{
try
{
2022-06-21 03:39:39 +12:00
List < DataRow > rowsToRemove = new ( ) ;
2022-06-18 00:38:09 +12:00
DataGridView dgv = menus [ 0 ] . GetDataGridView ( ) ;
DataTable dataTable = ( DataTable ) dgv . DataSource ;
foreach ( DataRow row in dataTable . Rows )
{
RowData rowData = ( RowData ) row [ 2 ] ;
2022-06-19 09:43:30 +12:00
if ( rowData . Path = = e . FullPath | |
rowData . Path . StartsWith ( $"{e.FullPath}\\" ) )
2022-06-18 00:38:09 +12:00
{
IconReader . RemoveIconFromCache ( rowData . Path ) ;
2022-06-19 09:43:30 +12:00
rowsToRemove . Add ( row ) ;
2022-06-18 00:38:09 +12:00
}
}
2022-06-19 09:43:30 +12:00
foreach ( DataRow rowToRemove in rowsToRemove )
2022-06-18 00:38:09 +12:00
{
dataTable . Rows . Remove ( rowToRemove ) ;
}
keyboardInput . ClearIsSelectedByKey ( ) ;
dgv . DataSource = dataTable ;
hideSubmenuDuringRefreshSearch = false ;
menus [ 0 ] . RefreshSearchText ( ) ;
hideSubmenuDuringRefreshSearch = true ;
}
catch ( Exception ex )
{
Log . Warn ( $"Failed to {nameof(DeleteItem)}: {e.FullPath}" , ex ) ;
}
}
private void CreateItem ( FileSystemEventArgs e )
{
try
{
FileAttributes attr = File . GetAttributes ( e . FullPath ) ;
bool isFolder = ( attr & FileAttributes . Directory ) = = FileAttributes . Directory ;
bool isAddionalItem = Path . GetDirectoryName ( e . FullPath ) ! = Config . Path ;
RowData rowData = new ( isFolder , isAddionalItem , false , 0 , e . FullPath ) ;
if ( FolderOptions . IsHidden ( rowData ) )
{
return ;
}
rowData . ReadIcon ( true ) ;
List < RowData > rowDatas = new ( ) ;
rowDatas . Add ( rowData ) ;
DataTable dataTable = ( DataTable ) menus [ 0 ] . GetDataGridView ( ) . DataSource ;
foreach ( DataRow row in dataTable . Rows )
{
rowDatas . Add ( ( RowData ) row [ 2 ] ) ;
}
rowDatas = MenusHelpers . SortItems ( rowDatas ) ;
keyboardInput . ClearIsSelectedByKey ( ) ;
AddItemsToMenu ( rowDatas , menus [ 0 ] , out _ , out _ ) ;
hideSubmenuDuringRefreshSearch = false ;
menus [ 0 ] . RefreshSearchText ( ) ;
hideSubmenuDuringRefreshSearch = true ;
menus [ 0 ] . TimerUpdateIconsStart ( ) ;
}
catch ( Exception ex )
{
Log . Warn ( $"Failed to {nameof(CreateItem)}: {e.FullPath}" , ex ) ;
}
}
2020-09-27 01:54:24 +12:00
}
}