mirror of
https://github.com/rizonesoft/Notepad3.git
synced 2026-06-14 21:09:05 +08:00
Merge pull request #5863 from RaiKoHoff/dev_master
fix: drag&drop files - temp handling (immediate deleted on drop)
This commit is contained in:
commit
8b96b6b84d
@ -60,6 +60,7 @@ SettingsVersion=5
|
||||
;SubWrappedLineSelectOnMarginClick=false
|
||||
;LaunchInstanceWndPosOffset=28
|
||||
;LaunchInstanceFullVisible=true
|
||||
;MaxFileDropInstances=20
|
||||
;UseOldStyleBraceMatching=0
|
||||
;WebTemplate1=https://google.com/search?q=%s
|
||||
;WebTmpl1MenuName=Open Web Action 1
|
||||
|
||||
@ -200,6 +200,7 @@
|
||||
#define IDS_MUI_INIFILE_READONLY 14022
|
||||
#define IDS_MUI_DOCUMENT_READONLY 14023
|
||||
#define IDS_MUI_ASK_CLEAR_UNDO 14024
|
||||
#define IDS_MUI_DROP_CAP_EXCEEDED 14025
|
||||
|
||||
#define IDS_MUI_ASK_SAVE 15000
|
||||
#define IDS_MUI_ASK_REVERT 15001
|
||||
|
||||
@ -202,6 +202,7 @@ BEGIN
|
||||
IDS_MUI_EXPORT_FAIL "Kon nie stylinstellings uitvoer na ""%s"""
|
||||
IDS_MUI_REGEX_INVALID "Kon nie regular expression evalueer nie. Expression is ongeldig!"
|
||||
IDS_MUI_DROP_NO_FILE "Geen geldige lêernaam opgehaal\nAs jy van 32-bit toepassing af sak, sleep na Notepad3 se nutsbalk"
|
||||
IDS_MUI_DROP_CAP_EXCEEDED "Dropped more files than the configured limit (%i). Increase Settings2.MaxFileDropInstances if needed."
|
||||
IDS_MUI_URL_DIR_EXISTS "URL gespesifiseerde gids bestaan!"
|
||||
IDS_MUI_URL_FILE_EXISTS "URL gespesifiseerde lêer bestaan!"
|
||||
IDS_MUI_URL_PATH_NOT_FOUND
|
||||
|
||||
@ -202,6 +202,7 @@ BEGIN
|
||||
IDS_MUI_EXPORT_FAIL "Памылка экспарту наладаў стыляў «%s»"
|
||||
IDS_MUI_REGEX_INVALID "Памылка апрацоўкі рэгулярнага выразу. Выраз несапраўдны!"
|
||||
IDS_MUI_DROP_NO_FILE "Не выйшла атрымаць імя файла\nКалі перацягваеце з 32-бітнай праграмы,\nперацягніце на паліцу прыладаў Notepad3"
|
||||
IDS_MUI_DROP_CAP_EXCEEDED "Dropped more files than the configured limit (%i). Increase Settings2.MaxFileDropInstances if needed."
|
||||
IDS_MUI_URL_DIR_EXISTS "Азначаная ва URL дырэкторыя існуе!"
|
||||
IDS_MUI_URL_FILE_EXISTS "Азначаны ва URL файл існуе!"
|
||||
IDS_MUI_URL_PATH_NOT_FOUND
|
||||
|
||||
@ -202,6 +202,7 @@ BEGIN
|
||||
IDS_MUI_EXPORT_FAIL "Fehler beim Export der Stil Einstellungen nach ""%s"""
|
||||
IDS_MUI_REGEX_INVALID "Fehler bei der Evaluierung des regulären Ausdrucks (RegEx), der Ausruck ist ungültig!"
|
||||
IDS_MUI_DROP_NO_FILE "Es wurde kein gültiger Dateiname erkannt. Falls aus einer 32-bit Applikation gedropped wurde, dann versuche die Datei auf die Werkzeugleiste zu ziehen"
|
||||
IDS_MUI_DROP_CAP_EXCEEDED "Dropped more files than the configured limit (%i). Increase Settings2.MaxFileDropInstances if needed."
|
||||
IDS_MUI_URL_DIR_EXISTS "URL spezifiziertes Verzeichnis existiert!"
|
||||
IDS_MUI_URL_FILE_EXISTS "URL spezifizierte Datei existiert!"
|
||||
IDS_MUI_URL_PATH_NOT_FOUND
|
||||
|
||||
@ -202,6 +202,7 @@ BEGIN
|
||||
IDS_MUI_EXPORT_FAIL "Σφάλμα κατά την εξαγωγή των ρυθμίσεων στυλ στο ""%s"""
|
||||
IDS_MUI_REGEX_INVALID "Σφάλμα κατά την αξιολόγηση της κανονικής έκφρασης. Η έκφραση δεν είναι έγκυρη!"
|
||||
IDS_MUI_DROP_NO_FILE "Δεν ανακτήθηκε έγκυρο όνομα αρχείου\nΕάν χρησιμοποιείται μια εφαρμογή 32-bit,\nκάντε μεταφορά κι απόθεση στη γραμμή εργαλείων του Notepad3"
|
||||
IDS_MUI_DROP_CAP_EXCEEDED "Dropped more files than the configured limit (%i). Increase Settings2.MaxFileDropInstances if needed."
|
||||
IDS_MUI_URL_DIR_EXISTS "Υπάρχει καθορισμένος κατάλογος διευθύνσεων URL!"
|
||||
IDS_MUI_URL_FILE_EXISTS "Υπάρχει καθορισμένο αρχείο διευθύνσεων URL!"
|
||||
IDS_MUI_URL_PATH_NOT_FOUND
|
||||
|
||||
@ -202,6 +202,7 @@ BEGIN
|
||||
IDS_MUI_EXPORT_FAIL "Error exporting style settings to ""%s"""
|
||||
IDS_MUI_REGEX_INVALID "Error evaluating regular expression. Expression is invalid!"
|
||||
IDS_MUI_DROP_NO_FILE "No valid filename retrieved\nIf dropping from 32-bit application,\nplease drag and drop to Notepad3's tool bar"
|
||||
IDS_MUI_DROP_CAP_EXCEEDED "Dropped more files than the configured limit (%i). Increase Settings2.MaxFileDropInstances if needed."
|
||||
IDS_MUI_URL_DIR_EXISTS "URL specified directory exists!"
|
||||
IDS_MUI_URL_FILE_EXISTS "URL specified file exists!"
|
||||
IDS_MUI_URL_PATH_NOT_FOUND
|
||||
|
||||
@ -202,6 +202,7 @@ BEGIN
|
||||
IDS_MUI_EXPORT_FAIL "Error exporting style settings to ""%s"""
|
||||
IDS_MUI_REGEX_INVALID "Error evaluating regular expression. Expression is invalid!"
|
||||
IDS_MUI_DROP_NO_FILE "No valid filename retrieved\nIf dropping from 32-bit application,\nplease drag and drop to Notepad3's tool bar"
|
||||
IDS_MUI_DROP_CAP_EXCEEDED "Dropped more files than the configured limit (%i). Increase Settings2.MaxFileDropInstances if needed."
|
||||
IDS_MUI_URL_DIR_EXISTS "URL specified directory exists!"
|
||||
IDS_MUI_URL_FILE_EXISTS "URL specified file exists!"
|
||||
IDS_MUI_URL_PATH_NOT_FOUND
|
||||
|
||||
@ -202,6 +202,7 @@ BEGIN
|
||||
IDS_MUI_EXPORT_FAIL "Error al exportar la configuración de estilo a ""%s"""
|
||||
IDS_MUI_REGEX_INVALID "Error al evaluar la expresión regular. ¡La expresión es inválida!"
|
||||
IDS_MUI_DROP_NO_FILE "No se recuperó un nombre de archivo válido\nSi se usa una aplicación de 32 bits,\nintente arrastrar el archivo a la barra de herramientas Notepad3"
|
||||
IDS_MUI_DROP_CAP_EXCEEDED "Dropped more files than the configured limit (%i). Increase Settings2.MaxFileDropInstances if needed."
|
||||
IDS_MUI_URL_DIR_EXISTS "¡Directorio especificado por URL existe!"
|
||||
IDS_MUI_URL_FILE_EXISTS "¡Archivo especificado por URL existe!"
|
||||
IDS_MUI_URL_PATH_NOT_FOUND
|
||||
|
||||
@ -202,6 +202,7 @@ BEGIN
|
||||
IDS_MUI_EXPORT_FAIL "Virhe tyyliasetusten viemisessä kohteeseen ""%s"""
|
||||
IDS_MUI_REGEX_INVALID "Virhe säännöllisen lausekkeen käytössä. Lauseke on virheellinen!"
|
||||
IDS_MUI_DROP_NO_FILE "Kelvollista tiedostonimeä ei saatu\nJos pudotat tiedoston 32-bittisestä sovelluksesta, vedä ja pudota se Notepad3:n työkalupalkkiin"
|
||||
IDS_MUI_DROP_CAP_EXCEEDED "Dropped more files than the configured limit (%i). Increase Settings2.MaxFileDropInstances if needed."
|
||||
IDS_MUI_URL_DIR_EXISTS "URL-osoitteessa määritetty hakemisto on olemassa!"
|
||||
IDS_MUI_URL_FILE_EXISTS "URL-osoitteessa määritetty tiedosto on olemassa!"
|
||||
IDS_MUI_URL_PATH_NOT_FOUND
|
||||
|
||||
@ -202,6 +202,7 @@ BEGIN
|
||||
IDS_MUI_EXPORT_FAIL "Erreur d'exportation des paramètres de style vers ""%s"""
|
||||
IDS_MUI_REGEX_INVALID "Erreur dans l'évaluation de l'expression régulière. L'expression est invalide !"
|
||||
IDS_MUI_DROP_NO_FILE "Aucun nom de fichier valide récupéré\nSi une application 32 bits est utilisée,\nessayez de faire glisser le fichier sur la barre d'outils de Notepad3"
|
||||
IDS_MUI_DROP_CAP_EXCEEDED "Dropped more files than the configured limit (%i). Increase Settings2.MaxFileDropInstances if needed."
|
||||
IDS_MUI_URL_DIR_EXISTS "Le répertoire spécifié par l'URL existe !"
|
||||
IDS_MUI_URL_FILE_EXISTS "Le fichier spécifié par l'URL existe !"
|
||||
IDS_MUI_URL_PATH_NOT_FOUND
|
||||
|
||||
@ -202,6 +202,7 @@ BEGIN
|
||||
IDS_MUI_EXPORT_FAIL "शैली सेटिंग ""%s"" को निर्यात करने में त्रुटि"
|
||||
IDS_MUI_REGEX_INVALID "सामान्य व्यंजक मूल्यांकन करने में त्रुटि। व्यंजक अवैध है!"
|
||||
IDS_MUI_DROP_NO_FILE "प्राप्त हुआ फ़ाईलनाम वैध नहीं है\nअगर 32-बिट एपलिकेशन से ड्रॉप कर रहे है,\nतो कृपया Notepad3 की औज़ार पट्टी पर ड्रैग और ड्रॉप करें"
|
||||
IDS_MUI_DROP_CAP_EXCEEDED "Dropped more files than the configured limit (%i). Increase Settings2.MaxFileDropInstances if needed."
|
||||
IDS_MUI_URL_DIR_EXISTS "URL निर्दिष्ट निदेशिका मौजूद है!"
|
||||
IDS_MUI_URL_FILE_EXISTS "URL निर्दिष्ट फ़ाईल मौजूद है!"
|
||||
IDS_MUI_URL_PATH_NOT_FOUND
|
||||
|
||||
@ -202,6 +202,7 @@ BEGIN
|
||||
IDS_MUI_EXPORT_FAIL "Hiba a színkiemelések beállításainak exportálásakor ide ""%s"""
|
||||
IDS_MUI_REGEX_INVALID "Hiba a reguláris kifejezés feldolgozásakor. A kifejezés érvénytelen!"
|
||||
IDS_MUI_DROP_NO_FILE "Nem sikerült érvényes fájlnevet kapni\nHa egy 32 bites alkalmazásból lett átdobva,\nkérem, ejtse újra a Notepad3 eszköztárra"
|
||||
IDS_MUI_DROP_CAP_EXCEEDED "Dropped more files than the configured limit (%i). Increase Settings2.MaxFileDropInstances if needed."
|
||||
IDS_MUI_URL_DIR_EXISTS "Az URL-ben megadott könyvtár létezik!"
|
||||
IDS_MUI_URL_FILE_EXISTS "Az URL-ben megadott fájl létezik!"
|
||||
IDS_MUI_URL_PATH_NOT_FOUND
|
||||
|
||||
@ -202,6 +202,7 @@ BEGIN
|
||||
IDS_MUI_EXPORT_FAIL "Kesalahan mengekspor pengaturan gaya ke ""%s"""
|
||||
IDS_MUI_REGEX_INVALID "Kesalahan mengevaluasi ekspresi reguler. Ekspresi tidak valid!"
|
||||
IDS_MUI_DROP_NO_FILE "Tidak ada nama berkas yang valid diambil\nJika telah lepas dari penggunaan aplikasi 32-bit,\nsilahkan seret berkas dan lepaskan ke bilah alat Notepad3"
|
||||
IDS_MUI_DROP_CAP_EXCEEDED "Dropped more files than the configured limit (%i). Increase Settings2.MaxFileDropInstances if needed."
|
||||
IDS_MUI_URL_DIR_EXISTS "URL direktori yang dituju tersedia!"
|
||||
IDS_MUI_URL_FILE_EXISTS "URL berkas yang dituju tersedia!"
|
||||
IDS_MUI_URL_PATH_NOT_FOUND
|
||||
|
||||
@ -202,6 +202,7 @@ BEGIN
|
||||
IDS_MUI_EXPORT_FAIL "Errore nell'esportazione degli Stili su '%s'"
|
||||
IDS_MUI_REGEX_INVALID "Errore nella valutazione dell'espressione regolare. La RegExp non è valida!"
|
||||
IDS_MUI_DROP_NO_FILE "Nessun nome file valido\nSe stai facendo drag-n-drop da una applicazione 32-bit,\nrilascia il file nella barra strumenti di Notepad3"
|
||||
IDS_MUI_DROP_CAP_EXCEEDED "Dropped more files than the configured limit (%i). Increase Settings2.MaxFileDropInstances if needed."
|
||||
IDS_MUI_URL_DIR_EXISTS "La cartella specificata nella URL esiste!"
|
||||
IDS_MUI_URL_FILE_EXISTS "Il file specificato nella URL esiste!"
|
||||
IDS_MUI_URL_PATH_NOT_FOUND
|
||||
|
||||
@ -202,6 +202,7 @@ BEGIN
|
||||
IDS_MUI_EXPORT_FAIL """%s"" への配色設定のエクスポートに失敗しました"
|
||||
IDS_MUI_REGEX_INVALID "正規表現の構文が正しくありません!"
|
||||
IDS_MUI_DROP_NO_FILE "読み込んだファイル名が正しくありません。(仮訳)\n32-bit のプログラムからドロップしたのであれば、\nNotepad3 のツールバーにドラッグ&&ドロップしてください"
|
||||
IDS_MUI_DROP_CAP_EXCEEDED "Dropped more files than the configured limit (%i). Increase Settings2.MaxFileDropInstances if needed."
|
||||
IDS_MUI_URL_DIR_EXISTS "URL で指定されたフォルダは存在します!"
|
||||
IDS_MUI_URL_FILE_EXISTS "URL で指定されたファイルは存在します!"
|
||||
IDS_MUI_URL_PATH_NOT_FOUND
|
||||
|
||||
@ -202,6 +202,7 @@ BEGIN
|
||||
IDS_MUI_EXPORT_FAIL "스타일 설정을 ""%s""으로 내보내는 중 오류가 발생하였습니다"
|
||||
IDS_MUI_REGEX_INVALID "정규 표현식을 평가하는 동안 오류가 발생했습니다. 표현식이 잘못되었습니다!"
|
||||
IDS_MUI_DROP_NO_FILE "검색된 유효한 파일 이름이 없습니다\n32비트 응용 프로그램에서 드롭하는 경우\nNotepad3의 도구 모음으로 끌어서 놓으세요"
|
||||
IDS_MUI_DROP_CAP_EXCEEDED "Dropped more files than the configured limit (%i). Increase Settings2.MaxFileDropInstances if needed."
|
||||
IDS_MUI_URL_DIR_EXISTS "URL 지정 디렉터리가 있습니다!"
|
||||
IDS_MUI_URL_FILE_EXISTS "URL 지정 파일이 있습니다!"
|
||||
IDS_MUI_URL_PATH_NOT_FOUND
|
||||
|
||||
@ -202,6 +202,7 @@ BEGIN
|
||||
IDS_MUI_EXPORT_FAIL "Fout bij het exporteren van stijlinstellingen naar ""%s"""
|
||||
IDS_MUI_REGEX_INVALID "Fout bij het evalueren van reguliere expressie. Ongeldige expressie!"
|
||||
IDS_MUI_DROP_NO_FILE "Ongeldige bestandsnaam\nAls een 32-bits toepassing wordt gebruikt,\nprobeer dan het bestand naar de werkbalk Notepad3 te slepen"
|
||||
IDS_MUI_DROP_CAP_EXCEEDED "Dropped more files than the configured limit (%i). Increase Settings2.MaxFileDropInstances if needed."
|
||||
IDS_MUI_URL_DIR_EXISTS "URL opgegeven map bestaat!"
|
||||
IDS_MUI_URL_FILE_EXISTS "URL opgegeven bestand bestaat!"
|
||||
IDS_MUI_URL_PATH_NOT_FOUND
|
||||
|
||||
@ -202,6 +202,7 @@ BEGIN
|
||||
IDS_MUI_EXPORT_FAIL "Błąd podczas eksportowania ustawień styu do ""%s"""
|
||||
IDS_MUI_REGEX_INVALID "Błąd podczas oceny regularnego wyrażenia. Wyrażenie jest nieuzasadnione!"
|
||||
IDS_MUI_DROP_NO_FILE "Nie rozpoznano żadnej sensownej nazwy pliku\nJeśli upuścisz aplikację 32-bitową,\nprzeciągnij i upuść do paska narzędzi Notepad3'a"
|
||||
IDS_MUI_DROP_CAP_EXCEEDED "Dropped more files than the configured limit (%i). Increase Settings2.MaxFileDropInstances if needed."
|
||||
IDS_MUI_URL_DIR_EXISTS "URL określony katalog istnieje!"
|
||||
IDS_MUI_URL_FILE_EXISTS "URL określony plik istnieje!"
|
||||
IDS_MUI_URL_PATH_NOT_FOUND
|
||||
|
||||
@ -202,6 +202,7 @@ BEGIN
|
||||
IDS_MUI_EXPORT_FAIL "Erro exportando configurações de estilo para ""%s"""
|
||||
IDS_MUI_REGEX_INVALID "Erro avaliando expressão regular. Expressão é invalida!"
|
||||
IDS_MUI_DROP_NO_FILE "Nenhum nome de arquivo válido foi obtido\nSe estiver carregando de uma applicação 32-bits,\npor favor, carregue e solte na barra de ferramentas do Notepad3"
|
||||
IDS_MUI_DROP_CAP_EXCEEDED "Dropped more files than the configured limit (%i). Increase Settings2.MaxFileDropInstances if needed."
|
||||
IDS_MUI_URL_DIR_EXISTS "Pasta especificada pelo URL existe!"
|
||||
IDS_MUI_URL_FILE_EXISTS "Arquivo especificado pelo URL existe!"
|
||||
IDS_MUI_URL_PATH_NOT_FOUND
|
||||
|
||||
@ -202,6 +202,7 @@ BEGIN
|
||||
IDS_MUI_EXPORT_FAIL "Erro ao exportar as definições de estilo para ""%s"""
|
||||
IDS_MUI_REGEX_INVALID "Erro a avaliar expressão regular. A expressão é inválida!"
|
||||
IDS_MUI_DROP_NO_FILE "Nenhum nome de ficheiro válido recuperado\nSe for proveniente de um programa 32-bit,\narraste e largue na barra de ferramentas do Notepad3"
|
||||
IDS_MUI_DROP_CAP_EXCEEDED "Dropped more files than the configured limit (%i). Increase Settings2.MaxFileDropInstances if needed."
|
||||
IDS_MUI_URL_DIR_EXISTS "Pasta especificada da URL já existe!"
|
||||
IDS_MUI_URL_FILE_EXISTS "Ficheiro especificado da URL já existe!"
|
||||
IDS_MUI_URL_PATH_NOT_FOUND
|
||||
|
||||
@ -202,6 +202,7 @@ BEGIN
|
||||
IDS_MUI_EXPORT_FAIL "Ошибка экспорта настроек стилей в «%s»"
|
||||
IDS_MUI_REGEX_INVALID "Ошибка обработки регулярного выражения. Выражение недействительно!"
|
||||
IDS_MUI_DROP_NO_FILE "Не удается получить имя файла\nЕсли перетаскиваете из 32-битного приложения,\nперетащите на панели инструментов Notepad3"
|
||||
IDS_MUI_DROP_CAP_EXCEEDED "Dropped more files than the configured limit (%i). Increase Settings2.MaxFileDropInstances if needed."
|
||||
IDS_MUI_URL_DIR_EXISTS "Указанная в URL папка существует!"
|
||||
IDS_MUI_URL_FILE_EXISTS "Указанный в URL файл существует!"
|
||||
IDS_MUI_URL_PATH_NOT_FOUND
|
||||
|
||||
@ -202,6 +202,7 @@ BEGIN
|
||||
IDS_MUI_EXPORT_FAIL "Chyba pri exportovaní nastavenia štýlu do ""%s"""
|
||||
IDS_MUI_REGEX_INVALID "Chyba pri vyhodnocovaní regulárneho výrazu. Výraz je neplatný!"
|
||||
IDS_MUI_DROP_NO_FILE "Nebol získaný žiadny platný názov súboru\nAk sa spúšťa 32-bitová aplikácia,\npresuňte myšou na panel nástrojov programu Notepad3"
|
||||
IDS_MUI_DROP_CAP_EXCEEDED "Dropped more files than the configured limit (%i). Increase Settings2.MaxFileDropInstances if needed."
|
||||
IDS_MUI_URL_DIR_EXISTS "URL zadaný adresár existuje!"
|
||||
IDS_MUI_URL_FILE_EXISTS "URL zadaný súbor existuje!"
|
||||
IDS_MUI_URL_PATH_NOT_FOUND
|
||||
|
||||
@ -202,6 +202,7 @@ BEGIN
|
||||
IDS_MUI_EXPORT_FAIL "Fel vid exportering av stilinställningar ""%s"""
|
||||
IDS_MUI_REGEX_INVALID "Fel vid utvärdering av reguljärt uttryck. Uttrycket är ogiltigt!"
|
||||
IDS_MUI_DROP_NO_FILE "Inget giltigt filnamn kunde hittas\nOm du drar-och-släpper från en 32-bits applikation,\nvar vänlig släpp på Notepad3s verktygsfält"
|
||||
IDS_MUI_DROP_CAP_EXCEEDED "Dropped more files than the configured limit (%i). Increase Settings2.MaxFileDropInstances if needed."
|
||||
IDS_MUI_URL_DIR_EXISTS "Angiven URL-mapp existerar redan!"
|
||||
IDS_MUI_URL_FILE_EXISTS "Angiven URL-fil existerar redan!"
|
||||
IDS_MUI_URL_PATH_NOT_FOUND
|
||||
|
||||
@ -202,6 +202,7 @@ BEGIN
|
||||
IDS_MUI_EXPORT_FAIL "Biçem ayarları ""%s"" dosyasına aktarılamadı"
|
||||
IDS_MUI_REGEX_INVALID "Kurallı ifade işlenirken sorun çıktı. Kurallı ifade geçersiz!"
|
||||
IDS_MUI_DROP_NO_FILE "Geçerli bir dosya adı alınamadı\nBir 32-bit uygulamadan sürükleyerek bıraktıysanız,\nNotepad3 araç çubuğuna bırakın"
|
||||
IDS_MUI_DROP_CAP_EXCEEDED "Dropped more files than the configured limit (%i). Increase Settings2.MaxFileDropInstances if needed."
|
||||
IDS_MUI_URL_DIR_EXISTS "İnternet adresinde belirtilen klasör var!"
|
||||
IDS_MUI_URL_FILE_EXISTS "İnternet adresinde belirtilen dosya var!"
|
||||
IDS_MUI_URL_PATH_NOT_FOUND
|
||||
|
||||
@ -202,6 +202,7 @@ BEGIN
|
||||
IDS_MUI_EXPORT_FAIL "Đã xảy ra lỗi khi xuất cài đặt kiểu sang ""%s"""
|
||||
IDS_MUI_REGEX_INVALID "Đã xảy ra lỗi khi đánh giá biểu thức chính quy. Biểu thức không hợp lệ!"
|
||||
IDS_MUI_DROP_NO_FILE "Không tìm thấy tên tệp hợp lệ\nNếu kéo từ ứng dụng 32-bit\nHãy kéo và thả vào thanh công cụ Notepad3"
|
||||
IDS_MUI_DROP_CAP_EXCEEDED "Dropped more files than the configured limit (%i). Increase Settings2.MaxFileDropInstances if needed."
|
||||
IDS_MUI_URL_DIR_EXISTS "Thư mục đã chỉ định URL đã tồn tại!"
|
||||
IDS_MUI_URL_FILE_EXISTS "Tệp đã chỉ định URL đã tồn tại!"
|
||||
IDS_MUI_URL_PATH_NOT_FOUND
|
||||
|
||||
@ -202,6 +202,7 @@ BEGIN
|
||||
IDS_MUI_EXPORT_FAIL "导出样式设置到文件“%s”时遇到错误"
|
||||
IDS_MUI_REGEX_INVALID "正则表达式无效"
|
||||
IDS_MUI_DROP_NO_FILE "未能获取文件名\n\n如果从 32 位应用程序拖放文件,请拖放到 Notepad3 的工具栏上"
|
||||
IDS_MUI_DROP_CAP_EXCEEDED "Dropped more files than the configured limit (%i). Increase Settings2.MaxFileDropInstances if needed."
|
||||
IDS_MUI_URL_DIR_EXISTS "URL 指定的目录已存在!"
|
||||
IDS_MUI_URL_FILE_EXISTS "URL 指定的文件已存在!"
|
||||
IDS_MUI_URL_PATH_NOT_FOUND
|
||||
|
||||
@ -202,6 +202,7 @@ BEGIN
|
||||
IDS_MUI_EXPORT_FAIL "匯出樣式設定到檔案「%s」時遇到錯誤"
|
||||
IDS_MUI_REGEX_INVALID "正規表示式無效"
|
||||
IDS_MUI_DROP_NO_FILE "未能獲取檔名\n\n如果從 32 位應用程式拖放檔案,請拖放到 Notepad3 的工具列上"
|
||||
IDS_MUI_DROP_CAP_EXCEEDED "Dropped more files than the configured limit (%i). Increase Settings2.MaxFileDropInstances if needed."
|
||||
IDS_MUI_URL_DIR_EXISTS "URL 指定目錄存在!"
|
||||
IDS_MUI_URL_FILE_EXISTS "URL 指定檔案存在!"
|
||||
IDS_MUI_URL_PATH_NOT_FOUND
|
||||
|
||||
@ -332,6 +332,12 @@ Pixel offset cascade applied to each new Notepad3 window launched as an addition
|
||||
|
||||
When `true` (default), newly launched windows are clamped so they remain fully visible on the target monitor, even if the `LaunchInstanceWndPosOffset` cascade would push them off-screen. Set to `false` to allow partially off-screen positions (e.g. if you rely on the OS to handle multi-monitor placement).
|
||||
|
||||
#### `MaxFileDropInstances=20`
|
||||
|
||||
Maximum number of new Notepad3 windows spawned in a single drag-and-drop operation. When the current document has unsaved changes (or `Ctrl` is held during the drop), each dropped file opens in its own new window so the dirty document is never disturbed. This setting caps that fan-out — files beyond the cap are skipped with a single warning. Range: `-1`–`100`. Set to `-1` to remove the cap; default is `20`.
|
||||
|
||||
Drops originating from a Windows temp root (`%TEMP%`, `%LOCALAPPDATA%\Temp`, `%LOCALAPPDATA%\Microsoft\Windows\INetCache`) — e.g. files dragged from 7-Zip, Outlook attachments, downloads — are first snapshotted into `<%TEMP%>\Notepad3\drops\` so the source can be deleted by the originating process without breaking the load. Snapshots are pruned automatically (older than one hour on each subsequent drop, older than 24 hours at startup).
|
||||
|
||||
### Appearance
|
||||
|
||||
#### `RenderingTechnology=1`
|
||||
|
||||
@ -1399,6 +1399,10 @@ void LoadSettings()
|
||||
|
||||
Settings2.LaunchInstanceWndPosOffset = clampi(IniSectionGetInt(IniSecSettings2, L"LaunchInstanceWndPosOffset", 28), -10000, 10000);
|
||||
Settings2.LaunchInstanceFullVisible = IniSectionGetBool(IniSecSettings2, L"LaunchInstanceFullVisible", true);
|
||||
Settings2.MaxFileDropInstances = clampi(IniSectionGetInt(IniSecSettings2, L"MaxFileDropInstances", 20), -1, 100);
|
||||
if (Settings2.MaxFileDropInstances < 0) {
|
||||
Settings2.MaxFileDropInstances = INT_MAX;
|
||||
}
|
||||
|
||||
Settings2.SciFontQuality = clampi(IniSectionGetInt(IniSecSettings2, L"SciFontQuality", SC_EFF_QUALITY_LCD_OPTIMIZED), SC_EFF_QUALITY_DEFAULT, SC_EFF_QUALITY_LCD_OPTIMIZED);
|
||||
|
||||
|
||||
475
src/Notepad3.c
475
src/Notepad3.c
@ -93,6 +93,7 @@ CONSTANTS_T const Constants = {
|
||||
, L"Suppressed Messages" // Inifile Section "SuppressedMessages"
|
||||
, { // SuppressKey — keys under [Suppressed Messages]
|
||||
L"AllowClearUndoHistory"
|
||||
, L"InfoDropCap"
|
||||
, L"InfoInstanceExist"
|
||||
, L"MsgConv1"
|
||||
, L"MsgConv2"
|
||||
@ -357,6 +358,10 @@ static inline void _SplitUndoTransaction()
|
||||
|
||||
static void _DelayClearCallTip(const LONG64 delay);
|
||||
static void _DelaySplitUndoTransaction(const LONG64 delay);
|
||||
static void _PruneOldDropSnapshots(DWORD maxAgeSecs);
|
||||
static bool _IsDropSnapshotPath(const HPATHL hpth);
|
||||
static void _RegisterDropSnapshot(const HPATHL hpth);
|
||||
static void _CleanupDropSnapshots(bool dropAll);
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
@ -1027,6 +1032,9 @@ static void _CleanUpResources(const HWND hwnd, bool bIsInitialized)
|
||||
KillTimer(hwnd, ID_TINYEXPRCOPYTIMER);
|
||||
}
|
||||
|
||||
// Delete any drag-and-drop snapshots this instance still owns.
|
||||
_CleanupDropSnapshots(true);
|
||||
|
||||
if (Globals.pStdDarkModeIniStyles) {
|
||||
FreeMem(Globals.pStdDarkModeIniStyles);
|
||||
Globals.pStdDarkModeIniStyles = NULL;
|
||||
@ -2420,6 +2428,17 @@ HWND InitInstance(const HINSTANCE hInstance, int nCmdShow)
|
||||
|
||||
Path_Release(hfile_pth);
|
||||
|
||||
// Clean up stale drag-and-drop snapshots left behind by crashed/killed
|
||||
// child instances (older than 24 h).
|
||||
_PruneOldDropSnapshots(24UL * 60UL * 60UL);
|
||||
|
||||
// If this instance was launched with a snapshot path as CLI argument
|
||||
// (i.e. spawned by a parent NP3 in MsgDropFiles), register it so we
|
||||
// own its cleanup on exit.
|
||||
if (_IsDropSnapshotPath(Paths.CurrentFile)) {
|
||||
_RegisterDropSnapshot(Paths.CurrentFile);
|
||||
}
|
||||
|
||||
s_bInitAppDone = true;
|
||||
|
||||
return Globals.hwndMain;
|
||||
@ -3793,12 +3812,296 @@ LRESULT MsgDrawItem(HWND hwnd, WPARAM wParam, LPARAM lParam)
|
||||
}
|
||||
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
// Drag-and-drop temp-file snapshots
|
||||
//
|
||||
// Sources like 7-Zip release (and delete) their temp files as soon as the
|
||||
// drag completes. If we delay (modal save prompt, ShellExecuteEx hand-off
|
||||
// to a new instance), the source is gone before we read it. The helpers
|
||||
// below copy ephemeral drops into <%TEMP%>\Notepad3\drops\ synchronously
|
||||
// inside MsgDropFiles so consumers find a stable on-disk copy. Older
|
||||
// snapshots are pruned on each drop (>1 h) and at startup (>24 h).
|
||||
//
|
||||
#define DROP_SNAPSHOT_SUBDIR L"Notepad3\\drops"
|
||||
|
||||
static bool _GetDropSnapshotDir(HPATHL hpth_out)
|
||||
{
|
||||
WCHAR wchTemp[MAX_PATH_EXPLICIT] = { L'\0' };
|
||||
DWORD const len = GetTempPathW(COUNTOF(wchTemp), wchTemp);
|
||||
if ((len == 0) || (len >= COUNTOF(wchTemp))) {
|
||||
return false;
|
||||
}
|
||||
Path_Reset(hpth_out, wchTemp);
|
||||
Path_Append(hpth_out, DROP_SNAPSHOT_SUBDIR);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool _PathHasPrefixW(const HPATHL hpth, LPCWSTR pfx)
|
||||
{
|
||||
if (!pfx || !*pfx) {
|
||||
return false;
|
||||
}
|
||||
LPCWSTR const p = Path_Get(hpth);
|
||||
if (!p) {
|
||||
return false;
|
||||
}
|
||||
size_t pfxLen = wcslen(pfx);
|
||||
// Trim a trailing separator on the prefix so the boundary check below is uniform.
|
||||
while ((pfxLen > 0) && ((pfx[pfxLen - 1] == L'\\') || (pfx[pfxLen - 1] == L'/'))) {
|
||||
--pfxLen;
|
||||
}
|
||||
if (pfxLen == 0) {
|
||||
return false;
|
||||
}
|
||||
size_t const pLen = wcslen(p);
|
||||
if (pLen < pfxLen) {
|
||||
return false;
|
||||
}
|
||||
if (CompareStringOrdinal(p, (int)pfxLen, pfx, (int)pfxLen, TRUE) != CSTR_EQUAL) {
|
||||
return false;
|
||||
}
|
||||
// Require a path boundary right after the prefix to avoid \Temp matching \TempOther.
|
||||
if (pLen == pfxLen) {
|
||||
return true;
|
||||
}
|
||||
return (p[pfxLen] == L'\\') || (p[pfxLen] == L'/');
|
||||
}
|
||||
|
||||
static bool _IsEphemeralPath(const HPATHL hpth)
|
||||
{
|
||||
if (Path_IsEmpty(hpth)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
WCHAR wchTemp[MAX_PATH_EXPLICIT] = { L'\0' };
|
||||
if (GetTempPathW(COUNTOF(wchTemp), wchTemp) && _PathHasPrefixW(hpth, wchTemp)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
HPATHL hLocalAppData = Path_Allocate(NULL);
|
||||
bool result = false;
|
||||
if (Path_GetKnownFolder(&FOLDERID_LocalAppData, hLocalAppData)) {
|
||||
HPATHL hCandidate = Path_Copy(hLocalAppData);
|
||||
Path_Append(hCandidate, L"Temp");
|
||||
if (_PathHasPrefixW(hpth, Path_Get(hCandidate))) {
|
||||
result = true;
|
||||
}
|
||||
Path_Release(hCandidate);
|
||||
|
||||
if (!result) {
|
||||
hCandidate = Path_Copy(hLocalAppData);
|
||||
Path_Append(hCandidate, L"Microsoft\\Windows\\INetCache");
|
||||
if (_PathHasPrefixW(hpth, Path_Get(hCandidate))) {
|
||||
result = true;
|
||||
}
|
||||
Path_Release(hCandidate);
|
||||
}
|
||||
}
|
||||
Path_Release(hLocalAppData);
|
||||
return result;
|
||||
}
|
||||
|
||||
static HPATHL _BuildSnapshotPath(const HPATHL orig, UINT idx)
|
||||
{
|
||||
HPATHL hDir = Path_Allocate(NULL);
|
||||
if (!_GetDropSnapshotDir(hDir)) {
|
||||
Path_Release(hDir);
|
||||
return NULL;
|
||||
}
|
||||
HRESULT const hr = Path_CreateDirectoryEx(hDir);
|
||||
if (FAILED(hr) && (hr != HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS))) {
|
||||
Path_Release(hDir);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
LPCWSTR const baseName = Path_FindFileName(orig);
|
||||
WCHAR wchLeaf[MAX_PATH_EXPLICIT];
|
||||
StringCchPrintfW(wchLeaf, COUNTOF(wchLeaf), L"np3drop-%lu-%llu-%u-%s",
|
||||
(unsigned long)GetCurrentProcessId(),
|
||||
(unsigned long long)GetTickCount64(), idx,
|
||||
(baseName && *baseName) ? baseName : L"untitled");
|
||||
|
||||
HPATHL hSnap = Path_Copy(hDir);
|
||||
Path_Append(hSnap, wchLeaf);
|
||||
Path_Release(hDir);
|
||||
return hSnap;
|
||||
}
|
||||
|
||||
static bool _IsProcessAlive(DWORD pid)
|
||||
{
|
||||
if (pid == 0) {
|
||||
return false;
|
||||
}
|
||||
HANDLE const h = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid);
|
||||
if (!h) {
|
||||
return false; // process gone (or denied — treat as gone)
|
||||
}
|
||||
DWORD exitCode = 0;
|
||||
BOOL const ok = GetExitCodeProcess(h, &exitCode);
|
||||
CloseHandle(h);
|
||||
return ok && (exitCode == STILL_ACTIVE);
|
||||
}
|
||||
|
||||
static void _PruneOldDropSnapshots(DWORD maxAgeSecs)
|
||||
{
|
||||
HPATHL hDir = Path_Allocate(NULL);
|
||||
if (!_GetDropSnapshotDir(hDir) || !Path_IsExistingDirectory(hDir)) {
|
||||
Path_Release(hDir);
|
||||
return;
|
||||
}
|
||||
|
||||
HPATHL hPattern = Path_Copy(hDir);
|
||||
Path_Append(hPattern, L"np3drop-*");
|
||||
|
||||
WIN32_FIND_DATAW fd = { 0 };
|
||||
HANDLE const hFind = Path_FindFirstFile(hPattern, &fd);
|
||||
Path_Release(hPattern);
|
||||
if (hFind == INVALID_HANDLE_VALUE) {
|
||||
Path_Release(hDir);
|
||||
return;
|
||||
}
|
||||
|
||||
// Filename layout: "np3drop-<pid>-<tick>-<idx>-<basename>".
|
||||
// <tick> is GetTickCount64() at creation — survives CopyFileW's mtime
|
||||
// preservation. <pid> lets us skip snapshots that another live NP3 owns.
|
||||
DWORD const ourPid = GetCurrentProcessId();
|
||||
ULONGLONG const nowTick = GetTickCount64();
|
||||
ULONGLONG const maxAgeMs = (ULONGLONG)maxAgeSecs * 1000ULL;
|
||||
|
||||
do {
|
||||
if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
||||
continue;
|
||||
}
|
||||
WCHAR const* p = fd.cFileName;
|
||||
if (wcsncmp(p, L"np3drop-", 8) != 0) {
|
||||
continue;
|
||||
}
|
||||
p += 8;
|
||||
WCHAR* endp = NULL;
|
||||
DWORD const filePid = (DWORD)wcstoul(p, &endp, 10);
|
||||
if (!endp || (endp == p) || (*endp != L'-')) {
|
||||
continue; // unexpected layout
|
||||
}
|
||||
p = endp + 1;
|
||||
ULONGLONG const createTick = _wcstoui64(p, &endp, 10);
|
||||
if (!endp || (endp == p) || (*endp != L'-')) {
|
||||
continue;
|
||||
}
|
||||
// Skip snapshots owned by another live NP3 instance.
|
||||
if ((filePid != ourPid) && _IsProcessAlive(filePid)) {
|
||||
continue;
|
||||
}
|
||||
// A reboot resets GetTickCount64, so any file whose tick is in the
|
||||
// future (relative to "now") is from a previous boot — definitely stale.
|
||||
bool const stale = (createTick > nowTick) || ((nowTick - createTick) > maxAgeMs);
|
||||
if (!stale) {
|
||||
continue;
|
||||
}
|
||||
HPATHL hFile = Path_Copy(hDir);
|
||||
Path_Append(hFile, fd.cFileName);
|
||||
// Never delete our own currently-loaded snapshot.
|
||||
if (Path_StrgComparePath(hFile, Paths.CurrentFile, Paths.ModuleDirectory, true) != 0) {
|
||||
Path_DeleteFile(hFile);
|
||||
}
|
||||
Path_Release(hFile);
|
||||
} while (FindNextFileW(hFind, &fd));
|
||||
|
||||
FindClose(hFind);
|
||||
Path_Release(hDir);
|
||||
}
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Snapshot registry
|
||||
//
|
||||
// Per-process list of drag-and-drop snapshots that this instance is
|
||||
// responsible for cleaning up. The age-based pruner alone is too lazy
|
||||
// (snapshots accumulate during long sessions); the registry adds:
|
||||
// * sweep after each MsgDropFiles dispatch (drops any registered snapshot
|
||||
// that is no longer the current document),
|
||||
// * drain at process exit (cleans whatever is left).
|
||||
// Snapshots handed off to spawned child instances are NOT registered by the
|
||||
// spawner — the child registers its own when it sees a snapshot-dir CLI arg.
|
||||
//
|
||||
typedef struct _DropSnapshotEntry {
|
||||
HPATHL hpth;
|
||||
struct _DropSnapshotEntry* next;
|
||||
} DropSnapshotEntry;
|
||||
|
||||
static DropSnapshotEntry* s_dropSnapshots = NULL;
|
||||
|
||||
static bool _IsDropSnapshotPath(const HPATHL hpth)
|
||||
{
|
||||
if (Path_IsEmpty(hpth)) {
|
||||
return false;
|
||||
}
|
||||
HPATHL hDir = Path_Allocate(NULL);
|
||||
bool result = false;
|
||||
if (_GetDropSnapshotDir(hDir)) {
|
||||
result = _PathHasPrefixW(hpth, Path_Get(hDir));
|
||||
}
|
||||
Path_Release(hDir);
|
||||
return result;
|
||||
}
|
||||
|
||||
static void _RegisterDropSnapshot(const HPATHL hpth)
|
||||
{
|
||||
if (!_IsDropSnapshotPath(hpth)) {
|
||||
return;
|
||||
}
|
||||
for (DropSnapshotEntry* e = s_dropSnapshots; e; e = e->next) {
|
||||
if (Path_StrgComparePath(e->hpth, hpth, Paths.ModuleDirectory, true) == 0) {
|
||||
return; // already registered
|
||||
}
|
||||
}
|
||||
DropSnapshotEntry* e = (DropSnapshotEntry*)AllocMem(sizeof(*e), HEAP_ZERO_MEMORY);
|
||||
if (!e) {
|
||||
return;
|
||||
}
|
||||
e->hpth = Path_Copy(hpth);
|
||||
e->next = s_dropSnapshots;
|
||||
s_dropSnapshots = e;
|
||||
}
|
||||
|
||||
// Delete registered snapshots and remove their entries.
|
||||
// dropAll = false → keep only the entry that is Paths.CurrentFile (sweep
|
||||
// orphans after each MsgDropFiles dispatch).
|
||||
// dropAll = true → drop everything (called from _CleanUpResources at
|
||||
// process exit).
|
||||
static void _CleanupDropSnapshots(bool dropAll)
|
||||
{
|
||||
DropSnapshotEntry** pp = &s_dropSnapshots;
|
||||
while (*pp) {
|
||||
DropSnapshotEntry* e = *pp;
|
||||
bool const keep = !dropAll &&
|
||||
Path_IsNotEmpty(Paths.CurrentFile) &&
|
||||
(Path_StrgComparePath(e->hpth, Paths.CurrentFile,
|
||||
Paths.ModuleDirectory, true) == 0);
|
||||
if (keep) {
|
||||
pp = &e->next;
|
||||
continue;
|
||||
}
|
||||
Path_DeleteFile(e->hpth);
|
||||
Path_Release(e->hpth);
|
||||
*pp = e->next;
|
||||
FreeMem(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
// _OnDropOneFile()
|
||||
//
|
||||
// Loads a single non-spawning drop into the current window. The Ctrl-and-dirty
|
||||
// spawn-to-new-instance routing is handled by MsgDropFiles' dispatcher; this
|
||||
// helper only sees in-window loads and directory drops.
|
||||
//
|
||||
static LRESULT _OnDropOneFile(HWND hwnd, HPATHL hFilePath, WININFO* wi)
|
||||
{
|
||||
UNREFERENCED_PARAMETER(wi);
|
||||
|
||||
if (IsIconic(hwnd)) {
|
||||
ShowWindow(hwnd, SW_RESTORE);
|
||||
}
|
||||
@ -3813,13 +4116,7 @@ static LRESULT _OnDropOneFile(HWND hwnd, HPATHL hFilePath, WININFO* wi)
|
||||
}
|
||||
}
|
||||
else if (Path_IsExistingFile(hFilePath)) {
|
||||
//~ ignore Flags.bReuseWindow
|
||||
bool const sameFile = (Path_StrgComparePath(hFilePath, Paths.CurrentFile, Paths.ModuleDirectory, true) == 0);
|
||||
if (IsKeyDown(VK_CONTROL) || wi) {
|
||||
DialogNewWindow(hwnd, sameFile, hFilePath, wi);
|
||||
} else {
|
||||
FileLoad(hFilePath, fLoadFlags, 0, 0);
|
||||
}
|
||||
FileLoad(hFilePath, fLoadFlags, 0, 0);
|
||||
} else {
|
||||
// Windows Bug: wParam (HDROP) pointer is corrupted if dropped from 32-bit App
|
||||
InfoBoxLng(MB_ICONWARNING, NULL, IDS_MUI_DROP_NO_FILE);
|
||||
@ -3832,33 +4129,155 @@ static LRESULT _OnDropOneFile(HWND hwnd, HPATHL hFilePath, WININFO* wi)
|
||||
//
|
||||
// MsgDropFiles() - Handles WM_DROPFILES
|
||||
//
|
||||
// Dirty current doc OR Ctrl held -> every file spawns a new NP3 instance
|
||||
// (capped by Settings2.MaxFileDropInstances). Clean current doc -> first
|
||||
// file loads in this window, remaining spawn. Sources under a Windows temp
|
||||
// root are snapshotted synchronously before DragFinish so 7-Zip / Outlook /
|
||||
// archiver temp-file deletion can't race the load.
|
||||
//
|
||||
LRESULT MsgDropFiles(HWND hwnd, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
UNREFERENCED_PARAMETER(lParam);
|
||||
|
||||
HDROP hDrop = (HDROP)wParam;
|
||||
|
||||
if (hDrop) {
|
||||
|
||||
bool const vkCtrlDown = IsKeyDown(VK_CONTROL);
|
||||
|
||||
HPATHL hdrop_pth = Path_Allocate(NULL);
|
||||
wchar_t* const drop_buf = Path_WriteAccessBuf(hdrop_pth, STRINGW_MAX_URL_LENGTH);
|
||||
UINT const cnt = DragQueryFileW(hDrop, UINT_MAX, NULL, 0);
|
||||
|
||||
int const offset = Settings2.LaunchInstanceWndPosOffset;
|
||||
bool const bFullVisible = Settings2.LaunchInstanceFullVisible;
|
||||
for (UINT i = 0; i < cnt; ++i) {
|
||||
WININFO wi = GetMyWindowPlacement(hwnd, NULL, (vkCtrlDown ? (offset * (i + 1)) : 0), bFullVisible);
|
||||
DragQueryFileW(hDrop, i, drop_buf, (UINT)Path_GetBufCount(hdrop_pth));
|
||||
Path_Sanitize(hdrop_pth);
|
||||
_OnDropOneFile(hwnd, hdrop_pth, (((0 == i) && !IsKeyDown(VK_CONTROL)) ? NULL : &wi));
|
||||
}
|
||||
|
||||
DragFinish(hDrop);
|
||||
Path_Release(hdrop_pth);
|
||||
UpdateToolbar_Now(hwnd);
|
||||
if (!hDrop) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool const vkCtrlDown = IsKeyDown(VK_CONTROL);
|
||||
bool const bDirty = IsSaveNeeded();
|
||||
bool const bForceNew = vkCtrlDown || bDirty;
|
||||
UINT const cnt = DragQueryFileW(hDrop, UINT_MAX, NULL, 0);
|
||||
if (cnt == 0) {
|
||||
DragFinish(hDrop);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Round-2 race-buster: if the watched current file is gone AND we have
|
||||
// no edits, the user's drop implicitly discards it. Resetting the
|
||||
// watcher events now suppresses both the FSF_Ask modal (FileSave checks
|
||||
// IsFileChangedFlagSet) and any queued WM_FILECHANGEDNOTIFY (the
|
||||
// file-deleted branch is silent when Paths.CurrentFile no longer exists).
|
||||
if (!bDirty && IsFileChangedFlagSet() &&
|
||||
Path_IsNotEmpty(Paths.CurrentFile) && !Path_IsExistingFile(Paths.CurrentFile)) {
|
||||
ResetFileObservationData(true);
|
||||
}
|
||||
|
||||
// Phase 0: opportunistic prune of stale snapshots (>1 h, once per drop).
|
||||
_PruneOldDropSnapshots(60UL * 60UL);
|
||||
|
||||
// Phase 1: synchronously snapshot ephemeral sources before DragFinish.
|
||||
typedef struct _DropEntry {
|
||||
HPATHL eff; // path the dispatcher will hand off (snapshot or original)
|
||||
HPATHL orig; // path as DragQueryFileW reported it
|
||||
bool isDir;
|
||||
bool isOk;
|
||||
} DropEntry;
|
||||
|
||||
DropEntry* entries = (DropEntry*)AllocMem(sizeof(DropEntry) * cnt, HEAP_ZERO_MEMORY);
|
||||
if (!entries) {
|
||||
DragFinish(hDrop);
|
||||
return 0;
|
||||
}
|
||||
|
||||
{
|
||||
HPATHL hScratch = Path_Allocate(NULL);
|
||||
wchar_t* const buf = Path_WriteAccessBuf(hScratch, STRINGW_MAX_URL_LENGTH);
|
||||
for (UINT i = 0; i < cnt; ++i) {
|
||||
DragQueryFileW(hDrop, i, buf, (UINT)Path_GetBufCount(hScratch));
|
||||
Path_Sanitize(hScratch);
|
||||
entries[i].orig = Path_Copy(hScratch);
|
||||
// Single attribute lookup covers both dir and file existence checks.
|
||||
DWORD const attrs = Path_GetFileAttributes(entries[i].orig);
|
||||
entries[i].isOk = (attrs != INVALID_FILE_ATTRIBUTES);
|
||||
entries[i].isDir = entries[i].isOk && (attrs & FILE_ATTRIBUTE_DIRECTORY);
|
||||
if (entries[i].isOk && !entries[i].isDir && _IsEphemeralPath(entries[i].orig)) {
|
||||
HPATHL snap = _BuildSnapshotPath(entries[i].orig, i);
|
||||
if (snap && CopyFileW(Path_Get(entries[i].orig), Path_Get(snap), FALSE)) {
|
||||
entries[i].eff = snap;
|
||||
} else {
|
||||
if (snap) {
|
||||
Path_Release(snap);
|
||||
}
|
||||
entries[i].eff = Path_Copy(entries[i].orig);
|
||||
}
|
||||
} else {
|
||||
entries[i].eff = Path_Copy(entries[i].orig);
|
||||
}
|
||||
}
|
||||
Path_Release(hScratch);
|
||||
}
|
||||
|
||||
DragFinish(hDrop); // safe — sources have been snapshotted (or were already stable)
|
||||
|
||||
// Phase 2: dispatch. Pause the watcher around the dispatch so an external
|
||||
// mtime change to Paths.CurrentFile mid-load can't post WM_FILECHANGEDNOTIFY
|
||||
// into the queue and stack a modal on top of FileSave's prompt.
|
||||
InstallFileWatching(false);
|
||||
|
||||
int const offset = Settings2.LaunchInstanceWndPosOffset;
|
||||
bool const fullVis = Settings2.LaunchInstanceFullVisible;
|
||||
int const cap = Settings2.MaxFileDropInstances;
|
||||
int spawned = 0;
|
||||
bool warned = false;
|
||||
|
||||
for (UINT i = 0; i < cnt; ++i) {
|
||||
DropEntry* const e = &entries[i];
|
||||
if (!e->isOk) {
|
||||
InfoBoxLng(MB_ICONWARNING, NULL, IDS_MUI_DROP_NO_FILE);
|
||||
} else if (e->isDir) {
|
||||
_OnDropOneFile(hwnd, e->eff, NULL);
|
||||
} else {
|
||||
// Honour SingleFileInstance: if the *original* path is already
|
||||
// open in another NP3 window, focus that window instead of spawning.
|
||||
HWND hwndSibling = NULL;
|
||||
if (Flags.bSingleFileInstance &&
|
||||
FindOtherInstance(&hwndSibling, e->orig) &&
|
||||
hwndSibling && (hwndSibling != hwnd)) {
|
||||
SetForegroundWindow(hwndSibling);
|
||||
// Snapshot is unused — register so the sweep at end of dispatch
|
||||
// (or the exit drain) cleans it up.
|
||||
_RegisterDropSnapshot(e->eff);
|
||||
} else {
|
||||
bool const sameFile = (Path_StrgComparePath(e->eff, Paths.CurrentFile,
|
||||
Paths.ModuleDirectory, true) == 0);
|
||||
bool const spawnThis = bForceNew || (i > 0);
|
||||
if (spawnThis) {
|
||||
if (spawned >= cap) {
|
||||
if (!warned) {
|
||||
InfoBoxLng(MB_ICONWARNING, Constants.SuppressKey.InfoDropCap,
|
||||
IDS_MUI_DROP_CAP_EXCEEDED, cap);
|
||||
warned = true;
|
||||
}
|
||||
// Over cap — snapshot orphan, register for sweep/drain.
|
||||
_RegisterDropSnapshot(e->eff);
|
||||
} else {
|
||||
WININFO wi = GetMyWindowPlacement(hwnd, NULL,
|
||||
offset * (spawned + 1), fullVis);
|
||||
// NB: don't register — the spawned child registers
|
||||
// its CLI-arg snapshot at startup and owns cleanup.
|
||||
DialogNewWindow(hwnd, sameFile, e->eff, &wi);
|
||||
spawned++;
|
||||
}
|
||||
} else {
|
||||
// In-process load — register before _OnDropOneFile so the
|
||||
// sweep below leaves it alone (it becomes Paths.CurrentFile).
|
||||
_RegisterDropSnapshot(e->eff);
|
||||
_OnDropOneFile(hwnd, e->eff, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
Path_Release(e->eff);
|
||||
Path_Release(e->orig);
|
||||
}
|
||||
FreeMem(entries);
|
||||
|
||||
// Drop any registered snapshot that is no longer the current document,
|
||||
// then re-arm the watcher on whatever is current now.
|
||||
_CleanupDropSnapshots(false);
|
||||
InstallFileWatching(true);
|
||||
|
||||
UpdateToolbar_Now(hwnd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@ -486,6 +486,7 @@ typedef struct CONSTANTS_T {
|
||||
// Add a field here when introducing a new suppressible dialog; never inline a literal at the call site.
|
||||
struct {
|
||||
const WCHAR* const AllowClearUndoHistory;
|
||||
const WCHAR* const InfoDropCap;
|
||||
const WCHAR* const InfoInstanceExist;
|
||||
const WCHAR* const MsgConv1;
|
||||
const WCHAR* const MsgConv2;
|
||||
@ -818,6 +819,7 @@ typedef struct SETTINGS2_T {
|
||||
int SciFontQuality;
|
||||
int LaunchInstanceWndPosOffset;
|
||||
bool LaunchInstanceFullVisible;
|
||||
int MaxFileDropInstances; // -1 = unlimited; 1..100 supported (read-only)
|
||||
int UpdateDelayMarkAllOccurrences;
|
||||
bool DenyVirtualSpaceAccess;
|
||||
bool UseOldStyleBraceMatching;
|
||||
|
||||
87
todo/TODO.md
87
todo/TODO.md
@ -36,6 +36,16 @@
|
||||
|
||||
- [ ] **(Q3) Scrollbar Marks** - Highlights in scrollbar (search matches, bookmarks)
|
||||
|
||||
- [ ] **(Q3) Plugin System** - Customizable / loadable plugin support
|
||||
- Issue: [#5071](https://github.com/rizonesoft/Notepad3/issues/5071)
|
||||
- Architectural: extension API surface, lifetime, sandboxing
|
||||
|
||||
- [ ] **(Q3) Code Compress/Pretty** - Minify/beautify code
|
||||
- Issue: [#5515](https://github.com/rizonesoft/Notepad3/issues/5515) - JSON format, compress, escape/unescape
|
||||
- Issue: [#1790](https://github.com/rizonesoft/Notepad3/issues/1790) - Call external formatter per scheme
|
||||
- Issue: [#2839](https://github.com/rizonesoft/Notepad3/issues/2839) - Tidy HTML/XML
|
||||
- (Maybe) Run Scripts: [#4045](https://github.com/rizonesoft/Notepad3/issues/4045) - Execute Python/Perl
|
||||
- Issue: [#4126](https://github.com/rizonesoft/Notepad3/issues/4126) - Custom Execute Document command
|
||||
|
||||
## High Priority
|
||||
|
||||
@ -101,6 +111,29 @@
|
||||
- [x] **(Q1) BUG: Black line in Language menu** - ✅ FIXED
|
||||
- Issue: [#5361](https://github.com/rizonesoft/Notepad3/issues/5361)
|
||||
- Fix: Removed `WM_UAHNCPAINTMENUPOPUP` from message interception - was using wrong window handle
|
||||
- [x] **(Q1) BUG: Typing causes cursor to flash, disappear and reappear** - Regression
|
||||
- Issue: [#4942](https://github.com/rizonesoft/Notepad3/issues/4942)
|
||||
- [x] **(Q1) BUG: Holding Ctrl+S inserts DC3 characters** - Save shortcut leaks into buffer when repeated
|
||||
- Issue: [#5178](https://github.com/rizonesoft/Notepad3/issues/5178)
|
||||
- [x] **(Q1) BUG: Redo button stays active after redo stack exhausted**
|
||||
- Issue: [#5149](https://github.com/rizonesoft/Notepad3/issues/5149)
|
||||
- [x] **(Q1) BUG: Ctrl+Shift+D redo deletes penultimate line and merges with next**
|
||||
- Issue: [#5150](https://github.com/rizonesoft/Notepad3/issues/5150)
|
||||
- [x] **(Q1) BUG: "Only One Instance per File" doesn't always work**
|
||||
- Issue: [#5122](https://github.com/rizonesoft/Notepad3/issues/5122),
|
||||
- Issue: [#4636](https://github.com/rizonesoft/Notepad3/issues/4636)
|
||||
- [x] **(Q1) BUG: New Empty Window prompts to save when nothing to save**
|
||||
- Issue: [#5164](https://github.com/rizonesoft/Notepad3/issues/5164)
|
||||
- [x] **(Q1) BUG: New Empty Window button broken when AutoLoadMRUFile=true**
|
||||
- Issue: [#5174](https://github.com/rizonesoft/Notepad3/issues/5174)
|
||||
- [x] **(Q1) BUG: Close-with-unsaved system dialog disappears on second instance close**
|
||||
- Issue: [#4945](https://github.com/rizonesoft/Notepad3/issues/4945)
|
||||
- [x] **(Q1) BUG: Screen needs manual refresh after opening file with /g switch**
|
||||
- Issue: [#5103](https://github.com/rizonesoft/Notepad3/issues/5103)
|
||||
- [x] **(Q2) BUG: Filename starting with "- " opens blank file from CLI**
|
||||
- Issue: [#5160](https://github.com/rizonesoft/Notepad3/issues/5160)
|
||||
- [x] **(Q2) BUG: Selection (Sel) and Occurrences (Occ) status counts incorrect**
|
||||
- Issue: [#5176](https://github.com/rizonesoft/Notepad3/issues/5176)
|
||||
|
||||
## Medium Priority
|
||||
|
||||
@ -110,14 +143,38 @@
|
||||
- [ ] (Q2) Installer testing on various Windows versions
|
||||
- [x] **AVX2 Build** - ✅ Added x64_AVX2 to CI matrix
|
||||
- Issue: [#4240](https://github.com/rizonesoft/Notepad3/issues/4240)
|
||||
- [ ] (Q2) Language file updates
|
||||
- [x] (Q2) Language file updates
|
||||
- [ ] **(Q1) Move Beta Page to rizonesoft.com** - Host beta downloads on main site
|
||||
- Issue: [#1129](https://github.com/rizonesoft/Notepad3/issues/1129)
|
||||
- [ ] **(Q2) Improve Temp File Handling** - Better UX for files from archives
|
||||
- [x] **(Q2) Improve Temp File Handling** - Better UX for files from archives - ✅ FIXED
|
||||
- Issue: [#5343](https://github.com/rizonesoft/Notepad3/issues/5343)
|
||||
- [x] **(Q1) Force Save Option** - ✅ FIXED
|
||||
- Issue: [#5444](https://github.com/rizonesoft/Notepad3/issues/5444)
|
||||
- Fix: Added `Settings.FixTrailingBlanks` check to early return in `FileSave()`
|
||||
- [ ] **(Q2) BUG: Korean IME composition box obscures the line of text being edited**
|
||||
- Issue: [#2982](https://github.com/rizonesoft/Notepad3/issues/2982)
|
||||
- [x] **(Q2) BUG: Mixed tiny menu / huge dialog icons across screens with different DPI**
|
||||
- Issue: [#3150](https://github.com/rizonesoft/Notepad3/issues/3150)
|
||||
- [ ] **(Q2) BUG: AutoCompleteWordCharSet setting has no effect**
|
||||
- Issue: [#4029](https://github.com/rizonesoft/Notepad3/issues/4029)
|
||||
- [ ] **(Q2) BUG: Column Wrap doesn't work on Chinese / CJK text**
|
||||
- Issue: [#4940](https://github.com/rizonesoft/Notepad3/issues/4940)
|
||||
- [x] **(Q2) BUG: Scrollbar can't reach bottom when processing very large files**
|
||||
- Issue: [#5092](https://github.com/rizonesoft/Notepad3/issues/5092)
|
||||
- [ ] **(Q2) BUG: Accented vowels typed lowercase with Caps Lock on**
|
||||
- Issue: [#5097](https://github.com/rizonesoft/Notepad3/issues/5097)
|
||||
- [x] **(Q2) BUG: Ctrl+Drag&Drop disabled while multi-cursor active**
|
||||
- Issue: [#5153](https://github.com/rizonesoft/Notepad3/issues/5153)
|
||||
- [x] **(Q2) BUG: Taskbar window order changes after minimize**
|
||||
- Issue: [#5159](https://github.com/rizonesoft/Notepad3/issues/5159)
|
||||
- [x] **(Q2) BUG: Menu Bar accelerator keys unreliable in non-English locales**
|
||||
- Issue: [#5169](https://github.com/rizonesoft/Notepad3/issues/5169)
|
||||
- [x] **(Q2) BUG: Taskbar icon refuses to combine/group**
|
||||
- Issue: [#5175](https://github.com/rizonesoft/Notepad3/issues/5175)
|
||||
- [x] **(Q2) BUG: Regex `{min,max}` quantifier not supported by current grammar**
|
||||
- Issue: [#5180](https://github.com/rizonesoft/Notepad3/issues/5180)
|
||||
- [x] **(Q2) BUG: Color dialog RGB fields accept only 2 of 3 digits when typed**
|
||||
- Issue: [#5185](https://github.com/rizonesoft/Notepad3/issues/5185)
|
||||
|
||||
## GitHub Actions
|
||||
|
||||
@ -155,6 +212,8 @@
|
||||
- [ ] Swift, Zig, Scala, F#, WASM, Vim, OCaml, Smali, GraphViz, Rebol
|
||||
- (Maybe) CSS in `<style>` tags: [#2061](https://github.com/rizonesoft/Notepad3/issues/2061) - Embedded language complexity
|
||||
- [ ] BUG: CSS keywords: [#4214](https://github.com/rizonesoft/Notepad3/issues/4214) - Update CSS property list
|
||||
- [ ] BUG: CSS syntax highlighting incorrect: [#3572](https://github.com/rizonesoft/Notepad3/issues/3572)
|
||||
- [ ] HTML: [#3470](https://github.com/rizonesoft/Notepad3/issues/3470) - Expand HTML lexer coverage / "full HTML support"
|
||||
- [ ] **(Q3) BATCH Code Folding** - Fold `if`/`for` blocks with parentheses
|
||||
- Issue: [#4484](https://github.com/rizonesoft/Notepad3/issues/4484)
|
||||
- BUG: [#4959](https://github.com/rizonesoft/Notepad3/issues/4959) - Complex for loops highlighting
|
||||
@ -196,19 +255,17 @@
|
||||
- Issue: [#5403](https://github.com/rizonesoft/Notepad3/issues/5403)
|
||||
- [ ] **(Q2) LaTeX Input Method** - LaTeX character insertion (`\alpha` → α)
|
||||
- [x] **(Q2) Base64 Encode/Decode** - ✅ IMPLEMENTED via `IDM_EDIT_BASE64ENCODE/DECODE`
|
||||
- [ ] **(Q1) Insert Unicode Control Characters** - LRM, RLM, ZWJ, ZWNJ, etc.
|
||||
- [x] **(Q1) Insert Unicode Control Characters** - LRM, RLM, ZWJ, ZWNJ, etc.
|
||||
- [x] **(Q2) Number Base Conversion** - Binary/Decimal/Octal/Hex - ✅ IMPLEMENTED
|
||||
- Issue: [#5059](https://github.com/rizonesoft/Notepad3/issues/5059) - TinyExpr output in hex/binary - ⚠ Validation ❗
|
||||
- [ ] **(Q2) Character Map Conversions** - Fullwidth↔Halfwidth, CJK transforms (`LCMapStringEx`)
|
||||
- [ ] **(Q3) Code Compress/Pretty** - Minify/beautify code
|
||||
- Issue: [#5515](https://github.com/rizonesoft/Notepad3/issues/5515) - JSON format, compress, escape/unescape
|
||||
- Issue: [#1790](https://github.com/rizonesoft/Notepad3/issues/1790) - Call external formatter per scheme
|
||||
- Issue: [#2839](https://github.com/rizonesoft/Notepad3/issues/2839) - Tidy HTML/XML
|
||||
- (Maybe) Run Scripts: [#4045](https://github.com/rizonesoft/Notepad3/issues/4045) - Execute Python/Perl
|
||||
- Issue: [#4126](https://github.com/rizonesoft/Notepad3/issues/4126) - Custom Execute Document command
|
||||
- [x] **(Q1) Insert GUID** - ✅ IMPLEMENTED via `IDM_EDIT_INSERT_GUID` (Ctrl+Shift+.)
|
||||
- [ ] **(Q1) Insert Shebang** - Insert interpreter line
|
||||
- [x] **(Q1) Insert Timestamps** - ✅ IMPLEMENTED via `IDM_EDIT_INSERT_SHORTDATE/LONGDATE` (Ctrl+F5, Shift+F5) and `CMD_INSERT_TIMESTAMP`
|
||||
- [ ] **(Q2) Time/Date Expressions in TinyExpr** - Support `time()` / date arithmetic in expression evaluator
|
||||
- Issue: [#4760](https://github.com/rizonesoft/Notepad3/issues/4760)
|
||||
- [ ] **(Q1) Clarify "Remove Duplicate Lines" naming** - Two submenu entries have different meanings, confusing UX
|
||||
- Issue: [#5154](https://github.com/rizonesoft/Notepad3/issues/5154)
|
||||
|
||||
### Navigation
|
||||
- [ ] **(Q3) Navigate Backward/Forward** - VS Code-like history navigation
|
||||
@ -239,6 +296,18 @@
|
||||
- Issue: [#4627](https://github.com/rizonesoft/Notepad3/issues/4627)
|
||||
- [x] **(Q2) Find Dialog Position Persistence** - ✅ IMPLEMENTED via `FindReplaceDlgPosX/Y` saved to INI
|
||||
- Issue: [#3905](https://github.com/rizonesoft/Notepad3/issues/3905) - ✅ Resolved
|
||||
- [ ] **(Q2) Configurable Font Priority/Fallback List** - User-editable preferred Code/Text font chain
|
||||
- Issue: [#4611](https://github.com/rizonesoft/Notepad3/issues/4611)
|
||||
- [ ] **(Q2) Improve Selection & Convert Panel layout**
|
||||
- Issue: [#5074](https://github.com/rizonesoft/Notepad3/issues/5074)
|
||||
- [ ] **(Q2) Separate Recent Files Menu** - Split MRU out of File menu
|
||||
- Issue: [#5177](https://github.com/rizonesoft/Notepad3/issues/5177)
|
||||
- [ ] **(Q3) Discussion: Gray out menu items when no selection**
|
||||
- Issue: [#4938](https://github.com/rizonesoft/Notepad3/issues/4938)
|
||||
- [ ] **(Q3) Keep current line visible after Word-Wrap toggle**
|
||||
- Issue: [#4944](https://github.com/rizonesoft/Notepad3/issues/4944)
|
||||
- [ ] **(Q2) MiniPath: "Minimize on Close" option**
|
||||
- Issue: [#4946](https://github.com/rizonesoft/Notepad3/issues/4946)
|
||||
|
||||
### Copy/Clipboard
|
||||
- [ ] **(Q2) Copy as RTF** - Rich text copy with syntax highlighting
|
||||
|
||||
Loading…
Reference in New Issue
Block a user