private void butImport_Click(object sender, EventArgs e) { OpenFileDialog openFD = new OpenFileDialog(); openFD.Multiselect = true; if (openFD.ShowDialog() != DialogResult.OK) { return; } Invalidate(); foreach (string fileName in openFD.FileNames) { //check file types? string destinationPath = WikiPages.GetWikiPath() + "\\" + Path.GetFileName(fileName); if (File.Exists(destinationPath)) { switch (MessageBox.Show(Lan.g(this, "Overwrite Existing File") + ": " + destinationPath, "", MessageBoxButtons.YesNoCancel)) { case DialogResult.No: //rename, do not overwrite InputBox ip = new InputBox(Lan.g(this, "New file name.")); ip.textResult.Text = Path.GetFileName(fileName); ip.ShowDialog(); if (ip.DialogResult != DialogResult.OK) { continue; //cancel, next file. } bool cancel = false; while (File.Exists(WikiPages.GetWikiPath() + "\\" + ip.textResult.Text) && !cancel) { MsgBox.Show(this, "File name already exists."); if (ip.ShowDialog() != DialogResult.OK) { cancel = true; } } if (cancel) { continue; //cancel rename, and go to next file. } destinationPath = WikiPages.GetWikiPath() + "\\" + ip.textResult.Text; break; //proceed to save file. case DialogResult.Yes: //overwrite try { File.Delete(destinationPath); } catch (Exception ex) { MessageBox.Show(Lan.g(this, "Cannot copy file") + ":" + fileName + "\r\n" + ex.Message); continue; } break; //file deleted, proceed to save. default: //cancel continue; //skip this file. } } File.Copy(fileName, destinationPath); } FillGrid(); }
/// <summary></summary> private void FillGrid() { gridMain.BeginUpdate(); gridMain.Columns.Clear(); ODGridColumn col = new ODGridColumn(Lan.g(this, "Image Name"), 70); gridMain.Columns.Add(col); gridMain.Rows.Clear(); string[] fileNames = System.IO.Directory.GetFiles(WikiPages.GetWikiPath()); //All files from the wiki file path, including images and other files. ImageNamesList = new List <string>(); for (int i = 0; i < fileNames.Length; i++) { //If the user has entered a search keyword, then only show file names which contain the keyword. if (textSearch.Text != "" && !Path.GetFileName(fileNames[i]).ToLower().Contains(textSearch.Text.ToLower())) { continue; } //Only add image files to the ImageNamesList, not other files such at text files. if (ImageHelper.HasImageExtension(fileNames[i])) { ImageNamesList.Add(fileNames[i]); } } for (int i = 0; i < ImageNamesList.Count; i++) { ODGridRow row = new ODGridRow(); row.Cells.Add(Path.GetFileName(ImageNamesList[i])); gridMain.Rows.Add(row); } gridMain.EndUpdate(); labelImageSize.Text = Lan.g(this, "Image Size") + ":"; picturePreview.Image = null; picturePreview.Invalidate(); }
private void Folder_Link_Click() { if (CloudStorage.IsCloudStorage) { FormFilePicker FormFP = new FormFilePicker(CloudStorage.PathTidy(WikiPages.GetWikiPath())); FormFP.DoHideLocalButton = true; FormFP.ShowDialog(); if (FormFP.DialogResult != DialogResult.OK) { return; } textContent.SelectedText = "[[foldercloud:" + FormFP.SelectedFiles[0] + "]]"; } else { FormWikiFileFolder formWFF = new FormWikiFileFolder(); formWFF.IsFolderMode = true; formWFF.ShowDialog(); if (formWFF.DialogResult != DialogResult.OK) { return; } textContent.SelectedText = "[[folder:" + formWFF.SelectedLink + "]]"; } textContent.SelectionLength = 0; //RefreshHtml(); }
private void File_Link_Click() { string fileLink; if (CloudStorage.IsCloudStorage) { FormFilePicker FormFP = new FormFilePicker(WikiPages.GetWikiPath()); FormFP.DoHideLocalButton = true; FormFP.ShowDialog(); if (FormFP.DialogResult != DialogResult.OK) { return; } fileLink = FormFP.SelectedFiles[0]; textContent.SelectedText = "[[filecloud:" + fileLink + "]]"; } else //Not cloud { FormWikiFileFolder FormWFF = new FormWikiFileFolder(); FormWFF.ShowDialog(); if (FormWFF.DialogResult != DialogResult.OK) { return; } fileLink = FormWFF.SelectedLink; textContent.SelectedText = "[[file:" + fileLink + "]]"; } textContent.SelectionLength = 0; //RefreshHtml(); }
/// <summary></summary> private void FillGrid() { gridMain.BeginUpdate(); gridMain.Columns.Clear(); ODGridColumn col = new ODGridColumn(Lan.g(this, "Image Name"), 70); gridMain.Columns.Add(col); gridMain.Rows.Clear(); string wikiPath = ""; try { wikiPath = WikiPages.GetWikiPath(); } catch (Exception ex) { MessageBox.Show(this, ex.Message); DialogResult = DialogResult.Cancel; return; } List <string> listFileNames = FileAtoZ.GetFilesInDirectory(wikiPath); //All files from the wiki file path, including images and other files. ImageNamesList = new List <string>(); for (int i = 0; i < listFileNames.Count; i++) { //If the user has entered a search keyword, then only show file names which contain the keyword. if (textSearch.Text != "" && !Path.GetFileName(listFileNames[i]).ToLower().Contains(textSearch.Text.ToLower())) { continue; } //Only add image files to the ImageNamesList, not other files such at text files. if (ImageHelper.HasImageExtension(listFileNames[i])) { ImageNamesList.Add(listFileNames[i]); } } for (int i = 0; i < ImageNamesList.Count; i++) { ODGridRow row = new ODGridRow(); row.Cells.Add(Path.GetFileName(ImageNamesList[i])); gridMain.Rows.Add(row); } gridMain.EndUpdate(); labelImageSize.Text = Lan.g(this, "Image Size") + ":"; picturePreview.Image = null; picturePreview.Invalidate(); }
private void Image_Click() { //if storing images in database, GetWikiPath will throw an exception, cannot be storing in database to use images in the wiki string wikiPath = ""; try { wikiPath = WikiPages.GetWikiPath(); } catch (Exception ex) { MessageBox.Show(this, ex.Message); return; } FormImagePicker FormWI = new FormImagePicker(wikiPath); FormWI.ShowDialog(); if (FormWI.DialogResult != DialogResult.OK) { return; } textContent.SelectionLength = 0; textContent.SelectedText = "[[img:" + FormWI.SelectedImageName + "]]"; //webBrowserWiki.AllowNavigation=true; //RefreshHtml(); }
///<summary>Validates content, and keywords. isForSaving can be false if just validating for refresh.</summary> public static bool ValidateMarkup(ODcodeBox textContent, bool isForSaving, bool showMsgBox = true, bool isEmail = false) { MatchCollection matches; //xml validation---------------------------------------------------------------------------------------------------- string s = textContent.Text; //"<",">", and "&"----------------------------------------------------------------------------------------------------------- s = s.Replace("&", "&"); s = s.Replace("&<", "<"); //because "&" was changed to "&" in the line above. s = s.Replace("&>", ">"); //because "&" was changed to "&" in the line above. s = "<body>" + s + "</body>"; XmlDocument doc = new XmlDocument(); StringReader reader = new StringReader(s); try { doc.Load(reader); } catch (Exception ex) { if (showMsgBox) { MessageBox.Show(ex.Message); } return(false); } try{ //we do it this way to skip checking the main node itself since it's a dummy node. if (!isEmail) //We are allowing any XHTML markup in emails. { MarkupEdit.ValidateNodes(doc.DocumentElement.ChildNodes); } } catch (Exception ex) { if (showMsgBox) { MessageBox.Show(ex.Message); } return(false); } //Cannot have CR within tag definition--------------------------------------------------------------------------------- //(?<!&) means only match strings that do not start with an '&'. This is so we can continue to use '&' as an escape character for '<'. //<.*?> means anything as short as possible that is contained inside a tag MatchCollection tagMatches = Regex.Matches(textContent.Text, "(?<!&)<.*?>", RegexOptions.Singleline); for (int i = 0; i < tagMatches.Count; i++) { if (tagMatches[i].ToString().Contains("\n")) { if (showMsgBox) { MessageBox.Show(Lans.g(_lanThis, "Error at line:") + " " + textContent.GetLineFromCharIndex(tagMatches[i].Index) + " - " + Lans.g(_lanThis, "Tag definitions cannot contain a return line:") + " " + tagMatches[i].Value.Replace("\n", "")); } return(false); } } //wiki image validation----------------------------------------------------------------------------------------------------- if (!isEmail) { string wikiImagePath = ""; try { wikiImagePath = WikiPages.GetWikiPath(); //this also creates folder if it's missing. } catch (Exception ex) { ex.DoNothing(); //do nothing, the wikiImagePath is only important if the user adds an image to the wiki page and is checked below } matches = Regex.Matches(textContent.Text, @"\[\[(img:).*?\]\]"); // [[img:myimage.jpg]] if (matches.Count > 0 && PrefC.AtoZfolderUsed == DataStorageType.InDatabase) { if (showMsgBox) { MessageBox.Show(Lans.g(_lanThis, "Error at line:") + " " + textContent.GetLineFromCharIndex(matches[0].Index) + " - " + Lans.g(_lanThis, "Cannot use images in wiki if storing images in database.")); } return(false); } if (isForSaving) { for (int i = 0; i < matches.Count; i++) { string imgPath = FileAtoZ.CombinePaths(wikiImagePath, matches[i].Value.Substring(6).Trim(']')); if (!FileAtoZ.Exists(imgPath)) { if (showMsgBox) { MessageBox.Show(Lans.g(_lanThis, "Error at line:") + " " + textContent.GetLineFromCharIndex(matches[i].Index) + " - " + Lans.g(_lanThis, "Not allowed to save because image does not exist:") + " " + imgPath); } return(false); } } } } //Email image validation---------------------------------------------------------------------------------------------- if (isEmail) { string emailImagePath = ""; try { emailImagePath = ImageStore.GetEmailImagePath(); } catch (Exception ex) { ex.DoNothing(); } matches = Regex.Matches(textContent.Text, @"\[\[(img:).*?\]\]"); if (isForSaving) { for (int i = 0; i < matches.Count; i++) { string imgPath = FileAtoZ.CombinePaths(emailImagePath, matches[i].Value.Substring(6).Trim(']')); if (!FileAtoZ.Exists(imgPath)) { if (showMsgBox) { MessageBox.Show(Lans.g(_lanThis, "Error at line:") + " " + textContent.GetLineFromCharIndex(matches[i].Index) + " - " + Lans.g(_lanThis, "Not allowed to save because image does not exist: ") + " " + imgPath); } } } } } //List validation----------------------------------------------------------------------------------------------------- matches = Regex.Matches(textContent.Text, @"\[\[(list:).*?\]\]"); // [[list:CustomList]] foreach (Match match in matches) { if (!WikiLists.CheckExists(match.Value.Substring(7).Trim(']'))) { if (showMsgBox) { MessageBox.Show(Lans.g(_lanThis, "Error at line:") + " " + textContent.GetLineFromCharIndex(match.Index) + " - " + Lans.g(_lanThis, "Wiki list does not exist in database:") + " " + match.Value.Substring(7).Trim(']')); } return(false); } } //spacing around bullets----------------------------------------------------------------------------------------------- string[] lines = textContent.Text.Split(new string[] { "\n" }, StringSplitOptions.None); for (int i = 0; i < lines.Length; i++) { if (lines[i].Trim().StartsWith("*")) { if (!lines[i].StartsWith("*")) { if (showMsgBox) { MessageBox.Show(Lans.g(_lanThis, "Error at line:") + " " + (i + 1) + " - " + Lans.g(_lanThis, "Stars used for lists may not have a space before them.")); } return(false); } if (lines[i].Trim().StartsWith("* ")) { if (showMsgBox) { MessageBox.Show(Lans.g(_lanThis, "Error at line:") + " " + (i + 1) + " - " + Lans.g(_lanThis, "Stars used for lists may not have a space after them.")); } return(false); } } if (lines[i].Trim().StartsWith("#")) { if (!lines[i].StartsWith("#")) { if (showMsgBox) { MessageBox.Show(Lans.g(_lanThis, "Error at line:") + " " + (i + 1) + " - " + Lans.g(_lanThis, "Hashes used for lists may not have a space before them.")); } return(false); } if (lines[i].Trim().StartsWith("# ")) { if (showMsgBox) { MessageBox.Show(Lans.g(_lanThis, "Error at line:") + " " + (i + 1) + " - " + Lans.g(_lanThis, "Hashes used for lists may not have a space after them.")); } return(false); } } } //Invalid characters inside of various tags-------------------------------------------- matches = Regex.Matches(textContent.Text, @"\[\[.*?\]\]"); foreach (Match match in matches) { if (match.Value.Contains("\"") && !match.Value.StartsWith("[[color:") && !match.Value.StartsWith("[[font:")) //allow colored text to have quotes. { if (showMsgBox) { MessageBox.Show(Lans.g(_lanThis, "Error at line:") + " " + textContent.GetLineFromCharIndex(match.Index) + " - " + Lans.g(_lanThis, "Link cannot contain double quotes:") + " " + match.Value); } return(false); } //This is not needed because our regex doesn't even catch them if the span a line break. It's just interpreted as plain text. //if(match.Value.Contains("\r") || match.Value.Contains("\n")) { // MessageBox.Show(Lan.g(this,"Link cannot contain carriage returns: ")+match.Value); // return false; //} if (match.Value.StartsWith("[[img:") || match.Value.StartsWith("[[keywords:") || match.Value.StartsWith("[[file:") || match.Value.StartsWith("[[folder:") || match.Value.StartsWith("[[list:") || match.Value.StartsWith("[[color:") || match.Value.StartsWith("[[font:")) { //other tags } else { if (match.Value.Contains("|")) { if (showMsgBox) { MessageBox.Show(Lans.g(_lanThis, "Error at line:") + " " + textContent.GetLineFromCharIndex(match.Index) + " - " + Lans.g(_lanThis, "Internal link cannot contain a pipe character:") + " " + match.Value); } return(false); } } } //Table markup rigorously formatted---------------------------------------------------------------------- //{| //!Width="100"|Column Heading 1!!Width="150"|Column Heading 2!!Width="75"|Column Heading 3 //|- //|Cell 1||Cell 2||Cell 3 //|- //|Cell A||Cell B||Cell C //|} //Although rarely needed, it might still come in handy in certain cases, like paste, or when user doesn't add the |} until later, and other hacks. matches = Regex.Matches(s, @"\{\|\n.+?\n\|\}", RegexOptions.Singleline); //matches = Regex.Matches(textContent.Text, // @"(?<=(?:\n|<body>))" //Checks for preceding newline or beggining of file // +@"\{\|.+?\n\|\}" //Matches the table markup. // +@"(?=(?:\n|</body>))" //Checks for following newline or end of file // ,RegexOptions.Singleline); foreach (Match match in matches) { lines = match.Value.Split(new string[] { "{|\n", "\n|-\n", "\n|}" }, StringSplitOptions.RemoveEmptyEntries); if (!lines[0].StartsWith("!")) { if (showMsgBox) { MessageBox.Show(Lans.g(_lanThis, "Error at line:") + " " + textContent.GetLineFromCharIndex(match.Index) + " - " + Lans.g(_lanThis, "The second line of a table markup section must start with ! to indicate column headers.")); } return(false); } if (lines[0].StartsWith("! ")) { if (showMsgBox) { MessageBox.Show(Lans.g(_lanThis, "Error at line:") + " " + textContent.GetLineFromCharIndex(match.Index) + " - " + Lans.g(_lanThis, "In the table, at line 2, there cannot be a space after the first !")); } return(false); } string[] cells = lines[0].Substring(1).Split(new string[] { "!!" }, StringSplitOptions.None); //this also strips off the leading ! for (int c = 0; c < cells.Length; c++) { if (!Regex.IsMatch(cells[c], @"^(Width="")\d+""\|")) //e.g. Width="90"| { if (showMsgBox) { MessageBox.Show(Lans.g(_lanThis, "Error at line:") + " " + textContent.GetLineFromCharIndex(match.Index) + " - " + Lans.g(_lanThis, "In the table markup, each header must be formatted like this: Width=\"#\"|...")); } return(false); } } for (int i = 1; i < lines.Length; i++) //loop through the lines after the header { if (!lines[i].StartsWith("|")) { if (showMsgBox) { MessageBox.Show(Lans.g(_lanThis, "Table rows must start with |. At line ") + (i + 1).ToString() + Lans.g(_lanThis, ", this was found instead:") + lines[i]); } return(false); } } } return(true); }
private void butImport_Click(object sender, EventArgs e) { OpenFileDialog openFD = new OpenFileDialog(); openFD.Multiselect = true; if (openFD.ShowDialog() != DialogResult.OK) { return; } Invalidate(); string wikiPath = ""; try { wikiPath = WikiPages.GetWikiPath(); } catch (Exception ex) { MessageBox.Show(this, ex.Message); return; } foreach (string fileName in openFD.FileNames) { //check file types? string destinationPath = FileAtoZ.CombinePaths(wikiPath, Path.GetFileName(fileName)); if (FileAtoZ.Exists(destinationPath)) { switch (MessageBox.Show(Lan.g(this, "Overwrite Existing File") + ": " + destinationPath, "", MessageBoxButtons.YesNoCancel)) { case DialogResult.No: //rename, do not overwrite InputBox ip = new InputBox(Lan.g(this, "New file name.")); ip.textResult.Text = Path.GetFileName(fileName); ip.ShowDialog(); if (ip.DialogResult != DialogResult.OK) { continue; //cancel, next file. } bool cancel = false; while (!cancel && FileAtoZ.Exists(FileAtoZ.CombinePaths(wikiPath, ip.textResult.Text))) { MsgBox.Show(this, "File name already exists."); if (ip.ShowDialog() != DialogResult.OK) { cancel = true; } } if (cancel) { continue; //cancel rename, and go to next file. } destinationPath = FileAtoZ.CombinePaths(wikiPath, ip.textResult.Text); break; //proceed to save file. case DialogResult.Yes: //overwrite try { FileAtoZ.Delete(destinationPath); } catch (Exception ex) { MessageBox.Show(Lan.g(this, "Cannot copy file") + ":" + fileName + "\r\n" + ex.Message); continue; } break; //file deleted, proceed to save. default: //cancel continue; //skip this file. } } FileAtoZ.Copy(fileName, destinationPath, FileAtoZSourceDestination.LocalToAtoZ); } FillGrid(); if (openFD.FileNames.Length == 1) //if importing exactly one image, select it upon returning. { textSearch.Text = Path.GetFileName(openFD.FileNames[0]); } }
///<summary>Validates content, and keywords. isForSaving can be false if just validating for refresh.</summary> private bool ValidateWikiPage(bool isForSaving) { //xml validation---------------------------------------------------------------------------------------------------- string s = textContent.Text; //"<",">", and "&"----------------------------------------------------------------------------------------------------------- s = s.Replace("&", "&"); s = s.Replace("&<", "<"); //because "&" was changed to "&" in the line above. s = s.Replace("&>", ">"); //because "&" was changed to "&" in the line above. s = "<body>" + s + "</body>"; XmlDocument doc = new XmlDocument(); StringReader reader = new StringReader(s); try { doc.Load(reader); } catch (Exception ex) { MessageBox.Show(ex.Message); return(false); } try{ //we do it this way to skip checking the main node itself since it's a dummy node. ValidateNodes(doc.DocumentElement.ChildNodes); } catch (Exception ex) { MessageBox.Show(ex.Message); return(false); } //Cannot have CR within tag definition--------------------------------------------------------------------------------- //(?<!&) means only match strings that do not start with an '&'. This is so we can continue to use '&' as an escape character for '<'. //<.*?> means anything as short as possible that is contained inside a tag MatchCollection tagMatches = Regex.Matches(textContent.Text, "(?<!&)<.*?>", RegexOptions.Singleline); for (int i = 0; i < tagMatches.Count; i++) { if (tagMatches[i].ToString().Contains("\r\n")) { MessageBox.Show(Lan.g(this, "Tag definitions cannot contain a return line: ") + tagMatches[i].Value.Replace("\r\n", "")); return(false); } } //image validation----------------------------------------------------------------------------------------------------- string wikiImagePath = WikiPages.GetWikiPath(); //this also creates folder if it's missing. MatchCollection matches = Regex.Matches(textContent.Text, @"\[\[(img:).*?\]\]"); // [[img:myimage.jpg]] if (matches.Count > 0 && !PrefC.AtoZfolderUsed) { MsgBox.Show(this, "Cannot use images in wiki if storing images in database."); return(false); } if (isForSaving) { for (int i = 0; i < matches.Count; i++) { string imgPath = ODFileUtils.CombinePaths(wikiImagePath, matches[i].Value.Substring(6).Trim(']')); if (!System.IO.File.Exists(imgPath)) { MessageBox.Show(Lan.g(this, "Not allowed to save because image does not exist: ") + imgPath); return(false); } } } //List validation----------------------------------------------------------------------------------------------------- matches = Regex.Matches(textContent.Text, @"\[\[(list:).*?\]\]"); // [[list:CustomList]] foreach (Match match in matches) { if (!WikiLists.CheckExists(match.Value.Substring(7).Trim(']'))) { MessageBox.Show(Lan.g(this, "Wiki list does not exist in database") + " : " + match.Value.Substring(7).Trim(']')); return(false); } } //spacing around bullets----------------------------------------------------------------------------------------------- string[] lines = textContent.Text.Split(new string[] { "\r\n" }, StringSplitOptions.None); for (int i = 0; i < lines.Length; i++) { if (lines[i].Trim().StartsWith("*")) { if (!lines[i].StartsWith("*")) { MsgBox.Show(this, "Stars used for lists may not have a space before them."); return(false); } if (lines[i].Trim().StartsWith("* ")) { MsgBox.Show(this, "Stars used for lists may not have a space after them."); return(false); } } if (lines[i].Trim().StartsWith("#")) { if (!lines[i].StartsWith("#")) { MsgBox.Show(this, "Hashes used for lists may not have a space before them."); return(false); } if (lines[i].Trim().StartsWith("# ")) { MsgBox.Show(this, "Hashes used for lists may not have a space after them."); return(false); } } } //Invalid characters inside of various tags-------------------------------------------- matches = Regex.Matches(textContent.Text, @"\[\[.*?\]\]"); foreach (Match match in matches) { if (match.Value.Contains("\"") && !match.Value.StartsWith("[[color:")) //allow colored text to have quotes. { MessageBox.Show(Lan.g(this, "Link cannot contain double quotes: ") + match.Value); return(false); } //This is not needed because our regex doesn't even catch them if the span a line break. It's just interpreted as plain text. //if(match.Value.Contains("\r") || match.Value.Contains("\n")) { // MessageBox.Show(Lan.g(this,"Link cannot contain carriage returns: ")+match.Value); // return false; //} if (match.Value.StartsWith("[[img:") || match.Value.StartsWith("[[keywords:") || match.Value.StartsWith("[[file:") || match.Value.StartsWith("[[folder:") || match.Value.StartsWith("[[list:") || match.Value.StartsWith("[[color:")) { //other tags } else { if (match.Value.Contains("|")) { MessageBox.Show(Lan.g(this, "Internal link cannot contain a pipe character:") + match.Value); return(false); } } } //Table markup rigorously formatted---------------------------------------------------------------------- //{| //!Width="100"|Column Heading 1!!Width="150"|Column Heading 2!!Width="75"|Column Heading 3 //|- //|Cell 1||Cell 2||Cell 3 //|- //|Cell A||Cell B||Cell C //|} //Although this is rarely needed, it might still come in handy in certain cases, like paste, or when user doesn't add the |} until later, and other hacks. matches = Regex.Matches(s, @"\{\|(.+?)\|\}", RegexOptions.Singleline); foreach (Match match in matches) { lines = match.Value.Split(new string[] { "{|\r\n", "\r\n|-\r\n", "\r\n|}" }, StringSplitOptions.RemoveEmptyEntries); if (!match.Value.StartsWith("{|")) { MsgBox.Show(this, "The first line of a table markup section must be exactly {|, with no additional characters."); return(false); } if (!lines[0].StartsWith("!")) { MsgBox.Show(this, "The second line of a table markup section must start with ! to indicate column headers."); return(false); } if (lines[0].StartsWith("! ")) { MsgBox.Show(this, "In the table, at line 2, there cannot be a space after the first !"); return(false); } string[] cells = lines[0].Substring(1).Split(new string[] { "!!" }, StringSplitOptions.None); //this also strips off the leading ! for (int c = 0; c < cells.Length; c++) { if (!Regex.IsMatch(cells[c], @"^(Width="")\d+""\|")) //e.g. Width="90"| { MsgBox.Show(this, "In the table markup, each header must be formatted like this: Width=\"#\"|..."); return(false); } } for (int i = 1; i < lines.Length; i++) //loop through the lines after the header { if (!lines[i].StartsWith("|")) { MessageBox.Show(Lan.g(this, "Table rows must start with |. At line ") + (i + 1).ToString() + Lan.g(this, ", this was found instead:") + lines[i]); return(false); } //if(lines[i].StartsWith("| ")) { // MessageBox.Show(Lan.g(this,"In the table, at line ")+(i+1).ToString()+Lan.g(this,", there cannot be a space after the first |.")); // return false; //} //lines[i].in //I guess we don't really care what they put in a row. We can just interpret garbage as a single cell. } if (!match.Value.EndsWith("\r\n|}")) { MsgBox.Show(this, "The last line of a table markup section must be exactly |}, with no additional characters."); //this doesn't work since the match stops after |}. return(false); } } return(true); }