/// <summary> /// 刪除一個儲存格的點字。 /// </summary> internal void DeleteCell(int row, int col) { // 防錯:如果不是有效的儲存格位置就直接返回。 if (!CheckCellPosition(row, col)) { return; } row = GetBrailleRowIndex(row); int lineIdx = GetBrailleLineIndex(row); int wordIdx = GetBrailleWordIndex(row, col); BrailleLine brLine = m_BrDoc.Lines[lineIdx]; if (brLine.Words.Count == 1) // 如果要刪除該列的最後一個字,就整列刪除。 { DeleteLine(row, col, false); return; } brLine.Words.RemoveAt(wordIdx); OnDataChanged(); // Update UI ReformatRow(row); brGrid.Selection.ResetSelection(false); // 修正選取的儲存格範圍。 SourceGrid.Position pos = new SourceGrid.Position(row, col); brGrid.Selection.Focus(pos, true); }
/// <summary> /// 輸出一列。 /// </summary> /// <param name="brLine">點字列。</param> /// <param name="graphics">圖形物件。</param> /// <param name="marginLeft">紙張的左邊界。</param> /// <param name="y">輸出的 y 軸座標。</param> /// <param name="maxCells">最大輸出幾方。</param> private void PrintLine(BrailleLine brLine, Graphics graphics, double marginLeft, double y, int maxCells) { if (brLine == null) { return; } BrailleWord brWord; int cellCnt = 0; double x; for (int wordIdx = 0; wordIdx < brLine.WordCount; wordIdx++) { brWord = brLine.Words[wordIdx]; if (cellCnt + brWord.CellCount > maxCells) // 如果目前要印的字將會超過最大方數 { break; // 則中止 } x = cellCnt * BrailleCellWidth + marginLeft; graphics.DrawString(brWord.Text, m_TextFont, m_TextBrush, (float)x, (float)y); cellCnt += brWord.CellCount; } }
/// <summary> /// 在指定的列之前插入一列。 /// </summary> internal void InsertLine(int row, int col) { // 防錯:如果不是有效的儲存格位置就直接返回。 if (!CheckCellPosition(row, col)) { return; } // 建立一列新的點字列,其中預設包含一個空方。 BrailleLine brLine = new BrailleLine(); brLine.Words.Add(BrailleWord.NewBlank()); row = GetBrailleRowIndex(row); int lineIdx = GetBrailleLineIndex(row); m_BrDoc.Lines.Insert(lineIdx, brLine); OnDataChanged(); // 更新 UI。 GridInsertRowAt(row); RefreshRowNumbers(); FillRow(brLine, row, true); // 將焦點移至新插入的那一列的第一個儲存格。 GridFocusCell(new SourceGrid.Position(row, brGrid.FixedColumns), true); }
/// <summary> /// 新增點字。 /// </summary> /// <param name="row">儲存格的列索引。</param> /// <param name="col">儲存格的行索引。</param> internal void InsertCell(int row, int col) { if (!CheckCellPosition(row, col)) { return; } EditCellForm form = new EditCellForm(); form.Mode = EditCellMode.Insert; if (form.ShowDialog() == DialogResult.OK) { int wordIdx = GetBrailleWordIndex(row, col); int lineIdx = GetBrailleLineIndex(row); BrailleLine brLine = m_BrDoc.Lines[lineIdx]; // 在第 wordIdx 個字之前插入新點字。 brLine.Words.Insert(wordIdx, form.BrailleWord); OnDataChanged(); // Update UI ReformatRow(row); SourceGrid.Position pos = new SourceGrid.Position(row, col + 1); brGrid.Selection.Focus(pos, true); // 修正選取的儲存格範圍。 } }
/// <summary> /// 傳回點字頁尾。 /// </summary> /// <param name="lineIdx">目前列印的列索引。用來計算頁尾的文件標題。</param> /// <param name="pageNum">頁碼。</param> /// <param name="beginOrgPageNum">起始原書頁碼。</param> /// <param name="endOrgPageNum">終止原書頁碼。</param> /// <returns></returns> /// <remarks>注意:點字頁碼的 # 號要固定印在第 37 方的位置(requested by 秋華)</remarks> private string GetBraillePageFoot(int lineIdx, int pageNum, int beginOrgPageNum, int endOrgPageNum) { StringBuilder sb = new StringBuilder(); StringBuilder sbPageNum = new StringBuilder(); // 標題 BrailleLine titleLine = m_BrDoc.GetPageTitle(lineIdx); string title = BrailleCharConverter.ToString(titleLine); // 原書頁碼 if (beginOrgPageNum >= 0) { string orgPageNum = ""; if (endOrgPageNum < 0) { orgPageNum = BrailleCharConverter.GetDigitCharCode(beginOrgPageNum, true); } else { if (beginOrgPageNum == endOrgPageNum) { orgPageNum = BrailleCharConverter.GetDigitCharCode(beginOrgPageNum, true); } else { orgPageNum = BrailleCharConverter.GetDigitCharCode(beginOrgPageNum, true) + BrailleCharConverter.ToChar(BrailleCell.DotsToByte(3, 6).ToString("X2")) + BrailleCharConverter.GetDigitCharCode(endOrgPageNum, true); } } sbPageNum.Append('#'); // 數字點 sbPageNum.Append(orgPageNum); // 原書頁碼 sbPageNum.Append(' '); // 空方 } sbPageNum.Append('#'); // 數字點 string pageNumStr = BrailleCharConverter.GetDigitCharCode(pageNum, true); sbPageNum.Append(pageNumStr.PadRight(3)); // 點字頁碼的數字部分固定佔三方,亦即 # 固定在第 37 方的位置 // 計算剩餘可容納標題的空間。 int roomForTitle = m_BrDoc.CellsPerLine - sbPageNum.Length - 1; // 多留一個空白 if (title.Length > roomForTitle) { title = title.Substring(0, roomForTitle); } else { title = title.PadRight(roomForTitle); } sb.Append(title); // 標題 sb.Append(' '); // 空方 sb.Append(sbPageNum.ToString()); // 原書頁碼、點字頁碼 return(sb.ToString()); }
/// <summary> /// 列印頁尾。 /// </summary> /// <param name="title">文件標題點字列。</param> /// <param name="pageNum">頁碼。</param> /// <param name="beginOrgPageNum">起始原書頁碼。</param> /// <param name="endOrgPageNum">終止原書頁碼。</param> /// <param name="graphics"></param> /// <param name="marginLeft"></param> /// <param name="marginTop"></param> private void PrintPageFoot(BrailleLine title, int pageNum, int beginOrgPageNum, int endOrgPageNum, Graphics graphics, double marginLeft, double marginTop) { int cellCnt; double x; double y = (m_PrintOptions.LinesPerPage - 1) * m_LineHeight + marginTop; // 取得一個數字字元的列印寬度 float textWidth = graphics.MeasureString("0", m_TextFont).Width; // 點字頁碼 cellCnt = pageNum.ToString().Length; x = (m_BrDoc.CellsPerLine - cellCnt) * BrailleCellWidth + marginLeft; if (cellCnt < 2) { x = x - BrailleCellWidth; // textWidth - 1.6; // 2008-8-26: 修正個位數頁碼位置太靠右邊的問題 } graphics.DrawString(pageNum.ToString(), m_TextFont, m_TextBrush, (float)x, (float)y); // 原書頁碼 if (beginOrgPageNum >= 0) { string orgPageNum = ""; if (endOrgPageNum < 0) { orgPageNum = beginOrgPageNum.ToString(); } else { if (beginOrgPageNum == endOrgPageNum) { orgPageNum = beginOrgPageNum.ToString(); } else { orgPageNum = String.Format("{0}-{1}", beginOrgPageNum, endOrgPageNum); } } cellCnt = orgPageNum.Length + cellCnt + 2; // 額外加點字頁碼的一個數字點位和一個空方。 x = (m_BrDoc.CellsPerLine - cellCnt) * BrailleCellWidth + marginLeft; if (cellCnt < 2) { x = x - BrailleCellWidth; // textWidth - 1.6; // 2008-8-26: 修正個位數頁碼位置太靠右邊的問題 } graphics.DrawString(orgPageNum.ToString(), m_TextFont, m_TextBrush, (float)x, (float)y); } // 文件標題 int maxCells = m_BrDoc.CellsPerLine - cellCnt - 2; // 最大可印方數。 PrintLine(title, graphics, marginLeft, y, maxCells); }
/// <summary> /// 將指定的列附加至上一列。 /// </summary> /// <param name="row"></param> private void JoinToPreviousRow(int row) { if (row < 0) // 防錯 { return; } row = GetBrailleRowIndex(row); // 確保列索引為點字列。 if (row <= brGrid.FixedRows) // 第一列的列首,無需處理。 { return; } int lineIdx = GetBrailleLineIndex(row); BrailleLine prevBrLine = m_BrDoc.Lines[lineIdx - 1]; BrailleLine currBrLine = m_BrDoc.Lines[lineIdx]; // 檢查上一列是否還有空間可以容納新點字 int avail = m_BrDoc.CellsPerLine - prevBrLine.CellCount; if (avail < currBrLine.Words[0].Cells.Count) { // 上一列的空間不夠,就算接上去,還是會在斷行時再度折下來,因此不處理。 return; } // 執行附加至上一列的動作。 prevBrLine.Append(currBrLine); // 清除本列 BrailleLine brLine = m_BrDoc.Lines[lineIdx]; brLine.Clear(); brLine = null; m_BrDoc.Lines.RemoveAt(lineIdx); OnDataChanged(); // 更新 UI:移除本列 brGrid.Rows.RemoveRange(row, 3); // 更新上一列 ReformatRow(row - 1); RefreshRowNumbers(); return; }
/// <summary> /// 將一列點字轉成對應的點字字型 ASCII 字串,以便顯示於螢幕上。 /// </summary> /// <param name="brLine"></param> /// <returns></returns> public static string ToString(BrailleLine brLine) { if (brLine == null) { return(""); } StringBuilder sb = new StringBuilder(); foreach (BrailleWord brWord in brLine.Words) { sb.Append(BrailleFontConverter.ToString(brWord)); } return(sb.ToString()); }
/// <summary> /// 判斷傳入的列是否為原書頁碼,如果是,則設定起始或終止原書頁碼。 /// </summary> /// <param name="brLine"></param> /// <param name="isFirstLineOfPage">是否為該頁的第一列。</param> private void SetOrgPageNumber(BrailleLine brLine, bool isFirstLineOfPage) { string line = brLine.ToString(); int orgPageNum = BrailleProcessor.GetOrgPageNumber(line); if (orgPageNum < 0) { return; } if (m_BeginOrgPageNumber < 0) { m_BeginOrgPageNumber = orgPageNum; } if (isFirstLineOfPage) // 如果該頁的第一列就是原書頁碼 { m_BeginOrgPageNumber = orgPageNum; // 則起始原書頁碼應該直接使用此頁碼。 } m_EndOrgPageNumber = orgPageNum; }
public void ConvertLineTestChinese(BrailleProcessor target) { string msg = "BrailleProcesser.ConvertLine 測試失敗: "; // 測試明眼字內含注音符號、冒號後面跟著"我"、以及引號、句號。 string line = "ㄅˇ你說:我是誰? 我說:「我是神。」"; string expected = "ㄅˇ你說: 我是誰? 我說:「我是神。」"; BrailleLine brLine = target.ConvertLine(line); string actual = brLine.ToString(); Assert.AreEqual(expected, actual, msg + line); // 測試破折號和刪節號。 line = "第一種破折號:─,第二種破折號:-,連續破折號:──,--。"; expected = "第一種破折號:─,第二種破折號:-,連續破折號:──,--。"; brLine = target.ConvertLine(line); actual = brLine.ToString(); Assert.AreEqual(expected, actual, msg + line); // 測試刪節號。 line = "單:…,雙:……"; expected = "單:…,雙:……"; brLine = target.ConvertLine(line); actual = brLine.ToString(); Assert.AreEqual(expected, actual, msg + line); // 測試連續多個全形空白:保留空白。 line = "空 白 "; expected = "空 白 "; brLine = target.ConvertLine(line); actual = brLine.ToString(); Assert.AreEqual(expected, actual, msg + line); // 測試私名號、書名號。 line = "<私名號>蔡煥麟</私名號>,<書名號>倚天屠龍記</書名號>"; expected = "╴╴蔡煥麟 ,﹏﹏倚天屠龍記 "; brLine = target.ConvertLine(line); actual = brLine.ToString(); Assert.AreEqual(expected, actual, msg + line); }
internal void DeleteLine(int row, int col, bool needConfirm) { // 防錯:如果不是有效的儲存格位置就直接返回。 if (!CheckCellPosition(row, col)) { return; } // 選取欲刪除的列,讓使用者容易知道。 SourceGrid.Position activePos = brGrid.Selection.ActivePosition; GridSelectRow(row, true); if (needConfirm && MsgBoxHelper.ShowOkCancel("確定要刪除整列?") != DialogResult.OK) { GridSelectRow(row, false); GridFocusCell(activePos, true); return; } row = GetBrailleRowIndex(row); // 確保列索引為點字列。 int lineIdx = GetBrailleLineIndex(row); BrailleLine brLine = m_BrDoc.Lines[lineIdx]; brLine.Clear(); brLine = null; m_BrDoc.Lines.RemoveAt(lineIdx); OnDataChanged(); // 更新 UI。 brGrid.Rows.RemoveRange(row, 3); RefreshRowNumbers(); GridSelectRow(row, false); GridFocusCell(activePos, true); }
/// <summary> /// 在行尾附加點字。 /// </summary> private void AppendCell(SourceGrid.Grid grid, int row, int col) { if (!CheckCellPosition(row, col)) { return; } EditCellForm form = new EditCellForm(); form.Mode = EditCellMode.Insert; if (form.ShowDialog() == DialogResult.OK) { int lineIdx = GetBrailleLineIndex(row); BrailleLine brLine = m_BrDoc.Lines[lineIdx]; // 在第 wordIdx 個字之前插入新點字。 brLine.Words.Add(form.BrailleWord); IsDirty = true; // Update UI ReformatRow(row); } }
/// <summary> /// 在指定處斷行。 /// </summary> /// <param name="row"></param> /// <param name="col"></param> private void BreakLine(int row, int col) { int wordIdx = GetBrailleWordIndex(row, col); if (wordIdx == 0) // 若在第一個字元處斷行,其實就等於插入一列。 { InsertLine(row, col); return; } int lineIdx = GetBrailleLineIndex(row); BrailleLine brLine = m_BrDoc.Lines[lineIdx]; BrailleLine newLine = brLine.Copy(wordIdx, 255); // 複製到新行。 newLine.TrimEnd(); // 去尾空白。 m_BrDoc.Lines.Insert(lineIdx + 1, newLine); brLine.RemoveRange(wordIdx, 255); // 從原始串列中刪除掉已經複製到新行的點字。 OnDataChanged(); // Update UI // 換上新列 RecreateRow(row); FillRow(m_BrDoc[lineIdx], row, true); // 插入新列 GridInsertRowAt(row + 3); FillRow(m_BrDoc[lineIdx + 1], row + 3, true); // 重新填列號 RefreshRowNumbers(); SourceGrid.Position pos = new SourceGrid.Position(row + 3, brGrid.FixedColumns); brGrid.Selection.Focus(pos, true); // 修正選取的儲存格範圍。 }
/// <summary> /// 插入一個空方。 /// </summary> /// <param name="grid"></param> /// <param name="row"></param> /// <param name="col"></param> /// <param name="count">插入幾個空方。</param> internal void InsertBlankCell(int row, int col, int count) { // 防錯:如果不是有效的儲存格位置就直接返回。 if (!CheckCellPosition(row, col)) { return; } int wordIdx = GetBrailleWordIndex(row, col); int lineIdx = GetBrailleLineIndex(row); BrailleLine brLine = m_BrDoc.Lines[lineIdx]; while (count > 0) { brLine.Words.Insert(wordIdx, BrailleWord.NewBlank()); count--; } OnDataChanged(); // Update UI. ReformatRow(row); SourceGrid.Position pos = new SourceGrid.Position(row, col + 1); brGrid.Selection.Focus(pos, true); // 修正選取的儲存格範圍。 }
/// <summary> /// 在行尾附加點字。 /// </summary> internal void AppendCell(int row, int col) { if (!CheckCellPosition(row, col)) { return; } EditCellForm form = new EditCellForm(); form.Mode = EditCellMode.Insert; if (form.ShowDialog() == DialogResult.OK) { int lineIdx = GetBrailleLineIndex(row); BrailleLine brLine = m_BrDoc.Lines[lineIdx]; // 在第 wordIdx 個字之前插入新點字。 brLine.Words.Add(form.BrailleWord); OnDataChanged(); // Update UI ReformatRow(row); } }
public void BreakLineTest() { string msg = "BrailleProcesser.BreakLine 測試失敗!"; BrailleProcessor target = BrailleProcessor.GetInstance(); ContextTagManager context = new ContextTagManager(); // 測試斷行:冒號+我。 string line = "一二三四:我"; BrailleLine brLine = target.ConvertLine(line); // 冒號後面會加一個空方 int cellsPerLine = 12; // 故意在空方之後斷行。 string expected = "一二三四:"; List <BrailleLine> brLines = target.BreakLine(brLine, cellsPerLine, context); string actual = brLines[0].ToString(); Assert.AreEqual(expected, actual, msg); brLines.Clear(); // 測試斷行:斷在句點。 line = "一二三四。"; brLine = target.ConvertLine(line); cellsPerLine = 10; // 故意斷在句號處。 string expected1 = "一二三"; // 應該把最後一個字連同句號斷至下一行。 string expected2 = "四。"; brLines = target.BreakLine(brLine, cellsPerLine, context); bool isOk = (brLines.Count == 2 && brLines[0].ToString() == expected1 && brLines[1].ToString() == expected2); Assert.IsTrue(isOk, msg); // 測試斷行:斷在英文字中間要加上連字號。 line = "this is a loooooong word."; brLine = target.ConvertLine(line); cellsPerLine = 12; // 故意斷在句號處。 expected1 = "this is a"; // 應該把最後一個字連同句號斷至下一行。 expected2 = "loooooong"; brLines = target.BreakLine(brLine, cellsPerLine, context); isOk = (brLines.Count == 3 && brLines[0].ToString() == expected1 && brLines[1].ToString() == expected2); Assert.IsTrue(isOk, msg); // 測試斷行:連續的數字不可斷開。 line = "12345 6789"; brLine = target.ConvertLine(line); cellsPerLine = 8; brLines = target.BreakLine(brLine, cellsPerLine, context); isOk = (brLines.Count == 2 && brLines[0].ToString() == "12345" && brLines[1].ToString() == "6789" && brLines[0][0].Cells[0].Value == (byte)BrailleCellCode.Digit && brLines[1][0].Cells[0].Value == (byte)BrailleCellCode.Digit); Assert.IsTrue(isOk, msg); // 測試斷行:斷在數字中間的逗號。 line = "abc 123,456"; brLine = target.ConvertLine(line); cellsPerLine = 8; // 故意斷在逗號處。 expected1 = "abc"; expected2 = "123,456"; brLines = target.BreakLine(brLine, cellsPerLine, context); isOk = (brLines.Count == 2 && brLines[0].ToString() == expected1 && brLines[1].ToString() == expected2 && brLines[1][0].Cells[0].Value == (byte)BrailleCellCode.Digit); Assert.IsTrue(isOk, msg); }
public void ConvertLineTestEnglish(BrailleProcessor target) { string msg = "BrailleProcesser.ConvertLine 測試失敗: "; // 測試一個大寫字母。 string line = "Hello"; string expected = "Hello"; BrailleLine brLine = target.ConvertLine(line); string actual = brLine.ToString(); Assert.AreEqual(expected, actual, msg + line); bool isOk = (brLine[0].Cells[0].Value == (byte)BrailleCellCode.Capital) && (brLine[0].Cells[1].Value == 0x13); Assert.IsTrue(isOk, msg + line); // 測試兩個大寫字母。 line = "ABC"; expected = "ABC"; brLine = target.ConvertLine(line); actual = brLine.ToString(); Assert.AreEqual(expected, actual, msg + line); isOk = (brLine[0].Cells[0].Value == (byte)BrailleCellCode.Capital) && (brLine[0].Cells[1].Value == (byte)BrailleCellCode.Capital) && (brLine[0].Cells[2].Value == 0x01) && // 'A' (brLine[1].Cells[0].Value == 0x03); // 'B' Assert.IsTrue(isOk, msg + line); // 測試數字。 line = "123,56 2006-09-29"; expected = "123,56 2006-09-29"; brLine = target.ConvertLine(line); actual = brLine.ToString(); isOk = (brLine[0].Cells[0].Value == (byte)BrailleCellCode.Digit) && (brLine[4].Cells[0].Value != (byte)BrailleCellCode.Capital) && // 逗號視為數字的延續,不用額外加數字記號。 (brLine[7].Cells[0].Value == (byte)BrailleCellCode.Digit) && (brLine[12].Cells[0].Value != (byte)BrailleCellCode.Capital); // 連字號視為數字的延續,不用額外加數字記號。 Assert.IsTrue(isOk, msg + line); // 測試編號。 line = "#1-2. 1"; expected = "1-2. 1"; brLine = target.ConvertLine(line); actual = brLine.ToString(); isOk = (actual == expected) && (brLine[0].Cells[0].Value == (byte)BrailleCellCode.Digit) && (brLine[0].Cells[1].Value == 0x01) && // 數字 1 的上位點。 (brLine[1].Cells[0].Value == 0x24) && // '-' (brLine[2].Cells[0].Value == 0x03) && // '2' (brLine[3].Cells[0].Value == 0x32) && // '.' (brLine[4].Cells[0].Value == 0x00) && // ' ' (brLine[5].Cells[0].Value == (byte)BrailleCellCode.Digit) && (brLine[5].Cells[1].Value == 0x02); // 數字 1 的下位點。 Assert.IsTrue(isOk, msg + line); // 測試連續多個空白:保留空白。 line = "a b "; expected = "a b "; brLine = target.ConvertLine(line); actual = brLine.ToString(); Assert.AreEqual(expected, actual, msg + line); }
/// <summary> /// 把一列點字填入指定的 grid 列(影響三列)。 /// </summary> /// <param name="brLine">點字串列。</param> /// <param name="row">欲填入 grid 中的哪一列。</param> /// <param name="autoSize">填完之後,是否要自動重新調整儲存格大小。</param> private void FillRow(BrailleLine brLine, int row, bool autoSize) { string brFontText; int col = brGrid.FixedColumns; // 確保列索引是點字所在的列。 row = GetBrailleRowIndex(row); brGrid.SuspendLayout(); try { foreach (BrailleWord brWord in brLine.Words) { // 處理點字 try { if (brWord.IsContextTag) { brFontText = " "; } else { brFontText = BrailleFontConverter.ToString(brWord); } } catch (Exception e) { MsgBoxHelper.ShowError(e.Message + "\r\n" + "列:" + row.ToString() + ", 行: " + col.ToString()); brFontText = ""; } brGrid[row, col] = new SourceGrid.Cells.Cell(brFontText); brGrid[row, col].ColumnSpan = brFontText.Length; brGrid[row, col].View = m_BrView; brGrid[row, col].Tag = brWord; brGrid[row, col].AddController(m_MenuController); brGrid[row, col].AddController(m_ClickController); // 處理明眼字 brGrid[row + 1, col] = new SourceGrid.Cells.Cell(brWord.Text); brGrid[row + 1, col].ColumnSpan = brFontText.Length; brGrid[row + 1, col].View = m_MingViewCJK; // TODO: 確認音標字形可以正確顯示. 否則要分開判斷,音標符號改用 m_MingView brGrid[row + 1, col].Tag = brWord; brGrid[row + 1, col].AddController(m_MenuController); brGrid[row + 1, col].AddController(m_ClickController); // 處理注音碼 brGrid[row + 2, col] = new SourceGrid.Cells.Cell(brWord.PhoneticCode); brGrid[row + 2, col].ColumnSpan = brFontText.Length; if (brWord.IsPolyphonic) { if (AppGlobals.Config.Braille.ErrorProneWords.IndexOf(brWord.Text) >= 0) { // 容易判斷錯誤的破音字用顯眼的紅色標示。 brGrid[row + 2, col].View = m_PhonView3; } else { // 一般破音字用黃色標示。 brGrid[row + 2, col].View = m_PhonView2; } } else { brGrid[row + 2, col].View = m_PhonView; } brGrid[row + 2, col].Tag = brWord; col += brFontText.Length; } } finally { brGrid.Rows.AutoSizeRow(row); brGrid.Rows.AutoSizeRow(row + 1); brGrid.Rows.AutoSizeRow(row + 2); brGrid.ResumeLayout(); } }