1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/lunarsf-Lunar-Markdown-Editor

Присоединиться к Gitlife
Откройте для себя и примите участие в публичных проектах с открытым исходным кодом с участием более 10 миллионов разработчиков. Приватные репозитории также полностью бесплатны :)
Присоединиться бесплатно
Это зеркальный репозиторий, синхронизируется ежедневно с исходного репозитория.
Клонировать/Скачать
MarkdownEditor.cs 150 КБ
Копировать Редактировать Исходные данные Просмотреть построчно История
LunarSF Отправлено 8 лет назад 97b1d3c
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505
using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media.Imaging;
using System.IO;
using System.Windows.Input;
using WinForm = System.Windows.Forms;
using System.Windows.Media;
using System.Text.RegularExpressions;
namespace LunarSF.SHomeWorkshop.LunarMarkdownEditor
{
/// <summary>
/// 用以提供类似 Vim 的移动光标模式——Vim 的单键移动太方便了。
/// </summary>
public enum EditingMode
{
NormalMode, //普通模式
VimMode, //类似 Vim 的移动模式
}
/// <summary>
/// 创建者:杨震宇
/// 创建时间:2013年3月29日
///
/// 说明:用作文档页面。
/// </summary>
public class MarkdownEditor : BetterWpfControls.TabItem
{
/// <summary>
/// 需要添加“加密解锁器”。
/// </summary>
private Grid baseGrid = new Grid();
/// <summary>
/// 密码设置面板
/// </summary>
private SetPasswordPanel setPasswordPanel;
private InputPasswordPanel inputPasswordPanel;
/// <summary>
/// 密码输入面板
/// </summary>
public InputPasswordPanel InputPasswordPanel
{
get
{
return inputPasswordPanel;
}
}
private MarkDownEditorBase editorBase;
/// <summary>
/// 主编辑器。
/// </summary>
public MarkDownEditorBase EditorBase { get { return this.editorBase; } }
private string fullFilePath = null;
/// <summary>
/// 文件路径。保存时使用。
/// </summary>
public string FullFilePath
{
get { return this.fullFilePath; }
set
{
this.fullFilePath = value;
if (string.IsNullOrEmpty(this.fullFilePath) || File.Exists(this.fullFilePath) == false)
{
this.shortFileName = "unnamed.md";
this.headerPanel.ToolTip = null;
}
else
{
int lastIndex = this.fullFilePath.LastIndexOf("\\");
if (lastIndex >= 0)
{
string name = this.fullFilePath.Substring(lastIndex + 1);
if (string.IsNullOrEmpty(name))
{
this.shortFileName = "unnamed.md";
}
else this.shortFileName = name;
}
else
{
this.shortFileName = "unnamed.md";
}
this.headerPanel.ToolTip = this.fullFilePath;
}
RefreshTitle();
if (ShortFileName.StartsWith("_"))
{
this.icon.Source = WorkspaceTreeViewItem.FolderDocumentImage;
}
else
{
this.icon.Source = WorkspaceTreeViewItem.FileBitmapImage;
}
}
}
private string title = "";
/// <summary>
/// 标签上的文本。如果文档设置了“标题”,应返回标题文本;如果没有,应返回文件短名。
/// </summary>
public string Title
{
get { return this.title; }
}
/// <summary>
/// 没有标题就显示文件短名,有就显示标题。
/// 标题是指 Markdown 文件中第一个以%开头的行的文本。
/// </summary>
private void RefreshTitle()
{
var title = MainWindow.GetTitleOfMdFile(this.fullFilePath);
if (string.IsNullOrWhiteSpace(title) == false)
{
string subTitle;
if (title.Length > 20)
{
subTitle = title.Substring(0, 20) + "...";
}
else subTitle = title;
this.headerTextBlock.Text = this.title = subTitle.Trim(new char[] { ' ', '\t', ' ', '\n', '\r', }); //这个首尾的空行还是不要出现的好。;
}
else
{
this.headerTextBlock.Text = this.title = this.shortFileName.Trim(new char[] { ' ', '\t', ' ', '\n', '\r', }); //这个首尾的空行还是不要出现的好。;
}
}
private string shortFileName;
/// <summary>
/// 取文件路径中最后一个反斜杠之后的部分。如果没有,返回“ex”这两个字符。
/// </summary>
public string ShortFileName
{
get
{
return this.shortFileName;
}
set
{
this.shortFileName = value;
var title = MainWindow.GetTitleOfMdFile(this.fullFilePath);
if (string.IsNullOrWhiteSpace(title) == false)
{
this.headerTextBlock.Text = title.Trim(new char[] { ' ', '\t', ' ', '\n', '\r', }); //这个首尾的空行还是不要出现的好。;
}
else
{
this.headerTextBlock.Text = this.shortFileName.Trim(new char[] { ' ', '\t', ' ', '\n', '\r', }); //这个首尾的空行还是不要出现的好。;
}
}
}
private ContextMenu headerContextMenu = new ContextMenu()
{
FontFamily = new FontFamily("SimSun"),
FontSize = 14,
Style = Globals.MainWindow.TryFindResource("MetroContextMenu") as Style,
};
private ContextMenu editorContextMenu = new ContextMenu()
{
FontFamily = new FontFamily("SimSun"),
FontSize = 14,
Style = Globals.MainWindow.TryFindResource("MetroContextMenu") as Style,
};
/// <summary>
/// 直接返回Editor.IsModified的值。
/// </summary>
public bool IsModified
{
get { return this.editorBase.IsModified; }
}
/// <summary>
/// 决定是否将此编辑器“钉”在主选项卡左侧。
/// </summary>
private MenuItem miPin;
private PresentationWindow presentationWindow = null;
/// <summary>
/// 试题演示窗口,考虑到再次打开时应仍然保持试题状态的必要性,每个编辑器都应有一个窗口实例。
/// </summary>
public PresentationWindow PresentationWindow
{
get
{
if (this.presentationWindow == null)
{
this.presentationWindow = new PresentationWindow(this)
{
Owner = App.Current.MainWindow,
WindowStartupLocation = System.Windows.WindowStartupLocation.CenterOwner,
ContentText = this.editorBase.Text,
};
this.presentationWindow.tbTitle.Text = this.headerTextBlock.Text;
}
return this.presentationWindow;
}
}
/// <summary>
/// [构造方法]创建一个 Markdown 编辑器。
/// </summary>
/// <param name="shortFileName">文件短名,用以显示在编辑器的标头上。</param>
/// <param name="isExamEnabled">是否启用试题编辑功能。(依赖“自动完成”)</param>
/// <param name="isAutoCompletionEnabled">是否启用“自动完成”功能。</param>
/// <param name="isEnToChineseDictEnabled">是否启用英译中自动提示与完成功能。(依赖“自动完成”)</param>
/// <param name="isShowSpaces">是否显示空格。</param>
/// <param name="isShowEndOfLine">是否显示段落标记。</param>
/// <param name="isShowTabs">是否显示 Tab 符。</param>
/// <param name="isCompareAreaEditor">是否对照区。如果是对照区,双击标题头不能预览。</param>
public MarkdownEditor(string shortFileName, bool isExamEnabled,
bool isAutoCompletionEnabled, bool isEnToChineseDictEnabled,
bool isShowSpaces, bool isShowEndOfLine, bool isShowTabs, bool isTextAutoWrap,
bool isCompareAreaEditor, MarkDownEditorBase.HighlightingType highlightingType)
{
this.editorBase = new LunarMarkdownEditor.MarkDownEditorBase(this) { SnapsToDevicePixels = true, };
RenderOptions.SetEdgeMode(this.editorBase, EdgeMode.Aliased);
this.isCompareAreaEditor = isCompareAreaEditor;
this.Style = App.Current.FindResource("BetterTabItemStyle") as Style;
this.ShortFileName = shortFileName;
this.IsExamEnabled = isExamEnabled;
this.IsAutoCompletionEnabled = isAutoCompletionEnabled;
this.IsEnToChineseDictEnabled = isEnToChineseDictEnabled;
this.IsShowSpaces = isShowSpaces;
this.IsShowEndOfLine = isShowEndOfLine;
this.IsShowTabs = isShowTabs;
this.IsTextAutoWrap = isTextAutoWrap;
this.HighlightingSetting = highlightingType;
#region 初始化标题上下文菜单。
//ControlTemplate SubMenuItemControlTemplate = Globals.MainWindow.TryFindResource("SubMenuItemControlTemplate") as ControlTemplate;
//ControlTemplate MidMenuItemControlTemplate = Globals.MainWindow.TryFindResource("MidMenuItemControlTemplate") as ControlTemplate;
//ControlTemplate MenuItemControlTemplate = Globals.MainWindow.TryFindResource("MenuItemControlTemplate") as ControlTemplate;
Style mmiStyle = TryFindResource("MetroMenuItem") as Style;
this.headerPanel.ContextMenu = this.headerContextMenu;
miPin = new MenuItem()
{
Header = "锁定在左侧(_L)",
Style = mmiStyle,
};
miPin.Click += miPin_Click;
this.headerContextMenu.Items.Add(miPin);
MenuItem miCloseDocument = new MenuItem()
{
Header = "关闭此文档(_C)",
InputGestureText = "Ctrl+W",
Style = mmiStyle,
};
miCloseDocument.Click += miCloseDocument_Click;
this.headerContextMenu.Items.Add(miCloseDocument);
MenuItem miCloseOtherDocument = new MenuItem()
{
Header = "关闭其它文档(_O)",
Style = mmiStyle,
};
miCloseOtherDocument.Click += MiCloseOtherDocument_Click;
this.headerContextMenu.Items.Add(miCloseOtherDocument);
this.headerContextMenu.Items.Add(new Separator());
MenuItem miCompileAndPreview = new MenuItem()
{
Header = "编译并预览Html文档(_P)",
InputGestureText = "F5",
Style = mmiStyle,
};
miCompileAndPreview.Click += miCompileAndPreview_Click;
this.headerContextMenu.Items.Add(miCompileAndPreview);
MenuItem miCallSystemDefaultExplorerOpen = new MenuItem()
{
Header = "在外部预览(_E)",
ToolTip = "先编译,再调用操作系统默认浏览器预览",
Style = mmiStyle,
};
miCallSystemDefaultExplorerOpen.Click += MiCallSystemDefaultWebBrowserPreview_Click;
this.headerContextMenu.Items.Add(miCallSystemDefaultExplorerOpen);
this.headerContextMenu.Items.Add(new Separator());
MenuItem miSendToLeftCompareArea = new MenuItem()
{
Header = "发送到临时对照区[左](_T)",
ToolTip = "会将当前选定文本发送过去,可编辑,不可保存",
Style = mmiStyle,
};
miSendToLeftCompareArea.Click += MiSendToLeftCompareArea_Click;
this.headerContextMenu.Items.Add(miSendToLeftCompareArea);
MenuItem miSendToRightCompareArea = new MenuItem()
{
Header = "发送到只读对照区[右](_R)",
ToolTip = "会将当前所有文本都发送过去,不可编辑,不可保存",
Style = mmiStyle,
};
miSendToRightCompareArea.Click += MiSendToRightCompareArea_Click;
this.headerContextMenu.Items.Add(miSendToRightCompareArea);
this.headerContextMenu.Opened += HeaderContextMenu_Opened;
#endregion
#region 初始化编辑区上下文菜单
this.editorBase.ContextMenu = this.editorContextMenu;
MenuItem miUndo = new MenuItem()
{
Header = "撤销(_U)",
InputGestureText = "Ctrl+Z",
Style = mmiStyle,
Icon = new Image()
{
Height = 16,
Width = 16,
SnapsToDevicePixels = true,
Source = new BitmapImage(new Uri("pack://application:,,,/LunarMarkdownEditor;component/Images/Edit_UndoHS.png")),
},
};
miUndo.Click += MiUndo_Click;
this.editorContextMenu.Items.Add(miUndo);
MenuItem miRedo = new MenuItem()
{
Header = "重做(_R)",
InputGestureText = "Ctrl+Y",
Style = mmiStyle,
Icon = new Image()
{
Height = 16,
Width = 16,
SnapsToDevicePixels = true,
Source = new BitmapImage(new Uri("pack://application:,,,/LunarMarkdownEditor;component/Images/Edit_RedoHS.png")),
},
};
miRedo.Click += MiRedo_Click;
this.editorContextMenu.Items.Add(miRedo);
this.editorContextMenu.Items.Add(new Separator());
MenuItem miCut = new MenuItem()
{
Header = "剪切(_X)",
InputGestureText = "Ctrl+X",
Style = mmiStyle,
Icon = new Image()
{
Height = 16,
Width = 16,
SnapsToDevicePixels = true,
Source = new BitmapImage(new Uri("pack://application:,,,/LunarMarkdownEditor;component/Images/CutHS.png")),
},
};
miCut.Click += MiCut_Click;
this.editorContextMenu.Items.Add(miCut);
MenuItem miCopy = new MenuItem()
{
Header = "复制(_C)",
InputGestureText = "Ctrl+C",
Style = mmiStyle,
Icon = new Image()
{
Height = 16,
Width = 16,
SnapsToDevicePixels = true,
Source = new BitmapImage(new Uri("pack://application:,,,/LunarMarkdownEditor;component/Images/CopyHS.png")),
},
};
miCopy.Click += MiCopy_Click;
this.editorContextMenu.Items.Add(miCopy);
MenuItem miPaste = new MenuItem()
{
Header = "粘贴(_P)",
InputGestureText = "Ctrl+V",
Style = mmiStyle,
Icon = new Image()
{
Height = 16,
Width = 16,
SnapsToDevicePixels = true,
Source = new BitmapImage(new Uri("pack://application:,,,/LunarMarkdownEditor;component/Images/PasteHS.png")),
},
};
miPaste.Click += MiPaste_Click;
this.editorContextMenu.Items.Add(miPaste);
if (isCompareAreaEditor == false)
{
//对照区是不应该显示这些菜单项的(即使是左对照区允许编辑也不应该)
this.editorContextMenu.Items.Add(new Separator());
MenuItem miFindLinkImage = new MenuItem()
{
Header = "预览引用的图像(_P)",
InputGestureText = "Ctrl+G",
SnapsToDevicePixels = true,
Style = mmiStyle,
};
miFindLinkImage.Click += MiFindLinkImage_Click;
this.editorContextMenu.Items.Add(miFindLinkImage);
MenuItem miOpenLinkedMarkdownFile = new MenuItem()
{
Header = "打开引用的 Markdown 文档(_M)",
InputGestureText = "Ctrl+G",
SnapsToDevicePixels = true,
Style = mmiStyle,
};
miOpenLinkedMarkdownFile.Click += MiOpenLinkedMarkdownFile_Click;
this.editorContextMenu.Items.Add(miOpenLinkedMarkdownFile);
this.editorContextMenu.Items.Add(new Separator());
MenuItem miSimpleEncrypt = new MenuItem()
{
Header = "设置密码(_E)",
ToolTip = "只支持简单加密,勿用于高要求场合!",
SnapsToDevicePixels = true,
Style = mmiStyle,
};
miSimpleEncrypt.Click += MiSimpleEncrypt_Click;
this.editorContextMenu.Items.Add(miSimpleEncrypt);
this.editorContextMenu.Items.Add(new Separator());
MenuItem miSendToLeftCompareAreaC = new MenuItem()
{
Header = "发送到临时对照区[左](_T)",
ToolTip = "仅发送选定文本或所在的树型文字表",
Style = mmiStyle,
};
miSendToLeftCompareAreaC.Click += MiSendToLeftCompareArea_Click;
this.editorContextMenu.Items.Add(miSendToLeftCompareAreaC);
MenuItem miSendToRightCompareAreaC = new MenuItem()
{
Header = "发送到只读对照区[右](_R)",
ToolTip = "发送当前文档所有文本",
Style = mmiStyle,
};
miSendToRightCompareAreaC.Click += MiSendToRightCompareArea_Click;
this.editorContextMenu.Items.Add(miSendToRightCompareAreaC);
}
this.editorContextMenu.Items.Add(new Separator());
MenuItem miCollapseCurrentFolder = new MenuItem()
{
Header = "折叠/展开当前区块(_B)",
InputGestureText = "Ctrl+Alt+Z",
SnapsToDevicePixels = true,
Style = mmiStyle,
};
miCollapseCurrentFolder.Click += MiCollapseCurrentFolder_Click;
this.editorContextMenu.Items.Add(miCollapseCurrentFolder);
MenuItem miCollapseAllBlocks = new MenuItem()
{
Header = "全部折叠(_A)",
SnapsToDevicePixels = true,
Style = mmiStyle,
};
miCollapseAllBlocks.Click += MiCollapseAllBlocks_Click;
this.editorContextMenu.Items.Add(miCollapseAllBlocks);
MenuItem miExpandAllBlocks = new MenuItem()
{
Header = "全部展开(_E)",
SnapsToDevicePixels = true,
Style = mmiStyle,
};
miExpandAllBlocks.Click += MiExpandAllBlocks_Click;
this.editorContextMenu.Items.Add(miExpandAllBlocks);
#endregion
if (isCompareAreaEditor)
{
this.closeImage.Visibility = Visibility.Collapsed;
this.icon.Visibility = Visibility.Collapsed;
}
this.headerPanel.Children.Add(icon);
this.headerPanel.Children.Add(this.modifiedTextBlock);
this.headerPanel.Children.Add(this.headerTextBlock);
this.closeImage.Source = closeImageSource;
this.closeImage.MouseLeftButtonDown += closeImage_MouseLeftButtonDown;
this.closeBorder.Child = this.closeImage;
this.headerPanel.Children.Add(this.closeBorder);
this.Header = this.headerPanel;
this.editorBase.Document.UndoStack.PropertyChanged += UndoStack_PropertyChanged;
this.headerTextBlock.MouseLeftButtonDown += HeaderTextBlock_MouseLeftButtonDown;
//处理拖动
this.AllowDrop = true;
this.Drop += MarkdownEditor_Drop;
this.closeBorder.MouseLeave += closeBorder_MouseLeave;
this.closeBorder.MouseEnter += closeBorder_MouseEnter;
this.PreviewKeyDown += MarkdownEditor_PreviewKeyDown;
this.PreviewTextInput += MarkdownEditor_PreviewTextInput;
this.PreviewKeyUp += MarkdownEditor_PreviewKeyUp;
this.editorBase.WordWrap = Globals.MainWindow.TextAutoWrap;
//处理加密面板布局
this.setPasswordPanel = new SetPasswordPanel(this);
this.inputPasswordPanel = new InputPasswordPanel(this);
this.Content = this.baseGrid;
this.baseGrid.Children.Add(this.editorBase);
this.baseGrid.Children.Add(this.setPasswordPanel);
this.baseGrid.Children.Add(this.InputPasswordPanel);
//如果文档未加密,直接显示
if (IsEncripied(this.fullFilePath) == false)
{
this.setPasswordPanel.Visibility =
this.InputPasswordPanel.Visibility = Visibility.Collapsed;
}
else
{
this.InputPasswordPanel.Visibility = Visibility.Visible;
this.setPasswordPanel.Visibility = Visibility.Collapsed;
}
//双击预览图像
this.editorBase.TextArea.PreviewMouseDoubleClick += TextArea_PreviewMouseDoubleClick;
}
#region 提供类似 Vim 的单键选取模式
private EditingMode editingMode = EditingMode.NormalMode;
public EditingMode EditingMode
{
get { return this.editingMode; }
set
{
this.editingMode = value;
if (value == EditingMode.VimMode)
{
this.EditorBase.EditingMode = ICSharpCode.AvalonEdit.EditingMode.VimMode;
this.editorBase.TextArea.Caret.CaretBrush = Brushes.Red;
this.editorBase.TextArea.MovingMode = true;
Globals.SwitchInputMethod(false);//自动切换输入法状态。
Globals.MainWindow.bdInputedCommandText.Visibility = Visibility.Visible;
}
else
{
this.EditorBase.EditingMode = ICSharpCode.AvalonEdit.EditingMode.NormalMode;
this.editorBase.TextArea.MovingMode = false;
this.editorBase.TextArea.Caret.CaretBrush = (Brush)this.editorBase.TextArea.TextView.GetValue(TextBlock.ForegroundProperty);
Globals.MainWindow.bdInputedCommandText.Visibility = Visibility.Collapsed;
}
}
}
private int vimKeyUpedCount = 0;
private DateTime? previewVimKeyPressedTime = null;
private InputMethodState? previewInputMethodState = null;
public InputMethodState? PreviewInputMethodState
{
get { return previewInputMethodState; }
set { previewInputMethodState = value; }
}
private void MarkdownEditor_PreviewKeyUp(object sender, KeyEventArgs e)
{
//半秒内连续按两次 vimKey。
var vimKey = Globals.VimKey;
if (e.Key != vimKey)
{
previewVimKeyPressedTime = null;
vimKeyUpedCount = 0;
e.Handled = true;
return;
}
if (previewVimKeyPressedTime == null)
{
vimKeyUpedCount += 1;
previewVimKeyPressedTime = DateTime.Now;
e.Handled = true;
return;
}
if (vimKeyUpedCount > 0 && previewVimKeyPressedTime.HasValue)
{
var timeSpan = DateTime.Now - previewVimKeyPressedTime;
if (timeSpan.Value.Milliseconds <= 500)
{
previewVimKeyPressedTime = null;
vimKeyUpedCount = 0;
e.Handled = true;
//半秒之内连续按两次 LeftShift 键,即开启“移动模式(MovingMode)”
if (InputMethod.Current != null)
{
this.previewInputMethodState = InputMethod.Current.ImeState;
}
this.EditingMode = EditingMode.VimMode;
//vim为防止出错,默认行为就是:返回命令模式时向前移动插入点(只有在行首是例外)。
var selStartOffset = editorBase.SelectionStart;
if (editorBase.SelectionLength > 0)
{
editorBase.Select(selStartOffset, 0);
}
var lineOffset = editorBase.Document.GetLineByOffset(selStartOffset).Offset;
if (selStartOffset > lineOffset)
{
editorBase.MoveCaret(ICSharpCode.AvalonEdit.Editing.CaretMovementType.CharLeft);
}
return;
}
else
{
vimKeyUpedCount += 1;
previewVimKeyPressedTime = DateTime.Now;
e.Handled = true;
return;
}
}
}
#endregion 提供类似 Vim 的单键选取模式
private void TextArea_PreviewMouseDoubleClick(object sender, MouseButtonEventArgs e)
{
if (Globals.MainWindow.PerspectiveMode == Perspective.EditingAndPresentation)
{
//在“演讲者模式”下,双击被折叠起来的图像链接、二维文字表、树型列表
foreach (var i in this.EditorBase.FoldingManager.AllFoldings)
{
if (i.IsFolded == false) continue;
if (this.editorBase.SelectionStart >= i.StartOffset && this.editorBase.SelectionStart <= i.EndOffset)
{
if (i.Title.StartsWith("[") && i.Title.EndsWith("]"))
{
//TODO:暂时不支持文件链接的预览。
return;
}
if (i.Title.StartsWith("![") && i.Title.EndsWith("]"))
{
PreviewLinkedImageFile(false, i.TextContent);
e.Handled = true;
return;
}
if ((i.Title.StartsWith("┣") && i.Title.EndsWith("┫")) ||
(i.Title.StartsWith("{") && i.Title.EndsWith("}")) ||
/*(i.Title.StartsWith("> ") || i.Title.StartsWith("引用块:")) ||*/
//实践证明引用块也支持这样的折叠效果不好——有画蛇添足的感觉。
CustomMarkdownSupport.IsTreeListTextLine(i.Title))
{
var tableText = i.TextContent;
CompileAndPresentateHtml(
CustomMarkdownSupport.PresentateHtmlSplitterType.TextSegment,
false, tableText);
//如果当前正在演示状态,就不展开折叠区了。
//如果在编辑状态,双击不但预览图像文件,而且展开折叠区。
//if (Globals.MainWindow.rdMainMenuArea.ActualHeight <= 0)
e.Handled = true;
return;
}
}
}
}
//二维文字表在展开时不支持双击预览!但单独占一行的图像文件链接则支持。
//这是因为二维文字表双击往往是用以编辑而非预览。
//图像链接的预览基本不影响编辑操作;
//但二维文字表的预览是通过弹出新窗口(并编译为Html)来实现的,如果支持“双击左键预览”会打断用户编辑。
Globals.MainWindow.PreviewImage();
}
private void MiCloseOtherDocument_Click(object sender, RoutedEventArgs e)
{
for (int i = Globals.MainWindow.mainTabControl.Items.Count - 1; i >= 0; i--)
{
var editor = Globals.MainWindow.mainTabControl.Items[i] as MarkdownEditor;
if (editor == null || editor == this) continue;
editor.Close();
}
}
/// <summary>
/// 表示是否元文件的图标。
/// </summary>
private Image icon = new Image() { Width = 16, Margin = new Thickness(0, 2, 2, 2), };
/// <summary>
/// 取当前编辑器使用的图标。
/// </summary>
public ImageSource IconSource
{
get { return icon.Source; }
}
/// <summary>
/// 展开所有折叠块。
/// </summary>
private void MiExpandAllBlocks_Click(object sender, RoutedEventArgs e)
{
foreach (var i in this.EditorBase.FoldingManager.AllFoldings)
{
i.IsFolded = false;
}
}
/// <summary>
/// 折叠所有可折叠的区域。
/// </summary>
private void MiCollapseAllBlocks_Click(object sender, RoutedEventArgs e)
{
foreach (var i in this.EditorBase.FoldingManager.AllFoldings)
{
i.IsFolded = true;
}
}
/// <summary>
/// 折叠当前所在的可折叠区域。
/// </summary>
private void MiCollapseCurrentFolder_Click(object sender, RoutedEventArgs e)
{
this.EditorBase.FoldSelectedBlock();
}
/// <summary>
/// 发送到右对照区。LME 不支持同一文档的多视图编辑,故需要对照时借用了右工具栏。
/// 右对照区不可编辑,且不会保持状态,程序关闭就消失。
/// </summary>
private void MiSendToRightCompareArea_Click(object sender, RoutedEventArgs e)
{
SendToRightCompareArea();
}
/// <summary>
/// 发送到右对照区。
/// </summary>
internal void SendToRightCompareArea()
{
if (this.inputPasswordPanel.Visibility != Visibility.Collapsed)
{
LMessageBox.Show("  请先输入密码打开文档!", Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
return;
}
MarkdownEditor compareEdit = null;
foreach (UIElement ue in Globals.MainWindow.tcRightToolBar.Items)
{
var edit = ue as MarkdownEditor;
if (edit == null) continue;
if (edit.ShortFileName == "对照区[只读]")
{
compareEdit = edit;
break;
}
}
if (compareEdit == null)
{
compareEdit = new MarkdownEditor("对照区[只读]", false, false, false,
Globals.MainWindow.IsShowTabs, Globals.MainWindow.IsShowEndOfLine,
Globals.MainWindow.IsShowTabs, Globals.MainWindow.TextAutoWrap, true, this.HighlightingSetting);
compareEdit.headerPanel.ContextMenu = null;
foreach (UIElement ue in compareEdit.editorContextMenu.Items)
{
MenuItem mi = ue as MenuItem;
if (mi == null)
{
var s = ue as Separator;
if (s != null) s.Visibility = Visibility.Collapsed;
continue;
}
if (mi.Header.ToString() == "复制(_C)")
{
continue;
}
else
{
mi.IsEnabled = false;
mi.Visibility = Visibility.Collapsed;
}
}
compareEdit.EditorBase.IsReadOnly = true;
compareEdit.EditorBase.FontSize = Globals.MainWindow.mainTabControl.FontSize;
compareEdit.editorBase.FontFamily = Globals.MainWindow.mainTabControl.FontFamily;
Globals.MainWindow.tcRightToolBar.Items.Add(compareEdit);
}
compareEdit.EditorBase.WordWrap = this.EditorBase.WordWrap;
compareEdit.EditorBase.Document.Text = this.EditorBase.Text;
compareEdit.EditorBase.IsModified = false;
compareEdit.EditorBase.IsShowEndOfLine = this.EditorBase.IsShowEndOfLine;
compareEdit.EditorBase.IsShowSpaces = this.EditorBase.IsShowSpaces;
compareEdit.EditorBase.IsShowTabs = this.EditorBase.IsShowTabs;
Globals.MainWindow.tcRightToolBar.SelectedItem = compareEdit;
if (Globals.MainWindow.tcRightToolBar.ActualWidth < 100)
{
Globals.MainWindow.cdMainEditArea.Width =
Globals.MainWindow.cdRightToolsArea.Width = new GridLength(3, GridUnitType.Star);
}
}
/// <summary>
/// 发送到左对照区。LME 不支持同一文档的多视图编辑,故需要对照时借用了右工具栏。
/// 左对照区可编辑,但也不会保持状态,程序关闭就消失。
/// 左对照区只会将选定文本发送到对照区,通常发送树型文字表比较好。
/// </summary>
private void MiSendToLeftCompareArea_Click(object sender, RoutedEventArgs e)
{
if (this.inputPasswordPanel.Visibility != Visibility.Collapsed)
{
LMessageBox.Show("  请先输入密码打开文档!", Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
return;
}
MarkdownEditor compareEdit = null;
foreach (UIElement ue in Globals.MainWindow.tcManagerPanels.Items)
{
var edit = ue as MarkdownEditor;
if (edit == null) continue;
if (edit.ShortFileName == "对照区[临时]")
{
compareEdit = edit;
break;
}
}
if (compareEdit == null)
{
compareEdit = new MarkdownEditor("对照区[临时]", false, false, false,
Globals.MainWindow.IsShowTabs, Globals.MainWindow.IsShowEndOfLine,
Globals.MainWindow.IsShowTabs, Globals.MainWindow.TextAutoWrap, true, this.HighlightingSetting);
compareEdit.headerPanel.ContextMenu = null;
#region 左对照区可编辑、不可保存
//foreach (UIElement ue in compareEdit.editorContextMenu.Items)
//{
// MenuItem mi = ue as MenuItem;
// if (mi == null)
// {
// var s = ue as Separator;
// if (s != null) s.Visibility = Visibility.Collapsed;
// continue;
// }
// if (mi.Header.ToString() == "复制(_C)")
// {
// continue;
// }
// else
// {
// mi.IsEnabled = false;
// mi.Visibility = Visibility.Collapsed;
// }
//}
#endregion
compareEdit.EditorBase.IsReadOnly = false;
compareEdit.EditorBase.FontSize = Globals.MainWindow.mainTabControl.FontSize;
compareEdit.editorBase.FontFamily = Globals.MainWindow.mainTabControl.FontFamily;
Globals.MainWindow.tcManagerPanels.Items.Add(compareEdit);
}
string willSendText;
if (this.editorBase.SelectionLength == 0)
{
var curLine = this.EditorBase.Document.GetLineByOffset(this.EditorBase.SelectionStart);
var curLineText = this.EditorBase.Document.GetText(curLine.Offset, curLine.Length);
string header, tail; int level;
if (MarkDownEditorBase.IsTreeListLine(curLineText, out header, out tail, out level) &&
CustomMarkdownSupport.IsImageLinkLine(curLineText) == false)
{
List<string> preLines = new List<string>();
var preLine = curLine.PreviousLine;
while (preLine != null)
{
var preLineText = this.EditorBase.Document.GetText(preLine.Offset, preLine.Length);
string preHeader, preTail; int preLevel;
if (MarkDownEditorBase.IsTreeListLine(preLineText, out preHeader, out preTail, out preLevel) &&
CustomMarkdownSupport.IsImageLinkLine(preLineText) == false)
{
preLines.Add(preLineText);
preLine = preLine.PreviousLine;
continue;
}
else break;
}
List<string> nextLines = new List<string>();
var nextLine = curLine.NextLine;
while (nextLine != null)
{
var nextLineText = this.EditorBase.Document.GetText(nextLine.Offset, nextLine.Length);
string nextHeader, nextTail; int nextLevel;
if (MarkDownEditorBase.IsTreeListLine(nextLineText, out nextHeader, out nextTail, out nextLevel) &&
CustomMarkdownSupport.IsImageLinkLine(nextLineText) == false)
{
nextLines.Add(nextLineText);
nextLine = nextLine.NextLine;
continue;
}
else break;
}
StringBuilder sb = new StringBuilder();
for (int i = preLines.Count - 1; i >= 0; i--)
{
sb.Append(preLines[i]);
sb.Append("\r\n");
}
for (int i = 0; i < nextLines.Count; i++)
{
sb.Append(nextLines[i]);
sb.Append("\r\n");
}
willSendText = sb.ToString();
}
else
{
var result = LMessageBox.Show("  没有选择文本,而且插入点所在位置也不是个树型文字表!\r\n  -------------------------------------------\r\n  要尝试将所有标题都当作提纲发送到临时对照区[左]吗?", Globals.AppName,
MessageBoxButton.YesNo, MessageBoxImage.Warning);
if (result == MessageBoxResult.Yes)
{
StringBuilder sb = new StringBuilder();
var line = this.editorBase.Document.GetLineByNumber(1);
while (line != null)
{
var lineText = this.editorBase.Document.GetText(line.Offset, line.Length);
if (lineText.StartsWith("#"))
{
sb.Append(lineText);
sb.Append("\r\n");
}
line = line.NextLine;
}
willSendText = sb.ToString();
}
else return;
}
}
else willSendText = this.editorBase.SelectedText;
compareEdit.EditorBase.WordWrap = this.EditorBase.WordWrap;
compareEdit.EditorBase.Document.Text = willSendText;
compareEdit.editorBase.IsModified = false;
compareEdit.EditorBase.IsShowEndOfLine = this.EditorBase.IsShowEndOfLine;
compareEdit.EditorBase.IsShowSpaces = this.EditorBase.IsShowSpaces;
compareEdit.EditorBase.IsShowTabs = this.EditorBase.IsShowTabs;
Globals.MainWindow.tcManagerPanels.SelectedItem = compareEdit;
if (Globals.MainWindow.leftToolBarGrid.Visibility != System.Windows.Visibility.Visible ||
Globals.MainWindow.leftToolBarGrid.ActualWidth < 100)
{
Globals.MainWindow.cdLeftToolsArea.Width = new GridLength(360, GridUnitType.Pixel);
}
}
private void HeaderContextMenu_Opened(object sender, RoutedEventArgs e)
{
miPin.IsChecked = BetterWpfControls.Panels.ScrollablePanel.GetIsLocked(this);
}
/// <summary>
/// 跳转到引用的 Markdown 文档。
/// </summary>
private void MiOpenLinkedMarkdownFile_Click(object sender, RoutedEventArgs e)
{
if (GotoAnchorInSameDocument()) return;
OpenLinkedMarkdownFile();
}
/// <summary>
/// 如果是页面内的锚,直接跳转即可。
/// </summary>
public bool GotoAnchorInSameDocument()
{
var line = this.editorBase.Document.GetLineByOffset(this.editorBase.SelectionStart);
var lineText = this.editorBase.Document.GetText(line.Offset, line.EndOffset - line.Offset);
Regex regexAnchorRef = new Regex(@"\[.*\]\(\#.{1,}\)");
MatchCollection matchesOfAnchor = regexAnchorRef.Matches(lineText);
if (matchesOfAnchor.Count > 0)
{
var selOffsetInLine = this.editorBase.SelectionStart - line.Offset;
foreach (Match match in matchesOfAnchor)
{
if (selOffsetInLine >= match.Index && selOffsetInLine < match.Index + match.Length)
{
Regex regexAnchorRef2 = new Regex(@"(?<=(\[.*\]\(\#)).{1,}(?=(\)))");
var match2 = regexAnchorRef2.Match(match.Value);
if (match2.Success)
{
Regex regexAnchor = new Regex(@"(?<=(\[.*\]\(@))" + match2.Value + @"(?=(\)))");
var matchAnchor = regexAnchor.Match(this.editorBase.Document.Text);
if (matchAnchor.Success)
{
var destLine = this.editorBase.Document.GetLineByOffset(matchAnchor.Index);
this.editorBase.Select(matchAnchor.Index, matchAnchor.Length);
this.editorBase.ScrollTo(destLine.LineNumber, matchAnchor.Index - destLine.Offset);
return true;
}
}
return false;
}
}
return false;
}
return false;
}
/// <summary>
/// 跳转到引用的 Markdown 文档。注意:此方法不支持跳转到同一个文档中引用的“锚”。
/// </summary>
internal void OpenLinkedMarkdownFile()
{
var line = this.editorBase.Document.GetLineByOffset(this.editorBase.SelectionStart);
var lineText = this.editorBase.Document.GetText(line.Offset, line.EndOffset - line.Offset);
var selIndex = this.editorBase.SelectionStart - line.Offset;
int leftBracketIndex;
int rightBracketIndex;
if (this.editorBase.SelectionStart == line.Offset && this.editorBase.SelectionLength == line.Length)
{
leftBracketIndex = lineText.IndexOf("](", selIndex);
rightBracketIndex = lineText.IndexOf(" \"", selIndex);
if (rightBracketIndex < 0)
rightBracketIndex = lineText.IndexOf(")", selIndex);
}
else
{
leftBracketIndex = lineText.LastIndexOf("](", selIndex);
if (leftBracketIndex < 0)
{
if (selIndex >= 1)
{
leftBracketIndex = lineText.LastIndexOf("[", selIndex - 1);
}
if (leftBracketIndex >= 0)
{
leftBracketIndex = lineText.IndexOf("](", leftBracketIndex + 1);
}
if (leftBracketIndex >= 0)
{
rightBracketIndex = lineText.IndexOf(" \"", leftBracketIndex + 1);
if (rightBracketIndex < 0)
rightBracketIndex = lineText.IndexOf(")", leftBracketIndex + 1);
}
else
{
rightBracketIndex = lineText.IndexOf(" \"", selIndex);
if (rightBracketIndex < 0)
rightBracketIndex = lineText.IndexOf(")", selIndex);
}
}
else
{
rightBracketIndex = lineText.IndexOf(" \"", leftBracketIndex + 1);
if (rightBracketIndex < 0)
rightBracketIndex = lineText.IndexOf(")", leftBracketIndex + 1);
}
}
if (leftBracketIndex <= 0 || rightBracketIndex <= 1 || rightBracketIndex <= leftBracketIndex + 1)
{
LMessageBox.Show("  当前插入点位置处的文本不像是个对 Markdown 文件的引用!", Globals.AppName,
MessageBoxButton.OK, MessageBoxImage.Warning);
return;
}
if (File.Exists(this.FullFilePath) == false)
{
LMessageBox.Show("  正在编辑的 Markdown 文件必须先保存到物理磁盘才可以查找引用!", Globals.AppName,
MessageBoxButton.OK, MessageBoxImage.Warning);
return;
}
var relativePath = lineText.Substring(leftBracketIndex + 2, rightBracketIndex - leftBracketIndex - 2).Trim(new char[] { ' ', ' ', '\t' }); ;
var index = relativePath.ToLower().LastIndexOf(".html");
if (index < 0)
{
LMessageBox.Show("  当前插入点位置处的文本不像是个对 Markdown 文件的引用!", Globals.AppName,
MessageBoxButton.OK, MessageBoxImage.Warning);
return;
}
var anchor = relativePath.Substring(index + 5);
relativePath = relativePath.Substring(0, index) + ".md";//这两行顺序不能倒。
if (anchor.StartsWith("#"))
{
anchor = $"](@{anchor.Substring(1)})";
}
else anchor = "";
var pieces = relativePath.Split(new char[] { '/', '\\' }, StringSplitOptions.RemoveEmptyEntries);
if (pieces.Length <= 0)
{
LMessageBox.Show("  当前插入点位置处的文本不像是个对 Markdown 文件的引用!", Globals.AppName,
MessageBoxButton.OK, MessageBoxImage.Warning);
return;
}
FileInfo thisFileInfo = new FileInfo(this.FullFilePath);
var directory = thisFileInfo.Directory;
int x = 0;
foreach (var s in pieces)
{
if (s == "..")
{
directory = directory.Parent;
x++;
}
else
{
var subPath = directory.FullName.EndsWith("\\") ? (directory.FullName + s) : (directory.FullName + "\\" + s);
if (Directory.Exists(subPath) == false)
{
var subPathInfo = new FileInfo(subPath);
//用 ~ 代替 _ 风险太高了!
//if(subPathInfo.Name.StartsWith("~") && subPathInfo.Extension == ".md")
//{
// subPath = subPathInfo.Directory.FullName + "\\_" + subPathInfo.Name.Substring(1);
//}
if (File.Exists(subPath))
{
//就是目标
Globals.MainWindow.OpenDocuments(new string[] { subPath });
MarkdownEditor newEditor = null;
var newIndexOfDocument = -1;
for (int i = 0; i < Globals.MainWindow.mainTabControl.Items.Count; i++)
{
var editor = Globals.MainWindow.mainTabControl.Items[i] as MarkdownEditor;
if (editor != null && editor.FullFilePath.ToLower() == subPath.ToLower())
{
newEditor = editor;
newIndexOfDocument = i;
break;
}
}
if (newEditor != null)
{
Globals.MainWindow.mainTabControl.SelectedIndex = newIndexOfDocument;
if (string.IsNullOrEmpty(anchor) == false && anchor.Length > 4)
{
var startIndex = newEditor.EditorBase.Text.IndexOf(anchor);
if (startIndex >= 0 && (startIndex + 3) < newEditor.editorBase.Document.TextLength)
{
newEditor.EditorBase.Select(startIndex + 3, anchor.Length - 4);
var destLine = newEditor.EditorBase.Document.GetLineByOffset(startIndex + 3);
newEditor.EditorBase.ScrollTo(destLine.LineNumber, startIndex + 3 - destLine.Offset);
}
}
}
return;
}
LMessageBox.Show("  当前插入点位置处的文本不像是个对 Markdown 文件的引用!",
Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
return;
}
else
{
directory = new DirectoryInfo(subPath);
x++;
}
}
}
if (x != pieces.Length - 1)
{
LMessageBox.Show("  当前插入点位置处的文本不像是个对 Markdown 文件的引用!",
Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
return;
}
LMessageBox.Show(directory.FullName + pieces[pieces.Length - 1]);
}
/// <summary>
/// 对当前文档进行简单地加密。
/// </summary>
private void MiSimpleEncrypt_Click(object sender, RoutedEventArgs e)
{
if (File.Exists(this.FullFilePath) == false || this.IsModified)
{
LMessageBox.Show("  此功能需要先保存文件!", Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
return;
}
if (string.IsNullOrEmpty(this.Password) == false)
{
var result = InputBox.Show(Globals.AppName, $"请输入现有密码以便设置新密码!\r\n\r\n提示:{this.PasswordTip}", "", false, null, true);
if (string.IsNullOrEmpty(result) || result != this.Password)
{
LMessageBox.Show("您输入的密码不正确!无法设置新密码!", Globals.AppName,
MessageBoxButton.OK, MessageBoxImage.Warning);
return;
}
}
setPasswordPanel.Visibility = Visibility.Visible;
if (InputPasswordPanel.Visibility != Visibility.Collapsed)
{
InputPasswordPanel.Visibility = Visibility.Collapsed;
}
}
private string passwordTip = "";
/// <summary>
/// 密码提示文本。
/// </summary>
public string PasswordTip
{
get { return this.passwordTip; }
set
{
this.passwordTip =
this.InputPasswordPanel.tipBlock.Text = value;
}
}
/// <summary>
/// 密码文本。
/// </summary>
public string Password { get; set; } = "";
/// <summary>
/// 首行文本,保存密码、提示问题等信息。
/// </summary>
public string EncryptedFirstLine
{
get
{
if (string.IsNullOrEmpty(this.Password) && string.IsNullOrEmpty(this.PasswordTip))
{
return "";//没有密码。
}
var fstLine = new String(SetPasswordPanel.TextEncrypt($"Question:{PasswordTip}|Password:{Password}", "DyBj#PpBb")).Replace("\r", "[[<r>]]").Replace("\n", "[[<n>]]");
return fstLine;
}
}
/// <summary>
/// 根据路径判断该文件是否已被加密。
/// </summary>
/// <param name="fullFilePath">要检查的 Markdown 文件的完整路径。</param>
private bool IsEncripied(string fullFilePath)
{
if (String.IsNullOrWhiteSpace(fullFilePath) || File.Exists(fullFilePath) == false) return false;
System.IO.StreamReader s = File.OpenText(fullFilePath);
var fstEncriptLine = s.ReadLine();
if (string.IsNullOrEmpty(fstEncriptLine)) return false;
var fstDecriptedLine = SetPasswordPanel.TextDecrypt(fstEncriptLine.Replace("[[<r>]]", "\r").Replace("[[<n>]]", "\n").ToCharArray(), "DyBj#PpBb");
if (fstDecriptedLine.Contains("Password:") && fstDecriptedLine.Contains("Question:") && fstDecriptedLine.Contains("|"))
{
return true;
}
return false;
}
/// <summary>
/// 预览引用的图像文件。
/// </summary>
private void MiFindLinkImage_Click(object sender, RoutedEventArgs e)
{
PreviewLinkedImageFile();
}
/// <summary>
/// 预览引用的图像文件。
/// </summary>
/// <returns>反对图像的Title文本。(失败会返回空字符串。)</returns>
internal string PreviewLinkedImageFile(bool showChildWindow = false, string imageLinkedText = null)
{
string relativePath;
string imageTitle = "";
if (string.IsNullOrWhiteSpace(imageLinkedText))
{
var line = this.editorBase.Document.GetLineByOffset(this.editorBase.SelectionStart);
var lineText = this.editorBase.Document.GetText(line.Offset, line.EndOffset - line.Offset);
var selIndex = this.editorBase.SelectionStart - line.Offset;
int leftBracketIndex;
int rightBracketIndex;
if (this.editorBase.SelectionStart == line.Offset && this.editorBase.SelectionLength == line.Length)
{
leftBracketIndex = lineText.IndexOf("](", selIndex);
rightBracketIndex = lineText.IndexOf(" \"", selIndex);
if (rightBracketIndex < 0)
rightBracketIndex = lineText.IndexOf(")", selIndex);
}
else
{
leftBracketIndex = lineText.LastIndexOf("](", selIndex);
if (leftBracketIndex < 0)
{
if (selIndex >= 1)
{
leftBracketIndex = lineText.LastIndexOf("![", selIndex - 1);
}
if (leftBracketIndex >= 0)
{
leftBracketIndex = lineText.IndexOf("](", leftBracketIndex + 2);
}
if (leftBracketIndex >= 0)
{
rightBracketIndex = lineText.IndexOf(" \"", leftBracketIndex + 1);
if (rightBracketIndex < 0)
rightBracketIndex = lineText.IndexOf(")", leftBracketIndex + 1);
}
else
{
rightBracketIndex = lineText.IndexOf(" \"", selIndex);
if (rightBracketIndex < 0)
rightBracketIndex = lineText.IndexOf(")", selIndex);
}
}
else
{
rightBracketIndex = lineText.IndexOf(" \"", leftBracketIndex + 1);
if (rightBracketIndex < 0)
rightBracketIndex = lineText.IndexOf(")", leftBracketIndex + 1);
}
}
if (leftBracketIndex <= 0 || rightBracketIndex <= 1 || rightBracketIndex <= leftBracketIndex + 1)
{
//LMessageBox.Show("  当前插入点位置处的文本不像是个对图像文件的引用!", Globals.AppName,
// MessageBoxButton.OK, MessageBoxImage.Warning);
return "";
}
if (File.Exists(this.FullFilePath) == false)
{
LMessageBox.Show("  正在编辑的 Markdown 文件必须先保存到物理磁盘才可以查找引用!", Globals.AppName,
MessageBoxButton.OK, MessageBoxImage.Warning);
return "";
}
var startSquareBracketIndex = lineText.LastIndexOf("[", leftBracketIndex);
if (startSquareBracketIndex >= 0 && startSquareBracketIndex < leftBracketIndex && leftBracketIndex > 2)
{
imageTitle = lineText.Substring(startSquareBracketIndex + 1, leftBracketIndex - startSquareBracketIndex - 1);
}
relativePath = lineText.Substring(leftBracketIndex + 2, rightBracketIndex - leftBracketIndex - 2).Trim(new char[] { ' ', ' ', '\t' });
//if (relativePath.ToLower().EndsWith(".html"))
// relativePath = relativePath.Substring(0, relativePath.Length - 4) + ".md";
}
else
{
Regex imageTitleReg = new Regex(@"(?<=(!\[)).*(?=\])");
Regex relativePathReg = new Regex(@"(?<=(!\[.*\]\()).*(?=[ )])");
var relativePathMatch = relativePathReg.Match(imageLinkedText);
if (relativePathMatch.Success)
{
relativePath = relativePathMatch.Value;
}
else
{
LMessageBox.Show("  当前插入点位置处的文本不像是个对图像文件的引用!", Globals.AppName,
MessageBoxButton.OK, MessageBoxImage.Warning);
return "";
}
var imageTitleMatch = imageTitleReg.Match(imageLinkedText);
if (imageTitleMatch.Success)
{
imageTitle = imageTitleMatch.Value;
}
}
var pieces = relativePath.Split(new char[] { '/', '\\' }, StringSplitOptions.RemoveEmptyEntries);
if (pieces.Length <= 0)
{
LMessageBox.Show("  当前插入点位置处的文本不像是个对图像文件的引用!", Globals.AppName,
MessageBoxButton.OK, MessageBoxImage.Warning);
return imageTitle;
}
FileInfo thisFileInfo = new FileInfo(this.FullFilePath);
var directory = thisFileInfo.Directory;
int x = 0;
foreach (var s in pieces)
{
if (s == "..")
{
directory = directory.Parent;
x++;
}
else
{
var subPath = directory.FullName.EndsWith("\\") ? (directory.FullName + s) : (directory.FullName + "\\" + s);
if (Directory.Exists(subPath) == false)
{
if (WorkspaceTreeViewItem.IsValidateImageFilePath(subPath))
{
//就是目标
if (showChildWindow)
{
ImagePreviewWindow ipw = new ImagePreviewWindow(subPath, imageTitle)
{
Owner = App.Current.MainWindow,
WindowStartupLocation = WindowStartupLocation.CenterOwner,
WindowState = WindowState.Normal,
Height = 300,
Width = 400,
};
ipw.Show();//允许多张图片同时演示
return imageTitle;
}
else
{
//在主界面左工具栏的图像预览区域预览
if (Globals.MainWindow.cdLeftToolsArea.ActualWidth < 100)
{
Globals.MainWindow.SwitchLeftToolBarToggle();
}
BitmapImage bitmapImage;
Globals.MainWindow.ImagePreview.Source =
bitmapImage = L.BuildBitmapImage(subPath);//会锁定文件:new BitmapImage(new Uri(subPath));
var w = bitmapImage.Width;
var h = bitmapImage.Height;
var cdw = Globals.MainWindow.cdLeftToolsArea.ActualWidth - 20;
var rdh = Math.Max(0,
Math.Min(Globals.MainWindow.leftToolBarGrid.ActualWidth * h / w +
Globals.MainWindow.mainTabControl.FontSize + 68,
Globals.MainWindow.leftToolBarGrid.ActualHeight));
Globals.MainWindow.rdLeftToolsTop.Height = new GridLength(1, GridUnitType.Star);
Globals.MainWindow.rdResourcePreviewArea.Height = new GridLength(rdh);
if (Globals.MainWindow.tcResourcePreviewArea.SelectedItem != Globals.MainWindow.tiImagePreview)
{
Globals.MainWindow.tcResourcePreviewArea.SelectedItem = Globals.MainWindow.tiImagePreview;
}
Globals.MainWindow.tbImageTitle.Text = imageTitle;
if (string.IsNullOrWhiteSpace(imageTitle))
{
Globals.MainWindow.tbImageTitle.Visibility = Visibility.Collapsed;
}
else
{
Globals.MainWindow.tbImageTitle.Visibility = Visibility.Visible;
}
return imageTitle;
}
}
LMessageBox.Show("  当前插入点位置处的文本不像是个对图像文件的引用!",
Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
return imageTitle;
}
else
{
directory = new DirectoryInfo(subPath);
x++;
}
}
}
if (x != pieces.Length - 1)
{
LMessageBox.Show("  当前插入点位置处的文本不像是个对图像文件的引用!",
Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
return imageTitle;
}
LMessageBox.Show(directory.FullName + pieces[pieces.Length - 1]);
return imageTitle;
}
private void MiRedo_Click(object sender, RoutedEventArgs e)
{
Globals.MainWindow.Redo();
}
private void MiUndo_Click(object sender, RoutedEventArgs e)
{
Globals.MainWindow.Undo();
}
private void MiPaste_Click(object sender, RoutedEventArgs e)
{
Globals.MainWindow.Paste();
}
private void MiCopy_Click(object sender, RoutedEventArgs e)
{
Globals.MainWindow.Copy();
}
private void MiCut_Click(object sender, RoutedEventArgs e)
{
Globals.MainWindow.Cut();
}
private bool isCompareAreaEditor = false;
/// <summary>
/// 是否对照区。对照区不能双击预览。
/// </summary>
public bool IsCompareAreaEditor
{
get { return isCompareAreaEditor; }
}
/// <summary>
/// 双击标头编译当前文档并预览。
/// </summary>
private void HeaderTextBlock_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (this.IsCompareAreaEditor) return;
if (e.ClickCount == 2)
{
this.CompileAndPreviewHtml();
}
}
/// <summary>
/// 编译并调用外部浏览器预览。
/// </summary>
private void MiCallSystemDefaultWebBrowserPreview_Click(object sender, RoutedEventArgs e)
{
this.CompileAndPreviewHtml(true);
}
/// <summary>
/// 编译当前 Markdown 文档并在右工具栏中预览。
/// </summary>
void miCompileAndPreview_Click(object sender, RoutedEventArgs e)
{
this.CompileAndPreviewHtml();
}
void closeBorder_MouseEnter(object sender, MouseEventArgs e)
{
this.closeBorder.Background = Brushes.LightGray;
}
void closeBorder_MouseLeave(object sender, MouseEventArgs e)
{
this.closeBorder.Background = Brushes.Transparent;
}
/// <summary>
/// 向编辑区拖入图像文件时,自动复制到图像资源文件夹并自动生成链接。
/// </summary>
void MarkdownEditor_Drop(object sender, DragEventArgs e)
{
Array dragDataArray = ((System.Array)e.Data.GetData(DataFormats.FileDrop));
if (dragDataArray == null || dragDataArray.Length <= 0) return;
var filenames = new System.Collections.Specialized.StringCollection();
foreach (string s in dragDataArray)
{
filenames.Add(s);
}
if (TryToDropResourcesFiles(filenames) == string.Empty) return;
}
private List<TextCommandItem> currentTextCommands = null;
private int currentTextCommandIndex = -1;
/// <summary>
/// 处理模拟 Vim 命令模式下的命令。
/// </summary>
private void MarkdownEditor_PreviewTextInput(object sender, TextCompositionEventArgs e)
{
if (this.EditingMode == EditingMode.VimMode)
{
e.Handled = true;//如果是命令,阻止其通过。
RunTextCommand(TextCommandManager.InputedCommandText, e.Text);
}
}
private void RunTextCommand(string prefixCommandText, string commandText)
{
List<TextCommandItem> matchedTextCommands;
var exeResult = TextCommandManager.TryToRun(prefixCommandText, commandText, out matchedTextCommands);
if (exeResult == "完成")
{
Globals.MainWindow.tbTextCommand.TextDecorations = TextDecorations.Strikethrough;
Globals.MainWindow.tbTextCommandResult.Foreground = Brushes.DarkGreen;
Globals.MainWindow.tbTextCommandPrompt.Text = "";
}
else
{
Globals.MainWindow.tbTextCommand.TextDecorations = null;
Globals.MainWindow.tbTextCommandResult.Foreground = Brushes.Black;
}
//注意顺序
Globals.MainWindow.tbTextCommand.Text = prefixCommandText + commandText;
if (matchedTextCommands.Count < 1)
{
currentTextCommands = null;
//无此命令
Globals.MainWindow.tbTextCommandDescription.Text = "--";
Globals.MainWindow.tbTextCommandResult.Text = "无此命令!";
}
else
{
currentTextCommands = matchedTextCommands;
//有一个以上的匹配命令
var win = Globals.MainWindow;
win.tbTextCommandDescription.Text = matchedTextCommands[0].Description;
currentTextCommandIndex = 0;
win.tbTextCommandResult.Text = exeResult;
win.spanOfMatchedCommandTextCount.Inlines.Clear();
win.spanOfMatchedCommandTextCount.Inlines.Add(1 + "/" + matchedTextCommands.Count.ToString());
}
}
void MarkdownEditor_PreviewKeyDown(object sender, System.Windows.Input.KeyEventArgs e)
{
if (e.Key != Globals.VimKey)
{
vimKeyUpedCount = 0;
}
var isLShift = ((Keyboard.GetKeyStates(Key.LeftShift) & KeyStates.Down) > 0);
var isRShift = ((Keyboard.GetKeyStates(Key.RightShift) & KeyStates.Down) > 0);
if (this.EditingMode == EditingMode.VimMode)
{
//Vim 命令模式下禁止其它操作,但必须保留消息,否则所有命令都不起作用的。
//e.Handled = true;
switch (e.Key)
{
case Key.Up:
case Key.Down:
case Key.Right:
case Key.Left:
{
e.Handled = true; //禁用光标键,因为会造成逻辑问题。
break;
}
case Key.Tab:
{
//切换显示匹配的各条命令。
if (currentTextCommands != null && currentTextCommands.Count > 0)
{
if (isLShift || isRShift)
{
if (currentTextCommandIndex > 0 && currentTextCommandIndex < currentTextCommands.Count)
{
currentTextCommandIndex--;
var win = Globals.MainWindow;
win.spanOfMatchedCommandTextCount.Inlines.Clear();
win.spanOfMatchedCommandTextCount.Inlines.Add(currentTextCommandIndex + 1 + "/" + currentTextCommands.Count);
win.tbTextCommandPrompt.Text = currentTextCommands[currentTextCommandIndex].CommandText;
win.tbTextCommandDescription.Text = currentTextCommands[currentTextCommandIndex].Description;
win.tbTextCommandResult.Text = "回车执行";
}
}
else
{
if (currentTextCommandIndex >= 0 && currentTextCommandIndex < currentTextCommands.Count - 1)
{
currentTextCommandIndex++;
var win = Globals.MainWindow;
win.spanOfMatchedCommandTextCount.Inlines.Clear();
win.spanOfMatchedCommandTextCount.Inlines.Add(currentTextCommandIndex + 1 + "/" + currentTextCommands.Count);
win.tbTextCommandPrompt.Text = currentTextCommands[currentTextCommandIndex].CommandText;
win.tbTextCommandDescription.Text = currentTextCommands[currentTextCommandIndex].Description;
win.tbTextCommandResult.Text = "回车执行";
}
}
}
e.Handled = true;
break;
}
case Key.Return:
{
//强制执行第一条匹配的命令
if (currentTextCommands != null && currentTextCommands.Count > 0 &&
currentTextCommandIndex >= 0 && currentTextCommandIndex < currentTextCommands.Count)
{
var cmdItem = currentTextCommands[currentTextCommandIndex];
var result = cmdItem.Run(cmdItem);
if (result)
{
TextCommandManager.InputedCommandText = "";
var win = Globals.MainWindow;
win.spanOfMatchedCommandTextCount.Inlines.Clear();
win.tbTextCommand.Text = cmdItem.CommandText;
win.tbTextCommandResult.Text = "完成";
win.tbTextCommandResult.Foreground = Brushes.DarkGreen;
win.tbTextCommand.TextDecorations = TextDecorations.Strikethrough;
}
else
{
var win = Globals.MainWindow;
win.spanOfMatchedCommandTextCount.Inlines.Clear();
win.tbTextCommandResult.Text = "无此命令";
win.tbTextCommandResult.Foreground = Brushes.Black;
win.tbTextCommand.TextDecorations = null;
}
}
e.Handled = true;
break;
}
case Key.Back:
{
var inputedCommandText = TextCommandManager.InputedCommandText;
var length = inputedCommandText.Length;
if (length > 0)
{
var win = Globals.MainWindow;
TextCommandManager.InputedCommandText = inputedCommandText.Substring(0, length - 1);
if (string.IsNullOrWhiteSpace(TextCommandManager.InputedCommandText))
{
currentTextCommands = null;
}
else
{
currentTextCommands = TextCommandManager.GetMatchedTextCommands(TextCommandManager.InputedCommandText);
}
if (currentTextCommands == null || currentTextCommands.Count <= 0)
{
currentTextCommandIndex = -1;
win.tbTextCommand.Text = TextCommandManager.InputedCommandText;
win.tbTextCommandResult.Text = "无此命令"; //有多个符合条件的命令。
win.tbTextCommandDescription.Text = "";
win.spanOfMatchedCommandTextCount.Inlines.Clear();
}
else
{
currentTextCommandIndex = 0;
win.tbTextCommand.Text = TextCommandManager.InputedCommandText;
win.tbTextCommandResult.Text = "等待继续输入..."; //有多个符合条件的命令。
win.tbTextCommandDescription.Text = currentTextCommands[0].Description;
win.spanOfMatchedCommandTextCount.Inlines.Clear();
win.spanOfMatchedCommandTextCount.Inlines.Add(1 + "/" + currentTextCommands.Count);
}
}
e.Handled = true;
break;
}
}
return;
}
bool isCtrl = false;
if ((Keyboard.GetKeyStates(Key.RightCtrl) & KeyStates.Down) > 0 || (Keyboard.GetKeyStates(Key.LeftCtrl) & KeyStates.Down) > 0)
{
isCtrl = true;
}
bool isShift = false;
if ((Keyboard.GetKeyStates(Key.RightShift) & KeyStates.Down) > 0 || (Keyboard.GetKeyStates(Key.LeftShift) & KeyStates.Down) > 0)
{
isShift = true;
}
bool isAlt = false;
if ((Keyboard.GetKeyStates(Key.RightAlt) & KeyStates.Down) > 0 || (Keyboard.GetKeyStates(Key.LeftAlt) & KeyStates.Down) > 0)
{
isAlt = true;
}
// 普通模式下的处理。
if (this.EditingMode == EditingMode.NormalMode)
{
switch (e.Key)
{
case Key.V:
{
if (isCtrl && isAlt == false)
{
if (isShift)
{
//粘贴代码
PasteCode();
}
else
{
if (string.IsNullOrEmpty(Clipboard.GetText()) == false)
{
this.editorBase.Paste();
}
else
{
//如果是粘贴图像,粘贴后不再继续操作。
var result = TryToPasteResourceFileLink();
if (result == string.Empty)
{
e.Handled = true;
break;
}
LMessageBox.Show(result, Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
}
}
e.Handled = true;
}
break;
}
case Key.F4:
{
if (isCtrl && isShift == false && isAlt == false)
{
Close();
e.Handled = true;
}
else
{
if (!isShift && !isAlt)
{
//单F4,生成选择题标签集
if (IsExamEnabled) editorBase.InsertChoiceQuestionTags();
}
}
break;
}
case Key.W:
{
if (isCtrl && isShift == false && isAlt == false)
{
Close();
e.Handled = true;
}
break;
}
case Key.F2:
{
if (isCtrl == false && isShift == false && isAlt == false)
{
if (this.EditorBase.SelectionLength <= 0)
{
//如果没选中文本,说明是打算输入一个新填空项。
//这时候直接定位到两个括号中间好。
this.editorBase.SelectedText = "【" + this.EditorBase.SelectedText + "】";
this.editorBase.Select(this.editorBase.SelectionStart + 1, 0);
}
else
{
//如果已经选定了一些文本,说明是打算将这些文本作为一个填空项,
//此时一般没必要再修改文本,直接定位到右括弧后面,可以接着输入,这样更方便些。
this.editorBase.SelectedText = "【" + this.EditorBase.SelectedText + "】";
this.editorBase.Select(this.editorBase.SelectionStart + this.editorBase.SelectionLength, 0);
}
}
break;
}
case Key.F8:
{
if (!isCtrl && !isAlt && !isShift)
{
if (IsExamEnabled) this.editorBase.InsertJudgeQuestionTags("正确");
}
break;
}
case Key.F9:
{
if (!isCtrl && !isAlt && !isShift)
{
if (IsExamEnabled) this.editorBase.InsertJudgeQuestionTags("错误");
}
break;
}
case Key.P:
{
if (isCtrl && !isAlt)
{
if (isShift)
{
Globals.MainWindow.PrintPreview();
}
else
{
Globals.MainWindow.PrintDocument();
}
e.Handled = true;
}
break;
}
case Key.R:
{
if (isAlt == false && isCtrl && isShift == false)
{
this.editorBase.WordWrap = !this.editorBase.WordWrap;
if (Globals.MainWindow != null)
{
Globals.MainWindow.RefreshTextAutoWrapToStatusBar();
}
e.Handled = true;
}
break;
}
case Key.F:
{
if (isCtrl)
{
if (isAlt)
{
//Ctrl+Alt+F
//移动到MainWindow//2017年1月10日
}
else
{
//Ctrl+F
//miFindInActiveDocument_Click(sender, e);
//此方法也曾经被移动到MainWindow。
//但问题在于:这样子做会导致在Html预览页中按Ctrl+F无法弹出查找框来查找Html页面中的文本,
// 又或者会导致在Html预览页面中按Ctrl+F时也弹出Markdown查找框——这显得十分累赘。
if (Globals.MainWindow.rdFindAndReplace.ActualHeight <= 20)
{
Globals.MainWindow.rdFindAndReplace.Height = new GridLength(0, GridUnitType.Auto);
Globals.MainWindow.cmbFindText.UpdateLayout();
}
var editor = Globals.MainWindow.ActivedEditor;
if (editor != null)
{
Globals.MainWindow.cmbFindText.Text = editor.EditorBase.SelectedText;
}
else return;
//if (Globals.MainWindow.mainTabControl.Items.Count <= 0)
//{
// Globals.MainWindow.cmbSearchArea.SelectedIndex = 2;//全工作区查找
//}
//else
//{
Globals.MainWindow.cmbSearchArea.SelectedIndex = 0;//当前文档查找
//}
Globals.MainWindow.cmbFindText.Focus();
if (Globals.MainWindow.cmbFindText.Text.Length > 0)
{
Globals.MainWindow.Find();
}
//e.Handled = true;
}
}
break;
}
}
}
}
private enum ResourceType { MarkdownFile, ImageFile, SoundFile, VedioFile };
/// <summary>
/// 向编辑区拖入图像、音乐、视频文件时,自动尝试复制图像、音乐、视频文件到对应资源目录下并自动生成链接文本。
/// </summary>
/// <param name="dropList">拖动的图像、音乐、视频文件的列表。</param>
public string TryToDropResourcesFiles(System.Collections.Specialized.StringCollection dropList, bool refreshWorkspace = false)
{
//try
//{
if (dropList.Count > 0)
{
var vedioFilesCount = 0;//暂时不支持视频文件拖放,因为无法保证浏览器兼容。
foreach (string fullPath in dropList)
{
var resourceType = ResourceType.MarkdownFile;
string shortResourceDirectoryName = "";
if (WorkspaceTreeViewItem.IsValidateImageFilePath(fullPath))
{
resourceType = ResourceType.ImageFile;
shortResourceDirectoryName = "Images~";
}
else if (WorkspaceTreeViewItem.IsValidateSoundFilePath(fullPath))
{
resourceType = ResourceType.SoundFile;
shortResourceDirectoryName = "Sounds~";
}
else if (WorkspaceTreeViewItem.IsValidateVedioFilePath(fullPath))
{
resourceType = ResourceType.VedioFile;
shortResourceDirectoryName = "Vedio~";
}
else
{
InsertAbsoluteFileOrDirectoryPathLink(fullPath);
continue;
}
switch (resourceType)
{
case ResourceType.ImageFile:
{
if (Directory.Exists(this.ImageResourceDirectoryPath) == false)
{
Directory.CreateDirectory(this.ImageResourceDirectoryPath);
}
break;
}
case ResourceType.SoundFile:
{
if (Directory.Exists(this.SoundResourceDirectoryPath) == false)
{
Directory.CreateDirectory(this.SoundResourceDirectoryPath);
}
break;
}
case ResourceType.VedioFile:
{
if (Directory.Exists(this.VedioResourceDirectoryPath) == false)
{
Directory.CreateDirectory(this.VedioResourceDirectoryPath);
}
break;
}
}
var shortFileInfo = new FileInfo(fullPath);
var shortFileName = shortFileInfo.Name;
string destFilePath;
switch (resourceType)
{
case ResourceType.ImageFile:
{
destFilePath = this.ImageResourceDirectoryPath + shortFileName;
break;
}
case ResourceType.SoundFile:
{
destFilePath = this.SoundResourceDirectoryPath + shortFileName;
break;
}
case ResourceType.VedioFile:
default:
{
LMessageBox.Show("  要插入兼容各版本浏览器的视频 Html 标签会导致文档很繁琐。故在想到好办法之前,不支持此功能。",
Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
//destFilePath = this.VedioResourceDirectoryPath + shortFileName;
//break;
return "";
}
}
if (fullPath.ToLower().StartsWith(Globals.PathOfWorkspace.ToLower()))
{
//如果资源文件就在工作区内,则生成相对引用链接而不是复制资源文件。
var srcPath = fullPath.ToLower();
var workspacePath = Globals.PathOfWorkspace.ToLower();
//思路:先找出共同的、最接近的祖先级目录
var separator = new char[] { '\\', '/' };
string[] src = srcPath.Split(separator, StringSplitOptions.RemoveEmptyEntries);
string[] dest = destFilePath.ToLower().Split(separator, StringSplitOptions.RemoveEmptyEntries);
var minIndex = Math.Min(src.Length, dest.Length) - 1;
if (minIndex < 0) return string.Empty;
int aIndex = -1;
for (int i = 0; i <= minIndex; i++)
{
if (src[i] != dest[i])
{
aIndex = i;
break;
}
}
//生成相对路径的前半部分,类似(../../)这样子。
string[] file = this.FullFilePath.Split(separator, StringSplitOptions.RemoveEmptyEntries);
string header;
string tail;
if (aIndex < 0)
{
header = "";//就已经在同一个文件的资源目录中
tail = (this.shortFileName.ToLower().EndsWith(".md") ? this.shortFileName.Substring(0, this.shortFileName.Length - 3) : this.shortFileName)
+ $"~/{shortResourceDirectoryName}/" + shortFileName;
}
else
{
header = BuildHtmlRefText(file.Length - aIndex - 1);
//生成相对路径的后半部分。
StringBuilder sb = new StringBuilder();
for (int i = aIndex; i < src.Length; i++)
{
sb.Append(src[i] + "/");
}
tail = sb.ToString();
if (tail.EndsWith("/"))
{
tail = tail.Substring(0, tail.Length - 1);
}
}
//this.editorBase.SelectedText = "<img alt='" + shortImageFileName + "' src='" + header + tail + "' />";
//var shortTitle = MainWindow.FormatDocumentTitle(shortImageFileInfo.Name.Substring(0, shortImageFileInfo.Name.Length - shortImageFileInfo.Extension.Length));
var shortTitle = MainWindow.FormatDocumentTitle(WorkspaceTreeViewItem.GetCommentShortName(shortFileInfo.FullName));
if (shortTitle.EndsWith(shortFileInfo.Extension))
{
shortTitle = shortTitle.Substring(0, shortTitle.Length - shortFileInfo.Extension.Length);
}
switch (resourceType)
{
case ResourceType.ImageFile:
{
if (dropList.Count == 1)
{
this.editorBase.SelectedText = "![" + shortTitle + "](" + header + tail + ")";
this.editorBase.Select(this.editorBase.SelectionStart + 2, shortTitle.Length);
}
else
{
this.editorBase.SelectedText = "![" + shortTitle + "](" + header + tail + ")\r\n";
this.editorBase.Select(this.editorBase.SelectionStart + this.editorBase.SelectionLength, 0);
}
break;
}
case ResourceType.SoundFile:
{
if (dropList.Count == 1)
{
this.editorBase.SelectedText = "<embed src=\"" + header + tail + "\" width=\"250\" height=\"60\"></embed>";
this.editorBase.Select(this.editorBase.SelectionStart + 12, (header + tail).Length);
}
else
{
this.editorBase.SelectedText = "<embed src=\"" + header + tail + "\" width=\"250\" height=\"60\"></embed>\r\n";
this.editorBase.Select(this.editorBase.SelectionStart + 12, (header + tail).Length);
}
break;
}
case ResourceType.VedioFile:
{
//if (dropList.Count == 1)
//{
// this.editorBase.SelectedText = "<embed src=\"" + header + tail + "\" width =\"250\" height =\"180\"></embed>";
// this.editorBase.Select(this.editorBase.SelectionStart + 12, (header + tail).Length);
//}
//else
//{
// this.editorBase.SelectedText = "<embed src=\"" + header + tail + "\" width =\"250\" height =\"180\"></embed>\r\n";
// this.editorBase.Select(this.editorBase.SelectionStart + 12, (header + tail).Length);
//}
LMessageBox.Show("  要插入兼容各版本浏览器的视频 Html 标签会导致文档很繁琐。故在想到好办法之前,不支持此功能。",
Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
break;
}
}
}
else
{
//如果不在工作区内,则直接将文件复制到工作区内对应资源文件目录。
if (File.Exists(destFilePath) == false)
{
var destFileInfo = new FileInfo(destFilePath);
var destShortPinYinName = ChinesePinYin.ToChinesePinYinText(destFileInfo.Name.Substring(0, destFileInfo.Name.Length - destFileInfo.Extension.Length));
var destShortCommentName = destFileInfo.Name;
var oldExtension = destFileInfo.Extension;
shortFileName = destShortPinYinName + oldExtension;
destFilePath = destFileInfo.DirectoryName + "\\" + shortFileName;
if (Directory.Exists(destFileInfo.DirectoryName) == false)
{
Directory.CreateDirectory(destFileInfo.DirectoryName);
}
File.Copy(fullPath, destFilePath);
destFileInfo = new FileInfo(destFilePath);
var commentTextFilePath = destFileInfo.Directory.FullName + "\\" + "~.txt";
if (File.Exists(commentTextFilePath))
{
using (var stream = File.AppendText(commentTextFilePath))
{
stream.WriteLine($"{destShortPinYinName}{oldExtension}|{destShortCommentName}");
}
}
else
{
using (var stream = File.CreateText(commentTextFilePath))
{
stream.WriteLine($"{destShortPinYinName}{oldExtension}|{destShortCommentName}");
}
}
}
//this.editorBase.SelectedText = "<img alt='" + shortImageFileName + "' src='./" + ShortResourceDirectoryPath + "/Images~/" + shortImageFileName + "' />";
//var shortTitle = shortImageFileInfo.Name.Substring(0, shortImageFileInfo.Name.Length - shortImageFileInfo.Extension.Length);
var shortTitle = MainWindow.FormatDocumentTitle(WorkspaceTreeViewItem.GetCommentShortName(shortFileInfo.FullName));
if (shortTitle.EndsWith(shortFileInfo.Extension))
{
shortTitle = shortTitle.Substring(0, shortTitle.Length - shortFileInfo.Extension.Length);
}
switch (resourceType)
{
case ResourceType.ImageFile:
{
Globals.MainWindow.InsertSubWorkspaceTreeViewItem(this.ImageResourceDirectoryPath, destFilePath);
if (dropList.Count == 1)//不然会出错。
{
this.editorBase.SelectedText = "![" + shortTitle + "](./" + ShortResourceDirectoryPath + "/Images~/" + shortFileName + ")";
this.editorBase.Select(this.editorBase.SelectionStart + 2, destFilePath.Length);
}
else
{
this.editorBase.SelectedText = "![" + shortTitle + "](./" + ShortResourceDirectoryPath + "/Images~/" + shortFileName + ")\r\n";
this.editorBase.Select(this.editorBase.SelectionStart + this.editorBase.SelectionLength, 0);
}
break;
}
case ResourceType.SoundFile:
{
Globals.MainWindow.InsertSubWorkspaceTreeViewItem(this.SoundResourceDirectoryPath, destFilePath);
if (dropList.Count == 1)
{
this.editorBase.SelectedText = "<embed src=\"./" + ShortResourceDirectoryPath + "/Sounds~/" + shortFileName + "\" width =\"250\" height =\"60\"></embed>";
this.editorBase.Select(this.editorBase.SelectionStart + 12, ShortResourceDirectoryPath.Length + 11 + shortFileName.Length);
}
else
{
this.editorBase.SelectedText = "<embed src=\"./" + ShortResourceDirectoryPath + "/Sounds~/" + shortFileName + "\" width =\"250\" height =\"60\"></embed>\r\n";
//this.editorBase.Select(this.editorBase.SelectionStart + 12, ShortResourceDirectoryPath.Length + 11 + shortFileName.Length);
//用上面这行会导致第二个链接被嵌入在第一个链接的 src="" 的引号中间。
this.editorBase.Select(this.editorBase.SelectionStart + this.editorBase.SelectionLength, 0);
}
break;
}
case ResourceType.VedioFile:
{
//Globals.MainWindow.InsertSubWorkspaceTreeViewItem(this.VedioResourceDirectoryPath, destFilePath);
//if (dropList.Count == 1)
//{
// this.editorBase.SelectedText = "<embed src=\"./" + ShortResourceDirectoryPath + "/Vedios~/" + shortFileName + "\" width =\"250\" height =\"180\"></embed>";
// this.editorBase.Select(this.editorBase.SelectionStart + 12, ShortResourceDirectoryPath.Length + 11 + ShortFileName.Length);
//}
//else
//{
// this.editorBase.SelectedText = "<embed src=\"./" + ShortResourceDirectoryPath + "/Vedios~/" + shortFileName + "\" width =\"250\" height =\"180\"></embed>\r\n";
// this.editorBase.Select(this.editorBase.SelectionStart + 12, ShortResourceDirectoryPath.Length + 11 + ShortFileName.Length);
//}
vedioFilesCount++;
break;
}
}
}
}
if (refreshWorkspace)
{
//在资源管理器界面进行刷新。
Globals.MainWindow.WorkspaceManager.RefreshWorkspaceTreeView(null);
//DONE: 已改掉,这个仅作将来备用,2017年1月21日。
}
if (vedioFilesCount > 0)
{
LMessageBox.Show("  要插入兼容各版本浏览器的视频 Html 标签会导致文档很繁琐。故在想到好办法之前,不支持添加视频链接。",
Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
}
}
else
{
return "没有资源文件可以拖放。";
}
//}
//catch (Exception ex)
//{
// LMessageBox.Show(ex.Message, Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
// return ex.Message;
//}
return string.Empty;
}
private void InsertAbsoluteFileOrDirectoryPathLink(string fullPath)
{
if (File.Exists(fullPath) == false && Directory.Exists(fullPath) == false) return;
var fileInfo = new FileInfo(fullPath);
this.editorBase.SelectedText = "[" + fileInfo.Name + "](file:///" + fullPath + ")";
this.editorBase.Select(this.editorBase.SelectionStart + this.editorBase.SelectionLength, 0);
}
/// <summary>
/// 创建引用的相对路径字符串前缀。
/// </summary>
/// <param name="layer">相对层级差。</param>
private string BuildHtmlRefText(int layer)
{
StringBuilder sb = new StringBuilder();
for (int i = 0; i < layer; i++)
{
sb.Append("../");
}
return sb.ToString();
}
/// <summary>
/// 尝试向当前编辑器粘贴图像数据块或音频文件(将来可能包括视频文件)。
/// 粘贴时会要求用户给图像起个名称,然后快速保存到当前文档的图像资源文件夹中,
/// 并自动生成对该文件的链接文本。
/// * 注意:如果 Windows 系统的剪贴板被其它程序占用,会无法粘贴。
/// </summary>
private string TryToPasteResourceFileLink()
{
WinForm.IDataObject oDataObj = WinForm.Clipboard.GetDataObject();
if (oDataObj != null && System.Windows.Clipboard.ContainsData(DataFormats.Bitmap))
{
var shortImageFileCommentName = InputBox.Show(Globals.AppName, "请输入图像文件名(不要后缀名,不要以“_”开头,尽量以字母开头):", "", true, "注意:中文字符会被自动转换为拼音首字母。");
if (string.IsNullOrEmpty(shortImageFileCommentName)) return "用户未输入文件名。";
var shortImageFileName = ChinesePinYin.ToChinesePinYinText(shortImageFileCommentName);
try
{
System.Drawing.Image oImgObj = oDataObj.GetData(DataFormats.Bitmap, true) as System.Drawing.Image;
if (Directory.Exists(this.ImageResourceDirectoryPath) == false)
{
Directory.CreateDirectory(this.ImageResourceDirectoryPath);
}
var destFilePath = this.ImageResourceDirectoryPath + shortImageFileName + ".png";
if (File.Exists(destFilePath))
{
var result = LMessageBox.Show("  同名文件已经存在,继续操作会覆盖原文件。\r\n\r\n  要继续吗?",
Globals.AppName, MessageBoxButton.YesNo, MessageBoxImage.Warning);
if (result != MessageBoxResult.Yes)
{
return "用户放弃操作!";
}
}
//this.editorBase.SelectedText = "<img alt='" + shortImageFileName + "' width='" + oImgObj.Width + "px' height='" + oImgObj.Height +
//"px' src='./" + ShortResourceDirectoryPath + "/Images~/" + shortImageFileName + ".png' />";
//oImgObj.Save(destFilePath, System.Drawing.Imaging.ImageFormat.Png);
//用上面这行执行第二次粘贴同一个图像数据到同名文件时会出现异常。
using (MemoryStream mStream = new MemoryStream())
{
oImgObj.Save(mStream, System.Drawing.Imaging.ImageFormat.Png);
oImgObj.Dispose();
using (FileStream fs = new FileStream(destFilePath, FileMode.Create))
{
mStream.WriteTo(fs);
}
}
var destFileInfo = new FileInfo(destFilePath);
var commentTextFilePath = destFileInfo.Directory.FullName + "\\" + "~.txt";
if (File.Exists(commentTextFilePath))
{
using (var stream = File.AppendText(commentTextFilePath))
{
stream.WriteLine($"{shortImageFileName}{destFileInfo.Extension}|{shortImageFileCommentName}{destFileInfo.Extension}");
}
}
else
{
using (var stream = File.CreateText(commentTextFilePath))
{
stream.WriteLine($"{shortImageFileName}{destFileInfo.Extension}|{shortImageFileCommentName}{destFileInfo.Extension}");
}
}
this.editorBase.SelectedText = "![" + shortImageFileCommentName + "](" + ShortResourceDirectoryPath + "/Images~/" + shortImageFileName + ".png)";
WinForm.Clipboard.Clear();//否则下次复制、粘贴文本时总会弹出一个对话框要求设置要粘贴的图像的文件名。
//在资源管理器界面进行刷新。
//Globals.MainWindow.WorkspaceManager.Refresh(null);
//效率太低
Globals.MainWindow.InsertSubWorkspaceTreeViewItem(this.ImageResourceDirectoryPath, destFilePath);
#region 刷新图像预览区域和可能存在的图像预览窗口中的图像。
//图像预览区域中直接显示当前粘贴的图像。
//Globals.MainWindow.ImagePreview.Source = L.BuildBitmapImage(destFileInfo.FullName);
var imageTitle = PreviewLinkedImageFile(false);
//图像预览窗口则要看当前预览窗口是否正好显示同名的文件(因为现在允许同时打开多个预览窗口)。
foreach (var win in Globals.MainWindow.OwnedWindows)
{
var ipw = win as ImagePreviewWindow;
if (ipw == null) continue;
if (destFilePath.ToLower() == ipw.ImageFilePath.ToLower())
{
ipw.previewImage.Source = L.BuildBitmapImage(destFilePath);
ipw.ImageTitle = imageTitle;
//通常不需要这行,但须考虑“手动更改Markdown图像图像链接中的标题”的情况。
}
}
#endregion
//var imgResourceDirectoryPath = this.ImageResourceDirectoryPath;
//if (imgResourceDirectoryPath.EndsWith("\\"))
// imgResourceDirectoryPath = imgResourceDirectoryPath.Substring(0, imgResourceDirectoryPath.Length - 1).ToLower();
//var imgResourceItem = Globals.MainWindow.FindWorkspaceTreeViewItem(imgResourceDirectoryPath);
//imgResourceItem.Items.Add(new WorkspaceTreeViewItem(destFilePath, Globals.MainWindow));
}
catch (Exception ex)
{
return ex.Message;
}
}
else
{
return TryToDropResourcesFiles(Clipboard.GetFileDropList(), false);
}
return string.Empty;
}
/// <summary>
/// 当前 Markdown 文件对应的资源文件夹的短名称。
/// </summary>
public string ShortResourceDirectoryPath
{
get
{
var shortFileName = this.ShortFileName.ToLower();
if (string.IsNullOrEmpty(shortFileName)) return string.Empty;
if (shortFileName.EndsWith(".md")) return shortFileName.Substring(0, shortFileName.Length - 3) + "~";
return string.Empty;
}
}
/// <summary>
/// 当前 Markdown 文件对应的资源文件夹的完整路径。
/// </summary>
public string ResourceDirectoryPath
{
get
{
if (File.Exists(this.FullFilePath) == false) return string.Empty;
return this.FullFilePath.Substring(0, this.fullFilePath.Length - 3) + "~\\";
}
}
/// <summary>
/// 当前 Markdown 文件对应的图像资源文件夹的完整路径。
/// </summary>
public string ImageResourceDirectoryPath
{
get
{
var resourceDirectoryPath = this.ResourceDirectoryPath;
if (string.IsNullOrEmpty(resourceDirectoryPath))
{
return string.Empty;
}
return resourceDirectoryPath + "Images~\\";
}
}
/// <summary>
/// 当前 Markdown 文件对应的声音资源文件夹的完整路径。
/// </summary>
public string SoundResourceDirectoryPath
{
get
{
var resourceDirectoryPath = this.ResourceDirectoryPath;
if (string.IsNullOrEmpty(resourceDirectoryPath))
{
return string.Empty;
}
return resourceDirectoryPath + "Sounds~\\";
}
}
/// <summary>
/// 当前 Markdown 文件对应的视频资源文件夹的完整路径。
/// </summary>
public string VedioResourceDirectoryPath
{
get
{
var resourceDirectoryPath = this.ResourceDirectoryPath;
if (string.IsNullOrEmpty(resourceDirectoryPath))
{
return string.Empty;
}
return resourceDirectoryPath + "Vedios~\\";
}
}
/// <summary>
/// 文档被修改状态变化时,更改编辑器标头状态字符。
/// </summary>
void UndoStack_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
//用于判断文档是否已被修改。
if (e.PropertyName == "IsOriginalFile")
{
modifiedTextBlock.Text = editorBase.Document.UndoStack.IsOriginalFile ? string.Empty : "*";
}
}
private string htmlDocumentPageHeader = string.Empty;
/// <summary>
/// Html 页面标题文本
/// </summary>
public string HtmlDocumentPageHeader
{
get
{
if (string.IsNullOrEmpty(this.htmlDocumentPageHeader))
{
return string.Empty;
}
return this.htmlDocumentPageHeader;
}
}
private string htmlDocumentTitle = string.Empty;
/// <summary>
/// 编译后的html文档的标题,在MarkDown文档中以%(或%)顶格开头。
/// </summary>
public string HtmlDocumentTitle
{
get
{
//if (string.IsNullOrEmpty(this.editorBase.Text)) return string.Empty;
//string[] lines = this.editorBase.Text.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
//foreach (var line in lines)
//{
// char[] trimStartChars = new char[] { '\t', ' ', ' ' };
// var s = line.TrimStart(trimStartChars);
// if (s.StartsWith("%") || s.StartsWith("%"))
// {
// if (s.Length >= 2)
// {
// if (s[1] != '%' && s[1] != '%')
// return s.Substring(1).Replace("%", "").Replace("%", "").Replace("\t", "").TrimEnd(new char[] { '\t', ' ', ' ' });
// }
// }
//}
//return string.Empty;
if (string.IsNullOrEmpty(this.htmlDocumentTitle))
{
return "Markdown Document";
}
return this.htmlDocumentTitle;
}
}
private string footerText = string.Empty;
/// <summary>
/// 编译后的html文本的脚注文本,在MarkDown文档档中以@(或·)顶格开头。
/// </summary>
public string FooterText
{
get { return footerText; }
}
private bool isExamEnabled = false;
/// <summary>
/// 是否启用试题编辑功能。(依赖“自动完成”功能)
/// </summary>
public bool IsExamEnabled
{
get
{
return isExamEnabled;
}
set
{
isExamEnabled = value;
this.editorBase.IsExamEnabled = isExamEnabled;
}
}
private bool isAutoCompletionEnabled = false;
/// <summary>
/// 是否启用“自动完成”功能。
/// </summary>
public bool IsAutoCompletionEnabled
{
get { return isAutoCompletionEnabled; }
set
{
isAutoCompletionEnabled = value;
this.editorBase.IsAutoCompletionEnabled = value;
}
}
private bool isEnToChineseDictEnabled = false;
/// <summary>
/// 是否启用英译中词条自动提示功能。(依赖“自动完成”功能)
/// </summary>
public bool IsEnToChineseDictEnabled
{
get { return isEnToChineseDictEnabled; }
set
{
isEnToChineseDictEnabled = value;
this.editorBase.IsEnToChineseDictEnabled = value;
}
}
private MarkDownEditorBase.HighlightingType highlightingSetting = MarkDownEditorBase.HighlightingType.Advance;
/// <summary>
/// 采用何种高亮显示方案。
/// </summary>
public MarkDownEditorBase.HighlightingType HighlightingSetting
{
get { return highlightingSetting; }
set
{
highlightingSetting = value;
this.editorBase.HighlightingSetting = value;
}
}
private bool isShowSpaces = false;
/// <summary>
/// 决定是否显示空格。
/// </summary>
public bool IsShowSpaces
{
get { return isShowSpaces; }
set
{
isShowSpaces = value;
this.editorBase.IsShowSpaces = value;
}
}
private bool isShowEndOfLine = false;
/// <summary>
/// 决定是否显示段落标记。
/// </summary>
public bool IsShowEndOfLine
{
get { return isShowEndOfLine; }
set
{
isShowEndOfLine = value;
this.editorBase.IsShowEndOfLine = value;
}
}
private bool isShowTabs = false;
/// <summary>
/// 决定是否显示空格。
/// </summary>
public bool IsShowTabs
{
get { return isShowTabs; }
set
{
isShowTabs = value;
this.editorBase.IsShowTabs = value;
}
}
private bool isShowLineNumbers = true;
/// <summary>
/// 决定是否显示行号。
/// </summary>
public bool IsShowLineNumbers
{
get { return isShowLineNumbers; }
set
{
isShowLineNumbers = value;
this.editorBase.ShowLineNumbers = value;
}
}
private bool isTextAutoWrap = false;
/// <summary>
/// 决定是否显示空格。
/// </summary>
public bool IsTextAutoWrap
{
get { return isTextAutoWrap; }
set
{
isTextAutoWrap = value;
this.editorBase.WordWrap = value;
}
}
/// <summary>
/// 编译当前编辑器中的 Markdown 文本并预览。
/// </summary>
/// <param name="callSystemDefaultWebBrowser">是否调用 Windows 系统默认浏览器预览。</param>
public void CompileAndPreviewHtml(bool callSystemDefaultWebBrowser = false)
{
if (File.Exists(this.fullFilePath) == false || this.editorBase.IsModified)
{
var result = LMessageBox.Show("  文件需要先保存才可以预览!要保存吗?", Globals.AppName,
MessageBoxButton.OKCancel, MessageBoxImage.Exclamation);
if (result != MessageBoxResult.OK)
{
this.editorBase.Focus();
return;
}
else
{
var saveresult = this.SaveDocument();
if (saveresult != string.Empty)
{
LMessageBox.Show(saveresult, Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
return;
}
}
}
if (this.fullFilePath.ToLower().StartsWith(Globals.PathOfWorkspace.ToLower()) == false)
{
LMessageBox.Show("  只有在默认工作区目录下的文件才可以预览!",
Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Exclamation);
this.editorBase.Focus();
return;
}
string htmlTitle = "";
string newFilePath = CustomMarkdownSupport.Compile(this.fullFilePath,
Globals.MainWindow.HtmlHeadersCollapse, null, 1, ref htmlTitle, false);
if (File.Exists(newFilePath))
{
//this.webBrowser.NavigateToString(htmlDocumentText);
//WinForm内置的WebBrowser总是出现“JSON未定义错误”,而现代浏览器则不会,所以直接调用外部浏览器好了。
//System.Diagnostics.Process.Start(newFilePath);
var destUri = new Uri(newFilePath);//不能加这个前缀
if (callSystemDefaultWebBrowser == false)
{
if (Globals.MainWindow.previewFrame.Source == null ||
Globals.MainWindow.previewFrame.Source.AbsolutePath != destUri.AbsolutePath)
{
Globals.MainWindow.previewFrame.Source = destUri;
}
else Globals.MainWindow.previewFrame.Refresh();
Globals.MainWindow.tcRightToolBar.SelectedItem = Globals.MainWindow.tiHtmlPreview;
Globals.MainWindow.tcRightToolBar.SelectedIndex = 0;
if (Globals.MainWindow.tcRightToolBar.ActualWidth < 100)
{
Globals.MainWindow.cdMainEditArea.Width =
Globals.MainWindow.cdRightToolsArea.Width = new GridLength(3, GridUnitType.Star);
}
this.editorBase.Focus();
}
else
{
System.Diagnostics.Process.Start("explorer.exe", $"\"{newFilePath}\"");
//不能用destUri.AbsoluteUri,因为会进行URL转码导致找不到文件;
//不能加这个前缀"file:///" +
}
}
}
/// <summary>
/// 按照一定的标准切分Markdown文档为一个或多个Markdonw片段,
/// 再编译当前编辑器中的 Markdown 文本为一个(或多个)Html文件并预览。
/// 注意:如果 TextSegment 非空,则只显示一页。
/// </summary>
public void CompileAndPresentateHtml(
CustomMarkdownSupport.PresentateHtmlSplitterType presentateHtmlSplitterType,
bool onlyPresentateHeaders = false, string textSegment = null)
{
if (File.Exists(this.fullFilePath) == false || this.editorBase.IsModified)
{
var result = LMessageBox.Show("  文件需要先保存才可以预览!要保存吗?", Globals.AppName,
MessageBoxButton.OKCancel, MessageBoxImage.Exclamation);
if (result != MessageBoxResult.OK)
{
this.editorBase.Focus();
return;
}
else
{
var saveresult = this.SaveDocument();
if (saveresult != string.Empty)
{
LMessageBox.Show(saveresult, Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
return;
}
}
}
if (this.fullFilePath.ToLower().StartsWith(Globals.PathOfWorkspace.ToLower()) == false)
{
LMessageBox.Show("  只有在默认工作区目录下的文件才可以演示!",
Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Exclamation);
this.editorBase.Focus();
return;
}
List<string> htmlFilesList = new List<string>();
List<string> mdFilesList = new List<string>();
CustomMarkdownSupport.PresentateHtmlSplitterType realPresentateHtmlSplitterType;
if (presentateHtmlSplitterType == CustomMarkdownSupport.PresentateHtmlSplitterType.ByDocument)
{
realPresentateHtmlSplitterType = this.PresentateHtmlSplitterType;
}
else
{
realPresentateHtmlSplitterType = presentateHtmlSplitterType;
}
var urisList = CustomMarkdownSupport.Compile(this.fullFilePath,
Globals.MainWindow.HtmlHeadersCollapse, realPresentateHtmlSplitterType,
ref htmlFilesList, ref mdFilesList, onlyPresentateHeaders, textSegment);
if (urisList != null && urisList.Count > 0)
{
if (string.IsNullOrEmpty(textSegment))
{
//单独弹出一个窗口来演示。
var pw = new PresentationHtmlWindow(this, htmlFilesList, mdFilesList)
{
//Owner = App.Current.MainWindow,
};
pw.UrisList = urisList;
//pw.Title = this.headerTextBlock.Text;
pw.Show();
}
else
{
//在主窗口左工具栏顶部的“Html片段预览区域”预览。
if (presentateHtmlSplitterType == CustomMarkdownSupport.PresentateHtmlSplitterType.TextSegment &&
urisList.Count >= 1)
{
if (Globals.MainWindow.segmentPreviewWebBrowser.Source == null ||
Globals.MainWindow.segmentPreviewWebBrowser.Source.LocalPath != urisList[0].Uri.LocalPath)
{
Globals.MainWindow.segmentPreviewWebBrowser.Source = urisList[0].Uri;
}
else
{
Globals.MainWindow.segmentPreviewWebBrowser.Refresh();
}
if (Globals.MainWindow.tcManagerPanels.SelectedItem != Globals.MainWindow.tiHtmlSegmentPreview)
{
Globals.MainWindow.tcManagerPanels.SelectedItem = Globals.MainWindow.tiHtmlSegmentPreview;
}
//设置片段预览区的高度,移动到MainWindow.xaml.cs中的segmentPreviewWebBrowser_LoadCompleted事件处理方法中
//Globals.MainWindow.rdResourcePreviewArea.Height = new GridLength(0);
//Globals.MainWindow.rdLeftToolsTop.Height = new GridLength(1, GridUnitType.Star);
}
}
}
else
{
switch (realPresentateHtmlSplitterType)
{
case CustomMarkdownSupport.PresentateHtmlSplitterType.Region:
LMessageBox.Show("  未找到可以演示的自定义折叠区。", Globals.AppName,
MessageBoxButton.OK, MessageBoxImage.Warning);
break;
case CustomMarkdownSupport.PresentateHtmlSplitterType.TopLevelHeader:
LMessageBox.Show("  未找到可以演示的一级标题。", Globals.AppName,
MessageBoxButton.OK, MessageBoxImage.Warning);
break;
case CustomMarkdownSupport.PresentateHtmlSplitterType.HorizontalLine:
LMessageBox.Show("  未找到可以演示的水平线切分区域。", Globals.AppName,
MessageBoxButton.OK, MessageBoxImage.Warning);
break;
case CustomMarkdownSupport.PresentateHtmlSplitterType.ByDocument:
LMessageBox.Show("  文档中没有可供演示的有效内容。", Globals.AppName,
MessageBoxButton.OK, MessageBoxImage.Warning);
break;
case CustomMarkdownSupport.PresentateHtmlSplitterType.WholeDocument:
LMessageBox.Show("  文档中没有可供演示的有效内容。", Globals.AppName,
MessageBoxButton.OK, MessageBoxImage.Warning);
break;
case CustomMarkdownSupport.PresentateHtmlSplitterType.TextSegment:
LMessageBox.Show("  未提供可供演示的文本片段。\r\n  请注意:三种注释中只有一种属于真正可以演示的内容。",
Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
break;
}
}
}
/// <summary>
/// 取当前文档中定义的演示模式。如果没有定义,返回“按一级标题演示”。
/// </summary>
public CustomMarkdownSupport.PresentateHtmlSplitterType PresentateHtmlSplitterType
{
get
{
var lines = this.editorBase.Text.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
Regex regex = new Regex(@"^[ ]{0,3}[;;](([pPpP][mMmM])|(演示(模式)?))[::]");
foreach (var line in lines)
{
var match = regex.Match(line);
if (match != null && match.Success)
{
var tail = line.Substring(match.Length).Trim(new char[] { ' ', ' ', '\t' });
if (new Regex(@"^[##].*$").Match(tail).Success) return CustomMarkdownSupport.PresentateHtmlSplitterType.TopLevelHeader;
if (new Regex(@"^[--].*$").Match(tail).Success) return CustomMarkdownSupport.PresentateHtmlSplitterType.HorizontalLine;
if (new Regex(@"^[rRrR\{\}{}].*$").Match(tail).Success) return CustomMarkdownSupport.PresentateHtmlSplitterType.Region;
return CustomMarkdownSupport.PresentateHtmlSplitterType.TopLevelHeader;
}
}
return CustomMarkdownSupport.PresentateHtmlSplitterType.TopLevelHeader;
}
}
/// <summary>
/// 关闭编辑器。
/// </summary>
void closeImage_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
Close();
}
/// <summary>
/// 关闭编辑器。
/// </summary>
private void Close()
{
if (this.IsModified)
{
string documentTitleAndFileName = null;
foreach (var line in this.editorBase.Text.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries))
{
documentTitleAndFileName = CustomMarkdownSupport.GetDocumentTitle(line);
if (string.IsNullOrWhiteSpace(documentTitleAndFileName) == false) break;
}
if (string.IsNullOrWhiteSpace(documentTitleAndFileName))
{
documentTitleAndFileName = Path.GetFileName(this.FullFilePath);
}
else
{
documentTitleAndFileName += "|" + Path.GetFileName(this.FullFilePath);
}
if (string.IsNullOrWhiteSpace(documentTitleAndFileName) == false)
documentTitleAndFileName = "【" + documentTitleAndFileName + "】";
MessageBoxResult r = LMessageBox.Show($"  文档{documentTitleAndFileName}尚未保存,要先保存文件吗?",
Globals.AppName, MessageBoxButton.YesNoCancel, MessageBoxImage.Exclamation);
switch (r)
{
case MessageBoxResult.Yes:
{
//保存
var saveResult = SaveDocument();
if (string.Empty != saveResult)
{
LMessageBox.Show(saveResult, Globals.AppName, MessageBoxButton.OKCancel, MessageBoxImage.Error);
return;
}
break;
}
case MessageBoxResult.No:
{
//不保存
break;
}
case MessageBoxResult.Cancel: return;
}
}
//关闭文档
BetterWpfControls.TabControl parentTabControl = this.Parent as BetterWpfControls.TabControl;
if (parentTabControl == null) return;
if (parentTabControl.Items.Contains(this))
{
parentTabControl.Items.Remove(this);
}
}
/// <summary>
/// [静态构造方法]
/// </summary>
static MarkdownEditor()
{
closeImageSource = new BitmapImage(new Uri("pack://application:,,,/LunarMarkdownEditor;component/Images/CloseHS.png"));
closeBorderMargin = new Thickness(4, 0, 0, 0);
}
private static Thickness closeBorderMargin;
private static BitmapImage closeImageSource;
private StackPanel headerPanel = new StackPanel() { Orientation = Orientation.Horizontal, Margin = new Thickness(0, 2, 0, 2), };
/// <summary>
/// 标头上用来显示文件短名的文本块。
/// </summary>
private TextBlock headerTextBlock = new TextBlock()
{
FontSize = 14,
VerticalAlignment = VerticalAlignment.Center,
FontFamily = new FontFamily("Simsun"),
Margin = new Thickness(0, 2, 0, 2),
};
/// <summary>
/// 标头上用以显示文件是否被修改的状态的文本块。
/// </summary>
private TextBlock modifiedTextBlock = new TextBlock() { FontSize = 16, };
private Image closeImage = new Image() { Width = 16, Height = 24, };
private Border closeBorder = new Border() { Margin = closeBorderMargin, ToolTip = "关闭[Ctrl+W | Ctrl+F4]", };
/// <summary>
/// 关闭文档。
/// </summary>
void miCloseDocument_Click(object sender, System.Windows.RoutedEventArgs e)
{
if (this.IsModified)
{
MessageBoxResult r = LMessageBox.Show("  当前文档已修改,要保存吗?", Globals.AppName, MessageBoxButton.YesNoCancel, MessageBoxImage.Warning);
switch (r)
{
case MessageBoxResult.Yes:
{
string saveInfo = SaveDocument();
if (saveInfo == string.Empty)
{
BetterWpfControls.TabControl parentTabControl = this.Parent as BetterWpfControls.TabControl;
if (parentTabControl != null)
{
if (parentTabControl.Items.Contains(this))
{
parentTabControl.Items.Remove(this);
}
}
}
else if (saveInfo == "用户取消操作")
{
return;//不关闭。
}
else
{
LMessageBox.Show("  未能保存文件!错误消息如下:\r\n" + saveInfo);
}
return;
}
case MessageBoxResult.No:
{
BetterWpfControls.TabControl parentTabControl = this.Parent as BetterWpfControls.TabControl;
if (parentTabControl != null)
{
if (parentTabControl.Items.Contains(this))
{
parentTabControl.Items.Remove(this);
}
}
return;
}
default://Cancel
{
return;
}
}
}
else
{
BetterWpfControls.TabControl parentTabControl = this.Parent as BetterWpfControls.TabControl;
if (parentTabControl != null)
{
if (parentTabControl.Items.Contains(this))
{
parentTabControl.Items.Remove(this);
}
}
}
}
/// <summary>
/// 格式化 Markdown 文本。
/// </summary>
public string FormatedMarkdownText()
{
return CustomMarkdownSupport.FormatMarkdownText(this.editorBase.Text);
}
/// <summary>
/// 格式化二维文字表。
/// </summary>
public void FormatTextTable()
{
this.editorBase.FormatTextTable();
}
/// <summary>
/// 保存文档。顺利完成就返回 string.Empth。否则返回错误消息。
/// </summary>
public string SaveDocument()
{
if (string.IsNullOrEmpty(this.fullFilePath))
{
return SaveDocumentAs();
}
try
{
var fstLine = EncryptedFirstLine;
if (string.IsNullOrEmpty(fstLine))
{
this.editorBase.Save(this.fullFilePath);//如果没有密码,则不加密。
}
else
{
File.WriteAllText(this.FullFilePath, $"{fstLine}\r\n{new String(SetPasswordPanel.TextEncrypt(this.editorBase.Document.Text, "DyBj#PpBb"))}");
}
this.editorBase.IsModified = false;//设置保存状态。
OnSaved();
return string.Empty;
}
catch (Exception ex)
{
return ex.Message;
}
}
/// <summary>
/// 文档另存为。顺利完成就返回 string.Empth。否则返回错误消息。
/// </summary>
public string SaveDocumentAs()
{
try
{
SaveFileDialog sfd = new SaveFileDialog() { Title = Globals.AppName, };
sfd.Filter = Globals.DocumentName +
"(*." + Globals.ExtensionName + ")|*." + Globals.ExtensionName;
sfd.FilterIndex = 0;
sfd.FileName = this.shortFileName;
sfd.InitialDirectory = Globals.PathOfWorkspace;
if (sfd.ShowDialog() == true)
{
if (sfd.FileName.ToLower().StartsWith(Globals.PathOfWorkspace.ToLower()) == false)
{
var reslut = LMessageBox.Show("  只有保存在默认工作区目录下才可以预览!真的要继续吗?", Globals.AppName,
MessageBoxButton.OKCancel, MessageBoxImage.Exclamation);
if (reslut != MessageBoxResult.OK) return "用户放弃保存。";
}
var fstLine = EncryptedFirstLine;
if (string.IsNullOrEmpty(fstLine))
{
this.editorBase.Save(this.fullFilePath);//要考虑加密,不能直接用这个
}
else
{
File.WriteAllText(this.FullFilePath, $"{fstLine}\r\n{new String(SetPasswordPanel.TextEncrypt(this.editorBase.Document.Text, "DyBj#PpBb"))}");
}
this.FullFilePath = sfd.FileName;//更新文件路径。
this.editorBase.IsModified = false;//设置保存状态。
OnSaved();
return string.Empty;
}
else
{
return "用户放弃保存";
}
}
catch (Exception ex)
{
return ex.Message;
}
}
public event EventHandler<DocumentSavedEventArgs> Saved;
protected void OnSaved()
{
UpdateTitleToHeader();
if (Saved != null)
{
Saved(this, new DocumentSavedEventArgs() { FileFullName = this.FullFilePath, });
}
}
/// <summary>
/// 保存文件后或载入文件后(加密文件)将文件标题刷新到编辑器头部。
/// </summary>
public void UpdateTitleToHeader()
{
string title = null;
foreach (var line in this.editorBase.Text.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries))
{
if (line.StartsWith("%"))
{
title = line.Substring(1).Trim();
break;
}
title = CustomMarkdownSupport.GetDocumentTitle(line);
if (string.IsNullOrWhiteSpace(title) == false) break;
int startIndex = line.IndexOf("标题>>");
int endIndex = line.IndexOf("<<标题");
if (startIndex >= 0 && endIndex >= 0)
{
title = line.Substring(startIndex + 4, endIndex - startIndex - 4);
break;
}
}
if (string.IsNullOrWhiteSpace(title) == false)
{
string subTitle;
if (title.Length > 20)
{
subTitle = title.Substring(0, 20) + "...";
}
else subTitle = title;
this.headerTextBlock.Text = subTitle.Trim(new char[] { ' ', '\t', ' ', '\n', '\r', }); //这个首尾的空行还是不要出现的好。;
}
else
{
this.headerTextBlock.Text = this.ShortFileName.Trim(new char[] { ' ', '\t', ' ', '\n', '\r', }); //这个首尾的空行还是不要出现的好。;
}
}
void miPin_Click(object sender, System.Windows.RoutedEventArgs e)
{
BetterWpfControls.Panels.ScrollablePanel.SetIsLocked(this, !BetterWpfControls.Panels.ScrollablePanel.GetIsLocked(this));
}
/// <summary>
/// 打开指定路径的 Markdown 文件。
/// </summary>
/// <param name="fullPath">要打开的 Markdown 文件的路径。</param>
/// <returns>顺利完成就返回 string.Empty。</returns>
internal string OpenDocument(string fullPath)
{
if (IsEncripied(fullPath))
{
this.ShortFileName = new FileInfo(fullPath).Name;
this.FullFilePath = fullPath;
var fstLine = new StreamReader(fullFilePath).ReadLine();
var decryptedFstLine = SetPasswordPanel.TextDecrypt(
fstLine.Replace("[[<r>]]", "\r").Replace("[[<n>]]", "\n").ToCharArray(), "DyBj#PpBb");
var indexOfPassword = decryptedFstLine.IndexOf("Password:");
var indexOfQuestion = decryptedFstLine.IndexOf("Question:");
if (indexOfQuestion >= 0 && indexOfPassword >= 10)
{
this.Password = decryptedFstLine.Substring(indexOfPassword + 9);
this.PasswordTip = decryptedFstLine.Substring(9, indexOfPassword - 10);
this.InputPasswordPanel.Visibility = Visibility.Visible;
if (this.setPasswordPanel.Visibility != Visibility.Collapsed)
{
this.setPasswordPanel.Visibility = Visibility.Collapsed;
}
return string.Empty;
}
}
if (string.IsNullOrEmpty(fullPath)) return "未提供有效文件名\r\n";
if (System.IO.File.Exists(fullPath) == false) return string.Format("  不存在路径为{0}的文件\r\n", fullPath);
this.FullFilePath = fullPath;
this.EditorBase.Load(this.FullFilePath);
//初始化文件保存状态。
this.EditorBase.IsModified = false;
//刷新折叠块状态
if (Globals.MainWindow.PerspectiveMode == Perspective.EditingAndPresentation)
{
this.editorBase.FoldingManager.FoldSpecialFoldings = true;
this.editorBase.FoldingStrategy.UpdateFoldings(
this.editorBase.FoldingManager, this.editorBase.Document);
this.editorBase.FoldingManager.FoldSpecialFoldings = false;
}
return string.Empty;
}
/// <summary>
/// 粘贴图像成链接或粘贴文本。
/// </summary>
internal void Paste()
{
//如果是粘贴图像,粘贴后不再继续操作。
var result = TryToPasteResourceFileLink();
if (result == string.Empty) return;
var clipboardText = Clipboard.GetText();
if (string.IsNullOrEmpty(clipboardText) == false)
{
this.editorBase.Paste();
}
else
{
LMessageBox.Show(result, Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
}
}
/// <summary>
/// 行分割符,根据操作系统不同而可能不同。这里只是以防万一。
/// </summary>
public string Splitter
{
get
{
string splitter;
if (this.editorBase.Text.Contains("\r\n"))
{
splitter = "\r\n";
}
else if (this.editorBase.Text.Contains("\n"))
{
splitter = "\n";
}
else
{
splitter = "\r";
}
return splitter;
}
}
/// <summary>
/// 粘贴源代码。会自动为粘贴的源代码添加行号、行首空格。
/// </summary>
internal void PasteCode()
{
var clipboardText = Clipboard.GetText().Replace("\t", " ");
if (string.IsNullOrEmpty(clipboardText)) return;
string[] lines = clipboardText.Replace("\r", "").Split(new char[] { '\n', }, StringSplitOptions.None);
if (lines.Length <= 0)
{
this.editorBase.SelectedText = "";
return;
}
bool allStartsWithFourSpace = true;
foreach (var line in lines)
{
if (line.StartsWith(" ") == false) allStartsWithFourSpace = false;
}
StringBuilder sb = new StringBuilder();
for (int i = 0; i < lines.Length; i++)
{
string line;
if (allStartsWithFourSpace)
{
line = lines[i].Substring(4);
}
else
{
line = lines[i];
}
string s = " " + (i + 1).ToString("D2") + "." + "\t" + line;
sb.Append(s + Splitter);
}
this.editorBase.SelectedText = Splitter + sb.ToString() + Splitter;
}
}
/// <summary>
/// 此类用于文件被保存时传递文件路径。
/// </summary>
public class DocumentSavedEventArgs : EventArgs
{
public string FileFullName { get; set; }
}
}

Комментарий ( 0 )

Вы можете оставить комментарий после Вход в систему

1
https://gitlife.ru/oschina-mirror/lunarsf-Lunar-Markdown-Editor.git
git@gitlife.ru:oschina-mirror/lunarsf-Lunar-Markdown-Editor.git
oschina-mirror
lunarsf-Lunar-Markdown-Editor
lunarsf-Lunar-Markdown-Editor
v0.4-beta8