diff --git a/language/common_res.h b/language/common_res.h index 9ce0f915d..c358b4285 100644 --- a/language/common_res.h +++ b/language/common_res.h @@ -750,6 +750,7 @@ #define IDM_EDIT_CUT_MARKED 40391 #define IDM_EDIT_COPY_MARKED 40392 #define IDM_EDIT_DELETE_MARKED 40393 +#define IDM_EDIT_COPYRTF 40394 #define IDM_VIEW_SCHEME 41001 #define IDM_VIEW_USE2NDDEFAULT 41002 diff --git a/language/np3_af_za/menu_af_za.rc b/language/np3_af_za/menu_af_za.rc index fce887823..32e86d06d 100644 --- a/language/np3_af_za/menu_af_za.rc +++ b/language/np3_af_za/menu_af_za.rc @@ -163,6 +163,7 @@ BEGIN MENUITEM "Ko&pieer\tCtrl+C", IDM_EDIT_COPY MENUITEM "Kop&ieer Alles\tAlt+C", IDM_EDIT_COPYALL MENUITEM "Kopieer &Voeg\tCtrl+E", IDM_EDIT_COPYADD + MENUITEM "Copy as &RTF", IDM_EDIT_COPYRTF MENUITEM "&Plak\tCtrl+V", IDM_EDIT_PASTE MENUITEM "&Uitruil\tCtrl+K", IDM_EDIT_SWAP MENUITEM "Kies &Alles\tCtrl+A", IDM_EDIT_SELECTALL diff --git a/language/np3_be_by/menu_be_by.rc b/language/np3_be_by/menu_be_by.rc index 2b642a54d..74d075d54 100644 --- a/language/np3_be_by/menu_be_by.rc +++ b/language/np3_be_by/menu_be_by.rc @@ -163,6 +163,7 @@ BEGIN MENUITEM "&Капіяваць\tCtrl+C", IDM_EDIT_COPY MENUITEM "Капі&яваць усё\tAlt+C", IDM_EDIT_COPYALL MENUITEM "Дадаць у канец буфера\tCtrl+E", IDM_EDIT_COPYADD + MENUITEM "Copy as &RTF", IDM_EDIT_COPYRTF MENUITEM "Уст&авіць\tCtrl+V", IDM_EDIT_PASTE MENUITEM "Памяняць\tCtrl+K", IDM_EDIT_SWAP MENUITEM "Вылучыць у&сё\tCtrl+A", IDM_EDIT_SELECTALL diff --git a/language/np3_de_de/menu_de_de.rc b/language/np3_de_de/menu_de_de.rc index b5ef3edf6..f449537d2 100644 --- a/language/np3_de_de/menu_de_de.rc +++ b/language/np3_de_de/menu_de_de.rc @@ -163,6 +163,7 @@ BEGIN MENUITEM "&Kopieren\tCtrl+C", IDM_EDIT_COPY MENUITEM "&Alles kopieren\tAlt+C", IDM_EDIT_COPYALL MENUITEM "Kopiere ans &Ende\tCtrl+E", IDM_EDIT_COPYADD + MENUITEM "Copy as &RTF", IDM_EDIT_COPYRTF MENUITEM "&Einfügen\tCtrl+V", IDM_EDIT_PASTE MENUITEM "&Auswahl <-> &Zwischenablage\tCtrl+K", IDM_EDIT_SWAP MENUITEM "&Alles auswählen\tCtrl+A", IDM_EDIT_SELECTALL diff --git a/language/np3_el_gr/menu_el_gr.rc b/language/np3_el_gr/menu_el_gr.rc index f11abc6ef..03904c63d 100644 --- a/language/np3_el_gr/menu_el_gr.rc +++ b/language/np3_el_gr/menu_el_gr.rc @@ -163,6 +163,7 @@ BEGIN MENUITEM "Αντι&γραφή\tCtrl+C", IDM_EDIT_COPY MENUITEM "Α&ντιγραφή όλων\tAlt+C", IDM_EDIT_COPYALL MENUITEM "Προσ&θήκη αντιγραφής\tCtrl+E", IDM_EDIT_COPYADD + MENUITEM "Copy as &RTF", IDM_EDIT_COPYRTF MENUITEM "Επ&ικόλληση\tCtrl+V", IDM_EDIT_PASTE MENUITEM "Αν&τιμετάθεση\tCtrl+K", IDM_EDIT_SWAP MENUITEM "Επιλ&ογή όλων\tCtrl+A", IDM_EDIT_SELECTALL diff --git a/language/np3_en_gb/menu_en_gb.rc b/language/np3_en_gb/menu_en_gb.rc index a23a8c91c..0ad2176e0 100644 --- a/language/np3_en_gb/menu_en_gb.rc +++ b/language/np3_en_gb/menu_en_gb.rc @@ -163,6 +163,7 @@ BEGIN MENUITEM "&Copy\tCtrl+C", IDM_EDIT_COPY MENUITEM "Copy &All\tAlt+C", IDM_EDIT_COPYALL MENUITEM "Cop&y Add\tCtrl+E", IDM_EDIT_COPYADD + MENUITEM "Copy as &RTF", IDM_EDIT_COPYRTF MENUITEM "&Paste\tCtrl+V", IDM_EDIT_PASTE MENUITEM "S&wap\tCtrl+K", IDM_EDIT_SWAP MENUITEM "&Select All\tCtrl+A", IDM_EDIT_SELECTALL diff --git a/language/np3_en_us/menu_en_us.rc b/language/np3_en_us/menu_en_us.rc index 4e34747ea..b04a293c2 100644 --- a/language/np3_en_us/menu_en_us.rc +++ b/language/np3_en_us/menu_en_us.rc @@ -163,6 +163,7 @@ BEGIN MENUITEM "&Copy\tCtrl+C", IDM_EDIT_COPY MENUITEM "Copy &All\tAlt+C", IDM_EDIT_COPYALL MENUITEM "Cop&y Add\tCtrl+E", IDM_EDIT_COPYADD + MENUITEM "Copy as &RTF", IDM_EDIT_COPYRTF MENUITEM "&Paste\tCtrl+V", IDM_EDIT_PASTE MENUITEM "S&wap\tCtrl+K", IDM_EDIT_SWAP MENUITEM "&Select All\tCtrl+A", IDM_EDIT_SELECTALL diff --git a/language/np3_es_es/menu_es_es.rc b/language/np3_es_es/menu_es_es.rc index b190dc54d..b527d7502 100644 --- a/language/np3_es_es/menu_es_es.rc +++ b/language/np3_es_es/menu_es_es.rc @@ -163,6 +163,7 @@ BEGIN MENUITEM "&Copiar\tCtrl+C", IDM_EDIT_COPY MENUITEM "C&opiar todo\tAlt+C", IDM_EDIT_COPYALL MENUITEM "Copiar a&ñadiendo\tCtrl+E", IDM_EDIT_COPYADD + MENUITEM "Copy as &RTF", IDM_EDIT_COPYRTF MENUITEM "&Pegar\tCtrl+V", IDM_EDIT_PASTE MENUITEM "Perm&utar\tCtrl+K", IDM_EDIT_SWAP MENUITEM "&Seleccionar todo\tCtrl+A", IDM_EDIT_SELECTALL diff --git a/language/np3_fi_fi/menu_fi_fi.rc b/language/np3_fi_fi/menu_fi_fi.rc index ddcafdf3b..92dac2d11 100644 --- a/language/np3_fi_fi/menu_fi_fi.rc +++ b/language/np3_fi_fi/menu_fi_fi.rc @@ -163,6 +163,7 @@ BEGIN MENUITEM "&Kopioi\tCtrl+C", IDM_EDIT_COPY MENUITEM "Kopioi k&aikki\tAlt+C", IDM_EDIT_COPYALL MENUITEM "Kop&ioi lisäys\tCtrl+E", IDM_EDIT_COPYADD + MENUITEM "Copy as &RTF", IDM_EDIT_COPYRTF MENUITEM "&Liitä\tCtrl+V", IDM_EDIT_PASTE MENUITEM "&Vaihda\tCtrl+K", IDM_EDIT_SWAP MENUITEM "Valit&se kaikki\tCtrl+A", IDM_EDIT_SELECTALL diff --git a/language/np3_fr_fr/menu_fr_fr.rc b/language/np3_fr_fr/menu_fr_fr.rc index 9943778e8..726196751 100644 --- a/language/np3_fr_fr/menu_fr_fr.rc +++ b/language/np3_fr_fr/menu_fr_fr.rc @@ -163,6 +163,7 @@ BEGIN MENUITEM "&Copier\tCtrl+C", IDM_EDIT_COPY MENUITEM "Copier &tout\tAlt+C", IDM_EDIT_COPYALL MENUITEM "Copier& / Ajouter\tCtrl+E", IDM_EDIT_COPYADD + MENUITEM "Copy as &RTF", IDM_EDIT_COPYRTF MENUITEM "Co&ller\tCtrl+V", IDM_EDIT_PASTE MENUITEM "P&ermuter\tCtrl+K", IDM_EDIT_SWAP MENUITEM "&Sélectionner tout\tCtrl+A", IDM_EDIT_SELECTALL diff --git a/language/np3_hi_in/menu_hi_in.rc b/language/np3_hi_in/menu_hi_in.rc index b6ae0e093..3b9a80559 100644 --- a/language/np3_hi_in/menu_hi_in.rc +++ b/language/np3_hi_in/menu_hi_in.rc @@ -163,6 +163,7 @@ BEGIN MENUITEM "कॉपी (&C)\tCtrl+C", IDM_EDIT_COPY MENUITEM "सारा कॉपी करें (&A)\tAlt+C", IDM_EDIT_COPYALL MENUITEM "कॉपी करें और जोड़ें (&Y)\tCtrl+E", IDM_EDIT_COPYADD + MENUITEM "Copy as &RTF", IDM_EDIT_COPYRTF MENUITEM "चिपकाएं (&P)\tCtrl+V", IDM_EDIT_PASTE MENUITEM "बदलें (&W)\tCtrl+K", IDM_EDIT_SWAP MENUITEM "सारा चुनें (&S)\tCtrl+A", IDM_EDIT_SELECTALL diff --git a/language/np3_hu_hu/menu_hu_hu.rc b/language/np3_hu_hu/menu_hu_hu.rc index 7f2bff135..09d838140 100644 --- a/language/np3_hu_hu/menu_hu_hu.rc +++ b/language/np3_hu_hu/menu_hu_hu.rc @@ -163,6 +163,7 @@ BEGIN MENUITEM "&Másolás\tCtrl+C", IDM_EDIT_COPY MENUITEM "Mind m&ásolása\tAlt+C", IDM_EDIT_COPYALL MENUITEM "Vág&ólaphoz ad\tCtrl+E", IDM_EDIT_COPYADD + MENUITEM "Copy as &RTF", IDM_EDIT_COPYRTF MENUITEM "&Beillesztés\tCtrl+V", IDM_EDIT_PASTE MENUITEM "&Csere\tCtrl+K", IDM_EDIT_SWAP MENUITEM "Min&d kijelölése\tCtrl+A", IDM_EDIT_SELECTALL diff --git a/language/np3_id_id/menu_id_id.rc b/language/np3_id_id/menu_id_id.rc index ea1331597..772cd3466 100644 --- a/language/np3_id_id/menu_id_id.rc +++ b/language/np3_id_id/menu_id_id.rc @@ -163,6 +163,7 @@ BEGIN MENUITEM "&Salin\tCtrl+C", IDM_EDIT_COPY MENUITEM "Sali&n Semua\tAlt+C", IDM_EDIT_COPYALL MENUITEM "Tambah ke Papan &Klip\tCtrl+E", IDM_EDIT_COPYADD + MENUITEM "Copy as &RTF", IDM_EDIT_COPYRTF MENUITEM "Te&mpel\tCtrl+V", IDM_EDIT_PASTE MENUITEM "Tuka&r\tCtrl+K", IDM_EDIT_SWAP MENUITEM "Pil&ih Semua\tCtrl+A", IDM_EDIT_SELECTALL diff --git a/language/np3_it_it/menu_it_it.rc b/language/np3_it_it/menu_it_it.rc index 4bfa13919..8645617d3 100644 --- a/language/np3_it_it/menu_it_it.rc +++ b/language/np3_it_it/menu_it_it.rc @@ -163,6 +163,7 @@ BEGIN MENUITEM "&Copia\tCtrl+C", IDM_EDIT_COPY MENUITEM "Copia &tutto\tAlt+C", IDM_EDIT_COPYALL MENUITEM "Copia aggiungi\tCtrl+E", IDM_EDIT_COPYADD + MENUITEM "Copy as &RTF", IDM_EDIT_COPYRTF MENUITEM "&Incolla\tCtrl+V", IDM_EDIT_PASTE MENUITEM "Scam&bia\tCtrl+K", IDM_EDIT_SWAP MENUITEM "&Seleziona tutto\tCtrl+A", IDM_EDIT_SELECTALL diff --git a/language/np3_ja_jp/menu_ja_jp.rc b/language/np3_ja_jp/menu_ja_jp.rc index fed01a54f..14490e599 100644 --- a/language/np3_ja_jp/menu_ja_jp.rc +++ b/language/np3_ja_jp/menu_ja_jp.rc @@ -163,6 +163,7 @@ BEGIN MENUITEM "コピー(&C)\tCtrl+C", IDM_EDIT_COPY MENUITEM "すべてコピー(&A)\tAlt+C", IDM_EDIT_COPYALL MENUITEM "クリップボード末尾に追加(&Y)\tCtrl+E", IDM_EDIT_COPYADD + MENUITEM "Copy as &RTF", IDM_EDIT_COPYRTF MENUITEM "貼り付け(&P)\tCtrl+V", IDM_EDIT_PASTE MENUITEM "クリップボードと入替(&W)\tCtrl+K", IDM_EDIT_SWAP MENUITEM "すべて選択(&S)\tCtrl+A", IDM_EDIT_SELECTALL diff --git a/language/np3_ko_kr/menu_ko_kr.rc b/language/np3_ko_kr/menu_ko_kr.rc index 02f87e3f3..f4d45b717 100644 --- a/language/np3_ko_kr/menu_ko_kr.rc +++ b/language/np3_ko_kr/menu_ko_kr.rc @@ -163,6 +163,7 @@ BEGIN MENUITEM "복사(&C)\tCtrl+C", IDM_EDIT_COPY MENUITEM "모두 복사(&L)\tAlt+C", IDM_EDIT_COPYALL MENUITEM "복사 추가(&A)\tCtrl+E", IDM_EDIT_COPYADD + MENUITEM "Copy as &RTF", IDM_EDIT_COPYRTF MENUITEM "붙여넣기(&P)\tCtrl+V", IDM_EDIT_PASTE MENUITEM "교환(&W)\tCtrl+K", IDM_EDIT_SWAP MENUITEM "모두 선택(&S)\tCtrl+A", IDM_EDIT_SELECTALL diff --git a/language/np3_nl_nl/menu_nl_nl.rc b/language/np3_nl_nl/menu_nl_nl.rc index bfad957fc..49b5564ca 100644 --- a/language/np3_nl_nl/menu_nl_nl.rc +++ b/language/np3_nl_nl/menu_nl_nl.rc @@ -163,6 +163,7 @@ BEGIN MENUITEM "&Kopiëren\tCtrl+C", IDM_EDIT_COPY MENUITEM "&Alles kopiëren\tAlt+C", IDM_EDIT_COPYALL MENUITEM "Kopie &toevoegen\tCtrl+E", IDM_EDIT_COPYADD + MENUITEM "Copy as &RTF", IDM_EDIT_COPYRTF MENUITEM "&Plakken\tCtrl+V", IDM_EDIT_PASTE MENUITEM "&Verwisselen\tCtrl+K", IDM_EDIT_SWAP MENUITEM "Alles &selecteren\tCtrl+A", IDM_EDIT_SELECTALL diff --git a/language/np3_pl_pl/menu_pl_pl.rc b/language/np3_pl_pl/menu_pl_pl.rc index 2f528e304..d740970d8 100644 --- a/language/np3_pl_pl/menu_pl_pl.rc +++ b/language/np3_pl_pl/menu_pl_pl.rc @@ -163,6 +163,7 @@ BEGIN MENUITEM "&Kopiuj\tCtrl+C", IDM_EDIT_COPY MENUITEM "Kopiuj wszystko\tAlt+C", IDM_EDIT_COPYALL MENUITEM "Dodaj do schowka\tCtrl+E", IDM_EDIT_COPYADD + MENUITEM "Copy as &RTF", IDM_EDIT_COPYRTF MENUITEM "&Wklej\tCtrl+V", IDM_EDIT_PASTE MENUITEM "Zamień\tCtrl+K", IDM_EDIT_SWAP MENUITEM "&Zaznacz wszystko\tCtrl+A", IDM_EDIT_SELECTALL diff --git a/language/np3_pt_br/menu_pt_br.rc b/language/np3_pt_br/menu_pt_br.rc index 5ce48c793..def8df49c 100644 --- a/language/np3_pt_br/menu_pt_br.rc +++ b/language/np3_pt_br/menu_pt_br.rc @@ -163,6 +163,7 @@ BEGIN MENUITEM "&Copiar\tCtrl+C", IDM_EDIT_COPY MENUITEM "Copiar &Tudo\tAlt+C", IDM_EDIT_COPYALL MENUITEM "Cop&iar Adicional\tCtrl+E", IDM_EDIT_COPYADD + MENUITEM "Copy as &RTF", IDM_EDIT_COPYRTF MENUITEM "&Colar\tCtrl+V", IDM_EDIT_PASTE MENUITEM "T&rocar\tCtrl+K", IDM_EDIT_SWAP MENUITEM "&Selecionar Tudo\tCtrl+A", IDM_EDIT_SELECTALL diff --git a/language/np3_pt_pt/menu_pt_pt.rc b/language/np3_pt_pt/menu_pt_pt.rc index 81c1bc509..92efddda1 100644 --- a/language/np3_pt_pt/menu_pt_pt.rc +++ b/language/np3_pt_pt/menu_pt_pt.rc @@ -163,6 +163,7 @@ BEGIN MENUITEM "&Copiar\tCtrl+C", IDM_EDIT_COPY MENUITEM "Copiar &tudo\tAlt+C", IDM_EDIT_COPYALL MENUITEM "Cop&iar Adicionar\tCtrl+E", IDM_EDIT_COPYADD + MENUITEM "Copy as &RTF", IDM_EDIT_COPYRTF MENUITEM "Co&lar\tCtrl+V", IDM_EDIT_PASTE MENUITEM "Trocar\tCtrl+K", IDM_EDIT_SWAP MENUITEM "&Seleccionar tudo\tCtrl+A", IDM_EDIT_SELECTALL diff --git a/language/np3_ru_ru/menu_ru_ru.rc b/language/np3_ru_ru/menu_ru_ru.rc index bc32dfc74..e0e2bb9f3 100644 --- a/language/np3_ru_ru/menu_ru_ru.rc +++ b/language/np3_ru_ru/menu_ru_ru.rc @@ -163,6 +163,7 @@ BEGIN MENUITEM "&Копировать\tCtrl+C", IDM_EDIT_COPY MENUITEM "Копи&ровать всё\tAlt+C", IDM_EDIT_COPYALL MENUITEM "Добавить в конец буфера\tCtrl+E", IDM_EDIT_COPYADD + MENUITEM "Copy as &RTF", IDM_EDIT_COPYRTF MENUITEM "Вст&авить\tCtrl+V", IDM_EDIT_PASTE MENUITEM "Поменять\tCtrl+K", IDM_EDIT_SWAP MENUITEM "Выделить в&сё\tCtrl+A", IDM_EDIT_SELECTALL diff --git a/language/np3_sk_sk/menu_sk_sk.rc b/language/np3_sk_sk/menu_sk_sk.rc index 66e89d575..73d9cefa7 100644 --- a/language/np3_sk_sk/menu_sk_sk.rc +++ b/language/np3_sk_sk/menu_sk_sk.rc @@ -163,6 +163,7 @@ BEGIN MENUITEM "&Kopírovať\tCtrl+C", IDM_EDIT_COPY MENUITEM "K&opírovať všetko\tAlt+C", IDM_EDIT_COPYALL MENUITEM "Kopírov&ať pridané\tCtrl+E", IDM_EDIT_COPYADD + MENUITEM "Copy as &RTF", IDM_EDIT_COPYRTF MENUITEM "&Vložiť\tCtrl+V", IDM_EDIT_PASTE MENUITEM "Z&ameniť\tCtrl+K", IDM_EDIT_SWAP MENUITEM "Vy&brať všetko\tCtrl+A", IDM_EDIT_SELECTALL diff --git a/language/np3_sv_se/menu_sv_se.rc b/language/np3_sv_se/menu_sv_se.rc index 1603f36da..f3ef313ca 100644 --- a/language/np3_sv_se/menu_sv_se.rc +++ b/language/np3_sv_se/menu_sv_se.rc @@ -163,6 +163,7 @@ BEGIN MENUITEM "Kopiera\tCtrl+C", IDM_EDIT_COPY MENUITEM "Kopiera allt\tAlt+C", IDM_EDIT_COPYALL MENUITEM "Lägg till i urklipp\tCtrl+E", IDM_EDIT_COPYADD + MENUITEM "Copy as &RTF", IDM_EDIT_COPYRTF MENUITEM "Klistra in\tCtrl+V", IDM_EDIT_PASTE MENUITEM "Byt\tCtrl+K", IDM_EDIT_SWAP MENUITEM "Markera allt\tCtrl+A", IDM_EDIT_SELECTALL diff --git a/language/np3_tr_tr/menu_tr_tr.rc b/language/np3_tr_tr/menu_tr_tr.rc index 8a6b95c84..b073f4220 100644 --- a/language/np3_tr_tr/menu_tr_tr.rc +++ b/language/np3_tr_tr/menu_tr_tr.rc @@ -163,6 +163,7 @@ BEGIN MENUITEM "&Kopyala\tCtrl+C", IDM_EDIT_COPY MENUITEM "Tümünü k&opyala\tAlt+C", IDM_EDIT_COPYALL MENUITEM "Tümünü y&apıştır\tCtrl+E", IDM_EDIT_COPYADD + MENUITEM "Copy as &RTF", IDM_EDIT_COPYRTF MENUITEM "&Yapıştır\tCtrl+V", IDM_EDIT_PASTE MENUITEM "&Değiştir\tCtrl+K", IDM_EDIT_SWAP MENUITEM "Tü&münü seç\tCtrl+A", IDM_EDIT_SELECTALL diff --git a/language/np3_vi_vn/menu_vi_vn.rc b/language/np3_vi_vn/menu_vi_vn.rc index 6137968e1..f715f4f25 100644 --- a/language/np3_vi_vn/menu_vi_vn.rc +++ b/language/np3_vi_vn/menu_vi_vn.rc @@ -163,6 +163,7 @@ BEGIN MENUITEM "Sao chép(&C)\tCtrl+C", IDM_EDIT_COPY MENUITEM "Sao chép tất cả(&L)\tAlt+C", IDM_EDIT_COPYALL MENUITEM "Sao chép thêm(&A)\tCtrl+E", IDM_EDIT_COPYADD + MENUITEM "Copy as &RTF", IDM_EDIT_COPYRTF MENUITEM "Dán(&P)\tCtrl+V", IDM_EDIT_PASTE MENUITEM "Hoán đổi(&W)\tCtrl+K", IDM_EDIT_SWAP MENUITEM "Chọn tất cả(&S)\tCtrl+A", IDM_EDIT_SELECTALL diff --git a/language/np3_zh_cn/menu_zh_cn.rc b/language/np3_zh_cn/menu_zh_cn.rc index 23dd06166..acc1c7ce7 100644 --- a/language/np3_zh_cn/menu_zh_cn.rc +++ b/language/np3_zh_cn/menu_zh_cn.rc @@ -163,6 +163,7 @@ BEGIN MENUITEM "复制(&C)\tCtrl+C", IDM_EDIT_COPY MENUITEM "全部复制(&L)\tAlt+C", IDM_EDIT_COPYALL MENUITEM "增量复制(&Y)\tCtrl+E", IDM_EDIT_COPYADD + MENUITEM "Copy as &RTF", IDM_EDIT_COPYRTF MENUITEM "粘贴(&P)\tCtrl+V", IDM_EDIT_PASTE MENUITEM "与剪贴板交换(&W)\tCtrl+K", IDM_EDIT_SWAP MENUITEM "全选(&A)\tCtrl+A", IDM_EDIT_SELECTALL diff --git a/language/np3_zh_tw/menu_zh_tw.rc b/language/np3_zh_tw/menu_zh_tw.rc index dca26ec5c..bcac21d00 100644 --- a/language/np3_zh_tw/menu_zh_tw.rc +++ b/language/np3_zh_tw/menu_zh_tw.rc @@ -163,6 +163,7 @@ BEGIN MENUITEM "複製(&C)\tCtrl+C", IDM_EDIT_COPY MENUITEM "全部複製(&L)\tAlt+C", IDM_EDIT_COPYALL MENUITEM "增量複製(&Y)\tCtrl+E", IDM_EDIT_COPYADD + MENUITEM "Copy as &RTF", IDM_EDIT_COPYRTF MENUITEM "貼上(&P)\tCtrl+V", IDM_EDIT_PASTE MENUITEM "與剪貼簿置換(&W)\tCtrl+K", IDM_EDIT_SWAP MENUITEM "全選(&A)\tCtrl+A", IDM_EDIT_SELECTALL diff --git a/other_sln/Notepad3DLL.vcxproj b/other_sln/Notepad3DLL.vcxproj index 31a1c8476..af72a48bc 100644 --- a/other_sln/Notepad3DLL.vcxproj +++ b/other_sln/Notepad3DLL.vcxproj @@ -427,6 +427,7 @@ + @@ -524,6 +525,7 @@ + diff --git a/other_sln/Notepad3DLL.vcxproj.filters b/other_sln/Notepad3DLL.vcxproj.filters index f73495714..dd3e6495a 100644 --- a/other_sln/Notepad3DLL.vcxproj.filters +++ b/other_sln/Notepad3DLL.vcxproj.filters @@ -51,6 +51,9 @@ Source Files + + Source Files + Source Files @@ -407,6 +410,9 @@ Header Files + + Header Files + Header Files diff --git a/src/EditRTF.cpp b/src/EditRTF.cpp new file mode 100644 index 000000000..660596a50 --- /dev/null +++ b/src/EditRTF.cpp @@ -0,0 +1,628 @@ +// encoding: UTF-8 +/****************************************************************************** +* * +* * +* Notepad3 * +* * +* EditRTF.cpp * +* Copy current selection (or whole document) to clipboard as RTF, * +* preserving Scintilla syntax highlighting. * +* * +* The RTF builder is a port of Notepad4's SaveToStreamRTF * +* (notepad4/src/Bridge.cpp), which itself derives from SciTE's * +* ExportRTF.cxx. Adapted to Notepad3 globals (Globals.fvCurFile, * +* g_Encodings[CPI_ANSI_DEFAULT]) and to upstream Scintilla's interleaved * +* SCI_GETSTYLEDTEXTFULL layout. UTF-8 decoding is done inline because * +* Notepad3's vendored Scintilla does not expose SCI_GETCHARACTERANDWIDTH. * +* * +* The clipboard transaction sets both CF_RTF and CF_UNICODETEXT so styled * +* consumers (Word/WordPad/OneNote/Outlook) and plain consumers (Notepad, * +* chat apps, terminals) both work. * +* * +* Per-line \cbpat paragraph background is emitted only when every visible * +* char on the line shares the same eolFilled background (markdown header * +* lines, uniformly-styled code-block lines). Mixed-style lines fall back * +* to char-only \highlight so a trailing inline-code span never paints * +* the whole line. * +* * +* (c) Rizonesoft 2008-2026 * +* https://rizonesoft.com * +* * +* * +*******************************************************************************/ +#include +#if !defined(WINVER) +#define WINVER _WIN32_WINNT_WIN10 +#endif +#if !defined(_WIN32_WINNT) +#define _WIN32_WINNT _WIN32_WINNT_WIN10 +#endif +#if !defined(NTDDI_VERSION) +#define NTDDI_VERSION NTDDI_WIN10_RS5 +#endif + +#define WIN32_LEAN_AND_MEAN +#define NOMINMAX +#include + +#include +#include +#include +#include +#include +#include + +extern "C" { +#include "Scintilla.h" +#include "SciCall.h" +#include "TypeDefs.h" +#include "Notepad3.h" +#include "Encoding.h" +} +#include "EditRTF.h" + +namespace { + +// ---- RTF control words (from Notepad4 Bridge.cpp) --------------------------- + +#define RTF_HEADEROPEN "{\\rtf1\\ansi\\ansicpg%u\\deff0\\uc1\\deftab%d" +#define RTF_FONTDEFOPEN "{\\fonttbl" +#define RTF_FONTDEFCLOSE "}" +#define RTF_COLORDEFOPEN "{\\colortbl;" +#define RTF_COLORDEFCLOSE "}" +#define RTF_HEADERCLOSE "\n" +// Per-paragraph properties (\pard\cbpat?\sb0\sa0) are emitted in the per-line +// loop, not in the body opener, so eolFilled styles can paint the whole line. +#define RTF_BODYOPEN "" +#define RTF_BODYCLOSE "}" +#define RTF_SETFONTFACE "\\f" +#define RTF_SETFONTSIZE "\\fs" +#define RTF_SETCOLOR "\\cf" +#define RTF_SETBACKGROUND "\\highlight" +#define RTF_BOLD_ON "\\b" +#define RTF_BOLD_OFF "\\b0" +#define RTF_ITALIC_ON "\\i" +#define RTF_ITALIC_OFF "\\i0" +#define RTF_UNDERLINE_ON "\\ul" +#define RTF_UNDERLINE_OFF "\\ulnone" +#define RTF_STRIKE_ON "\\strike" +#define RTF_STRIKE_OFF "\\strike0" +#define RTF_PAR "\\par\n" +#define RTF_TAB "\\tab " + +// font face, size, color, bold, italic, underline, strike, highlight +constexpr int RTF_MAX_STYLEPROP = 8; +constexpr int RTF_MAX_STYLEDEF = 128; + +// ---- Style snapshot -------------------------------------------------------- + +struct StyleDefinition { + int fontSize; + COLORREF foreColor; + COLORREF backColor; + int weight; + bool italic; + bool underline; + bool strike; + bool eolFilled; + int charset; + WCHAR fontWide[LF_FACESIZE]; + char fontFace[LF_FACESIZE]; + uint16_t backIndex; +}; + +static_assert(__is_standard_layout(StyleDefinition)); + +void GetStyleDefinitionFor(int style, StyleDefinition &d) noexcept +{ + d.fontSize = SciCall_StyleGetSizeFractional(style); + d.foreColor = SciCall_StyleGetFore(style); + d.backColor = SciCall_StyleGetBack(style); + d.weight = SciCall_StyleGetWeight(style); + d.italic = SciCall_StyleGetItalic(style); + d.underline = SciCall_StyleGetUnderline(style); + d.strike = SciCall_StyleGetStrike(style); + // EOL-fill drives whole-paragraph background (\cbpat) so e.g. markdown + // header lines paint edge-to-edge in the pasted RTF, not just behind the + // visible characters. + d.eolFilled = SciCall_StyleGetEOLFilled(style); + d.charset = SciCall_StyleGetCharacterSet(style); + d.backIndex = 0; + memset(d.fontFace, 0, sizeof(d.fontFace)); + memset(d.fontWide, 0, sizeof(d.fontWide)); + SciCall_StyleGetFont(style, d.fontFace); +} + +struct DocumentStyledText { + std::unique_ptr styleList; + unsigned styleCount; + UINT cpEdit; +}; + +DocumentStyledText GetDocumentStyledText(uint8_t (&styleMap)[STYLE_MAX + 1], + const unsigned char *styles, + size_t numChars) noexcept +{ + uint32_t styleUsed[8]{}; + styleUsed[STYLE_DEFAULT >> 5] |= (1U << (STYLE_DEFAULT & 31)); + unsigned maxStyle = STYLE_DEFAULT; + + for (size_t i = 0; i < numChars; i++) { + const uint8_t s = styles[i]; + styleUsed[s >> 5] |= (1U << (s & 31)); + if (s > maxStyle) { + maxStyle = s; + } + } + ++maxStyle; + + memset(styleMap, 0, STYLE_MAX + 1); + + auto styleList = std::make_unique(maxStyle); + unsigned styleCount = 0; + + // STYLE_DEFAULT (== 32) always sits at index 0, since it is the implicit + // baseline that following style deltas are computed against. + if constexpr (STYLE_DEFAULT != 0) { + styleCount = 1; + styleUsed[STYLE_DEFAULT >> 5] &= ~(1U << (STYLE_DEFAULT & 31)); + GetStyleDefinitionFor(STYLE_DEFAULT, styleList[0]); + styleMap[STYLE_DEFAULT] = 0; + } + + for (unsigned style = 0; style < maxStyle; style++) { + if (!(styleUsed[style >> 5] & (1U << (style & 31)))) { + continue; + } + StyleDefinition &def = styleList[styleCount]; + GetStyleDefinitionFor(style, def); + unsigned idx = 0; + for (; idx < styleCount; idx++) { + if (memcmp(&def, &styleList[idx], sizeof(StyleDefinition)) == 0) { + memset(def.fontFace, 0, sizeof(def.fontFace)); + break; + } + } + styleMap[style] = static_cast(idx); + if (idx == styleCount) { + styleCount++; + } + } + + const UINT cpEdit = static_cast(SciCall_GetCodePage()); + return { std::move(styleList), styleCount, cpEdit }; +} + +// ---- RTF style-delta helpers ----------------------------------------------- + +void GetRTFNextControl(const char **style, char *control) noexcept +{ + const char *pos = *style; + *control = '\0'; + if ('\0' == *pos) { + return; + } + pos++; // implicit skip over leading '\' + while ('\0' != *pos && '\\' != *pos) { + pos++; + } + const size_t len = pos - *style; + memcpy(control, *style, len); + control[len] = '\0'; + *style = pos; +} + +void GetRTFStyleChange(std::string &delta, const char *last, const char *current) +{ + char lastControl[RTF_MAX_STYLEDEF]; + char currentControl[RTF_MAX_STYLEDEF]; + lastControl[0] = '\0'; + currentControl[0] = '\0'; + const char *lastPos = last; + const char *currentPos = current; + const size_t prevLen = delta.length(); + for (int i = 0; i < RTF_MAX_STYLEPROP; i++) { + GetRTFNextControl(&lastPos, lastControl); + GetRTFNextControl(¤tPos, currentControl); + if (strcmp(lastControl, currentControl) != 0) { + delta += currentControl; + } + } + if (prevLen != delta.length()) { + delta += ' '; + } +} + +constexpr int GetRTFFontSize(int sizeFractional) noexcept +{ + return sizeFractional / (SC_FONT_SIZE_MULTIPLIER / 2); +} + +// Decode one UTF-8 codepoint starting at p (with `available` bytes remaining +// in the source buffer). Sets `width` to bytes consumed (1..4). On a +// truncated multi-byte sequence, falls back to emitting the lead byte raw. +uint32_t DecodeUtf8(const char *p, size_t available, int &width) noexcept +{ + if (available == 0) { + width = 1; + return 0; + } + const unsigned char c0 = static_cast(p[0]); + if (c0 < 0x80 || available < 2) { + width = 1; + return c0; + } + if ((c0 & 0xE0) == 0xC0) { + width = 2; + return ((c0 & 0x1Fu) << 6) + | (static_cast(p[1]) & 0x3Fu); + } + if ((c0 & 0xF0) == 0xE0 && available >= 3) { + width = 3; + return ((c0 & 0x0Fu) << 12) + | ((static_cast(p[1]) & 0x3Fu) << 6) + | (static_cast(p[2]) & 0x3Fu); + } + if (available >= 4) { + width = 4; + return ((c0 & 0x07u) << 18) + | ((static_cast(p[1]) & 0x3Fu) << 12) + | ((static_cast(p[2]) & 0x3Fu) << 6) + | (static_cast(p[3]) & 0x3Fu); + } + // Truncated multi-byte sequence — emit the lead byte raw. + width = 1; + return c0; +} + +// ---- Main RTF builder ------------------------------------------------------ + +void SaveToStreamRTF(std::string &os, + const unsigned char *styles, + const char *textBuffer, + size_t numChars) +{ + uint8_t styleMap[STYLE_MAX + 1]; + auto sdt = GetDocumentStyledText(styleMap, styles, numChars); + StyleDefinition *styleList = sdt.styleList.get(); + const unsigned styleCount = sdt.styleCount; + const UINT cpEdit = sdt.cpEdit; + + auto stylesText = std::make_unique(styleCount); + auto fontList = std::make_unique(styleCount); + auto colorList = std::make_unique(2 * styleCount + 1); + + UINT legacyACP = cpEdit; + if (legacyACP == SC_CP_UTF8 || legacyACP == 0) { + legacyACP = g_Encodings[CPI_ANSI_DEFAULT].uCodePage; + } + + // Reader-default tab width in twips. ~120 twips per char at 10pt monospace + // (one char is roughly 8pt wide → 120 twips). Only matters when tabs are + // emitted as RTF \tab tokens (i.e. bTabsAsSpaces is false); when we expand + // tabs to spaces ourselves, the reader's \deftab is unused. + const int defTabTwips = (Globals.fvCurFile.iTabWidth > 0) + ? Globals.fvCurFile.iTabWidth * 120 + : 480; + char fmtbuf[RTF_MAX_STYLEDEF]; + unsigned fmtlen = snprintf(fmtbuf, sizeof(fmtbuf), + RTF_HEADEROPEN RTF_FONTDEFOPEN, legacyACP, defTabTwips); + os += std::string_view{fmtbuf, fmtlen}; + + // STYLE_DEFAULT is always laid out at styleList[0] by GetDocumentStyledText. + // Its backColor is the document's baseline page background; styles matching + // it get \highlight0 (no marker), styles differing get a \highlight + // pulled from the color table. + const COLORREF defaultBack = (styleCount > 0) ? styleList[0].backColor : 0; + + int fontCount = 0; + int colorCount = 0; + for (unsigned styleIndex = 0; styleIndex < styleCount; styleIndex++) { + StyleDefinition &d = styleList[styleIndex]; + + int iFont = 0; + for (; iFont < fontCount; iFont++) { + if (_stricmp(fontList[iFont], d.fontFace) == 0) { + break; + } + } + if (iFont == fontCount) { + fontList[fontCount++] = d.fontFace; + char fontFaceACP[LF_FACESIZE]{}; + MultiByteToWideChar(CP_UTF8, 0, d.fontFace, -1, d.fontWide, + static_cast(_countof(d.fontWide))); + WideCharToMultiByte(legacyACP, 0, d.fontWide, -1, fontFaceACP, + static_cast(_countof(fontFaceACP)), nullptr, nullptr); + const int charset = d.charset; + if (charset == DEFAULT_CHARSET || charset == ANSI_CHARSET) { + fmtlen = snprintf(fmtbuf, sizeof(fmtbuf),"{\\f%d\\fnil %s;}", iFont, fontFaceACP); + } else { + fmtlen = snprintf(fmtbuf, sizeof(fmtbuf),"{\\f%d\\fnil\\fcharset%d %s;}", iFont, charset, fontFaceACP); + } + os += std::string_view{fmtbuf, fmtlen}; + } + + int iFore = 0; + for (; iFore < colorCount; iFore++) { + if (colorList[iFore] == d.foreColor) { + break; + } + } + if (iFore == colorCount) { + colorList[colorCount++] = d.foreColor; + } + + // Only register a background colour if this style actually deviates + // from the page baseline; baseline-matching styles get \highlight0. + unsigned highlightIdx = 0; + if (d.backColor != defaultBack) { + int iBack = 0; + for (; iBack < colorCount; iBack++) { + if (colorList[iBack] == d.backColor) { + break; + } + } + if (iBack == colorCount) { + colorList[colorCount++] = d.backColor; + } + highlightIdx = static_cast(iBack + 1); + } + d.backIndex = static_cast(highlightIdx); + + fmtlen = snprintf(fmtbuf, sizeof(fmtbuf),RTF_SETFONTFACE "%d" RTF_SETFONTSIZE "%d" RTF_SETCOLOR "%d", + iFont, GetRTFFontSize(d.fontSize), iFore + 1); + + std::string osStyle(fmtbuf, fmtlen); + osStyle += (d.weight >= FW_SEMIBOLD ? RTF_BOLD_ON : RTF_BOLD_OFF); + osStyle += (d.italic ? RTF_ITALIC_ON : RTF_ITALIC_OFF); + osStyle += (d.underline ? RTF_UNDERLINE_ON : RTF_UNDERLINE_OFF); + osStyle += (d.strike ? RTF_STRIKE_ON : RTF_STRIKE_OFF); + fmtlen = snprintf(fmtbuf, sizeof(fmtbuf), RTF_SETBACKGROUND "%u", highlightIdx); + osStyle.append(fmtbuf, fmtlen > 0 ? static_cast(fmtlen) : 0); + stylesText[styleIndex] = std::move(osStyle); + } + + os += RTF_FONTDEFCLOSE RTF_COLORDEFOPEN; + for (int i = 0; i < colorCount; i++) { + const COLORREF color = colorList[i]; + fmtlen = snprintf(fmtbuf, sizeof(fmtbuf),"\\red%d\\green%d\\blue%d;", + static_cast(color & 0xff), + static_cast((color >> 8) & 0xff), + static_cast((color >> 16) & 0xff)); + os += std::string_view{fmtbuf, fmtlen}; + } + os += RTF_COLORDEFCLOSE RTF_HEADERCLOSE RTF_BODYOPEN; + + // Returns the colortbl index to use for \cbpat (paragraph background) on + // the line beginning at `startOffset`. Requires every visible char on the + // line to share the same eolFilled background; otherwise the line has at + // least one span that wants char-only fill and a \cbpat would over-paint. + // 0 means "no paragraph fill" (Word's page background shows through). + auto getLineCbpat = [&](size_t startOffset) noexcept -> unsigned { + if (startOffset >= numChars) { + return 0; + } + size_t end = startOffset; + while (end < numChars && textBuffer[end] != '\r' && textBuffer[end] != '\n') { + end++; + } + if (end == startOffset) { + return 0; // empty line + } + const uint8_t sFirst = styleMap[styles[startOffset]]; + if (!styleList[sFirst].eolFilled) { + return 0; + } + const COLORREF bg = styleList[sFirst].backColor; + for (size_t i = startOffset + 1; i < end; i++) { + const uint8_t s = styleMap[styles[i]]; + if (!styleList[s].eolFilled || styleList[s].backColor != bg) { + return 0; + } + } + return styleList[sFirst].backIndex; + }; + + auto emitParagraphOpen = [&](unsigned cbpatIdx) { + if (cbpatIdx > 0) { + const int n = snprintf(fmtbuf, sizeof(fmtbuf), + "\\pard\\cbpat%u\\sb0\\sa0 ", cbpatIdx); + if (n > 0) { + os.append(fmtbuf, static_cast(n)); + } + } else { + os += "\\pard\\sb0\\sa0 "; + } + }; + + emitParagraphOpen(getLineCbpat(0)); + + const char *lastStyle = ""; + unsigned styleCurrent = STYLE_MAX + 1; + unsigned column = 0; + + for (size_t offset = 0; offset < numChars; offset++) { + uint8_t style = styles[offset]; + style = styleMap[style]; + if (style != styleCurrent) { + styleCurrent = style; + const char *currentStyle = stylesText[style].c_str(); + GetRTFStyleChange(os, lastStyle, currentStyle); + lastStyle = currentStyle; + } + + const char ch = textBuffer[offset]; + std::string_view sv; + column++; + + if (ch == '\t') { + if (!Globals.fvCurFile.bTabsAsSpaces) { + sv = RTF_TAB; + } else { + const unsigned tabWidth = static_cast(Globals.fvCurFile.iTabWidth); + const unsigned padding = tabWidth - ((column - 1) % tabWidth); + column += padding; + os.append(padding, ' '); + } + } else if (ch == '\r' || ch == '\n') { + os += RTF_PAR; + column = 0; + if (ch == '\r' && (offset + 1) < numChars && textBuffer[offset + 1] == '\n') { + offset += 1; + } + // Open the next paragraph (if any) with its own cbpat state. + if (offset + 1 < numChars) { + emitParagraphOpen(getLineCbpat(offset + 1)); + } + continue; + } else if (static_cast(ch) < 0 && cpEdit == SC_CP_UTF8) { + int width = 1; + const uint32_t u32 = DecodeUtf8(textBuffer + offset, numChars - offset, width); + offset += static_cast(width - 1); + if (u32 < 0x10000) { + fmtlen = snprintf(fmtbuf, sizeof(fmtbuf),"\\u%d?", static_cast(u32)); + } else { + fmtlen = snprintf(fmtbuf, sizeof(fmtbuf),"\\u%d?\\u%d?", + static_cast(((u32 - 0x10000) >> 10) + 0xD800), + static_cast((u32 & 0x3ff) + 0xDC00)); + } + sv = std::string_view{fmtbuf, fmtlen}; + } + + if (sv.empty()) { + if (ch != '\t') { + if (ch == '{' || ch == '}' || ch == '\\') { + os += '\\'; + } + os += ch; + } + } else { + os += sv; + } + } + + os += RTF_BODYCLOSE; +} + +// ---- Clipboard helpers ----------------------------------------------------- + +// Allocate a movable global block and copy `len` bytes (no NUL-terminator +// management; caller passes the desired payload length including any NUL). +HGLOBAL AllocClipboardBlock(const void *data, size_t bytes) noexcept +{ + HGLOBAL h = ::GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, bytes); + if (!h) { + return nullptr; + } + void *p = ::GlobalLock(h); + if (!p) { + ::GlobalFree(h); + return nullptr; + } + memcpy(p, data, bytes); + ::GlobalUnlock(h); + return h; +} + +} // namespace + +// ---- Public entry point ---------------------------------------------------- + +extern "C" void EditCopyAsRTF(HWND hwnd) +{ + try { + DocPos startPos = SciCall_GetSelectionStart(); + DocPos endPos = SciCall_GetSelectionEnd(); + if (startPos == endPos) { + startPos = 0; + endPos = SciCall_GetLength(); + if (endPos == 0) { + return; + } + } + if (endPos < startPos) { + std::swap(startPos, endPos); + } + + const DocPos numCharsPos = endPos - startPos; + if (numCharsPos <= 0) { + return; + } + const size_t numChars = static_cast(numCharsPos); + + // Make sure styling is computed for the range. + SciCall_Colourise(startPos, endPos); + + // Retrieve interleaved [c0,s0,c1,s1,...,cn-1,sn-1,0,0] from upstream Scintilla. + auto interleaved = std::make_unique(2 * numChars + 2); + Sci_TextRangeFull tr{}; + tr.chrg.cpMin = startPos; + tr.chrg.cpMax = endPos; + tr.lpstrText = interleaved.get(); + SciCall_GetStyledTextFull(&tr); + + // De-interleave so the RTF builder can address chars and styles by char index. + // make_unique value-initialises, so the trailing terminator slot is + // already zero — no explicit '\0' write needed. + auto chars = std::make_unique(numChars + 1); + auto styles = std::make_unique(numChars + 1); + for (size_t i = 0; i < numChars; i++) { + chars[i] = interleaved[2 * i]; + styles[i] = static_cast(interleaved[2 * i + 1]); + } + interleaved.reset(); + + // Build RTF. (Outer try/catch in EditCopyAsRTF handles any allocation + // failure; we don't need a separate inner guard here.) + std::string rtf; + rtf.reserve(numChars * 2); + SaveToStreamRTF(rtf, styles.get(), chars.get(), numChars); + + // Build UTF-16 plain-text companion. make_unique value-initialises, + // so the trailing L'\0' is already in place. + const int wlen = ::MultiByteToWideChar(CP_UTF8, 0, chars.get(), + static_cast(numChars), nullptr, 0); + std::unique_ptr wbuf; + if (wlen > 0) { + wbuf = std::make_unique(static_cast(wlen) + 1); + ::MultiByteToWideChar(CP_UTF8, 0, chars.get(), + static_cast(numChars), wbuf.get(), wlen); + } + + // Push both formats in one clipboard transaction. + HWND owner = hwnd ? hwnd : Globals.hwndMain; + if (!::OpenClipboard(owner)) { + return; + } + + HGLOBAL hRTF = AllocClipboardBlock(rtf.c_str(), rtf.length() + 1); + HGLOBAL hTxt = nullptr; + if (wbuf) { + hTxt = AllocClipboardBlock(wbuf.get(), + (static_cast(wlen) + 1) * sizeof(WCHAR)); + } + + if (hRTF || hTxt) { + ::EmptyClipboard(); + if (hRTF) { + UINT cfRTF = ::RegisterClipboardFormatW(L"Rich Text Format"); + if (cfRTF) { + if (!::SetClipboardData(cfRTF, hRTF)) { + ::GlobalFree(hRTF); + } + } else { + ::GlobalFree(hRTF); + } + } + if (hTxt) { + if (!::SetClipboardData(CF_UNICODETEXT, hTxt)) { + ::GlobalFree(hTxt); + } + } + } + ::CloseClipboard(); + } catch (...) { + // Swallow std::bad_alloc and any other C++ exceptions so they never + // unwind into the C caller (UB on the Win32 C-ABI dispatch path). + } +} diff --git a/src/EditRTF.h b/src/EditRTF.h new file mode 100644 index 000000000..a33091c11 --- /dev/null +++ b/src/EditRTF.h @@ -0,0 +1,37 @@ +// encoding: UTF-8 +/****************************************************************************** +* * +* * +* Notepad3 * +* * +* EditRTF.h * +* Copy current selection (or whole document) to clipboard as RTF, * +* preserving Scintilla syntax highlighting. * +* Based on Notepad4's SaveToStreamRTF (in turn based on SciTE's * +* ExportRTF.cxx). * +* * +* (c) Rizonesoft 2008-2026 * +* https://rizonesoft.com * +* * +* * +*******************************************************************************/ +#pragma once +#ifndef _NP3_EDIT_RTF_H_ +#define _NP3_EDIT_RTF_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +// Copy current selection as RTF + UTF-16 plain text to clipboard. +// If selection is empty, falls back to the whole document. +// No-op on empty document. +void EditCopyAsRTF(HWND hwnd); + +#ifdef __cplusplus +} +#endif + +#endif // _NP3_EDIT_RTF_H_ diff --git a/src/Notepad3.c b/src/Notepad3.c index 8e6a56ec0..89a0b8585 100644 --- a/src/Notepad3.c +++ b/src/Notepad3.c @@ -34,6 +34,7 @@ #include "PathLib.h" #include "Edit.h" +#include "EditRTF.h" #include "Styles.h" #include "Dialogs.h" #include "crypto/crypto.h" @@ -4860,6 +4861,7 @@ LRESULT MsgInitMenu(HWND hwnd, WPARAM wParam, LPARAM lParam) EnableCmd(hmenu, IDM_EDIT_COPYALL, !te); EnableCmd(hmenu, IDM_EDIT_COPYADD, !te); + EnableCmd(hmenu, IDM_EDIT_COPYRTF, !te); EnableCmd(hmenu, IDM_EDIT_PASTE, pst && !ro); EnableCmd(hmenu, IDM_EDIT_SWAP, (!se || pst) && !ro); @@ -5822,6 +5824,15 @@ static bool _HandleEditBasicCommands(HWND hwnd, UINT umsg, WPARAM wParam, LPARAM break; + case IDM_EDIT_COPYRTF: { + if (s_flagPasteBoard) { + s_bLastCopyFromMe = true; + } + EditCopyAsRTF(Globals.hwndEdit); + } + break; + + case IDM_EDIT_PASTE: if (SciCall_CanPaste()) { if (s_flagPasteBoard) { diff --git a/src/Notepad3.vcxproj b/src/Notepad3.vcxproj index cf1875b0a..658932f7d 100644 --- a/src/Notepad3.vcxproj +++ b/src/Notepad3.vcxproj @@ -1055,6 +1055,7 @@ + @@ -1181,6 +1182,7 @@ + diff --git a/src/Notepad3.vcxproj.filters b/src/Notepad3.vcxproj.filters index 17135586f..15140399a 100644 --- a/src/Notepad3.vcxproj.filters +++ b/src/Notepad3.vcxproj.filters @@ -60,6 +60,9 @@ Source Files + + Source Files + Source Files @@ -464,6 +467,9 @@ Header Files + + Header Files + Header Files diff --git a/todo/TODO.md b/todo/TODO.md index 8f8308735..44b60be32 100644 --- a/todo/TODO.md +++ b/todo/TODO.md @@ -286,6 +286,8 @@ - [ ] **(Q3) Split View** - View two parts of same document - Issue: [#2577](https://github.com/rizonesoft/Notepad3/issues/2577) - [ ] **(Q1) Show Unicode Control Characters** - Toggle visibility + - [x] ✅ IMPLEMENTED (show non printing chars) + - [ ] Show other Unicode Control Characters ??? - [ ] **(Q2) Line Selection Modes** - VS style, Normal, Old VS - [x] **(Q1) Disable Multiple Cursors Option** - ✅ IMPLEMENTED via `IDM_SET_MULTIPLE_SELECTION` toggle - Issue: [#4033](https://github.com/rizonesoft/Notepad3/issues/4033) - ✅ Resolved @@ -298,19 +300,20 @@ - 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) + - Related: https://github.com/zufuliu/notepad4/issues/690 - [ ] **(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** +- [x] **(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** +- [x] **(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 +- [x] **(Q2) Copy as RTF** - Rich text copy with syntax highlighting - Issue: [#5052](https://github.com/rizonesoft/Notepad3/issues/5052) - **Reference**: [Notepad4](https://github.com/zufuliu/notepad4) implements via `IDM_EDIT_COPYRTF` - [ ] **(Q2) Copy/Cut/Paste Binary** - Binary data handling