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 ;
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 ;
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
2021-11-29 01:40:42 +13:00
if ( Properties . Settings . Default . CacheMainMenu & &
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 ( ) ; } ) ;
menus [ 0 ] . ResetSearchText ( ) ;
}
else
{
MenuData menuData = ( MenuData ) e . Result ;
switch ( menuData . Validity )
{
case MenuDataValidity . Valid :
2021-11-13 20:18:33 +13:00
if ( Properties . Settings . Default . CacheMainMenu )
{
if ( IconReader . MainPreload )
{
workerMainMenu . DoWork - = LoadMenu ;
menus [ 0 ] = Create ( menuData , Path . GetFileName ( Config . Path ) ) ;
IconReader . MainPreload = false ;
2021-11-26 11:49:50 +13:00
if ( showMenuAfterMainPreload )
{
AsEnumerable . ToList ( ) . ForEach ( m = > { m . ShowWithFade ( ) ; } ) ;
}
2021-11-13 20:18:33 +13:00
}
else
{
AsEnumerable . ToList ( ) . ForEach ( m = > { m . ShowWithFade ( ) ; } ) ;
}
}
else
2021-11-11 11:39:52 +13:00
{
DisposeMenu ( menus [ menuData . Level ] ) ;
menus [ 0 ] = Create ( menuData , Path . GetFileName ( Config . Path ) ) ;
2021-11-14 01:55:01 +13:00
if ( IconReader . MainPreload )
{
IconReader . MainPreload = false ;
2021-11-26 11:49:50 +13:00
if ( showMenuAfterMainPreload )
{
AsEnumerable . ToList ( ) . ForEach ( m = > { m . ShowWithFade ( ) ; } ) ;
}
2021-11-14 01:55:01 +13:00
}
else
{
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 ;
case MenuDataValidity . AbortedOrUnknown :
Log . Info ( "MenuDataValidity.AbortedOrUnknown" ) ;
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 & &
( menus [ rowData . MenuLevel + 1 ] = = null | |
menus [ rowData . MenuLevel + 1 ] . Tag as RowData ! = rowData ) )
{
2021-10-04 07:24:22 +13:00
CreateAndShowLoadingMenu ( rowData ) ;
void CreateAndShowLoadingMenu ( RowData rowData )
{
2021-11-17 12:13:46 +13:00
MenuData menuDataLoading = new ( )
2021-10-04 07:24:22 +13:00
{
RowDatas = new List < RowData > ( ) ,
Validity = MenuDataValidity . Valid ,
Level = rowData . MenuLevel + 1 ,
} ;
2021-12-10 08:00:33 +13:00
Menu menuLoading = Create ( menuDataLoading , Path . GetFileName ( rowData . TargetFilePathOrig ) ) ;
2021-10-04 07:24:22 +13:00
menuLoading . IsLoadingMenu = true ;
AdjustMenusSizeAndLocation ( ) ;
menus [ rowData . MenuLevel + 1 ] = menuLoading ;
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
if ( menuData . Validity ! = MenuDataValidity . AbortedOrUnknown & &
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 ;
void MouseEnterOk ( DataGridView dgv , int rowIndex )
{
if ( menus [ 0 ] . IsUsable )
{
if ( keyboardInput . InUse )
{
keyboardInput . ClearIsSelectedByKey ( ) ;
keyboardInput . InUse = false ;
}
keyboardInput . Select ( dgv , rowIndex , false ) ;
}
}
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 ;
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 ( ) ;
}
internal static MenuData GetData ( BackgroundWorker worker , string path , int level )
{
2021-11-17 12:13:46 +13:00
MenuData menuData = new ( )
2020-09-27 01:54:24 +12:00
{
RowDatas = new List < RowData > ( ) ,
Validity = MenuDataValidity . AbortedOrUnknown ,
Level = level ,
} ;
2021-11-11 11:39:52 +13:00
string [ ] directoriesToAddToMainMenu = Array . Empty < string > ( ) ;
string [ ] filesToAddToMainMenu = Array . Empty < string > ( ) ;
if ( level = = 0 )
{
AddFoldersToMainMenu ( ref directoriesToAddToMainMenu , ref filesToAddToMainMenu ) ;
}
2020-09-27 01:54:24 +12:00
if ( ! worker . CancellationPending )
{
string [ ] directories = Array . Empty < string > ( ) ;
2021-12-13 04:52:40 +13:00
bool isSharedDirectory = false ;
2020-09-27 01:54:24 +12:00
try
{
2021-05-21 06:36:59 +12:00
if ( string . IsNullOrEmpty ( path ) )
{
Log . Info ( $"path is null or empty" ) ;
}
else if ( FileLnk . IsNetworkRoot ( path ) )
2020-09-27 01:54:24 +12:00
{
2021-12-13 04:52:40 +13:00
isSharedDirectory = true ;
2020-09-27 01:54:24 +12:00
directories = GetDirectoriesInNetworkLocation ( path ) ;
static string [ ] GetDirectoriesInNetworkLocation ( string networkLocationRootPath )
{
2021-11-17 12:13:46 +13:00
List < string > directories = new ( ) ;
Process cmd = new ( ) ;
2020-09-27 01:54:24 +12:00
cmd . StartInfo . FileName = "cmd.exe" ;
cmd . StartInfo . RedirectStandardInput = true ;
cmd . StartInfo . RedirectStandardOutput = true ;
cmd . StartInfo . CreateNoWindow = true ;
cmd . StartInfo . UseShellExecute = false ;
cmd . StartInfo . WindowStyle = ProcessWindowStyle . Hidden ;
cmd . Start ( ) ;
cmd . StandardInput . WriteLine ( $"net view {networkLocationRootPath}" ) ;
cmd . StandardInput . Flush ( ) ;
cmd . StandardInput . Close ( ) ;
string output = cmd . StandardOutput . ReadToEnd ( ) ;
cmd . WaitForExit ( ) ;
cmd . Close ( ) ;
bool resolvedSomething = false ;
List < string > lines = output
. Split ( new [ ] { '\r' , '\n' } , StringSplitOptions . RemoveEmptyEntries ) . ToList ( ) ;
if ( lines . Count > 8 )
{
foreach ( string line in lines . Skip ( 6 ) . SkipLast ( 2 ) )
{
2020-10-25 09:31:56 +13:00
int indexOfFirstSpace = line . IndexOf ( " " , StringComparison . InvariantCulture ) ;
2020-09-27 01:54:24 +12:00
if ( indexOfFirstSpace > 0 )
{
string directory = Path . Combine (
2020-10-25 00:12:59 +13:00
networkLocationRootPath ,
2021-12-12 02:34:28 +13:00
line [ . . indexOfFirstSpace ] ) ;
2020-10-25 00:12:59 +13:00
2020-09-27 03:00:41 +13:00
directories . Add ( directory ) ;
resolvedSomething = true ;
2020-09-27 01:54:24 +12:00
}
}
}
if ( ! resolvedSomething )
{
Log . Info ( $"Could not resolve network root folder: {networkLocationRootPath} , output:{output}" ) ;
}
return directories . ToArray ( ) ;
}
}
else
{
directories = Directory . GetDirectories ( path ) ;
2021-11-11 11:39:52 +13:00
directories = directories . Concat ( directoriesToAddToMainMenu ) . ToArray ( ) ;
2020-09-27 01:54:24 +12:00
}
Array . Sort ( directories , new WindowsExplorerSort ( ) ) ;
}
catch ( UnauthorizedAccessException ex )
{
Log . Warn ( $"path:'{path}'" , ex ) ;
menuData . Validity = MenuDataValidity . NoAccess ;
}
2021-09-30 09:21:02 +13:00
catch ( Exception ex )
2020-09-27 01:54:24 +12:00
{
Log . Warn ( $"path:'{path}'" , ex ) ;
}
2021-05-21 06:36:59 +12:00
foreach ( string directoryWithIllegalCharacters in directories )
2020-09-27 01:54:24 +12:00
{
if ( worker ! = null & & worker . CancellationPending )
{
break ;
}
2021-05-21 06:36:59 +12:00
// https://github.com/Hofknecht/SystemTrayMenu/issues/171
string directory = directoryWithIllegalCharacters . Replace ( "\x00" , string . Empty ) ;
2020-09-27 01:54:24 +12:00
bool hiddenEntry = false ;
2021-12-13 04:52:40 +13:00
if ( ! isSharedDirectory & &
FolderOptions . IsHidden ( directory , ref hiddenEntry ) )
2020-09-27 01:54:24 +12:00
{
continue ;
}
2022-01-02 00:37:33 +13:00
RowData rowData = ReadRowData ( directory , false , true ) ;
2020-09-27 01:54:24 +12:00
rowData . HiddenEntry = hiddenEntry ;
string resolvedLnkPath = string . Empty ;
2021-11-19 09:12:03 +13:00
rowData . ReadIcon ( true , ref resolvedLnkPath , level ) ;
2020-09-27 01:54:24 +12:00
rowData . MenuLevel = level ;
menuData . RowDatas . Add ( rowData ) ;
}
}
if ( ! worker . CancellationPending )
{
string [ ] files = Array . Empty < string > ( ) ;
try
{
2021-05-21 06:36:59 +12:00
if ( string . IsNullOrEmpty ( path ) )
{
Log . Info ( $"path is null or empty" ) ;
}
else if ( ! FileLnk . IsNetworkRoot ( path ) )
2020-09-27 01:54:24 +12:00
{
2022-05-26 03:08:11 +12:00
files = DirectoryBySearchPattern . GetFiles ( path , Config . SearchPattern ) ;
2021-11-11 11:39:52 +13:00
files = files . Concat ( filesToAddToMainMenu ) . ToArray ( ) ;
2020-09-27 01:54:24 +12:00
}
Array . Sort ( files , new WindowsExplorerSort ( ) ) ;
}
catch ( UnauthorizedAccessException ex )
{
Log . Warn ( $"path:'{path}'" , ex ) ;
menuData . Validity = MenuDataValidity . NoAccess ;
}
2021-09-30 09:21:02 +13:00
catch ( Exception ex )
2020-09-27 01:54:24 +12:00
{
Log . Warn ( $"path:'{path}'" , ex ) ;
}
2021-05-22 01:40:43 +12:00
foreach ( string fileWithIllegalCharacters in files )
2020-09-27 01:54:24 +12:00
{
if ( worker ! = null & & worker . CancellationPending )
{
break ;
}
2021-05-22 01:40:43 +12:00
// https://github.com/Hofknecht/SystemTrayMenu/issues/171
string file = fileWithIllegalCharacters . Replace ( "\x00" , string . Empty ) ;
2020-09-27 01:54:24 +12:00
bool hiddenEntry = false ;
if ( FolderOptions . IsHidden ( file , ref hiddenEntry ) )
{
continue ;
}
2022-01-02 00:37:33 +13:00
RowData rowData = ReadRowData ( file , false , false ) ;
2022-01-08 00:40:55 +13:00
rowData . HiddenEntry = hiddenEntry ;
2022-01-08 23:39:23 +13:00
if ( Properties . Settings . Default . ShowOnlyAsSearchResult )
{
rowData . ShowOnlyWhenSearch = filesToAddToMainMenu . Contains ( fileWithIllegalCharacters ) ;
}
2020-09-27 01:54:24 +12:00
string resolvedLnkPath = string . Empty ;
2021-11-19 09:12:03 +13:00
if ( rowData . ReadIcon ( false , ref resolvedLnkPath , level ) )
2020-09-27 01:54:24 +12:00
{
2022-01-02 00:37:33 +13:00
rowData = ReadRowData ( resolvedLnkPath , true , true , rowData ) ;
2020-09-27 01:54:24 +12:00
rowData . HiddenEntry = hiddenEntry ;
}
menuData . RowDatas . Add ( rowData ) ;
}
}
if ( ! worker . CancellationPending )
{
if ( menuData . Validity = = MenuDataValidity . AbortedOrUnknown )
{
if ( menuData . RowDatas . Count = = 0 )
{
menuData . Validity = MenuDataValidity . Empty ;
}
else
{
menuData . Validity = MenuDataValidity . Valid ;
}
}
}
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
}
2021-11-13 20:18:33 +13:00
if ( ! Properties . Settings . Default . CacheMainMenu )
2021-09-24 08:53:46 +12:00
{
2021-11-13 20:18:33 +13:00
DisposeMenu ( menus [ 0 ] ) ;
2021-09-24 08:53:46 +12:00
}
2020-09-27 01:54:24 +12:00
}
internal void StartWorker ( )
{
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 )
{
string path = Config . Path ;
int level = 0 ;
RowData rowData = eDoWork . Argument as RowData ;
if ( rowData ! = null )
{
path = rowData . TargetFilePath ;
level = rowData . MenuLevel + 1 ;
}
2021-12-11 03:20:24 +13:00
if ( Properties . Settings . Default . GenerateShortcutsToDrives )
{
GenerateDriveShortcuts . Start ( ) ;
}
2021-11-11 11:39:52 +13:00
MenuData menuData = GetData ( ( BackgroundWorker ) senderDoWork , path , level ) ;
menuData . RowDataParent = rowData ;
eDoWork . Result = menuData ;
}
private static void AddFoldersToMainMenu ( ref string [ ] directoriesToAddToMainMenu , ref string [ ] filesToAddToMainMenu )
{
string pathAddToMainMenu = string . Empty ;
bool recursive = false ;
try
{
foreach ( string pathAndRecursivString in Properties . Settings . Default . PathsAddToMainMenu . Split ( @"|" ) )
{
if ( string . IsNullOrEmpty ( pathAndRecursivString ) )
{
continue ;
}
pathAddToMainMenu = pathAndRecursivString . Split ( "recursiv:" ) [ 0 ] . Trim ( ) ;
recursive = pathAndRecursivString . Split ( "recursiv:" ) [ 1 ] . StartsWith ( "True" ) ;
bool onlyFiles = pathAndRecursivString . Split ( "onlyFiles:" ) [ 1 ] . StartsWith ( "True" ) ;
string [ ] directoriesToConcat = Array . Empty < string > ( ) ;
string [ ] filesToAddToConcat = Array . Empty < string > ( ) ;
if ( recursive )
{
GetDirectoriesAndFilesRecursive ( ref directoriesToConcat , ref filesToAddToConcat , pathAddToMainMenu ) ;
}
else
{
directoriesToConcat = Directory . GetDirectories ( pathAddToMainMenu ) ;
2022-05-26 03:08:11 +12:00
filesToAddToConcat = DirectoryBySearchPattern . GetFiles ( pathAddToMainMenu , Config . SearchPattern ) ;
2021-11-11 11:39:52 +13:00
}
if ( ! onlyFiles )
{
directoriesToAddToMainMenu = directoriesToAddToMainMenu . Concat ( directoriesToConcat ) . ToArray ( ) ;
}
filesToAddToMainMenu = filesToAddToMainMenu . Concat ( filesToAddToConcat ) . ToArray ( ) ;
}
}
catch ( Exception ex )
{
Log . Warn ( $"path:'{pathAddToMainMenu}' recursiv:{recursive}" , ex ) ;
}
}
private static void GetDirectoriesAndFilesRecursive ( ref string [ ] directoriesToConcat , ref string [ ] filesToAddToConcat , string pathAddToMainMenu )
{
try
{
string [ ] directories = Directory . GetDirectories ( pathAddToMainMenu ) ;
try
{
2022-05-26 03:08:11 +12:00
filesToAddToConcat = filesToAddToConcat . Concat ( DirectoryBySearchPattern . GetFiles ( pathAddToMainMenu , Config . SearchPattern ) ) . ToArray ( ) ;
2021-11-11 11:39:52 +13:00
}
catch ( Exception ex )
{
Log . Warn ( $"GetDirectoriesAndFilesRecursive path:'{pathAddToMainMenu}'" , ex ) ;
}
foreach ( string directory in directories )
{
GetDirectoriesAndFilesRecursive ( ref directoriesToConcat , ref filesToAddToConcat , directory ) ;
}
directoriesToConcat = directoriesToConcat . Concat ( directories ) . ToArray ( ) ;
}
catch ( Exception ex )
{
Log . Warn ( $"GetDirectoriesAndFilesRecursive path:'{pathAddToMainMenu}'" , ex ) ;
}
}
2022-01-02 00:37:33 +13:00
private static RowData ReadRowData ( string fileName , bool isResolvedLnk , bool containsMenu , RowData rowData = null )
2020-09-27 01:54:24 +12:00
{
if ( rowData = = null )
{
rowData = new RowData ( ) ;
}
2022-01-02 00:37:33 +13:00
rowData . ContainsMenu = containsMenu ;
2020-09-27 01:54:24 +12:00
rowData . IsResolvedLnk = isResolvedLnk ;
try
{
rowData . FileInfo = new FileInfo ( fileName ) ;
rowData . TargetFilePath = rowData . FileInfo . FullName ;
if ( ! isResolvedLnk )
{
if ( string . IsNullOrEmpty ( rowData . FileInfo . Name ) )
{
string path = rowData . FileInfo . FullName ;
int directoryNameBegin = path . LastIndexOf ( @"\" , StringComparison . InvariantCulture ) + 1 ;
2021-10-11 04:33:54 +13:00
rowData . SetText ( path [ directoryNameBegin . . ] ) ;
2020-09-27 01:54:24 +12:00
}
2022-01-02 00:37:33 +13:00
else if ( ! rowData . ContainsMenu & & Config . IsHideFileExtension ( ) )
{
rowData . SetText ( Path . GetFileNameWithoutExtension ( rowData . FileInfo . Name ) ) ;
}
2020-09-27 01:54:24 +12:00
else
{
rowData . SetText ( rowData . FileInfo . Name ) ;
}
rowData . TargetFilePathOrig = rowData . FileInfo . FullName ;
}
}
catch ( Exception ex )
{
2021-09-30 09:21:02 +13:00
Log . Warn ( $"fileName:'{fileName}'" , ex ) ;
2020-09-27 01:54:24 +12:00
}
return rowData ;
}
2021-06-26 23:24:56 +12:00
private static void OpenFolder ( string pathToFolder = "" )
2020-09-27 01:54:24 +12:00
{
2021-06-26 23:24:56 +12:00
string path = pathToFolder ;
if ( string . IsNullOrEmpty ( path ) )
{
path = Config . Path ;
}
Log . ProcessStart ( path ) ;
2020-09-27 01:54:24 +12:00
}
2022-02-06 00:47:27 +13:00
private static int GetRowUnderCursor ( DataGridView dgv , Point location )
{
DataGridView . HitTestInfo myHitTest = dgv . HitTest ( location . X , location . Y ) ;
return myHitTest . RowIndex ;
}
2022-02-08 10:45:10 +13:00
private static void InvalidateRowIfIndexInRange ( DataGridView dgv , int rowIndex )
{
if ( rowIndex > - 1 & & rowIndex < dgv . Rows . Count )
{
dgv . InvalidateRow ( rowIndex ) ;
}
}
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
{
2021-06-28 00:44:12 +12:00
title = Path . GetFileName ( menuData . RowDataParent . TargetFilePath ) ;
path = menuData . RowDataParent . TargetFilePath ;
}
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 ) ;
static void AddItemsToMenu ( List < RowData > data , Menu menu , out int foldersCount , out int filesCount )
2020-09-27 01:54:24 +12:00
{
2021-06-28 00:44:12 +12:00
foldersCount = 0 ;
filesCount = 0 ;
2020-09-27 01:54:24 +12:00
DataGridView dgv = menu . GetDataGridView ( ) ;
2021-11-17 12:13:46 +13:00
DataTable dataTable = new ( ) ;
2020-09-27 01:54:24 +12:00
dataTable . Columns . Add ( dgv . Columns [ 0 ] . Name , typeof ( Icon ) ) ;
dataTable . Columns . Add ( dgv . Columns [ 1 ] . Name , typeof ( string ) ) ;
dataTable . Columns . Add ( "data" , typeof ( RowData ) ) ;
2021-04-15 07:06:54 +12:00
dataTable . Columns . Add ( "SortIndex" ) ;
2020-09-27 01:54:24 +12:00
foreach ( RowData rowData in data )
{
2022-01-08 23:39:23 +13:00
if ( ! rowData . ShowOnlyWhenSearch )
2021-06-28 00:44:12 +12:00
{
2022-01-08 23:39:23 +13:00
if ( rowData . ContainsMenu )
{
foldersCount + + ;
}
else
{
filesCount + + ;
}
2021-06-28 00:44:12 +12:00
}
2020-09-27 01:54:24 +12:00
rowData . SetData ( rowData , dataTable ) ;
}
dgv . DataSource = dataTable ;
2021-05-29 19:13:14 +12:00
dgv . Columns [ "data" ] . Visible = false ;
2021-06-03 22:52:47 +12:00
dgv . Columns [ "SortIndex" ] . Visible = false ;
2022-01-08 23:39:23 +13:00
string columnSortIndex = "SortIndex" ;
foreach ( DataRow row in dataTable . Rows )
{
RowData rowData = ( RowData ) row [ 2 ] ;
if ( rowData . ShowOnlyWhenSearch )
{
row [ columnSortIndex ] = 99 ;
}
else
{
row [ columnSortIndex ] = 0 ;
}
}
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 )
{
DataGridView dgv = menu . GetDataGridView ( ) ;
( ( DataTable ) dgv . DataSource ) . DefaultView . RowFilter = "[SortIndex] LIKE '%0%'" ;
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-11-19 09:12:03 +13:00
if ( ! Properties . Settings . Default . CacheMainMenu )
{
MainPreload ( ) ;
}
2021-10-07 10:22:58 +13:00
}
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 )
{
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
}
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 ;
string [ ] files = new string [ ] { rowData . TargetFilePathOrig } ;
// 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
{
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 )
{
RowData trigger = ( RowData ) dgv . Rows [ hitTestInfo . RowIndex ] . Cells [ 2 ] . Value ;
2021-05-02 23:12:18 +12:00
trigger . 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 ( ) ;
2021-10-29 03:29:04 +13:00
row . Cells [ 0 ] . Value = rowData . ReadLoadedIcon ( ) ;
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 ( ) ;
}
2020-09-27 01:54:24 +12:00
private void Menu_SearchTextChanged ( object sender , EventArgs e )
{
2021-06-28 00:44:12 +12:00
Menu menu = ( Menu ) sender ;
keyboardInput . SearchTextChanged ( menu ) ;
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 ] ;
if ( menuToClose ! = null )
{
HideOldMenu ( menuToClose ) ;
}
2021-06-28 00:44:12 +12:00
}
2020-09-27 01:54:24 +12:00
}
}
}