/// /// Copyright (C) 2006-2007 Andrej Vodopivec /// /// This program is free software; you can redistribute it and/or modify /// it under the terms of the GNU General Public License as published by /// the Free Software Foundation; either version 2 of the License, or /// (at your option) any later version. /// /// This program is distributed in the hope that it will be useful, /// but WITHOUT ANY WARRANTY; without even the implied warranty of /// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the /// GNU General Public License for more details. /// /// /// You should have received a copy of the GNU General Public License /// along with this program; if not, write to the Free Software /// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA /// #include #include "EditorCell.h" #include "wxMaxima.h" #include "wxMaximaFrame.h" #define INCREASE_SIZE 6 EditorCell::EditorCell() : MathCell() { m_text = wxEmptyString; m_fontSize = -1; m_positionOfCaret = 0; m_selectionStart = -1; m_selectionEnd = -1; m_isActive = false; m_matchParens = true; m_paren1 = m_paren2 = -1; m_isDirty = false; m_hasFocus = false; m_underlined = false; m_fontWeight = wxFONTWEIGHT_NORMAL; m_fontStyle = wxNORMAL; m_fontEncoding = wxFONTENCODING_DEFAULT; } EditorCell::~EditorCell() { if (m_next != NULL) delete m_next; } MathCell *EditorCell::Copy(bool all) { EditorCell *tmp = new EditorCell(); tmp->SetValue(m_text); CopyData(this, tmp); if (all && m_next != NULL) tmp->AppendCell(m_next->Copy(all)); return tmp; } void EditorCell::Destroy() { m_next = NULL; } wxString EditorCell::ToString(bool all) { wxString text = m_text; return text + MathCell::ToString(all); } wxString EditorCell::ToTeX(bool all) { wxString text = m_text; return text + MathCell::ToTeX(all); } void EditorCell::RecalculateWidths(CellParser& parser, int fontsize, bool all) { m_isDirty = false; if (m_height == -1 || m_width == -1 || fontsize != m_fontSize || parser.ForceUpdate()) { m_fontSize = fontsize; wxDC& dc = parser.GetDC(); double scale = parser.GetScale(); SetFont(parser, fontsize); dc.GetTextExtent(wxT("X"), &m_charWidth, &m_charHeight); unsigned int newLinePos = 0, prevNewLinePos = 0; int width = 0, width1, height1; m_numberOfLines = 1; while (newLinePos < m_text.Length()) { while (newLinePos < m_text.Length()) { if (m_text.GetChar(newLinePos) == '\n') break; newLinePos++; } dc.GetTextExtent(m_text.SubString(prevNewLinePos, newLinePos), &width1, &height1); width = MAX(width, width1); while (newLinePos < m_text.Length() && m_text.GetChar(newLinePos) == '\n') { newLinePos++; m_numberOfLines++; } prevNewLinePos = newLinePos; } if (m_text == wxEmptyString) width = m_charWidth; m_width = width + 2 * SCALE_PX(2, scale); m_height = m_numberOfLines * m_charHeight + 2 * SCALE_PX(2, scale); m_center = m_charHeight / 2 + SCALE_PX(2, scale); } MathCell::RecalculateWidths(parser, fontsize, all); } void EditorCell::RecalculateSize(CellParser& parser, int fontsize, bool all) { MathCell::RecalculateSize(parser, fontsize, all); } void EditorCell::Draw(CellParser& parser, wxPoint point1, int fontsize, bool all) { double scale = parser.GetScale(); wxDC& dc = parser.GetDC(); wxPoint point(point1); if (m_width == -1 || m_height == -1) RecalculateWidths(parser, fontsize, false); if (DrawThisCell(parser, point) && !m_isHidden) { SetBackground(parser, point1); SetForeground(parser); SetPen(parser); SetFont(parser, fontsize); unsigned int newLinePos = 0, prevNewLinePos = 0, numberOfLines = 0; // // Draw the text // while (newLinePos < m_text.Length()) { while (newLinePos < m_text.Length()) { if (m_text.GetChar(newLinePos) == '\n') break; newLinePos++; } dc.DrawText(m_text.SubString(prevNewLinePos, newLinePos - 1), point.x + SCALE_PX(2, scale), point.y - m_center + SCALE_PX(2, scale) + m_charHeight * numberOfLines); newLinePos++; prevNewLinePos = newLinePos; numberOfLines++; } if (m_isActive) { // // Draw the caret // if (m_displayCaret && m_hasFocus) { int caretInLine = 0; int caretInColumn = 0; PositionToXY(m_positionOfCaret, &caretInColumn, &caretInLine); wxString line = GetLineString(caretInLine, 0, caretInColumn); int lineWidth, lineHeight; dc.GetTextExtent(line, &lineWidth, &lineHeight); dc.DrawLine(point.x + SCALE_PX(2, scale) + lineWidth, point.y + SCALE_PX(2, scale) - m_center + caretInLine * m_charHeight, point.x + SCALE_PX(2, scale) + lineWidth, point.y + SCALE_PX(2, scale) - m_center + (caretInLine + 1) * m_charHeight); } // // Mark selection // if (m_selectionStart > -1) { dc.SetLogicalFunction(wxAND); dc.SetBrush(*wxLIGHT_GREY_BRUSH); dc.SetPen(*wxLIGHT_GREY_PEN); wxPoint point; long start = MIN(m_selectionStart, m_selectionEnd); long end = MAX(m_selectionStart, m_selectionEnd); long pos1 = start, pos2 = start; wxPoint point1; while (pos1 < end) { while (pos1 < end && m_text.GetChar(pos1) != '\n') pos1++; point = PositionToPoint(parser, pos2); point1 = PositionToPoint(parser, pos1); dc.DrawRectangle(point.x + SCALE_PX(2, scale), point.y + SCALE_PX(2, scale) - m_center, point1.x - point.x, m_charHeight); pos1++; pos2 = pos1; } dc.SetLogicalFunction(wxCOPY); } // // Matching parens // else if (m_paren1 != -1 && m_paren2 != -1) { dc.SetLogicalFunction(wxAND); dc.SetBrush(*wxLIGHT_GREY_BRUSH); dc.SetPen(*wxLIGHT_GREY_PEN); wxPoint point = PositionToPoint(parser, m_paren1); int width, height; dc.GetTextExtent(m_text.GetChar(m_paren1), &width, &height); dc.DrawRectangle(point.x + SCALE_PX(2, scale), point.y + SCALE_PX(2, scale) - m_center, width, height); point = PositionToPoint(parser, m_paren2); dc.GetTextExtent(m_text.GetChar(m_paren1), &width, &height); dc.DrawRectangle(point.x + SCALE_PX(2, scale), point.y + SCALE_PX(2, scale) - m_center, width, height); dc.SetLogicalFunction(wxCOPY); } } UnsetPen(parser); } MathCell::Draw(parser, point1, fontsize, all); } void EditorCell::SetFont(CellParser& parser, int fontsize) { wxDC& dc = parser.GetDC(); double scale = parser.GetScale(); m_fontSize = fontsize; int fontsize1 = (int) (((double)fontsize) * scale + 0.5); fontsize1 = MAX(fontsize1, 1); m_fontName = parser.GetFontName(); m_fontEncoding = parser.GetFontEncoding(); m_fontStyle = parser.IsItalic(TS_NORMAL_TEXT); m_fontWeight = parser.IsBold(TS_NORMAL_TEXT); switch(m_type) { case MC_TYPE_TITLE: fontsize1 += SCALE_PX(INCREASE_SIZE, scale); m_fontStyle = wxFONTSTYLE_SLANT; case MC_TYPE_SECTION: fontsize1 += SCALE_PX(INCREASE_SIZE, scale); m_fontWeight = wxFONTWEIGHT_BOLD; m_underlined = true; case MC_TYPE_COMMENT: m_fontEncoding = parser.GetFontEncoding(); break; default: m_fontStyle = parser.IsItalic(TS_INPUT); m_fontWeight = parser.IsBold(TS_INPUT); m_underlined = parser.IsUnderlined(TS_INPUT); m_fontEncoding = parser.GetFontEncoding(); break; } dc.SetFont(wxFont(fontsize1, wxMODERN, m_fontStyle, m_fontWeight, m_underlined, m_fontName, m_fontEncoding)); } void EditorCell::SetForeground(CellParser& parser) { wxDC& dc = parser.GetDC(); switch (m_type) { case MC_TYPE_COMMENT: case MC_TYPE_SECTION: case MC_TYPE_TITLE: dc.SetTextForeground(wxTheColourDatabase->Find(parser.GetColor(TS_NORMAL_TEXT))); break; default: dc.SetTextForeground(wxTheColourDatabase->Find(parser.GetColor(TS_INPUT))); break; } } #ifndef WX_USE_UNICODE int ChangeNumpadToChar(int c) { switch (c) { case WXK_NUMPAD0: return '0'; break; case WXK_NUMPAD1: return '1'; break; case WXK_NUMPAD2: return '2'; break; case WXK_NUMPAD3: return '3'; break; case WXK_NUMPAD4: return '4'; break; case WXK_NUMPAD5: return '5'; break; case WXK_NUMPAD6: return '6'; break; case WXK_NUMPAD7: return '7'; break; case WXK_NUMPAD8: return '8'; break; case WXK_NUMPAD9: return '9'; break; case WXK_NUMPAD_DECIMAL: return '.'; break; } return c; } #endif void EditorCell::ProcessEvent(wxKeyEvent &event) { switch (event.GetKeyCode()) { case WXK_ESCAPE: { wxCommandEvent ev(wxEVT_COMMAND_MENU_SELECTED, deactivate_cell_cancel); (wxGetApp().GetTopWindow())->ProcessEvent(ev); } break; case WXK_LEFT: if (event.ShiftDown()) { if (m_selectionStart == -1) m_selectionEnd = m_selectionStart = m_positionOfCaret; } else m_selectionEnd = m_selectionStart = -1; if (m_positionOfCaret > 0) m_positionOfCaret--; if (event.ShiftDown()) m_selectionEnd = m_positionOfCaret; break; case WXK_RIGHT: if (event.ShiftDown()) { if (m_selectionStart == -1) m_selectionEnd = m_selectionStart = m_positionOfCaret; } else m_selectionEnd = m_selectionStart = -1; if (m_positionOfCaret < m_text.Length()) m_positionOfCaret++; if (event.ShiftDown()) m_selectionEnd = m_positionOfCaret; break; case WXK_PAGEDOWN: case WXK_DOWN: { if (event.ShiftDown()) { if (m_selectionStart == -1) m_selectionEnd = m_selectionStart = m_positionOfCaret; } else m_selectionEnd = m_selectionStart = -1; int column, line; PositionToXY(m_positionOfCaret, &column, &line); line = line < m_numberOfLines-1 ? line + 1 : line; m_positionOfCaret = XYToPosition(column, line); if (event.ShiftDown()) m_selectionEnd = m_positionOfCaret; } break; case WXK_PAGEUP: case WXK_UP: { if (event.ShiftDown()) { if (m_selectionStart == -1) m_selectionEnd = m_selectionStart = m_positionOfCaret; } else m_selectionEnd = m_selectionStart = -1; int column, line; PositionToXY(m_positionOfCaret, &column, &line); line = line > 0 ? line - 1 : 0; m_positionOfCaret = XYToPosition(column, line); if (event.ShiftDown()) m_selectionEnd = m_positionOfCaret; } break; case WXK_RETURN: m_text = m_text.SubString(0, m_positionOfCaret - 1) + wxT("\n") + m_text.SubString(m_positionOfCaret, m_text.Length()); m_positionOfCaret++; m_isDirty = true; break; case WXK_END: if (event.ShiftDown()) { if (m_selectionStart == -1) m_selectionEnd = m_selectionStart = m_positionOfCaret; } else m_selectionEnd = m_selectionStart = -1; if (event.ControlDown()) m_positionOfCaret = m_text.Length(); else { while (m_positionOfCaret < m_text.Length() && m_text.GetChar(m_positionOfCaret) != '\n') m_positionOfCaret++; } if (event.ShiftDown()) m_selectionEnd = m_positionOfCaret; break; case WXK_HOME: { if (event.ShiftDown()) { if (m_selectionStart == -1) m_selectionEnd = m_selectionStart = m_positionOfCaret; } else m_selectionEnd = m_selectionStart = -1; if (event.ControlDown()) m_positionOfCaret = 0; else { int col, lin; PositionToXY(m_positionOfCaret, &col, &lin); m_positionOfCaret = XYToPosition(0, lin); } if (event.ShiftDown()) m_selectionEnd = m_positionOfCaret; } break; case WXK_DELETE: m_isDirty = true; if (m_positionOfCaret < m_text.Length()) { if (m_selectionStart == -1) m_text = m_text.SubString(0, m_positionOfCaret - 1) + m_text.SubString(m_positionOfCaret + 1, m_text.Length()); else { long start = MIN(m_selectionEnd, m_selectionStart); long end = MAX(m_selectionEnd, m_selectionStart); m_text = m_text.SubString(0, start - 1) + m_text.SubString(end, m_text.Length()); m_positionOfCaret = start; m_selectionEnd = m_selectionStart = -1; } } break; case WXK_BACK: m_isDirty = true; if (m_selectionStart > -1) { long start = MIN(m_selectionEnd, m_selectionStart); long end = MAX(m_selectionEnd, m_selectionStart); m_text = m_text.SubString(0, start - 1) + m_text.SubString(end, m_text.Length()); m_positionOfCaret = start; m_selectionEnd = m_selectionStart = -1; break; } else if (m_positionOfCaret > 0) { m_text = m_text.SubString(0, m_positionOfCaret - 2) + m_text.SubString(m_positionOfCaret, m_text.Length()); m_positionOfCaret--; } break; case WXK_TAB: m_isDirty = true; { if (m_selectionStart > -1) { long start = MIN(m_selectionEnd, m_selectionStart); long end = MAX(m_selectionEnd, m_selectionStart); m_text = m_text.SubString(0, start - 1) + m_text.SubString(end, m_text.Length()); m_positionOfCaret = start; m_selectionEnd = m_selectionStart = -1; break; } int col, line; PositionToXY(m_positionOfCaret, &col, &line); wxString ins; do { col++; ins += wxT(" "); } while (col%4 != 0); m_text = m_text.SubString(0, m_positionOfCaret - 1) + ins + m_text.SubString(m_positionOfCaret, m_text.Length()); m_positionOfCaret += ins.Length(); } break; default: if (event.ControlDown()) break; m_isDirty = true; if (m_selectionStart > -1) { long start = MIN(m_selectionEnd, m_selectionStart); long end = MAX(m_selectionEnd, m_selectionStart); m_text = m_text.SubString(0, start - 1) + m_text.SubString(end, m_text.Length()); m_positionOfCaret = start; m_selectionEnd = m_selectionStart = -1; } m_text = m_text.SubString(0, m_positionOfCaret - 1) + #if wxUSE_UNICODE event.GetUnicodeKey() + #else wxString::Format(wxT("%c"), ChangeNumpadToChar(event.GetKeyCode())) + #endif m_text.SubString(m_positionOfCaret, m_text.Length()); m_positionOfCaret++; if (m_matchParens) { switch (event.GetKeyCode()) { case '(': m_text = m_text.SubString(0, m_positionOfCaret - 1) + wxT(")") + m_text.SubString(m_positionOfCaret, m_text.Length()); break; case '[': m_text = m_text.SubString(0, m_positionOfCaret - 1) + wxT("]") + m_text.SubString(m_positionOfCaret, m_text.Length()); break; case '{': m_text = m_text.SubString(0, m_positionOfCaret - 1) + wxT("}") + m_text.SubString(m_positionOfCaret, m_text.Length()); break; } } break; } if (m_type == MC_TYPE_INPUT) FindMatchingParens(); if (m_isDirty) m_width = m_maxDrop = -1; m_displayCaret = true; } void EditorCell::FindMatchingParens() { m_paren2 = m_positionOfCaret; if (wxString(wxT("([{}])")).Find(m_text.GetChar(m_paren2)) == -1) { m_paren2--; if (wxString(wxT("([{}])")).Find(m_text.GetChar(m_paren2)) == -1) { m_paren1 = m_paren2 = -1; return ; } } wxChar first = m_text.GetChar(m_paren2); wxChar second; int dir; switch (first) { case '(': second = ')'; dir = 1; break; case '[': second = ']'; dir = 1; break; case '{': second = '}'; dir = 1; break; case ')': second = '('; dir = -1; break; case ']': second = '['; dir = -1; break; case '}': second = '{'; dir = -1; break; default: return; } m_paren1 = m_paren2 + dir; int depth = 1; while (m_paren1 >= 0 && m_paren1 < (int)m_text.Length()) { if (m_text.GetChar(m_paren1) == second) depth--; else if (m_text.GetChar(m_paren1) == first) depth++; if (depth == 0) break; m_paren1 += dir; } if (m_paren1 < 0 || m_paren1 >= (int)m_text.Length()) m_paren1 = m_paren2 = -1; } bool EditorCell::ActivateCell() { m_isActive = !m_isActive; m_displayCaret = true; m_hasFocus = true; m_selectionEnd = m_selectionStart = -1; return true; } void EditorCell::AddEnding() { wxString text = m_text.Trim(); if (text.Right(1) != wxT(";") && text.Right(1) != wxT("$")) m_text += wxT(";"); } // // lines and columns are counted from zero // position of caret is pos if caret is just before the character // at position pos in m_text. // void EditorCell::PositionToXY(int position, int* x, int* y) { int col = 0, lin = 0; int pos = 0; while (pos < position) { if (m_text.GetChar(pos) == '\n') { col = 0, lin++; } else col++; pos++; } *x = col; *y = lin; } int EditorCell::XYToPosition(int x, int y) { int col = 0, lin = 0, pos = 0; while (pos < (int)m_text.Length() && lin < y) { if (m_text.GetChar(pos) == '\n') lin++; pos++; } while (pos < (int)m_text.Length() && col < x) { if (m_text.GetChar(pos) == '\n') break; pos++; col++; } return pos; } wxPoint EditorCell::PositionToPoint(CellParser& parser, int pos) { wxDC& dc = parser.GetDC(); SetFont(parser, m_fontSize); int x = m_currentPoint.x, y = m_currentPoint.y; int height, width; int cX, cY; wxString line = wxEmptyString; if (pos == -1) pos = m_positionOfCaret; if (x == -1 || y == -1) return wxPoint(-1, -1); PositionToXY(pos, &cX, &cY); if (cX > 0) line = GetLineString(cY, 0, cX); dc.GetTextExtent(line, &width, &height); x += width; y += m_charHeight * cY; return wxPoint(x, y); } void EditorCell::SelectPointText(wxDC& dc, wxPoint& point) { wxString s; int fontsize1 = m_fontSize; if (m_type == MC_TYPE_TITLE) fontsize1 += 2*INCREASE_SIZE; else if (m_type == MC_TYPE_SECTION) fontsize1 += INCREASE_SIZE; dc.SetFont(wxFont(fontsize1, wxMODERN, m_fontStyle, m_fontWeight, m_underlined, m_fontName, m_fontEncoding)); m_selectionEnd = m_selectionStart = -1; wxPoint translate(point); translate.x -= m_currentPoint.x - 2; translate.y -= m_currentPoint.y - 2 - m_center; int lin = translate.y / m_charHeight; int width, height; int lineStart = XYToPosition(0, lin); m_positionOfCaret = lineStart; while (m_text.GetChar(m_positionOfCaret) != '\n' && m_positionOfCaret < m_text.Length()) { s = m_text.SubString(lineStart, m_positionOfCaret); dc.GetTextExtent(m_text.SubString(lineStart, m_positionOfCaret), &width, &height); if (width > translate.x) break; m_positionOfCaret++; } m_positionOfCaret = MIN(m_positionOfCaret, m_text.Length()); m_displayCaret = true; if (GetType() == MC_TYPE_INPUT) FindMatchingParens(); } void EditorCell::SelectRectText(wxDC &dc, wxPoint& one, wxPoint& two) { SelectPointText(dc, one); long start = m_positionOfCaret; SelectPointText(dc, two); m_selectionEnd = m_positionOfCaret; m_selectionStart = start; m_paren2 = m_paren1 = -1; if (m_selectionStart == m_selectionEnd) { m_selectionStart = -1; m_selectionEnd = -1; } } bool EditorCell::CopyToClipboard() { if (m_selectionStart == -1) return false; if (wxTheClipboard->Open()) { long start = MIN(m_selectionStart, m_selectionEnd); long end = MAX(m_selectionStart, m_selectionEnd) - 1; wxString s = m_text.SubString(start, end); wxTheClipboard->SetData(new wxTextDataObject(s)); wxTheClipboard->Close(); } return true; } bool EditorCell::CutToClipboard() { if (m_selectionStart == -1) return false; CopyToClipboard(); long start = MIN(m_selectionStart, m_selectionEnd); long end = MAX(m_selectionStart, m_selectionEnd); m_positionOfCaret = start; m_text = m_text.SubString(0, start - 1) + m_text.SubString(end, m_text.Length()); m_selectionEnd = m_selectionStart = -1; m_width = m_height = m_maxDrop = m_center = -1; return true; } void EditorCell::PasteFromClipboard() { if (wxTheClipboard->Open()) { if (wxTheClipboard->IsSupported(wxDF_TEXT)) { wxTextDataObject obj; wxTheClipboard->GetData(obj); if (m_selectionStart > -1) { long start = MIN(m_selectionStart, m_selectionEnd); long end = MAX(m_selectionStart, m_selectionEnd); m_positionOfCaret = start; m_text = m_text.SubString(0, start - 1) + m_text.SubString(end, m_text.Length()); } wxString data = obj.GetText(); m_text = m_text.SubString(0, m_positionOfCaret - 1) + data + m_text.SubString(m_positionOfCaret, m_text.Length()); m_selectionStart = m_positionOfCaret; m_positionOfCaret += data.Length(); m_selectionEnd = m_positionOfCaret; } wxTheClipboard->Close(); } m_width = m_height = m_maxDrop = m_center = -1; } wxString EditorCell::GetLineString(int line, int start, int end) { if (start >= end) return wxEmptyString; int posStart = 0, posEnd = 0; posStart = XYToPosition(start, line); if (end == -1) { posEnd = XYToPosition(0, line+1); posEnd--; } else posEnd = XYToPosition(end, line); return m_text.SubString(posStart, posEnd - 1); } void EditorCell::SetBackground(CellParser& parser, wxPoint& point) { if (GetType() != MC_TYPE_INPUT && !m_isActive) { wxDC &dc = parser.GetDC(); wxRect rect = GetRect(false); int y = rect.GetY(); if (m_height > 0 && m_width > 0 && y>=0) { wxBrush br(wxColor(parser.GetColor(TS_TEXT_BACKGROUND))); dc.SetBrush(br); wxPen pen(wxColor(parser.GetColor(TS_TEXT_BACKGROUND))); dc.SetPen(pen); int height = rect.GetHeight(); dc.DrawRectangle(0, y - 1, 10000, height + 2); } } }