From 9e7f1e514c44b74f2b4dabac1608ca265f2347a8 Mon Sep 17 00:00:00 2001
From: Don Ho <don.h@free.fr>
Date: Thu, 1 Feb 2024 13:25:09 +0100
Subject: [PATCH] Make copy/cut line while no selection optional

With this commit, there's the same issue in both JsonTools & NavigateTo (in C#), described here:
https://community.notepad-plus-plus.org/topic/25315/notepad-v8-6-1-release/2?_=1706795482129 

To reproduce:
1. Uncheck "Enable Copy/Cut Line without selection" checkbox in Editing section of Preferences.
2. Make sure there's no selection in the current editor
3. Launch "Open JSON tree viewer", and try to Cut "@" symbol on the top left text field with Ctrl-X

To remedy such problem, the plugin should call NPPM_MODELESSDIALOG with MODELESSDIALOGADD on the handle (HWND) of modeless dialog, just after the dialog creation.
(https://github.com/notepad-plus-plus/notepad-plus-plus/blob/master/PowerEditor/src/MISC/PluginsManager/Notepad_plus_msgs.h#L93)
With the registration of dialog handle, Notepad++ will pass all events which belongs to dialog's controls to the registered dialog.

Both source code of JsonTools & NavigateTo have been examinated, NPPM_MODELESSDIALOG which should be used has not been called.
Not sure it's the case of all the plugin in C#, it's not the issue for the C/C++ plugins anyway, because the dockable modeless dialog (inherited from StaticDialog) is registered after its creation:
https://github.com/notepad-plus-plus/notepad-plus-plus/blob/master/PowerEditor/src/WinControls/StaticDialog/StaticDialog.cpp#L257

Fix #14638, close #14660
---
 PowerEditor/src/Notepad_plus.cpp              |  7 +++++--
 PowerEditor/src/Notepad_plus_Window.cpp       |  1 +
 PowerEditor/src/NppBigSwitch.cpp              | 19 +++++++++++++++++++
 PowerEditor/src/NppCommands.cpp               | 13 ++++++++++---
 PowerEditor/src/Parameters.cpp                | 10 ++++++++++
 PowerEditor/src/Parameters.h                  |  4 ++--
 .../src/WinControls/Preference/preference.rc  |  1 +
 .../WinControls/Preference/preferenceDlg.cpp  |  9 +++++++++
 .../WinControls/Preference/preference_rc.h    |  2 +-
 PowerEditor/src/resource.h                    |  1 +
 10 files changed, 59 insertions(+), 8 deletions(-)

diff --git a/PowerEditor/src/Notepad_plus.cpp b/PowerEditor/src/Notepad_plus.cpp
index 5cbb50315..d9d313ce8 100644
--- a/PowerEditor/src/Notepad_plus.cpp
+++ b/PowerEditor/src/Notepad_plus.cpp
@@ -2567,9 +2567,12 @@ void Notepad_plus::checkClipboard()
 {
 	bool hasSelection = _pEditView->hasSelection();
 	bool canPaste = (_pEditView->execute(SCI_CANPASTE) != 0);
-	//enableCommand(IDM_EDIT_CUT, hasSelection, MENU | TOOLBAR);
-	//enableCommand(IDM_EDIT_COPY, hasSelection, MENU | TOOLBAR);
 
+	if (!NppParameters::getInstance().getSVP()._lineCopyCutWithoutSelection)
+	{
+		enableCommand(IDM_EDIT_CUT, hasSelection, MENU | TOOLBAR);
+		enableCommand(IDM_EDIT_COPY, hasSelection, MENU | TOOLBAR);
+	}
 	enableCommand(IDM_EDIT_PASTE, canPaste, MENU | TOOLBAR);
 	enableCommand(IDM_EDIT_DELETE, hasSelection, MENU | TOOLBAR);
 	enableCommand(IDM_EDIT_UPPERCASE, hasSelection, MENU);
diff --git a/PowerEditor/src/Notepad_plus_Window.cpp b/PowerEditor/src/Notepad_plus_Window.cpp
index b05a9c99a..e64be906c 100644
--- a/PowerEditor/src/Notepad_plus_Window.cpp
+++ b/PowerEditor/src/Notepad_plus_Window.cpp
@@ -320,6 +320,7 @@ void Notepad_plus_Window::init(HINSTANCE hInst, HWND parent, const TCHAR *cmdLin
 
 	::SendMessage(_hSelf, NPPM_INTERNAL_ENABLECHANGEHISTORY, 0, 0);
 
+	::SendMessage(_hSelf, NPPM_INTERNAL_LINECUTCOPYWITHOUTSELECTION, 0, 0);
 
 	if (nppGUI._newDocDefaultSettings._addNewDocumentOnStartup && nppGUI._rememberLastSession)
 	{
diff --git a/PowerEditor/src/NppBigSwitch.cpp b/PowerEditor/src/NppBigSwitch.cpp
index 57d23c6a9..38196464f 100644
--- a/PowerEditor/src/NppBigSwitch.cpp
+++ b/PowerEditor/src/NppBigSwitch.cpp
@@ -2286,6 +2286,25 @@ LRESULT Notepad_plus::process(HWND hwnd, UINT message, WPARAM wParam, LPARAM lPa
 			return TRUE;
 		}
 
+		case NPPM_INTERNAL_LINECUTCOPYWITHOUTSELECTION:
+		{
+			if (nppParam.getSVP()._lineCopyCutWithoutSelection) // "Disable Copy Cut Line Without Selection" is just unchecked: From NOT using it to using this feature
+			{
+				// Enable Copy & Cut unconditionally
+				enableCommand(IDM_EDIT_CUT, true, MENU | TOOLBAR);
+				enableCommand(IDM_EDIT_COPY, true, MENU | TOOLBAR);
+
+			}
+			else // "Disable Copy Cut Line Without Selection" is just checked: From using this feature to NOT using it
+			{
+				// Check the current selection to disable/enable Copy & Cut
+				bool hasSelection = _pEditView->hasSelection();
+				enableCommand(IDM_EDIT_CUT, hasSelection, MENU | TOOLBAR);
+				enableCommand(IDM_EDIT_COPY, hasSelection, MENU | TOOLBAR);
+			}
+			return TRUE;
+		}
+
 		case WM_QUERYENDSESSION:
 		{
 			// app should return TRUE or FALSE immediately upon receiving this message,
diff --git a/PowerEditor/src/NppCommands.cpp b/PowerEditor/src/NppCommands.cpp
index 41bda4bee..e18c92826 100644
--- a/PowerEditor/src/NppCommands.cpp
+++ b/PowerEditor/src/NppCommands.cpp
@@ -364,7 +364,7 @@ void Notepad_plus::command(int id)
 				{
 					_pEditView->execute(WM_CUT);
 				}
-				else // Cut the entire line
+				else if (NppParameters::getInstance().getSVP()._lineCopyCutWithoutSelection) // Cut the entire line with EOL
 				{
 					_pEditView->execute(SCI_COPYALLOWLINE);
 					_pEditView->execute(SCI_LINEDELETE);
@@ -382,8 +382,15 @@ void Notepad_plus::command(int id)
 			HWND focusedHwnd = ::GetFocus();
 			if (focusedHwnd == _pEditView->getHSelf())
 			{
-				_pEditView->execute(SCI_COPYALLOWLINE); // Copy selected text if any.
-														// Otherwise copy the entire line with EOL, for pasting before any line where the caret is.
+				if (_pEditView->hasSelection())
+				{
+					_pEditView->execute(WM_COPY);
+				}
+				else if (NppParameters::getInstance().getSVP()._lineCopyCutWithoutSelection)
+				{
+					_pEditView->execute(SCI_COPYALLOWLINE); // Copy without selected text, it will copy the whole line with EOL, for pasting before any line where the caret is.
+				}
+
 			}
 			else
 			{
diff --git a/PowerEditor/src/Parameters.cpp b/PowerEditor/src/Parameters.cpp
index 420226f20..e599698a1 100644
--- a/PowerEditor/src/Parameters.cpp
+++ b/PowerEditor/src/Parameters.cpp
@@ -6625,6 +6625,15 @@ void NppParameters::feedScintillaParam(TiXmlNode *node)
 		if (val >= 3 && val <= 9)
 			_svp._distractionFreeDivPart = static_cast<unsigned char>(val);
 	}
+
+	nm = element->Attribute(TEXT("lineCopyCutWithoutSelection"));
+	if (nm)
+	{
+		if (!lstrcmp(nm, TEXT("yes")))
+			_svp._lineCopyCutWithoutSelection = true;
+		else if (!lstrcmp(nm, TEXT("no")))
+			_svp._lineCopyCutWithoutSelection = false;
+	}
 }
 
 
@@ -6899,6 +6908,7 @@ bool NppParameters::writeScintillaParams()
 	(scintNode->ToElement())->SetAttribute(TEXT("paddingLeft"), _svp._paddingLeft);
 	(scintNode->ToElement())->SetAttribute(TEXT("paddingRight"), _svp._paddingRight);
 	(scintNode->ToElement())->SetAttribute(TEXT("distractionFreeDivPart"), _svp._distractionFreeDivPart);
+	(scintNode->ToElement())->SetAttribute(TEXT("lineCopyCutWithoutSelection"), _svp._lineCopyCutWithoutSelection ? TEXT("yes") : TEXT("no"));
 	return true;
 }
 
diff --git a/PowerEditor/src/Parameters.h b/PowerEditor/src/Parameters.h
index 1a6ea3d78..f2b4bd2a6 100644
--- a/PowerEditor/src/Parameters.h
+++ b/PowerEditor/src/Parameters.h
@@ -991,6 +991,8 @@ struct ScintillaViewParams
 			paddingLen = editViewWidth / defaultDiviser;
 		return paddingLen;
 	};
+
+	bool _lineCopyCutWithoutSelection = true;
 };
 
 const int NB_LIST = 20;
@@ -1887,7 +1889,6 @@ public:
 	bool isSelectFgColorEnabled() const { return _isSelectFgColorEnabled; };
 	bool isRegForOSAppRestartDisabled() const { return _isRegForOSAppRestartDisabled; };
 	bool doColumn2MultiSelect() const { return _column2MultiSelect; };
-	bool useLineCopyCutDelete() const { return _useLineCopyCutDelete; };
 
 private:
 	bool _isAnyShortcutModified = false;
@@ -1956,7 +1957,6 @@ private:
 	bool _isSelectFgColorEnabled = false;
 	bool _isRegForOSAppRestartDisabled = false;
 	bool _column2MultiSelect = true;
-	bool _useLineCopyCutDelete = true;
 
 	bool _doNppLogNetworkDriveIssue = false;
 	bool _doNppLogNulContentCorruptionIssue = false;
diff --git a/PowerEditor/src/WinControls/Preference/preference.rc b/PowerEditor/src/WinControls/Preference/preference.rc
index 996711780..a0dd61dfe 100644
--- a/PowerEditor/src/WinControls/Preference/preference.rc
+++ b/PowerEditor/src/WinControls/Preference/preference.rc
@@ -116,6 +116,7 @@ BEGIN
     CONTROL         "Keep selection when right-click outside of selection", IDC_CHECK_RIGHTCLICKKEEPSSELECTION, "Button", BS_AUTOCHECKBOX | WS_TABSTOP, 173, 133, 270, 10
     CONTROL         "Enable scrolling beyond last line",IDC_CHECK_SCROLLBEYONDLASTLINE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,173,148,270,10
     CONTROL         "Disable advanced scrolling feature due to touchpad issue",IDC_CHECK_DISABLEADVANCEDSCROLL,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,173,163,270,10
+    CONTROL         "Enable Copy/Cut Line without selection",IDC_CHECK_LINECUTCOPYWITHOUTSELECTION,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,173,178,270,10
 END
 
 
diff --git a/PowerEditor/src/WinControls/Preference/preferenceDlg.cpp b/PowerEditor/src/WinControls/Preference/preferenceDlg.cpp
index 2f6e0bc5a..de67148f2 100644
--- a/PowerEditor/src/WinControls/Preference/preferenceDlg.cpp
+++ b/PowerEditor/src/WinControls/Preference/preferenceDlg.cpp
@@ -826,6 +826,7 @@ void EditingSubDlg::initScintParam()
 	::SendDlgItemMessage(_hSelf, IDC_CHECK_SCROLLBEYONDLASTLINE, BM_SETCHECK, svp._scrollBeyondLastLine, 0);
 	::SendDlgItemMessage(_hSelf, IDC_CHECK_RIGHTCLICKKEEPSSELECTION, BM_SETCHECK, svp._rightClickKeepsSelection, 0);
 	::SendDlgItemMessage(_hSelf, IDC_CHECK_DISABLEADVANCEDSCROLL, BM_SETCHECK, svp._disableAdvancedScrolling, 0);
+	::SendDlgItemMessage(_hSelf, IDC_CHECK_LINECUTCOPYWITHOUTSELECTION, BM_SETCHECK, svp._lineCopyCutWithoutSelection, 0);
 }
 
 void EditingSubDlg::changeLineHiliteMode(bool enableSlider)
@@ -1248,6 +1249,14 @@ intptr_t CALLBACK EditingSubDlg::run_dlgProc(UINT message, WPARAM wParam, LPARAM
 					::SendMessage(::GetParent(_hParent), NPPM_INTERNAL_SCROLLBEYONDLASTLINE, 0, 0);
 					return TRUE;
 
+				case IDC_CHECK_LINECUTCOPYWITHOUTSELECTION:
+				{
+					bool isChecked = BST_CHECKED == ::SendDlgItemMessage(_hSelf, IDC_CHECK_LINECUTCOPYWITHOUTSELECTION, BM_GETCHECK, 0, 0);
+					svp._lineCopyCutWithoutSelection = isChecked;
+					::SendMessage(::GetParent(_hParent), NPPM_INTERNAL_LINECUTCOPYWITHOUTSELECTION, 0, 0);
+					return TRUE;
+				}
+
 				case IDC_CHECK_RIGHTCLICKKEEPSSELECTION:
 					svp._rightClickKeepsSelection = (BST_CHECKED == ::SendDlgItemMessage(_hSelf, IDC_CHECK_RIGHTCLICKKEEPSSELECTION, BM_GETCHECK, 0, 0));
 					return TRUE;
diff --git a/PowerEditor/src/WinControls/Preference/preference_rc.h b/PowerEditor/src/WinControls/Preference/preference_rc.h
index e53528106..97a0a0119 100644
--- a/PowerEditor/src/WinControls/Preference/preference_rc.h
+++ b/PowerEditor/src/WinControls/Preference/preference_rc.h
@@ -112,7 +112,7 @@
 	#define	IDC_VES_GB_STATIC (IDD_PREFERENCE_SUB_EDITING + 11)
 	#define	IDC_DISTRACTIONFREE_STATIC (IDD_PREFERENCE_SUB_EDITING + 12)
 	#define	IDC_CHECK_EDGEBGMODE (IDD_PREFERENCE_SUB_EDITING + 13)
-	//#define	IDC_CHECK_CURRENTLINEHILITE (IDD_PREFERENCE_SUB_EDITING + 14)
+	#define	IDC_CHECK_LINECUTCOPYWITHOUTSELECTION (IDD_PREFERENCE_SUB_EDITING + 14)
 	#define	IDC_CHECK_SMOOTHFONT (IDD_PREFERENCE_SUB_EDITING + 15)
 
 	#define	IDC_CARETSETTING_STATIC (IDD_PREFERENCE_SUB_EDITING + 16)
diff --git a/PowerEditor/src/resource.h b/PowerEditor/src/resource.h
index d8a7f801a..e6d77afa9 100644
--- a/PowerEditor/src/resource.h
+++ b/PowerEditor/src/resource.h
@@ -656,6 +656,7 @@
 	#define NPPM_INTERNAL_CLOSEDOC                      (NOTEPADPLUS_USER_INTERNAL + 75)
 	#define NPPM_INTERNAL_EXTERNALLEXERBUFFER           (NOTEPADPLUS_USER_INTERNAL + 76)
 	#define NPPM_INTERNAL_CHECKUNDOREDOSTATE            (NOTEPADPLUS_USER_INTERNAL + 77)
+	#define NPPM_INTERNAL_LINECUTCOPYWITHOUTSELECTION   (NOTEPADPLUS_USER_INTERNAL + 78)
 
 // See Notepad_plus_msgs.h
 //#define NOTEPADPLUS_USER   (WM_USER + 1000)
