diff options
author | nyamatongwe <unknown> | 2000-03-08 01:43:56 +0000 |
---|---|---|
committer | nyamatongwe <unknown> | 2000-03-08 01:43:56 +0000 |
commit | c196d2fc7c3ece7ccb7d89c425499a75ead7e59b (patch) | |
tree | 3ea3c536f04e88499b86ed82e8a9a457f96b4978 | |
parent | 7fbd8e2a34d2f5084ce26ad95d7c70ae4de6a233 (diff) | |
download | scintilla-mirror-c196d2fc7c3ece7ccb7d89c425499a75ead7e59b.tar.gz |
Initial revision
-rw-r--r-- | src/Editor.cxx | 3705 | ||||
-rw-r--r-- | src/Editor.h | 281 | ||||
-rw-r--r-- | src/Indicator.cxx | 45 | ||||
-rw-r--r-- | src/Indicator.h | 18 | ||||
-rw-r--r-- | src/KeyMap.cxx | 111 | ||||
-rw-r--r-- | src/KeyMap.h | 35 | ||||
-rw-r--r-- | src/KeyWords.cxx | 2648 | ||||
-rw-r--r-- | src/LineMarker.cxx | 125 | ||||
-rw-r--r-- | src/LineMarker.h | 22 | ||||
-rw-r--r-- | src/PropSet.cxx | 399 | ||||
-rw-r--r-- | src/SVector.h | 110 | ||||
-rw-r--r-- | src/ScintillaBase.cxx | 397 | ||||
-rw-r--r-- | src/ScintillaBase.h | 68 | ||||
-rw-r--r-- | src/Style.cxx | 89 | ||||
-rw-r--r-- | src/Style.h | 39 | ||||
-rw-r--r-- | src/ViewStyle.cxx | 226 | ||||
-rw-r--r-- | src/ViewStyle.h | 72 | ||||
-rw-r--r-- | win32/Margin.cur | bin | 0 -> 326 bytes | |||
-rw-r--r-- | win32/PlatWin.cxx | 673 | ||||
-rw-r--r-- | win32/PlatformRes.h | 6 | ||||
-rw-r--r-- | win32/ScintRes.rc | 40 | ||||
-rw-r--r-- | win32/ScintillaWin.cxx | 1349 | ||||
-rw-r--r-- | win32/makefile | 98 | ||||
-rw-r--r-- | win32/makefile_bor | 120 | ||||
-rw-r--r-- | win32/makefile_vc | 125 |
25 files changed, 10801 insertions, 0 deletions
diff --git a/src/Editor.cxx b/src/Editor.cxx new file mode 100644 index 000000000..1ead18d34 --- /dev/null +++ b/src/Editor.cxx @@ -0,0 +1,3705 @@ +// Scintilla source code edit control +// Editor.cxx - main code for the edit control +// Copyright 1998-2000 by Neil Hodgson <neilh@scintilla.org> +// The License.txt file describes the conditions under which this software may be distributed. + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <ctype.h> + +#include "Platform.h" + +#include "Scintilla.h" +#include "ContractionState.h" +#include "SVector.h" +#include "CellBuffer.h" +#include "KeyMap.h" +#include "Indicator.h" +#include "LineMarker.h" +#include "Style.h" +#include "ViewStyle.h" +#include "Document.h" +#include "Editor.h" + +Caret::Caret() : +active(true), on(true), period(500) {} + +Timer::Timer() : +ticking(false), ticksToWait(0), tickerID(0) {} + +Editor::Editor() { + ctrlID = 0; + + stylesValid = false; + + hideSelection = false; + inOverstrike = false; + + bufferedDraw = true; + + lastClickTime = 0; + ptMouseLast.x = 0; + ptMouseLast.y = 0; + firstExpose = true; + inDragDrop = false; + dropWentOutside = false; + posDrag = invalidPosition; + posDrop = invalidPosition; + selectionType = selChar; + + lastXChosen = 0; + lineAnchor = 0; + originalAnchorPos = 0; + + dragChars = 0; + lenDrag = 0; + dragIsRectangle = false; + selType = selStream; + xStartSelect = 0; + xEndSelect = 0; + + caretPolicy = CARET_SLOP; + caretSlop = 0; + + searchAnchor = 0; + + ucWheelScrollLines = 0; + cWheelDelta = 0; //wheel delta from roll + + xOffset = 0; + xCaretMargin = 50; + + currentPos = 0; + anchor = 0; + + topLine = 0; + posTopLine = 0; + + needUpdateUI = true; + braces[0]=invalidPosition; + braces[1]=invalidPosition; + bracesMatchStyle = STYLE_BRACEBAD; + + edgeState = EDGE_NONE; + theEdge = 0; + + paintState = notPainting; + + modEventMask = SC_MODEVENTMASKALL; + + pdoc = new Document(); + pdoc ->AddRef(); + pdoc->AddWatcher(this, 0); + +#ifdef MACRO_SUPPORT + recordingMacro = 0; +#endif + foldFlags = 0; +} + +Editor::~Editor() { + pdoc->RemoveWatcher(this, 0); + pdoc->Release(); + pdoc = 0; + DropGraphics(); + + delete []dragChars; + dragChars = 0; + lenDrag = 0; +} + +void Editor::Finalise() { +} + +void Editor::DropGraphics() { + pixmapLine.Release(); + pixmapSelMargin.Release(); + pixmapSelPattern.Release(); +} + +void Editor::InvalidateStyleData() { + stylesValid = false; + palette.Release(); + DropGraphics(); +} + +void Editor::InvalidateStyleRedraw() { + InvalidateStyleData(); + Redraw(); +} + +void Editor::RefreshColourPalette(Palette &pal, bool want) { + vs.RefreshColourPalette(pal, want); +} + +void Editor::RefreshStyleData() { + if (!stylesValid) { + stylesValid = true; + Surface surface; + surface.Init(); + vs.Refresh(surface); + RefreshColourPalette(palette, true); + palette.Allocate(wMain); + RefreshColourPalette(palette, false); + SetScrollBars(); + } +} + +PRectangle Editor::GetClientRectangle() { + return wDraw.GetClientPosition(); +} + +PRectangle Editor::GetTextRectangle() { + PRectangle rc = GetClientRectangle(); + rc.left += vs.fixedColumnWidth; + rc.right -= vs.rightMarginWidth; + return rc; +} + +int Editor::LinesOnScreen() { + PRectangle rcClient = GetClientRectangle(); + int htClient = rcClient.bottom - rcClient.top; + //Platform::DebugPrintf("lines on screen = %d\n", htClient / lineHeight + 1); + return htClient / vs.lineHeight; +} + +int Editor::LinesToScroll() { + int retVal = LinesOnScreen() - 1; + if (retVal < 1) + return 1; + else + return retVal; +} + +int Editor::MaxScrollPos() { + //Platform::DebugPrintf("Lines %d screen = %d maxScroll = %d\n", + //LinesTotal(), LinesOnScreen(), LinesTotal() - LinesOnScreen() + 1); + int retVal = cs.LinesDisplayed() - LinesOnScreen(); + if (retVal < 0) + return 0; + else + return retVal; +} + +bool IsControlCharacter(char ch) { + // iscntrl returns true for lots of chars > 127 which are displayable + return ch >= 0 && ch < ' '; +} + +const char *ControlCharacterString(char ch) { + const char *reps[] = { + "NUL", "SOH", "STX", "ETX", "EOT", "ENQ", "ACK", "BEL", + "BS", "HT", "LF", "VT", "FF", "CR", "SO", "SI", + "DLE", "DC1", "DC2", "DC3", "DC4", "NAK", "SYN", "ETB", + "CAN", "EM", "SUB", "ESC", "FS", "GS", "RS", "US" + }; + if (ch < (sizeof(reps) / sizeof(reps[0]))) { + return reps[ch]; + } else { + return "BAD"; + } +} + +Point Editor::LocationFromPosition(unsigned int pos) { + RefreshStyleData(); + int line = pdoc->LineFromPosition(pos); + int lineVisible = cs.DisplayFromDoc(line); + //Platform::DebugPrintf("line=%d\n", line); + Surface surface; + surface.Init(); + Point pt; + pt.y = (lineVisible - topLine) * vs.lineHeight; // + half a lineheight? + unsigned int posLineStart = pdoc->LineStart(line); + if ((pos - posLineStart) > LineLayout::maxLineLength) { + // very long line so put x at arbitrary large position + pt.x = 30000 + vs.fixedColumnWidth - xOffset; + } else { + LineLayout ll; + LayoutLine(line, &surface, vs, ll); + pt.x = ll.positions[pos - posLineStart] + vs.fixedColumnWidth - xOffset; + } + return pt; +} + +int Editor::XFromPosition(unsigned int pos) { + Point pt = LocationFromPosition(pos); + return pt.x - vs.fixedColumnWidth + xOffset; +} + +int Editor::LineFromLocation(Point pt) { + return cs.DocFromDisplay(pt.y / vs.lineHeight + topLine); +} + +void Editor::SetTopLine(int topLineNew) { + topLine = topLineNew; + posTopLine = pdoc->LineStart(topLine); +} + +int Editor::PositionFromLocation(Point pt) { + RefreshStyleData(); + pt.x = pt.x - vs.fixedColumnWidth + xOffset; + int line = cs.DocFromDisplay(pt.y / vs.lineHeight + topLine); + if (pt.y < 0) { // Division rounds towards 0 + line = cs.DocFromDisplay((pt.y - (vs.lineHeight - 1)) / vs.lineHeight + topLine); + } + if (line < 0) + return 0; + if (line >= pdoc->LinesTotal()) + return pdoc->Length(); +//Platform::DebugPrintf("Position of (%d,%d) line = %d top=%d\n", pt.x, pt.y, line, topLine); + Surface surface; + surface.Init(); + unsigned int posLineStart = pdoc->LineStart(line); + + LineLayout ll; + LayoutLine(line, &surface, vs, ll); + for (int i = 0; i < ll.numCharsInLine; i++) { + if (pt.x < ((ll.positions[i] + ll.positions[i + 1]) / 2) || + ll.chars[i] == '\r' || ll.chars[i] == '\n') { + return i + posLineStart; + } + } + + return ll.numCharsInLine + posLineStart; +} + +int Editor::PositionFromLineX(int line, int x) { + RefreshStyleData(); + if (line >= pdoc->LinesTotal()) + return pdoc->Length(); + //Platform::DebugPrintf("Position of (%d,%d) line = %d top=%d\n", pt.x, pt.y, line, topLine); + Surface surface; + surface.Init(); + unsigned int posLineStart = pdoc->LineStart(line); + + LineLayout ll; + LayoutLine(line, &surface, vs, ll); + for (int i = 0; i < ll.numCharsInLine; i++) { + if (x < ((ll.positions[i] + ll.positions[i + 1]) / 2) || + ll.chars[i] == '\r' || ll.chars[i] == '\n') { + return i + posLineStart; + } + } + + return ll.numCharsInLine + posLineStart; +} + +void Editor::RedrawRect(PRectangle rc) { + //Platform::DebugPrintf("Redraw %d %d - %d %d\n", rc.left, rc.top, rc.right, rc.bottom); + wDraw.InvalidateRectangle(rc); +} + +void Editor::Redraw() { + //Platform::DebugPrintf("Redraw all\n"); + wDraw.InvalidateAll(); +} + +void Editor::RedrawSelMargin() { + if (vs.maskInLine) { + Redraw(); + } else { + PRectangle rcSelMargin = GetClientRectangle(); + rcSelMargin.right = vs.fixedColumnWidth; + wDraw.InvalidateRectangle(rcSelMargin); + } +} + +PRectangle Editor::RectangleFromRange(int start, int end) { + int minPos = start; + if (minPos > end) + minPos = end; + int maxPos = start; + if (maxPos < end) + maxPos = end; + int minLine = cs.DisplayFromDoc(pdoc->LineFromPosition(minPos)); + int maxLine = cs.DisplayFromDoc(pdoc->LineFromPosition(maxPos)); + PRectangle rcClient = GetTextRectangle(); + PRectangle rc; + rc.left = vs.fixedColumnWidth; + rc.top = (minLine - topLine) * vs.lineHeight; + if (rc.top < 0) + rc.top = 0; + rc.right = rcClient.right; + rc.bottom = (maxLine - topLine + 1) * vs.lineHeight; + // Ensure PRectangle is within 16 bit space + rc.top = Platform::Clamp(rc.top, -32000, 32000); + rc.bottom = Platform::Clamp(rc.bottom, -32000, 32000); + + return rc; +} + +void Editor::InvalidateRange(int start, int end) { + RedrawRect(RectangleFromRange(start, end)); +} + +int Editor::CurrentPosition() { + return currentPos; +} + +bool Editor::SelectionEmpty() { + return anchor == currentPos; +} + +int Editor::SelectionStart(int line) { + if ((line == -1) || (selType == selStream)) { + return Platform::Minimum(currentPos, anchor); + } else { // selType == selRectangle + int selStart = SelectionStart(); + int selEnd = SelectionEnd(); + int lineStart = pdoc->LineFromPosition(selStart); + int lineEnd = pdoc->LineFromPosition(selEnd); + if (line < lineStart || line > lineEnd) { + return -1; + } else { + int minX = Platform::Minimum(xStartSelect, xEndSelect); + //return PositionFromLineX(line, minX + vs.fixedColumnWidth - xOffset); + return PositionFromLineX(line, minX); + } + } +} + +int Editor::SelectionEnd(int line) { + if ((line == -1) || (selType == selStream)) { + return Platform::Maximum(currentPos, anchor); + } else { // selType == selRectangle + int selStart = SelectionStart(); + int selEnd = SelectionEnd(); + int lineStart = pdoc->LineFromPosition(selStart); + int lineEnd = pdoc->LineFromPosition(selEnd); + if (line < lineStart || line > lineEnd) { + return -1; + } else { + int maxX = Platform::Maximum(xStartSelect, xEndSelect); + // measure line and return character closest to minx + return PositionFromLineX(line, maxX); + } + } +} + +void Editor::SetSelection(int currentPos_, int anchor_) { + currentPos_ = pdoc->ClampPositionIntoDocument(currentPos_); + anchor_ = pdoc->ClampPositionIntoDocument(anchor_); + if ((currentPos != currentPos_) || (anchor != anchor_)) { + int firstAffected = anchor; + if (firstAffected > currentPos) + firstAffected = currentPos; + if (firstAffected > anchor_) + firstAffected = anchor_; + if (firstAffected > currentPos_) + firstAffected = currentPos_; + int lastAffected = anchor; + if (lastAffected < currentPos) + lastAffected = currentPos; + if (lastAffected < anchor_) + lastAffected = anchor_; + if (lastAffected < (currentPos_ + 1)) // +1 ensures caret repainted + lastAffected = (currentPos_ + 1); + currentPos = currentPos_; + anchor = anchor_; + needUpdateUI = true; + InvalidateRange(firstAffected, lastAffected); + } + ClaimSelection(); +} + +void Editor::SetSelection(int currentPos_) { + currentPos_ = pdoc->ClampPositionIntoDocument(currentPos_); + if (currentPos != currentPos_) { + int firstAffected = anchor; + if (firstAffected > currentPos) + firstAffected = currentPos; + if (firstAffected > currentPos_) + firstAffected = currentPos_; + int lastAffected = anchor; + if (lastAffected < currentPos) + lastAffected = currentPos; + if (lastAffected < (currentPos_ + 1)) // +1 ensures caret repainted + lastAffected = (currentPos_ + 1); + currentPos = currentPos_; + needUpdateUI = true; + InvalidateRange(firstAffected, lastAffected); + } + ClaimSelection(); +} + +void Editor::SetEmptySelection(int currentPos_) { + SetSelection(currentPos_, currentPos_); +} + +int Editor::MovePositionTo(int newPos, bool extend) { + int delta = newPos - currentPos; + newPos = pdoc->ClampPositionIntoDocument(newPos); + newPos = pdoc->MovePositionOutsideChar(newPos, delta); + if (extend) { + SetSelection(newPos); + } else { + SetEmptySelection(newPos); + } + EnsureCaretVisible(); + ShowCaretAtCurrentPosition(); + return 0; +} + +int Editor::MovePositionSoVisible(int pos, int moveDir) { + pos = pdoc->ClampPositionIntoDocument(pos); + pos = pdoc->MovePositionOutsideChar(pos, moveDir); + int lineDoc = pdoc->LineFromPosition(pos); + if (cs.GetVisible(lineDoc)) { + return pos; + } else { + int lineDisplay = cs.DisplayFromDoc(lineDoc); + if (moveDir > 0) { + lineDisplay = Platform::Clamp(lineDisplay + 1, 0, cs.LinesDisplayed()); + return pdoc->LineStart(cs.DocFromDisplay(lineDisplay)); + } else { + // lineDisplay is already line before fold as lines in fold use display line of line before fold + lineDisplay = Platform::Clamp(lineDisplay, 0, cs.LinesDisplayed()); + return pdoc->LineEndPosition(pdoc->LineStart(cs.DocFromDisplay(lineDisplay))); + } + } +} + +// Choose the x position that the caret will try to stick to as it is moves up and down +void Editor::SetLastXChosen() { + Point pt = LocationFromPosition(currentPos); + lastXChosen = pt.x; +} + +void Editor::ScrollTo(int line) { + int topLineNew = Platform::Clamp(line, 0, MaxScrollPos()); + if (topLineNew != topLine) { + // Try to optimise small scrolls + int linesToMove = topLine - topLineNew; + SetTopLine(topLineNew); + ShowCaretAtCurrentPosition(); + // Perform redraw rather than scroll if many lines would be redrawn anyway. + if (abs(linesToMove) <= 10) { + ScrollText(linesToMove); + } else { + Redraw(); + } + SetVerticalScrollPos(); + } +} + +void Editor::ScrollText(int linesToMove) { + //Platform::DebugPrintf("Editor::ScrollText %d\n", linesToMove); + Redraw(); +} + +void Editor::HorizontalScrollTo(int xPos) { + //Platform::DebugPrintf("HorizontalScroll %d\n", xPos); + xOffset = xPos; + if (xOffset < 0) + xOffset = 0; + SetHorizontalScrollPos(); + Redraw(); +} + +void Editor::EnsureCaretVisible(bool useMargin) { + //Platform::DebugPrintf("EnsureCaretVisible %d\n", xOffset); + PRectangle rcClient = GetTextRectangle(); + int posCaret = currentPos; + if (posDrag >= 0) + posCaret = posDrag; + Point pt = LocationFromPosition(posCaret); + Point ptEOL = LocationFromPosition(pdoc->LineEndPosition(posCaret)); + Point ptBottomCaret = pt; + int lineCaret = cs.DisplayFromDoc(pdoc->LineFromPosition(posCaret)); + ptBottomCaret.y += vs.lineHeight - 1; + + // Ensure the caret is reasonably visible in context. + int xMargin = Platform::Clamp(xCaretMargin, 2, Platform::Maximum(rcClient.Width() - 10, 4) / 2); + if (!useMargin) + xMargin = 2; + + // Ensure certain amount of text visible on both sides of caretSo move if caret just on edge + rcClient.left = rcClient.left + xMargin; + rcClient.right = rcClient.right - xMargin; + + if (!rcClient.Contains(pt) || !rcClient.Contains(ptBottomCaret) || (caretPolicy & CARET_STRICT)) { + //Platform::DebugPrintf("EnsureCaretVisible move, (%d,%d) (%d,%d)\n", pt.x, pt.y, rcClient.left, rcClient.right); + // It should be possible to scroll the window to show the caret, + // but this fails to remove the caret on GTK+ + if (caretPolicy & CARET_SLOP) { + if ((topLine > lineCaret) || ((caretPolicy & CARET_STRICT) && (topLine + caretSlop > lineCaret))) { + SetTopLine(Platform::Clamp(lineCaret - caretSlop, 0, MaxScrollPos())); + SetVerticalScrollPos(); + Redraw(); + } else if ((lineCaret > topLine + LinesOnScreen() - 1) || + ((caretPolicy & CARET_STRICT) && (lineCaret > topLine + LinesOnScreen() - 1 - caretSlop))) { + SetTopLine(Platform::Clamp(lineCaret - LinesOnScreen() + 1 + caretSlop, 0, MaxScrollPos())); + SetVerticalScrollPos(); + Redraw(); + } + } else { + if ((topLine > lineCaret) || (lineCaret > topLine + LinesOnScreen() - 1) || (caretPolicy & CARET_STRICT)) { + SetTopLine(Platform::Clamp(lineCaret - LinesOnScreen() / 2 + 1, 0, MaxScrollPos())); + SetVerticalScrollPos(); + Redraw(); + } + } + int xOffsetNew = xOffset; + if (pt.x < rcClient.left) { + xOffsetNew = xOffset - (rcClient.left - pt.x); + } else if (pt.x >= rcClient.right) { + xOffsetNew = xOffset + (pt.x - rcClient.right); + int xOffsetEOL = xOffset + (ptEOL.x - rcClient.right) - xMargin + 2; + //Platform::DebugPrintf("Margin %d %d\n", xOffsetNew, xOffsetEOL); + // Ensure don't scroll out into empty space + if (xOffsetNew > xOffsetEOL) + xOffsetNew = xOffsetEOL; + } + if (xOffsetNew < 0) + xOffsetNew = 0; + if (xOffset != xOffsetNew) { + xOffset = xOffsetNew; + SetHorizontalScrollPos(); + Redraw(); + } + } +} + +void Editor::ShowCaretAtCurrentPosition() { + if (!wMain.HasFocus()) { + caret.active = false; + caret.on = false; + return; + } + caret.active = true; + caret.on = true; + SetTicking(true); +} + +void Editor::DropCaret() { + caret.active = false; + InvalidateCaret(); +} + +void Editor::InvalidateCaret() { + if (posDrag >= 0) + InvalidateRange(posDrag, posDrag + 1); + else + InvalidateRange(currentPos, currentPos + 1); +} + +void Editor::PaintSelMargin(Surface *surfWindow, PRectangle &rc) { + if (vs.fixedColumnWidth == 0) + return; + + PRectangle rcMargin = GetClientRectangle(); + rcMargin.right = vs.fixedColumnWidth; + + if (!rc.Intersects(rcMargin)) + return; + + Surface *surface; + if (bufferedDraw) { + surface = &pixmapSelMargin; + } else { + surface = surfWindow; + } + + PRectangle rcSelMargin = rcMargin; + rcSelMargin.right = rcMargin.left; + + for (int margin=0; margin < vs.margins; margin++) { + if (vs.ms[margin].width > 0) { + + rcSelMargin.left = rcSelMargin.right; + rcSelMargin.right = rcSelMargin.left + vs.ms[margin].width; + + if (vs.ms[margin].symbol) { + /* alternate scheme: + if (vs.ms[margin].mask & SC_MASK_FOLDERS) + surface->FillRectangle(rcSelMargin, vs.styles[STYLE_DEFAULT].back.allocated); + else + // Required because of special way brush is created for selection margin + surface->FillRectangle(rcSelMargin, pixmapSelPattern); + */ + if (vs.ms[margin].mask & SC_MASK_FOLDERS) + // Required because of special way brush is created for selection margin + surface->FillRectangle(rcSelMargin, pixmapSelPattern); + else + surface->FillRectangle(rcSelMargin, vs.styles[STYLE_LINENUMBER].back.allocated); + } else { + surface->FillRectangle(rcSelMargin, vs.styles[STYLE_LINENUMBER].back.allocated); + } + + int visibleLine = topLine; + int line = cs.DocFromDisplay(visibleLine); + int yposScreen = 0; + + while (line < pdoc->LinesTotal() && yposScreen < rcMargin.bottom) { + int marks = pdoc->GetMark(line); + if (pdoc->GetLevel(line) & SC_FOLDLEVELHEADERFLAG) { + if (cs.GetExpanded(line)) { + marks |= 1 << SC_MARKNUM_FOLDEROPEN; + } else { + marks |= 1 << SC_MARKNUM_FOLDER; + } + } + marks &= vs.ms[margin].mask; + PRectangle rcMarker = rcSelMargin; + rcMarker.top = yposScreen; + rcMarker.bottom = yposScreen + vs.lineHeight; + if (!vs.ms[margin].symbol) { + char number[100]; + number[0] = '\0'; + sprintf(number, "%d", line + 1); + if (foldFlags & 64) + sprintf(number, "%X", pdoc->GetLevel(line)); + int xpos = 0; + PRectangle rcNumber=rcMarker; + // Right justify + int width = surface->WidthText(vs.styles[STYLE_LINENUMBER].font, number, strlen(number)); + xpos = rcNumber.right - width - 3; + rcNumber.left = xpos; + if ((visibleLine < cs.LinesDisplayed()) && cs.GetVisible(line)) { + surface->DrawText(rcNumber, vs.styles[STYLE_LINENUMBER].font, + rcNumber.top + vs.maxAscent, number, strlen(number), + vs.styles[STYLE_LINENUMBER].fore.allocated, + vs.styles[STYLE_LINENUMBER].back.allocated); + } + } + + if (marks) { + for (int markBit = 0; (markBit < 32) && marks; markBit++) { + if (marks & 1) { + rcMarker.top++; + rcMarker.bottom--; + vs.markers[markBit].Draw(surface, rcMarker); + } + marks >>= 1; + } + } + + visibleLine++; + line = cs.DocFromDisplay(visibleLine); + yposScreen += vs.lineHeight; + } + } + } + + PRectangle rcBlankMargin = rcMargin; + rcBlankMargin.left = rcSelMargin.right; + surface->FillRectangle(rcBlankMargin, vs.styles[STYLE_DEFAULT].back.allocated); + + if (bufferedDraw) { + surfWindow->Copy(rcMargin, Point(), pixmapSelMargin); + } +} + +void DrawTabArrow(Surface *surface, PRectangle rcTab, int ymid) { + int ydiff = (rcTab.bottom - rcTab.top) / 2; + int xhead = rcTab.right - 1 - ydiff; + if ((rcTab.left + 2) < (rcTab.right - 1)) + surface->MoveTo(rcTab.left + 2, ymid); + else + surface->MoveTo(rcTab.right - 1, ymid); + surface->LineTo(rcTab.right - 1, ymid); + surface->LineTo(xhead, ymid - ydiff); + surface->MoveTo(rcTab.right - 1, ymid); + surface->LineTo(xhead, ymid + ydiff); +} + +void Editor::LayoutLine(int line, Surface *surface, ViewStyle &vstyle, LineLayout &ll) { + int numCharsInLine = 0; + int posLineStart = pdoc->LineStart(line); + int posLineEnd = pdoc->LineStart(line + 1); + Font &ctrlCharsFont = vstyle.styles[STYLE_CONTROLCHAR].font; + char styleByte = 0; + int styleMask = pdoc->stylingBitsMask; + for (int charInDoc = posLineStart; + charInDoc < posLineEnd && numCharsInLine < LineLayout::maxLineLength - 1; + charInDoc++) { + char chDoc = pdoc->CharAt(charInDoc); + styleByte = pdoc->StyleAt(charInDoc); + if (vstyle.viewEOL || ((chDoc != '\r') && (chDoc != '\n'))) { + ll.chars[numCharsInLine] = chDoc; + ll.styles[numCharsInLine] = styleByte & styleMask; + ll.indicators[numCharsInLine] = styleByte & ~styleMask; + numCharsInLine++; + } + } + ll.chars[numCharsInLine] = 0; + ll.styles[numCharsInLine] = styleByte; // For eolFilled + ll.indicators[numCharsInLine] = 0; + + // Layout the line, determining the position of each character + int startseg = 0; + int startsegx = 0; + ll.positions[0] = 0; + unsigned int tabWidth = vstyle.spaceWidth * pdoc->tabInChars; + + for (int charInLine = 0; charInLine < numCharsInLine; charInLine++) { + if ((ll.styles[charInLine] != ll.styles[charInLine + 1]) || + IsControlCharacter(ll.chars[charInLine]) || IsControlCharacter(ll.chars[charInLine + 1])) { + ll.positions[startseg] = 0; + if (IsControlCharacter(ll.chars[charInLine])) { + if (ll.chars[charInLine] == '\t') { + ll.positions[charInLine + 1] = ((((startsegx + 2) / + tabWidth) + 1) * tabWidth) - startsegx; + } else { + const char *ctrlChar = ControlCharacterString(ll.chars[charInLine]); + // +3 For a blank on front and rounded edge each side: + ll.positions[charInLine + 1] = surface->WidthText(ctrlCharsFont, ctrlChar, strlen(ctrlChar)) + 3; + } + } else { + surface->MeasureWidths(vstyle.styles[ll.styles[charInLine]].font, ll.chars + startseg, + charInLine - startseg + 1, ll.positions + startseg + 1); + } + for (int posToIncrease = startseg; posToIncrease <= (charInLine + 1); posToIncrease++) { + ll.positions[posToIncrease] += startsegx; + } + startsegx = ll.positions[charInLine + 1]; + startseg = charInLine + 1; + } + } + ll.numCharsInLine = numCharsInLine; +} + +void Editor::DrawLine(Surface *surface, ViewStyle &vsDraw, int line, int xStart, PRectangle rcLine, LineLayout &ll) { + + PRectangle rcSegment = rcLine; + + // Using one font for all control characters so it can be controlled independently to ensure + // the box goes around the characters tightly. Seems to be no way to work out what height + // is taken by an individual character - internal leading gives varying results. + Font &ctrlCharsFont = vsDraw.styles[STYLE_CONTROLCHAR].font; + + int marks = 0; + Colour markBack = Colour(0, 0, 0); + if (vsDraw.maskInLine) { + marks = pdoc->GetMark(line) & vsDraw.maskInLine; + if (marks) { + for (int markBit = 0; (markBit < 32) && marks; markBit++) { + if (marks & 1) { + markBack = vsDraw.markers[markBit].back.allocated; + } + marks >>= 1; + } + } + marks = pdoc->GetMark(line) & vsDraw.maskInLine; + } + + int posLineStart = pdoc->LineStart(line); + int posLineEnd = pdoc->LineStart(line + 1); + + int selStart = SelectionStart(line); + int selEnd = SelectionEnd(line); + + int styleMask = pdoc->stylingBitsMask; + int startseg = 0; + for (int i = 0; i < ll.numCharsInLine; i++) { + + int iDoc = i + posLineStart; + // If there is the end of a style run for any reason + if ((ll.styles[i] != ll.styles[i + 1]) || + IsControlCharacter(ll.chars[i]) || IsControlCharacter(ll.chars[i + 1]) || + ((selStart != selEnd) && ((iDoc + 1 == selStart) || (iDoc + 1 == selEnd))) || + (i == (theEdge-1))) { + int styleMain = ll.styles[i]; + Colour textBack = vsDraw.styles[styleMain].back.allocated; + Colour textFore = vsDraw.styles[styleMain].fore.allocated; + Font &textFont = vsDraw.styles[styleMain].font; + bool inSelection = (iDoc >= selStart) && (iDoc < selEnd) && (selStart != selEnd); + if (inSelection && !hideSelection) { + if (vsDraw.selbackset) + textBack = vsDraw.selbackground.allocated; + if (vsDraw.selforeset) + textFore = vsDraw.selforeground.allocated; + } else { + if (marks) + textBack = markBack; + if ((edgeState == EDGE_BACKGROUND) && (i >= theEdge) && (ll.chars[i] != '\n') && (ll.chars[i] != '\r')) + textBack = vs.edgecolour.allocated; + } + // Manage tab display + if (ll.chars[i] == '\t') { + rcSegment.left = ll.positions[i] + xStart; + rcSegment.right = ll.positions[i + 1] + xStart; + surface->FillRectangle(rcSegment, textBack); + if (vsDraw.viewWhitespace) { + surface->PenColour(textFore); + PRectangle rcTab(rcSegment.left + 1, rcSegment.top + 4, + rcSegment.right - 1, rcSegment.bottom - vsDraw.maxDescent); + DrawTabArrow(surface, rcTab, rcSegment.top + vsDraw.lineHeight / 2); + } + // Manage control character display + } else if (IsControlCharacter(ll.chars[i])) { + const char *ctrlChar = ControlCharacterString(ll.chars[i]); + rcSegment.left = ll.positions[i] + xStart; + rcSegment.right = ll.positions[i + 1] + xStart; + surface->FillRectangle(rcSegment, textBack); + int normalCharHeight = surface->Ascent(ctrlCharsFont) - + surface->InternalLeading(ctrlCharsFont); + PRectangle rcCChar = rcSegment; + rcCChar.left = rcCChar.left + 1; + rcCChar.top = rcSegment.top + vsDraw.maxAscent - normalCharHeight; + rcCChar.bottom = rcSegment.top + vsDraw.maxAscent + 1; + PRectangle rcCentral = rcCChar; + rcCentral.top++; + rcCentral.bottom--; + surface->FillRectangle(rcCentral, textFore); + PRectangle rcChar = rcCChar; + rcChar.left++; + rcChar.right--; + surface->DrawTextClipped(rcChar, ctrlCharsFont, + rcSegment.top + vsDraw.maxAscent, ctrlChar, strlen(ctrlChar), + textBack, textFore); + // Manage normal display + } else { + rcSegment.left = ll.positions[startseg] + xStart; + rcSegment.right = ll.positions[i + 1] + xStart; + // Only try do draw if really visible - enhances performance by not calling environment to + // draw strings that are completely past the right side of the window. + if (rcSegment.left <= rcLine.right) { + surface->DrawText(rcSegment, textFont, + rcSegment.top + vsDraw.maxAscent, ll.chars + startseg, + i - startseg + 1, textFore, textBack); + if (vsDraw.viewWhitespace) { + for (int cpos = 0; cpos <= i - startseg; cpos++) { + if (ll.chars[cpos + startseg] == ' ') { + int xmid = (ll.positions[cpos + startseg] + ll.positions[cpos + startseg + 1]) / 2; + PRectangle rcDot(xmid + xStart, rcSegment.top + vsDraw.lineHeight / 2, 0, 0); + rcDot.right = rcDot.left + 1; + rcDot.bottom = rcDot.top + 1; + surface->FillRectangle(rcDot, textFore); + } + } + } + } + } + startseg = i + 1; + } + } + + // Draw indicators + int indStart[INDIC_MAX + 1] = {0}; + for (int indica = 0; indica <= INDIC_MAX; indica++) + indStart[indica] = 0; + + for (int indicPos = 0; indicPos <= ll.numCharsInLine; indicPos++) { + if (ll.indicators[indicPos] != ll.indicators[indicPos + 1]) { + int mask = 1 << pdoc->stylingBits; + for (int indicnum = 0; mask <= 0x100; indicnum++) { + if ((ll.indicators[indicPos + 1] & mask) && !(ll.indicators[indicPos] & mask)) { + indStart[indicnum] = ll.positions[indicPos + 1]; + } + if (!(ll.indicators[indicPos + 1] & mask) && (ll.indicators[indicPos] & mask)) { + PRectangle rcIndic( + indStart[indicnum] + xStart, + rcLine.top + vsDraw.maxAscent, + ll.positions[indicPos + 1] + xStart, + rcLine.top + vsDraw.maxAscent + 3); + vsDraw.indicators[indicnum].Draw(surface, rcIndic); + } + mask = mask << 1; + } + } + } + // End of the drawing of the current line + + // Fill in a PRectangle representing the end of line characters + int xEol = ll.positions[ll.numCharsInLine]; + rcSegment.left = xEol + xStart; + rcSegment.right = xEol + vsDraw.aveCharWidth + xStart; + bool eolInSelection = (posLineEnd > selStart) && (posLineEnd <= selEnd) && (selStart != selEnd); + if (eolInSelection && !hideSelection && vsDraw.selbackset && (line < pdoc->LinesTotal()-1)) { + surface->FillRectangle(rcSegment, vsDraw.selbackground.allocated); + } else if (marks) { + surface->FillRectangle(rcSegment, markBack); + } else { + surface->FillRectangle(rcSegment, vsDraw.styles[ll.styles[ll.numCharsInLine] & styleMask].back.allocated); + } + + rcSegment.left = xEol + vsDraw.aveCharWidth + xStart; + rcSegment.right = rcLine.right; + if (marks) { + surface->FillRectangle(rcSegment, markBack); + } else if (vsDraw.styles[ll.styles[ll.numCharsInLine] & styleMask].eolFilled) { + surface->FillRectangle(rcSegment, vsDraw.styles[ll.styles[ll.numCharsInLine] & styleMask].back.allocated); + } else { + surface->FillRectangle(rcSegment, vsDraw.styles[STYLE_DEFAULT].back.allocated); + } + + if (edgeState == EDGE_LINE) { + int edgeX = theEdge * vsDraw.spaceWidth; + rcSegment.left = edgeX + xStart; + rcSegment.right = rcSegment.left + 1; + surface->FillRectangle(rcSegment, vs.edgecolour.allocated); + } +} + +void Editor::Paint(Surface *surfaceWindow, PRectangle rcArea) { + //Platform::DebugPrintf("Paint %d %d - %d %d\n", rcArea.left, rcArea.top, rcArea.right, rcArea.bottom); + RefreshStyleData(); + + PRectangle rcClient = GetClientRectangle(); + //Platform::DebugPrintf("Client: (%3d,%3d) ... (%3d,%3d) %d\n", + // rcClient.left, rcClient.top, rcClient.right, rcClient.bottom); + + if (!pixmapSelPattern.Initialised()) { + pixmapSelPattern.InitPixMap(8, 8, surfaceWindow); + // This complex procedure is to reproduce the checker board dithered pattern used by windows + // for scroll bars and Visual Studio for its selection margin. The colour of this pattern is half + // way between the chrome colour and the chrome highlight colour making a nice transition + // between the window chrome and the content area. And it works in low colour depths. + PRectangle rcPattern(0, 0, 8, 8); + if (vs.selbarlight.desired == Colour(0xff, 0xff, 0xff)) { + pixmapSelPattern.FillRectangle(rcPattern, vs.selbar.allocated); + pixmapSelPattern.PenColour(vs.selbarlight.allocated); + for (int stripe = 0; stripe < 8; stripe++) { + pixmapSelPattern.MoveTo(0, stripe * 2); + pixmapSelPattern.LineTo(8, stripe * 2 - 8); + } + } else { + // User has chosen an unusual chrome colour scheme so just use the highlight edge colour. + pixmapSelPattern.FillRectangle(rcPattern, vs.selbarlight.allocated); + } + } + + if (bufferedDraw) { + if (!pixmapLine.Initialised()) { + pixmapLine.InitPixMap(rcClient.Width(), rcClient.Height(), + surfaceWindow); + pixmapSelMargin.InitPixMap(vs.fixedColumnWidth, + rcClient.Height(), surfaceWindow); + } + } + + surfaceWindow->SetPalette(&palette, true); + pixmapLine.SetPalette(&palette, !wMain.HasFocus()); + + //Platform::DebugPrintf("Paint: (%3d,%3d) ... (%3d,%3d)\n", + // rcArea.left, rcArea.top, rcArea.right, rcArea.bottom); + + int screenLinePaintFirst = rcArea.top / vs.lineHeight; + // The area to be painted plus one extra line is styled. + // The extra line is to determine when a style change, such as statrting a comment flows on to other lines. + int lineStyleLast = topLine + (rcArea.bottom-1) / vs.lineHeight + 1; + //Platform::DebugPrintf("Paint lines = %d .. %d\n", topLine + screenLinePaintFirst, lineStyleLast); + int endPosPaint = pdoc->Length(); + if (lineStyleLast < cs.LinesDisplayed()) + endPosPaint = pdoc->LineStart(cs.DocFromDisplay(lineStyleLast + 1)); + + int xStart = vs.fixedColumnWidth - xOffset; + int ypos = 0; + if (!bufferedDraw) + ypos += screenLinePaintFirst * vs.lineHeight; + int yposScreen = screenLinePaintFirst * vs.lineHeight; + + if (endPosPaint > pdoc->GetEndStyled()) { + // Notify container to do some more styling + NotifyStyleNeeded(endPosPaint); + } + if (needUpdateUI) { + NotifyUpdateUI(); + needUpdateUI = false; + } + + PaintSelMargin(surfaceWindow, rcArea); + + PRectangle rcRightMargin = rcClient; + rcRightMargin.left = rcRightMargin.right - vs.rightMarginWidth; + if (rcArea.Intersects(rcRightMargin)) { + surfaceWindow->FillRectangle(rcRightMargin, vs.styles[STYLE_DEFAULT].back.allocated); + } + + if (paintState == paintAbandoned) { + // Either NotifyStyleNeeded or NotifyUpdateUI noticed that painting is needed + // outside the current painting rectangle + //Platform::DebugPrintf("Abandoning paint\n"); + return; + } + //Platform::DebugPrintf("start display %d, offset = %d\n", pdoc->Length(), xOffset); + + Surface *surface = 0; + if (rcArea.right > vs.fixedColumnWidth) { + + if (bufferedDraw) { + surface = &pixmapLine; + } else { + surface = surfaceWindow; + } + + int visibleLine = topLine + screenLinePaintFirst; + int line = cs.DocFromDisplay(visibleLine); + + int posCaret = currentPos; + if (posDrag >= 0) + posCaret = posDrag; + int lineCaret = pdoc->LineFromPosition(posCaret); + + // Remove selection margin from drawing area so text will not be drawn + // on it in unbuffered mode. + PRectangle rcTextArea = rcClient; + rcTextArea.left = vs.fixedColumnWidth; + rcTextArea.right -= vs.rightMarginWidth; + surfaceWindow->SetClip(rcTextArea); + //GTimer *tim=g_timer_new(); + while (visibleLine <= cs.LinesDisplayed() && yposScreen < rcArea.bottom) { + //g_timer_start(tim); + //Platform::DebugPrintf("Painting line %d\n", line); + + int posLineStart = pdoc->LineStart(line); + int posLineEnd = pdoc->LineStart(line + 1); + //Platform::DebugPrintf("line %d %d - %d\n", line, posLineStart, posLineEnd); + + PRectangle rcLine = rcClient; + rcLine.top = ypos; + rcLine.bottom = ypos + vs.lineHeight; + + // Copy this line and its styles from the document into local arrays + // and determine the x position at which each character starts. + LineLayout ll; + LayoutLine(line, surface, vs, ll); + + // Highlight the current braces if any + if ((braces[0] >= posLineStart) && (braces[0] < posLineEnd)) + ll.styles[braces[0] - posLineStart] = bracesMatchStyle; + if ((braces[1] >= posLineStart) && (braces[1] < posLineEnd)) + ll.styles[braces[1] - posLineStart] = bracesMatchStyle; + + // Draw the line + if (cs.GetVisible(line)) + DrawLine(surface, vs, line, xStart, rcLine, ll); + + bool expanded = cs.GetExpanded(line); + if ( (expanded && (foldFlags & 2)) || (!expanded && (foldFlags & 4)) ) { + if (pdoc->GetLevel(line) & SC_FOLDLEVELHEADERFLAG) { + PRectangle rcFoldLine = rcLine; + rcFoldLine.bottom = rcFoldLine.top + 1; + surface->FillRectangle(rcFoldLine, vs.styles[STYLE_DEFAULT].fore.allocated); + } + } + if ( (expanded && (foldFlags & 8)) || (!expanded && (foldFlags & 16)) ) { + if (pdoc->GetLevel(line) & SC_FOLDLEVELHEADERFLAG) { + PRectangle rcFoldLine = rcLine; + rcFoldLine.top = rcFoldLine.bottom - 1; + surface->FillRectangle(rcFoldLine, vs.styles[STYLE_DEFAULT].fore.allocated); + } + } + + // Draw the Caret + if (line == lineCaret) { + int xposCaret = ll.positions[posCaret - posLineStart] + xStart; + int widthOverstrikeCaret = + ll.positions[posCaret - posLineStart + 1] - ll.positions[posCaret - posLineStart]; + if (posCaret == pdoc->Length()) // At end of document + widthOverstrikeCaret = vs.aveCharWidth; + if ((posCaret - posLineStart) >= ll.numCharsInLine) // At end of line + widthOverstrikeCaret = vs.aveCharWidth; + if (widthOverstrikeCaret < 3) // Make sure its visible + widthOverstrikeCaret = 3; + if (((caret.active && caret.on) || (posDrag >= 0)) && xposCaret >= 0) { + PRectangle rcCaret = rcLine; + if (posDrag >= 0) { + rcCaret.left = xposCaret; + rcCaret.right = xposCaret + 1; + } else { + if (inOverstrike) { + rcCaret.top = rcCaret.bottom - 2; + rcCaret.left = xposCaret + 1; + rcCaret.right = rcCaret.left + widthOverstrikeCaret - 1; + } else { + rcCaret.left = xposCaret; + rcCaret.right = xposCaret + 1; + } + } + surface->FillRectangle(rcCaret, vs.caretcolour.allocated); + } + } + + if (cs.GetVisible(line)) { + if (bufferedDraw) { + Point from(vs.fixedColumnWidth, 0); + PRectangle rcCopyArea(vs.fixedColumnWidth, yposScreen, + rcClient.right, yposScreen + vs.lineHeight); + surfaceWindow->Copy(rcCopyArea, from, pixmapLine); + } + } + + if (!bufferedDraw) { + ypos += vs.lineHeight; + } + + yposScreen += vs.lineHeight; + visibleLine++; + line = cs.DocFromDisplay(visibleLine); + //gdk_flush(); + //g_timer_stop(tim); + //Platform::DebugPrintf("Paint [%0d] took %g\n", line, g_timer_elapsed(tim, 0)); + } + //g_timer_destroy(tim); + PRectangle rcBeyondEOF = rcClient; + rcBeyondEOF.left = vs.fixedColumnWidth; + rcBeyondEOF.right = rcBeyondEOF.right; + rcBeyondEOF.top = (cs.LinesDisplayed() - topLine) * vs.lineHeight; + if (rcBeyondEOF.top < rcBeyondEOF.bottom) { + surfaceWindow->FillRectangle(rcBeyondEOF, vs.styles[STYLE_DEFAULT].back.allocated); + if (edgeState == EDGE_LINE) { + int edgeX = theEdge * vs.spaceWidth; + rcBeyondEOF.left = edgeX + xStart; + rcBeyondEOF.right = rcBeyondEOF.left + 1; + surfaceWindow->FillRectangle(rcBeyondEOF, vs.edgecolour.allocated); + } + } + } +} + +// Space (3 space characters) between line numbers and text when printing. +#define lineNumberPrintSpace " " + +// This is mostly copied from the Paint method but with some things omitted +// such as the margin markers, line numbers, selection and caret +// Should be merged back into a combined Draw method. +long Editor::FormatRange(bool draw, FORMATRANGE *pfr) { + if (!pfr) + return 0; + + Surface *surface = new Surface(); + surface->Init(pfr->hdc); + Surface *surfaceMeasure = new Surface(); + surfaceMeasure->Init(pfr->hdcTarget); + + ViewStyle vsPrint(vs); + + // Modify the view style for printing as do not normally want any of the transient features to be printed + // Printing supports only the line number margin. + int lineNumberIndex = -1; + for (int margin=0; margin < ViewStyle::margins; margin++) { + if ((!vsPrint.ms[margin].symbol) && (vsPrint.ms[margin].width > 0)) { + lineNumberIndex = margin; + } else { + vsPrint.ms[margin].width = 0; + } + } + vsPrint.showMarkedLines = false; + vsPrint.fixedColumnWidth = 0; + vsPrint.zoomLevel = 0; + // Don't show the selection when printing + vsPrint.selbackset = false; + vsPrint.selforeset = false; + // White background for the line numbers + vsPrint.styles[STYLE_LINENUMBER].back.desired = Colour(0xff,0xff,0xff); + + vsPrint.Refresh(*surfaceMeasure); + // Ensure colours are set up + vsPrint.RefreshColourPalette(palette, true); + vsPrint.RefreshColourPalette(palette, false); + // Determining width must hapen after fonts have been realised in Refresh + int lineNumberWidth = 0; + if (lineNumberIndex >= 0) { + lineNumberWidth = surface->WidthText(vsPrint.styles[STYLE_LINENUMBER].font, + "9999" lineNumberPrintSpace, 4 + strlen(lineNumberPrintSpace)); + vsPrint.ms[lineNumberIndex].width = lineNumberWidth; + } + + int linePrintStart = pdoc->LineFromPosition(pfr->chrg.cpMin); + int linePrintLast = linePrintStart + (pfr->rc.bottom - pfr->rc.top) / vsPrint.lineHeight - 1; + if (linePrintLast < linePrintStart) + linePrintLast = linePrintStart; + int linePrintMax = pdoc->LineFromPosition(pfr->chrg.cpMax - 1); + if (linePrintLast > linePrintMax) + linePrintLast = linePrintMax; + //Platform::DebugPrintf("Formatting lines=[%0d,%0d,%0d] top=%0d bottom=%0d line=%0d %0d\n", + // linePrintStart, linePrintLast, linePrintMax, pfr->rc.top, pfr->rc.bottom, vsPrint.lineHeight, + // surfaceMeasure->Height(vsPrint.styles[STYLE_LINENUMBER].font)); + int endPosPrint = pdoc->Length(); + if (linePrintLast < pdoc->LinesTotal()) + endPosPrint = pdoc->LineStart(linePrintLast + 1); + + if (endPosPrint > pdoc->GetEndStyled()) { + // Notify container to do some more styling + NotifyStyleNeeded(endPosPrint); + } + int xStart = vsPrint.fixedColumnWidth + pfr->rc.left + lineNumberWidth; + int ypos = pfr->rc.top; + int line = linePrintStart; + + if (draw) { // Otherwise just measuring + + while (line <= linePrintLast && ypos < pfr->rc.bottom) { + + PRectangle rcLine; + rcLine.left = pfr->rc.left + lineNumberWidth; + rcLine.top = ypos; + rcLine.right = pfr->rc.right; + rcLine.bottom = ypos + vsPrint.lineHeight; + + if (lineNumberWidth) { + char number[100]; + sprintf(number, "%d" lineNumberPrintSpace, line + 1); + PRectangle rcNumber = rcLine; + rcNumber.right = rcNumber.left + lineNumberWidth; + // Right justify + rcNumber.left += lineNumberWidth - + surface->WidthText(vsPrint.styles[STYLE_LINENUMBER].font, number, strlen(number)); + surface->DrawText(rcNumber, vsPrint.styles[STYLE_LINENUMBER].font, + ypos + vsPrint.maxAscent, number, strlen(number), + vsPrint.styles[STYLE_LINENUMBER].fore.allocated, + vsPrint.styles[STYLE_LINENUMBER].back.allocated); + } + + // Copy this line and its styles from the document into local arrays + // and determine the x position at which each character starts. + LineLayout ll; + LayoutLine(line, surfaceMeasure, vsPrint, ll); + + // Draw the line + DrawLine(surface, vsPrint, line, xStart, rcLine, ll); + + ypos += vsPrint.lineHeight; + line++; + } + } + + delete surface; + delete surfaceMeasure; + + return endPosPrint; +} + +void Editor::SetScrollBarsTo(PRectangle) { + RefreshStyleData(); + + int nMax = cs.LinesDisplayed(); + int nPage = cs.LinesDisplayed() - MaxScrollPos() + 1; + bool modified = ModifyScrollBars(nMax, nPage); + + // TODO: ensure always showing as many lines as possible + // May not be, if, for example, window made larger + if (topLine > MaxScrollPos()) { + SetTopLine(Platform::Clamp(topLine, 0, MaxScrollPos())); + SetVerticalScrollPos(); + Redraw(); + } + if (modified) + Redraw(); + //Platform::DebugPrintf("end max = %d page = %d\n", nMax, nPage); +} + +void Editor::SetScrollBars() { + PRectangle rsClient = GetClientRectangle(); + SetScrollBarsTo(rsClient); +} + +void Editor::AddChar(char ch) { + bool wasSelection = currentPos != anchor; + ClearSelection(); + if (inOverstrike && !wasSelection) { + if (currentPos < (pdoc->Length() - 1)) { + if ((pdoc->CharAt(currentPos) != '\r') && (pdoc->CharAt(currentPos) != '\n')) { + pdoc->DelChar(currentPos); + } + } + } + pdoc->InsertChar(currentPos, ch); + SetEmptySelection(currentPos + 1); + EnsureCaretVisible(); + SetLastXChosen(); + NotifyChar(ch); +} + +void Editor::ClearSelection() { + if (selType == selRectangle) { + pdoc->BeginUndoAction(); + int lineStart = pdoc->LineFromPosition(SelectionStart()); + int lineEnd = pdoc->LineFromPosition(SelectionEnd()); + int startPos = SelectionStart(); + int line; + for (line=lineStart; line <= lineEnd; line++) { + startPos = SelectionStart(line); + unsigned int chars = SelectionEnd(line) - startPos; + if (0 != chars) { + pdoc->DeleteChars(startPos, chars); + } + } + SetEmptySelection(startPos); + selType = selStream; + pdoc->EndUndoAction(); + } else { + int startPos = SelectionStart(); + unsigned int chars = SelectionEnd() - startPos; + SetEmptySelection(startPos); + if (0 != chars) { + pdoc->DeleteChars(startPos, chars); + } + } +} + +void Editor::ClearAll() { + if (0 != pdoc->Length()) { + pdoc->DeleteChars(0, pdoc->Length()); + } + cs.Clear(); + anchor = 0; + currentPos = 0; + SetTopLine(0); + SetVerticalScrollPos(); +} + +void Editor::Cut() { + Copy(); + ClearSelection(); +} + +void Editor::PasteRectangular(int pos, const char *ptr, int len) { + currentPos = pos; + int insertPos = currentPos; + int xInsert = XFromPosition(currentPos); + int line = pdoc->LineFromPosition(currentPos); + bool prevCr = false; + for (int i=0; i<len; i++) { + if ((ptr[i] == '\r') || (ptr[i] == '\n')) { + if ((ptr[i] == '\r') || (!prevCr)) + line++; + if (line >= pdoc->LinesTotal()) { + if (pdoc->eolMode != SC_EOL_LF) + pdoc->InsertChar(pdoc->Length(), '\r'); + if (pdoc->eolMode != SC_EOL_CR) + pdoc->InsertChar(pdoc->Length(), '\n'); + } + currentPos = PositionFromLineX(line, xInsert); + prevCr = ptr[i] == '\r'; + } else { + pdoc->InsertString(currentPos, ptr+i, 1); + currentPos++; + insertPos = currentPos; + prevCr = false; + } + } + SetEmptySelection(insertPos); +} + +void Editor::Clear() { + if (currentPos == anchor) { + DelChar(); + } else { + ClearSelection(); + } + SetEmptySelection(currentPos); +} + +void Editor::SelectAll() { + SetSelection(0, pdoc->Length()); + Redraw(); +} + +void Editor::Undo() { + if (pdoc->CanUndo()) { + int newPos = pdoc->Undo(); + SetEmptySelection(newPos); + EnsureCaretVisible(); + } +} + +void Editor::Redo() { + if (pdoc->CanRedo()) { + int newPos = pdoc->Redo(); + SetEmptySelection(newPos); + EnsureCaretVisible(); + } +} + +void Editor::DelChar() { + pdoc->DelChar(currentPos); +} + +void Editor::DelCharBack() { + if (currentPos == anchor) { + int newPos = pdoc->DelCharBack(currentPos); + SetEmptySelection(newPos); + } else { + ClearSelection(); + SetEmptySelection(currentPos); + } +} + +void Editor::NotifyFocus(bool) { +} + +void Editor::NotifyStyleNeeded(int endStyleNeeded) { + SCNotification scn; + scn.nmhdr.code = SCN_STYLENEEDED; + scn.position = endStyleNeeded; + NotifyParent(scn); +} + +void Editor::NotifyChar(char ch) { + SCNotification scn; + scn.nmhdr.code = SCN_CHARADDED; + scn.ch = ch; + NotifyParent(scn); +#ifdef MACRO_SUPPORT + if (recordingMacro) { + char txt[2]; + txt[0] = ch; + txt[1] = '\0'; + NotifyMacroRecord(EM_REPLACESEL, 0, (LPARAM) txt); + } +#endif +} + +void Editor::NotifySavePoint(bool isSavePoint) { + SCNotification scn; + if (isSavePoint) { + scn.nmhdr.code = SCN_SAVEPOINTREACHED; + } else { + scn.nmhdr.code = SCN_SAVEPOINTLEFT; + } + NotifyParent(scn); +} + +void Editor::NotifyModifyAttempt() { + SCNotification scn; + scn.nmhdr.code = SCN_MODIFYATTEMPTRO; + NotifyParent(scn); +} + +void Editor::NotifyDoubleClick(Point, bool) { + SCNotification scn; + scn.nmhdr.code = SCN_DOUBLECLICK; + NotifyParent(scn); +} + +void Editor::NotifyUpdateUI() { + SCNotification scn; + scn.nmhdr.code = SCN_UPDATEUI; + NotifyParent(scn); +} + +bool Editor::NotifyMarginClick(Point pt, bool shift, bool ctrl, bool alt) { + int marginClicked = -1; + int x = 0; + for (int margin=0; margin < ViewStyle::margins; margin++) { + if ((pt.x > x) && (pt.x < x + vs.ms[margin].width)) + marginClicked = margin; + x += vs.ms[margin].width; + } + if ((marginClicked >= 0) && vs.ms[marginClicked].sensitive) { + SCNotification scn; + scn.nmhdr.code = SCN_MARGINCLICK; + scn.modifiers = (shift ? SCI_SHIFT : 0) | (ctrl ? SCI_CTRL : 0) | + (alt ? SCI_ALT : 0); + scn.position = pdoc->LineStart(LineFromLocation(pt)); + scn.margin = marginClicked; + NotifyParent(scn); + return true; + } else { + return false; + } +} + +void Editor::NotifyNeedShown(int pos, int len) { + SCNotification scn; + scn.nmhdr.code = SCN_NEEDSHOWN; + scn.position = pos; + scn.length = len; + NotifyParent(scn); +} + +// Notifications from document +void Editor::NotifyModifyAttempt(Document*, void *) { + //Platform::DebugPrintf("** Modify Attempt\n"); + NotifyModifyAttempt(); +} + +void Editor::NotifySavePoint(Document*, void *, bool atSavePoint) { + //Platform::DebugPrintf("** Save Point %s\n", atSavePoint ? "On" : "Off"); + NotifySavePoint(atSavePoint); +} + +void Editor::NotifyModified(Document*, DocModification mh, void *) { + needUpdateUI = true; + if (paintState == painting) { + CheckForChangeOutsidePaint(Range(mh.position, mh.position + mh.length)); + } else if (paintState == notPainting) { + if (mh.modificationType & SC_MOD_CHANGESTYLE) { + if (mh.position < pdoc->LineStart(topLine)) { + // Styling performed before this view + Redraw(); + } else { + InvalidateRange(mh.position, mh.position + mh.length); + } + } else { + // Move selection and brace highlights + if (mh.modificationType & SC_MOD_INSERTTEXT) { + if (currentPos > mh.position) { + currentPos += mh.length; + } + if (anchor > mh.position) { + anchor += mh.length; + } + if (braces[0] > mh.position) { + braces[0] += mh.length; + } + if (braces[1] > mh.position) { + braces[1] += mh.length; + } + } else { // SC_MOD_DELETETEXT + int endPos = mh.position + mh.length; + if (currentPos > mh.position) { + if (currentPos > endPos) { + currentPos -= mh.length; + } else { + currentPos = endPos; + } + } + if (anchor > mh.position) { + if (anchor > endPos) { + anchor -= mh.length; + } else { + anchor = endPos; + } + } + if (braces[0] > mh.position) { + if (braces[0] > endPos) { + braces[0] -= mh.length; + } else { + braces[0] = endPos; + } + } + if (braces[1] > mh.position) { + if (braces[1] > endPos) { + braces[1] -= mh.length; + } else { + braces[1] = endPos; + } + } + } + if (mh.linesAdded != 0) { + + // Update contraction state for inserted and removed lines + // lineOfPos should be calculated in context of state before modification, shouldn't it + int lineOfPos = pdoc->LineFromPosition(mh.position); + if (mh.linesAdded > 0) { + NotifyNeedShown(mh.position, mh.length); + cs.InsertLines(lineOfPos, mh.linesAdded); + } else { + cs.DeleteLines(lineOfPos, -mh.linesAdded); + } + // Avoid scrolling of display if change before current display + if (mh.position < posTopLine) { + int newTop = Platform::Clamp(topLine + mh.linesAdded, 0, MaxScrollPos()); + if (newTop != topLine) { + SetTopLine(newTop); + SetVerticalScrollPos(); + } + } + + //Platform::DebugPrintf("** %x Doc Changed\n", this); + // TODO: could invalidate from mh.startModification to end of screen + //InvalidateRange(mh.position, mh.position + mh.length); + Redraw(); + } else { + //Platform::DebugPrintf("** %x Line Changed %d .. %d\n", this, + // mh.position, mh.position + mh.length); + InvalidateRange(mh.position, mh.position + mh.length); + } + } + } // else paintState == paintAbandoned so no need to do anything + + if (mh.linesAdded != 0) { + SetScrollBars(); + } + + // If client wants to see this modification + if (mh.modificationType & modEventMask) { + if ((mh.modificationType & SC_MOD_CHANGESTYLE) == 0) { + // Real modification made to text of document. + NotifyChange(); // Send EN_CHANGE + } + SCNotification scn; + scn.nmhdr.code = SCN_MODIFIED; + scn.position = mh.position; + scn.modificationType = mh.modificationType; + scn.text = mh.text; + scn.length = mh.length; + scn.linesAdded = mh.linesAdded; + scn.line = mh.line; + scn.foldLevelNow = mh.foldLevelNow; + scn.foldLevelPrev = mh.foldLevelPrev; + NotifyParent(scn); + } +} + +void Editor::NotifyDeleted(Document *document, void *userData) { + /* Do nothing */ +} + +#ifdef MACRO_SUPPORT +void Editor::NotifyMacroRecord(UINT iMessage, WPARAM wParam, LPARAM lParam) { + + // Enumerates all macroable messages + switch (iMessage) { + case WM_CUT: + case WM_COPY: + case WM_PASTE: + case WM_CLEAR: + case EM_REPLACESEL: + case SCI_ADDTEXT: + case SCI_INSERTTEXT: + case SCI_CLEARALL: + case SCI_SELECTALL: + case SCI_GOTOLINE: + case SCI_GOTOPOS: + case SCI_SEARCHANCHOR: + case SCI_SEARCHNEXT: + case SCI_SEARCHPREV: + case SCI_LINEDOWN: + case SCI_LINEDOWNEXTEND: + case SCI_LINEUP: + case SCI_LINEUPEXTEND: + case SCI_CHARLEFT: + case SCI_CHARLEFTEXTEND: + case SCI_CHARRIGHT: + case SCI_CHARRIGHTEXTEND: + case SCI_WORDLEFT: + case SCI_WORDLEFTEXTEND: + case SCI_WORDRIGHT: + case SCI_WORDRIGHTEXTEND: + case SCI_HOME: + case SCI_HOMEEXTEND: + case SCI_LINEEND: + case SCI_LINEENDEXTEND: + case SCI_DOCUMENTSTART: + case SCI_DOCUMENTSTARTEXTEND: + case SCI_DOCUMENTEND: + case SCI_DOCUMENTENDEXTEND: + case SCI_PAGEUP: + case SCI_PAGEUPEXTEND: + case SCI_PAGEDOWN: + case SCI_PAGEDOWNEXTEND: + case SCI_EDITTOGGLEOVERTYPE: + case SCI_CANCEL: + case SCI_DELETEBACK: + case SCI_TAB: + case SCI_BACKTAB: + case SCI_NEWLINE: + case SCI_FORMFEED: + case SCI_VCHOME: + case SCI_VCHOMEEXTEND: + case SCI_DELWORDLEFT: + case SCI_DELWORDRIGHT: + break; + + // Filter out all others (display changes, etc) + default: +// printf("Filtered out %ld of macro recording\n", iMessage); + return; + } + + // Send notification + SCNotification scn; + scn.nmhdr.code = SCN_MACRORECORD; + scn.message = iMessage; + scn.wParam = wParam; + scn.lParam = lParam; + NotifyParent(scn); +} +#endif + +// Force scroll and keep position relative to top of window +void Editor::PageMove(int direction, bool extend) { + Point pt = LocationFromPosition(currentPos); + int topLineNew = Platform::Clamp( + topLine + direction * LinesToScroll(), 0, MaxScrollPos()); + int newPos = PositionFromLocation( + Point(lastXChosen, pt.y + direction * (vs.lineHeight * LinesToScroll()))); + if (topLineNew != topLine) { + SetTopLine(topLineNew); + MovePositionTo(newPos, extend); + Redraw(); + SetVerticalScrollPos(); + } else { + MovePositionTo(newPos, extend); + } +} + +int Editor::KeyCommand(UINT iMessage) { + Point pt = LocationFromPosition(currentPos); + + switch (iMessage) { + case SCI_LINEDOWN: + MovePositionTo(PositionFromLocation( + Point(lastXChosen, pt.y + vs.lineHeight))); + break; + case SCI_LINEDOWNEXTEND: + MovePositionTo(PositionFromLocation( + Point(lastXChosen, pt.y + vs.lineHeight)), true); + break; + case SCI_LINEUP: + MovePositionTo(PositionFromLocation( + Point(lastXChosen, pt.y - vs.lineHeight))); + break; + case SCI_LINEUPEXTEND: + MovePositionTo(PositionFromLocation( + Point(lastXChosen, pt.y - vs.lineHeight)), true); + break; + case SCI_CHARLEFT: + if (SelectionEmpty()) { + MovePositionTo(MovePositionSoVisible(currentPos - 1, -1)); + } else { + MovePositionTo(SelectionStart()); + } + SetLastXChosen(); + break; + case SCI_CHARLEFTEXTEND: + MovePositionTo(MovePositionSoVisible(currentPos - 1, -1), true); + SetLastXChosen(); + break; + case SCI_CHARRIGHT: + if (SelectionEmpty()) { + MovePositionTo(MovePositionSoVisible(currentPos + 1, 1)); + } else { + MovePositionTo(SelectionEnd()); + } + SetLastXChosen(); + break; + case SCI_CHARRIGHTEXTEND: + MovePositionTo(MovePositionSoVisible(currentPos + 1, 1), true); + SetLastXChosen(); + break; + case SCI_WORDLEFT: + MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(currentPos, -1), -1)); + SetLastXChosen(); + break; + case SCI_WORDLEFTEXTEND: + MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(currentPos, -1), -1), true); + SetLastXChosen(); + break; + case SCI_WORDRIGHT: + MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(currentPos, 1), 1)); + SetLastXChosen(); + break; + case SCI_WORDRIGHTEXTEND: + MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(currentPos, 1), 1), true); + SetLastXChosen(); + break; + case SCI_HOME: + MovePositionTo(pdoc->LineStart(pdoc->LineFromPosition(currentPos))); + SetLastXChosen(); + break; + case SCI_HOMEEXTEND: + MovePositionTo(pdoc->LineStart(pdoc->LineFromPosition(currentPos)), true); + SetLastXChosen(); + break; + case SCI_LINEEND: + MovePositionTo(pdoc->LineEndPosition(currentPos)); + SetLastXChosen(); + break; + case SCI_LINEENDEXTEND: + MovePositionTo(pdoc->LineEndPosition(currentPos), true); + SetLastXChosen(); + break; + case SCI_DOCUMENTSTART: + MovePositionTo(0); + SetLastXChosen(); + break; + case SCI_DOCUMENTSTARTEXTEND: + MovePositionTo(0, true); + SetLastXChosen(); + break; + case SCI_DOCUMENTEND: + MovePositionTo(pdoc->Length()); + SetLastXChosen(); + break; + case SCI_DOCUMENTENDEXTEND: + MovePositionTo(pdoc->Length(), true); + SetLastXChosen(); + break; + case SCI_PAGEUP: + PageMove( -1); + break; + case SCI_PAGEUPEXTEND: + PageMove( -1, true); + break; + case SCI_PAGEDOWN: + PageMove(1); + break; + case SCI_PAGEDOWNEXTEND: + PageMove(1, true); + break; + case SCI_EDITTOGGLEOVERTYPE: + inOverstrike = !inOverstrike; + DropCaret(); + ShowCaretAtCurrentPosition(); + break; + case SCI_CANCEL: // Cancel any modes - handled in subclass + // Also unselect text + SetEmptySelection(currentPos); + break; + case SCI_DELETEBACK: + DelCharBack(); + EnsureCaretVisible(); + break; + case SCI_TAB: + Indent(true); + break; + case SCI_BACKTAB: + Indent(false); + break; + case SCI_NEWLINE: + ClearSelection(); + if (pdoc->eolMode == SC_EOL_CRLF) { + pdoc->InsertString(currentPos, "\r\n"); + SetEmptySelection(currentPos + 2); + NotifyChar('\r'); + NotifyChar('\n'); + } else if (pdoc->eolMode == SC_EOL_CR) { + pdoc->InsertChar(currentPos, '\r'); + SetEmptySelection(currentPos + 1); + NotifyChar('\r'); + } else if (pdoc->eolMode == SC_EOL_LF) { + pdoc->InsertChar(currentPos, '\n'); + SetEmptySelection(currentPos + 1); + NotifyChar('\n'); + } + SetLastXChosen(); + EnsureCaretVisible(); + break; + case SCI_FORMFEED: + AddChar('\f'); + break; + case SCI_VCHOME: + MovePositionTo(pdoc->VCHomePosition(currentPos)); + SetLastXChosen(); + break; + case SCI_VCHOMEEXTEND: + MovePositionTo(pdoc->VCHomePosition(currentPos), true); + SetLastXChosen(); + break; + case SCI_ZOOMIN: + if (vs.zoomLevel < 20) + vs.zoomLevel++; + InvalidateStyleRedraw(); + break; + case SCI_ZOOMOUT: + if (vs.zoomLevel > -10) + vs.zoomLevel--; + InvalidateStyleRedraw(); + break; + case SCI_DELWORDLEFT: { + int startWord = pdoc->NextWordStart(currentPos, -1); + pdoc->DeleteChars(startWord, currentPos - startWord); + MovePositionTo(startWord); + } + break; + case SCI_DELWORDRIGHT: { + int endWord = pdoc->NextWordStart(currentPos, 1); + pdoc->DeleteChars(currentPos, endWord - currentPos); + } + break; + } + return 0; +} + +int Editor::KeyDefault(int, int) { + return 0; +} + +int Editor::KeyDown(int key, bool shift, bool ctrl, bool alt) { + int modifiers = (shift ? SCI_SHIFT : 0) | (ctrl ? SCI_CTRL : 0) | + (alt ? SCI_ALT : 0); + int msg = kmap.Find(key, modifiers); + if (msg) + return WndProc(msg, 0, 0); + else + return KeyDefault(key, modifiers); +} + +void Editor::SetWhitespaceVisible(bool view) { + vs.viewWhitespace = view; +} + +bool Editor::GetWhitespaceVisible() { + return vs.viewWhitespace; +} + +void Editor::Indent(bool forwards) { + //Platform::DebugPrintf("INdent %d\n", forwards); + int lineOfAnchor = pdoc->LineFromPosition(anchor); + int lineCurrentPos = pdoc->LineFromPosition(currentPos); + if (lineOfAnchor == lineCurrentPos) { + ClearSelection(); + pdoc->InsertChar(currentPos, '\t'); + //pdoc->InsertChar(currentPos++, '\t'); + SetEmptySelection(currentPos + 1); + } else { + int anchorPosOnLine = anchor - pdoc->LineStart(lineOfAnchor); + int currentPosPosOnLine = currentPos - pdoc->LineStart(lineCurrentPos); + // Multiple lines selected so indent / dedent + int lineTopSel = Platform::Minimum(lineOfAnchor, lineCurrentPos); + int lineBottomSel = Platform::Maximum(lineOfAnchor, lineCurrentPos); + if (pdoc->LineStart(lineBottomSel) == anchor || pdoc->LineStart(lineBottomSel) == currentPos) + lineBottomSel--; // If not selecting any characters on a line, do not indent + pdoc->BeginUndoAction(); + pdoc->Indent(forwards, lineBottomSel, lineTopSel); + pdoc->EndUndoAction(); + if (lineOfAnchor < lineCurrentPos) { + if (currentPosPosOnLine == 0) + SetSelection(pdoc->LineStart(lineCurrentPos), pdoc->LineStart(lineOfAnchor)); + else + SetSelection(pdoc->LineStart(lineCurrentPos + 1), pdoc->LineStart(lineOfAnchor)); + } else { + if (anchorPosOnLine == 0) + SetSelection(pdoc->LineStart(lineCurrentPos), pdoc->LineStart(lineOfAnchor)); + else + SetSelection(pdoc->LineStart(lineCurrentPos), pdoc->LineStart(lineOfAnchor + 1)); + } + } +} + +long Editor::FindText(UINT iMessage, WPARAM wParam, LPARAM lParam) { + FINDTEXTEX *ft = reinterpret_cast<FINDTEXTEX *>(lParam); + int pos = pdoc->FindText(ft->chrg.cpMin, ft->chrg.cpMax, ft->lpstrText, + wParam & FR_MATCHCASE, wParam & FR_WHOLEWORD); + if (pos != -1) { + if (iMessage == EM_FINDTEXTEX) { + ft->chrgText.cpMin = pos; + ft->chrgText.cpMax = pos + strlen(ft->lpstrText); + } + } + return pos; +} + +// Relocatable search support : Searches relative to current selection +// point and sets the selection to the found text range with +// each search. + +// Anchor following searches at current selection start: This allows +// multiple incremental interactive searches to be macro recorded +// while still setting the selection to found text so the find/select +// operation is self-contained. +void Editor::SearchAnchor() { + searchAnchor = SelectionStart(); +} + +// Find text from current search anchor: Must call SearchAnchor first. +// Accepts both SCI_SEARCHNEXT and SCI_SEARCHPREV. +// wParam contains search modes : ORed FR_MATCHCASE and FR_WHOLEWORD. +// lParam contains the text to search for. +long Editor::SearchText(UINT iMessage, WPARAM wParam, LPARAM lParam) { + const char *txt = reinterpret_cast<char *>(lParam); + int pos; + + if (iMessage == SCI_SEARCHNEXT) { + pos = pdoc->FindText(searchAnchor, pdoc->Length(), txt, + wParam & FR_MATCHCASE, + wParam & FR_WHOLEWORD); + } else { + pos = pdoc->FindText(searchAnchor, 0, txt, + wParam & FR_MATCHCASE, + wParam & FR_WHOLEWORD); + } + + if (pos != -1) { + SetSelection(pos, pos + strlen(txt)); + } + + return pos; +} + +void Editor::GoToLine(int lineNo) { + if (lineNo > pdoc->LinesTotal()) + lineNo = pdoc->LinesTotal(); + if (lineNo < 0) + lineNo = 0; + SetEmptySelection(pdoc->LineStart(lineNo)); + ShowCaretAtCurrentPosition(); + EnsureCaretVisible(); +} + +static bool Close(Point pt1, Point pt2) { + if (abs(pt1.x - pt2.x) > 3) + return false; + if (abs(pt1.y - pt2.y) > 3) + return false; + return true; +} + +char *Editor::CopyRange(int start, int end) { + char *text = 0; + if (start < end) { + int len = end - start; + text = new char[len + 1]; + if (text) { + for (int i = 0; i < len; i++) { + text[i] = pdoc->CharAt(start + i); + } + text[len] = '\0'; + } + } + return text; +} + +int Editor::SelectionRangeLength() { + if (selType == selRectangle) { + int lineStart = pdoc->LineFromPosition(SelectionStart()); + int lineEnd = pdoc->LineFromPosition(SelectionEnd()); + int totalSize = 0; + for (int line=lineStart; line <= lineEnd; line++) { + totalSize += SelectionEnd(line) - SelectionStart(line) + 1; + if (pdoc->eolMode == SC_EOL_CRLF) + totalSize++; + } + return totalSize; + } else { + return SelectionEnd() - SelectionStart(); + } +} + +char *Editor::CopySelectionRange() { + if (selType == selRectangle) { + char *text = 0; + int lineStart = pdoc->LineFromPosition(SelectionStart()); + int lineEnd = pdoc->LineFromPosition(SelectionEnd()); + int totalSize = SelectionRangeLength(); + if (totalSize > 0) { + text = new char[totalSize + 1]; + if (text) { + int j = 0; + for (int line=lineStart; line <= lineEnd; line++) { + for (int i=SelectionStart(line);i<SelectionEnd(line);i++) { + text[j++] = pdoc->CharAt(i); + } + if (pdoc->eolMode != SC_EOL_LF) + text[j++] = '\r'; + if (pdoc->eolMode != SC_EOL_CR) + text[j++] = '\n'; + } + text[totalSize] = '\0'; + } + } + return text; + } else { + return CopyRange(SelectionStart(), SelectionEnd()); + } +} + +void Editor::CopySelectionIntoDrag() { + delete []dragChars; + dragChars = 0; + lenDrag = SelectionRangeLength(); + dragChars = CopySelectionRange(); + dragIsRectangle = selType == selRectangle; + if (!dragChars) { + lenDrag = 0; + } +} + +void Editor::SetDragPosition(int newPos) { + if (newPos >= 0) { + newPos = pdoc->MovePositionOutsideChar(newPos, 1); + posDrop = newPos; + } + if (posDrag != newPos) { + caret.on = true; + SetTicking(true); + InvalidateCaret(); + posDrag = newPos; + InvalidateCaret(); + } +} + +void Editor::StartDrag() { + // Always handled by subclasses + //SetMouseCapture(true); + //wDraw.SetCursor(Window::cursorArrow); +} + +void Editor::DropAt(int position, const char *value, bool moving, bool rectangular) { + //Platform::DebugPrintf("DropAt %d\n", inDragDrop); + if (inDragDrop) + dropWentOutside = false; + + int positionWasInSelection = PositionInSelection(position); + + if ((!inDragDrop) || !(0 == positionWasInSelection)) { + + int selStart = SelectionStart(); + int selEnd = SelectionEnd(); + + pdoc->BeginUndoAction(); + + int positionAfterDeletion = position; + if (inDragDrop && moving) { + // Remove dragged out text + if (rectangular) { + int lineStart = pdoc->LineFromPosition(SelectionStart()); + int lineEnd = pdoc->LineFromPosition(SelectionEnd()); + for (int line=lineStart; line <= lineEnd; line++) { + int startPos = SelectionStart(line); + int endPos = SelectionEnd(line); + if (position >= startPos) { + if (position > endPos) { + positionAfterDeletion -= endPos - startPos; + } else { + positionAfterDeletion -= position - startPos; + } + } + } + } else { + if (position > selStart) { + positionAfterDeletion -= selEnd - selStart; + } + } + ClearSelection(); + } + position = positionAfterDeletion; + + if (rectangular) { + PasteRectangular(position, value, strlen(value)); + pdoc->EndUndoAction(); + // Should try to select new rectangle but it may not be a rectangle now so just select the drop position + SetSelection(position, position); + } else { + position = pdoc->MovePositionOutsideChar(position, currentPos - position); + pdoc->InsertString(position, value); + pdoc->EndUndoAction(); + SetSelection(position + strlen(value), position); + } + } else if (inDragDrop) { + SetSelection(position, position); + } +} + +static int BeforeInOrAfter(int val, int minim, int maxim) { + if (val < minim) + return -1; + else if (val > maxim) + return 1; + else + return 0; +} + +int Editor::PositionInSelection(int pos) { + pos = pdoc->MovePositionOutsideChar(pos, currentPos - pos); + if (selType == selRectangle) { + if (pos < SelectionStart()) + return -1; + if (pos > SelectionEnd()) + return 1; + int linePos = pdoc->LineFromPosition(pos); + return BeforeInOrAfter(pos, SelectionStart(linePos), SelectionEnd(linePos)); + } else { + if (currentPos > anchor) { + return BeforeInOrAfter(pos, anchor, currentPos); + } else if (currentPos < anchor) { + return BeforeInOrAfter(pos, currentPos, anchor); + } + } + return 1; +} + +bool Editor::PointInSelection(Point pt) { + // TODO: fix up for rectangular selection + int pos = PositionFromLocation(pt); + if (0 == PositionInSelection(pos)) { + if (pos == SelectionStart()) { + // see if just before selection + Point locStart = LocationFromPosition(pos); + if (pt.x < locStart.x) + return false; + } + if (pos == SelectionEnd()) { + // see if just after selection + Point locEnd = LocationFromPosition(pos); + if (pt.x > locEnd.x) + return false; + } + return true; + } + return false; +} + +bool Editor::PointInSelMargin(Point pt) { + // Really means: "Point in a margin" + if (vs.fixedColumnWidth > 0) { // There is a margin + PRectangle rcSelMargin = GetClientRectangle(); + rcSelMargin.right = vs.fixedColumnWidth - vs.leftMarginWidth; + return rcSelMargin.Contains(pt); + } else { + return false; + } +} + +void Editor::ButtonDown(Point pt, unsigned int curTime, bool shift, bool ctrl, bool alt) { + //Platform::DebugPrintf("Scintilla:ButtonDown %d %d = %d alt=%d\n", curTime, lastClickTime, curTime - lastClickTime, alt); + ptMouseLast = pt; + int newPos = PositionFromLocation(pt); + newPos = pdoc->MovePositionOutsideChar(newPos, currentPos - newPos); + inDragDrop = false; + + bool processed = NotifyMarginClick(pt, shift, ctrl, alt); + if (processed) + return; + + if (shift) { + SetSelection(newPos); + } + if (((curTime - lastClickTime) < Platform::DoubleClickTime()) && Close(pt, lastClick)) { + //Platform::DebugPrintf("Double click %d %d = %d\n", curTime, lastClickTime, curTime - lastClickTime); + SetMouseCapture(true); + SetEmptySelection(newPos); + bool doubleClick = false; + // Stop mouse button bounce changing selection type + if (curTime != lastClickTime) { + if (selectionType == selChar) { + selectionType = selWord; + doubleClick = true; + } else if (selectionType == selWord) { + selectionType = selLine; + } else { + selectionType = selChar; + originalAnchorPos = currentPos; + } + } + + if (selectionType == selWord) { + if (currentPos >= originalAnchorPos) { // Moved forward + SetSelection(pdoc->ExtendWordSelect(currentPos, 1), + pdoc->ExtendWordSelect(originalAnchorPos, -1)); + } else { // Moved backward + SetSelection(pdoc->ExtendWordSelect(currentPos, -1), + pdoc->ExtendWordSelect(originalAnchorPos, 1)); + } + } else if (selectionType == selLine) { + lineAnchor = LineFromLocation(pt); + SetSelection(pdoc->LineStart(lineAnchor + 1), pdoc->LineStart(lineAnchor)); + //Platform::DebugPrintf("Triple click: %d - %d\n", anchor, currentPos); + } + else { + SetEmptySelection(currentPos); + } + //Platform::DebugPrintf("Double click: %d - %d\n", anchor, currentPos); + if (doubleClick) + NotifyDoubleClick(pt, shift); + } else { // Single click + if (PointInSelMargin(pt)) { + if (ctrl) { + SelectAll(); + lastClickTime = curTime; + return; + } + lineAnchor = LineFromLocation(pt); + // While experimenting with folding turn off line selection + if (!shift) { + // Single click in margin: select whole line + SetSelection(pdoc->LineStart(lineAnchor + 1), pdoc->LineStart(lineAnchor)); + } else { + // Single shift+click in margin: select from anchor to beginning of clicked line + SetSelection(pdoc->LineStart(lineAnchor), anchor); + } + SetDragPosition(invalidPosition); + SetMouseCapture(true); + selectionType = selLine; + } else { + if (!shift) { + inDragDrop = PointInSelection(pt); + } + if (inDragDrop) { + SetMouseCapture(false); + SetDragPosition(newPos); + CopySelectionIntoDrag(); + StartDrag(); + } else { + selType = alt ? selRectangle : selStream; + xStartSelect = pt.x - vs.fixedColumnWidth + xOffset; + xEndSelect = pt.x - vs.fixedColumnWidth + xOffset; + SetDragPosition(invalidPosition); + SetMouseCapture(true); + if (!shift) + SetEmptySelection(newPos); + selectionType = selChar; + originalAnchorPos = currentPos; + } + } + } + lastClickTime = curTime; + lastXChosen = pt.x; + ShowCaretAtCurrentPosition(); +} + +void Editor::ButtonMove(Point pt) { + //Platform::DebugPrintf("Move %d %d\n", pt.x, pt.y); + if (HaveMouseCapture()) { + xEndSelect = pt.x - vs.fixedColumnWidth + xOffset; + ptMouseLast = pt; + int movePos = PositionFromLocation(pt); + movePos = pdoc->MovePositionOutsideChar(movePos, currentPos - movePos); + if (posDrag >= 0) { + SetDragPosition(movePos); + } else { + if (selectionType == selChar) { + SetSelection(movePos); + } else if (selectionType == selWord) { + // Continue selecting by word + if (currentPos > originalAnchorPos) { // Moved forward + SetSelection(pdoc->ExtendWordSelect(movePos, 1), + pdoc->ExtendWordSelect(originalAnchorPos, -1)); + } else { // Moved backward + SetSelection(pdoc->ExtendWordSelect(movePos, -1), + pdoc->ExtendWordSelect(originalAnchorPos, 1)); + } + } else { + // Continue selecting by line + int lineMove = LineFromLocation(pt); + if (lineAnchor < lineMove) { + SetSelection(pdoc->LineStart(lineMove + 1), + pdoc->LineStart(lineAnchor)); + } else { + SetSelection(pdoc->LineStart(lineMove), + pdoc->LineStart(lineAnchor + 1)); + } + } + } + EnsureCaretVisible(false); + } else { + if (vs.fixedColumnWidth > 0) { // There is a margin + if (PointInSelMargin(pt)) { + wDraw.SetCursor(Window::cursorReverseArrow); + return; // No need to test for selection + } + } + // Display regular (drag) cursor over selection + if (PointInSelection(pt)) + wDraw.SetCursor(Window::cursorArrow); + else + wDraw.SetCursor(Window::cursorText); + } + +} + +void Editor::ButtonUp(Point pt, unsigned int curTime, bool ctrl) { + //Platform::DebugPrintf("ButtonUp %d\n", HaveMouseCapture()); + if (HaveMouseCapture()) { + if (PointInSelMargin(pt)) { + wDraw.SetCursor(Window::cursorReverseArrow); + } else { + wDraw.SetCursor(Window::cursorText); + } + xEndSelect = pt.x - vs.fixedColumnWidth + xOffset; + ptMouseLast = pt; + SetMouseCapture(false); + int newPos = PositionFromLocation(pt); + newPos = pdoc->MovePositionOutsideChar(newPos, currentPos - newPos); + if (inDragDrop) { + int selStart = SelectionStart(); + int selEnd = SelectionEnd(); + if (selStart < selEnd) { + if (dragChars && lenDrag) { + if (ctrl) { + pdoc->InsertString(newPos, dragChars, lenDrag); + SetSelection(newPos, newPos + lenDrag); + } else if (newPos < selStart) { + pdoc->DeleteChars(selStart, lenDrag); + pdoc->InsertString(newPos, dragChars, lenDrag); + SetSelection(newPos, newPos + lenDrag); + } else if (newPos > selEnd) { + pdoc->DeleteChars(selStart, lenDrag); + newPos -= lenDrag; + pdoc->InsertString(newPos, dragChars, lenDrag); + SetSelection(newPos, newPos + lenDrag); + } else { + SetEmptySelection(newPos); + } + delete []dragChars; + dragChars = 0; + lenDrag = 0; + } + selectionType = selChar; + } + } else { + if (selectionType == selChar) { + SetSelection(newPos); + } + } + lastClickTime = curTime; + lastClick = pt; + lastXChosen = pt.x; + inDragDrop = false; + EnsureCaretVisible(false); + } +} + +// Called frequently to perform background UI including +// caret blinking and automatic scrolling. +void Editor::Tick() { + if (HaveMouseCapture()) { + // Auto scroll + ButtonMove(ptMouseLast); + } + if (caret.period > 0) { + timer.ticksToWait -= timer.tickSize; + if (timer.ticksToWait <= 0) { + caret.on = !caret.on; + timer.ticksToWait = caret.period; + InvalidateCaret(); + } + } +} + +static bool IsIn(int a, int minimum, int maximum) { + return (a >= minimum) && (a <= maximum); +} + +static bool IsOverlap(int mina, int maxa, int minb, int maxb) { + return + IsIn(mina, minb, maxb) || + IsIn(maxa, minb, maxb) || + IsIn(minb, mina, maxa) || + IsIn(maxb, mina, maxa); +} + +void Editor::CheckForChangeOutsidePaint(Range r) { + if (paintState == painting && !paintingAllText) { + //Platform::DebugPrintf("Checking range in paint %d-%d\n", r.start, r.end); + if (!r.Valid()) + return; + + PRectangle rcText = GetTextRectangle(); + // Determine number of lines displayed including a possible partially displayed last line + int linesDisplayed = (rcText.bottom - rcText.top - 1) / vs.lineHeight + 1; + int bottomLine = topLine + linesDisplayed - 1; + + int lineRangeStart = cs.DisplayFromDoc(pdoc->LineFromPosition(r.start)); + int lineRangeEnd = cs.DisplayFromDoc(pdoc->LineFromPosition(r.end)); + if (!IsOverlap(topLine, bottomLine, lineRangeStart, lineRangeEnd)) { + //Platform::DebugPrintf("No overlap (%d-%d) with window(%d-%d)\n", + // lineRangeStart, lineRangeEnd, topLine, bottomLine); + return; + } + + // Assert rcPaint contained within or equal to rcText + if (rcPaint.top > rcText.top) { + // does range intersect rcText.top .. rcPaint.top + int paintTopLine = ((rcPaint.top - rcText.top-1) / vs.lineHeight) + topLine; + // paintTopLine is the top line of the paint rectangle or the line just above if that line is completely inside the paint rectangle + if (IsOverlap(topLine, paintTopLine, lineRangeStart, lineRangeEnd)) { + //Platform::DebugPrintf("Change (%d-%d) in top npv(%d-%d)\n", + // lineRangeStart, lineRangeEnd, topLine, paintTopLine); + paintState = paintAbandoned; + return; + } + } + if (rcPaint.bottom < rcText.bottom) { + // does range intersect rcPaint.bottom .. rcText.bottom + int paintBottomLine = ((rcPaint.bottom - rcText.top-1) / vs.lineHeight + 1) + topLine; + // paintTopLine is the bottom line of the paint rectangle or the line just below if that line is completely inside the paint rectangle + if (IsOverlap(paintBottomLine, bottomLine, lineRangeStart, lineRangeEnd)) { + //Platform::DebugPrintf("Change (%d-%d) in bottom npv(%d-%d)\n", + // lineRangeStart, lineRangeEnd, paintBottomLine, bottomLine); + paintState = paintAbandoned; + return; + } + } + } +} + +char BraceOpposite(char ch) { + switch (ch) { + case '(': return ')'; + case ')': return '('; + case '[': return ']'; + case ']': return '['; + case '{': return '}'; + case '}': return '{'; + case '<': return '>'; + case '>': return '<'; + default: return '\0'; + } +} + +// TODO: should be able to extend styled region to find matching brace +// TODO: may need to make DBCS safe +// so should be moved into Document +int Editor::BraceMatch(int position, int maxReStyle) { + char chBrace = pdoc->CharAt(position); + char chSeek = BraceOpposite(chBrace); + if (!chSeek) + return - 1; + char styBrace = pdoc->StyleAt(position) & pdoc->stylingBitsMask; + int direction = -1; + if (chBrace == '(' || chBrace == '[' || chBrace == '{' || chBrace == '<') + direction = 1; + int depth = 1; + position = position + direction; + while ((position >= 0) && (position < pdoc->Length())) { + char chAtPos = pdoc->CharAt(position); + char styAtPos = pdoc->StyleAt(position) & pdoc->stylingBitsMask; + if ((position > pdoc->GetEndStyled()) || (styAtPos == styBrace)) { + if (chAtPos == chBrace) + depth++; + if (chAtPos == chSeek) + depth--; + if (depth == 0) + return position; + } + position = position + direction; + } + return - 1; +} + +void Editor::SetBraceHighlight(Position pos0, Position pos1, int matchStyle) { + if ((pos0 != braces[0]) || (pos1 != braces[1]) || (matchStyle != bracesMatchStyle)) { + if ((braces[0] != pos0) || (matchStyle != bracesMatchStyle)) { + CheckForChangeOutsidePaint(Range(braces[0])); + CheckForChangeOutsidePaint(Range(pos0)); + braces[0] = pos0; + } + if ((braces[1] != pos1) || (matchStyle != bracesMatchStyle)) { + CheckForChangeOutsidePaint(Range(braces[1])); + CheckForChangeOutsidePaint(Range(pos1)); + braces[1] = pos1; + } + bracesMatchStyle = matchStyle; + if (paintState == notPainting) { + Redraw(); + } + } +} + +void Editor::SetDocPointer(Document *document) { + //Platform::DebugPrintf("** %x setdoc to %x\n", pdoc, document); + pdoc->RemoveWatcher(this, 0); + pdoc->Release(); + if (document == NULL) { + pdoc = new Document(); + } else { + pdoc = document; + } + pdoc->AddRef(); + // Reset the contraction state to fully shown. + cs.Clear(); + cs.InsertLines(0, pdoc->LinesTotal()); + + pdoc->AddWatcher(this, 0); + Redraw(); + SetScrollBars(); +} + +// Recursively expand a fold, making lines visible except where they have an unexpanded parent +void Editor::Expand(int &line, bool doExpand) { + int lineMaxSubord = pdoc->GetLastChild(line); + line++; + while (line <= lineMaxSubord) { + if (doExpand) + cs.SetVisible(line, line, true); + int level = pdoc->GetLevel(line); + if (level & SC_FOLDLEVELHEADERFLAG) { + if (doExpand && cs.GetExpanded(line)) { + Expand(line, true); + } else { + Expand(line, false); + } + } else { + line++; + } + } +} + +void Editor::ToggleContraction(int line) { + if (pdoc->GetLevel(line) & SC_FOLDLEVELHEADERFLAG) { + if (cs.GetExpanded(line)) { + int lineMaxSubord = pdoc->GetLastChild(line); + cs.SetExpanded(line, 0); + if (lineMaxSubord > line) { + cs.SetVisible(line+1, lineMaxSubord, false); + SetScrollBars(); + Redraw(); + } + } else { + cs.SetExpanded(line, 1); + Expand(line, true); + SetScrollBars(); + Redraw(); + } + } +} + +// Recurse up from this line to find any folds that prevent this line from being visible +// and unfold them all. +void Editor::EnsureLineVisible(int line) { + if (!cs.GetVisible(line)) { + int lineParent = pdoc->GetFoldParent(line); + if (lineParent >= 0) { + if (line != lineParent) + EnsureLineVisible(lineParent); + if (!cs.GetExpanded(lineParent)) { + cs.SetExpanded(lineParent, 1); + Expand(lineParent, true); + } + } + SetScrollBars(); + Redraw(); + } +} + +LRESULT Editor::WndProc(UINT iMessage, WPARAM wParam, LPARAM lParam) { + //Platform::DebugPrintf("S start wnd proc %d %d %d\n",iMessage, wParam, lParam); + + // Optional macro recording hook +#ifdef MACRO_SUPPORT + if (recordingMacro) + NotifyMacroRecord(iMessage, wParam, lParam); +#endif + + switch (iMessage) { + + case WM_GETTEXT: + { + if (lParam == 0) + return 0; + char *ptr = reinterpret_cast<char *>(lParam); + unsigned int iChar = 0; + for (; iChar < wParam-1; iChar++) + ptr[iChar] = pdoc->CharAt(iChar); + ptr[iChar] = '\0'; + return iChar; + } + + case WM_SETTEXT: + { + if (lParam == 0) + return FALSE; + pdoc->DeleteChars(0, pdoc->Length()); + SetEmptySelection(0); + pdoc->InsertString(0, reinterpret_cast<char *>(lParam)); + return TRUE; + } + + case WM_GETTEXTLENGTH: + return pdoc->Length(); + + case WM_NOTIFY: + //Platform::DebugPrintf("S notify %d %d\n", wParam, lParam); + break; + + case WM_CUT: + Cut(); + SetLastXChosen(); + break; + + case WM_COPY: + Copy(); + break; + + case WM_PASTE: + Paste(); + SetLastXChosen(); + break; + + case WM_CLEAR: + Clear(); + SetLastXChosen(); + break; + + case WM_UNDO: + Undo(); + SetLastXChosen(); + break; + + // Edit control mesages + + // Not supported (no-ops): + // EM_GETWORDBREAKPROC + // EM_GETWORDBREAKPROCEX + // EM_SETWORDBREAKPROC + // EM_SETWORDBREAKPROCEX + // EM_GETWORDWRAPMODE + // EM_SETWORDWRAPMODE + // EM_LIMITTEXT + // EM_EXLIMITTEXT + // EM_SETRECT + // EM_SETRECTNP + // EM_FMTLINES + // EM_GETHANDLE + // EM_SETHANDLE + // EM_GETPASSWORDCHAR + // EM_SETPASSWORDCHAR + // EM_SETTABSTOPS + // EM_FINDWORDBREAK + // EM_GETCHARFORMAT + // EM_SETCHARFORMAT + // EM_GETOLEINTERFACE + // EM_SETOLEINTERFACE + // EM_SETOLECALLBACK + // EM_GETPARAFORMAT + // EM_SETPARAFORMAT + // EM_PASTESPECIAL + // EM_REQUESTRESIZE + // EM_GETBKGNDCOLOR + // EM_SETBKGNDCOLOR + // EM_STREAMIN + // EM_STREAMOUT + // EM_GETIMECOLOR + // EM_SETIMECOLOR + // EM_GETIMEOPTIONS + // EM_SETIMEOPTIONS + // EM_GETOPTIONS + // EM_SETOPTIONS + // EM_GETPUNCTUATION + // EM_SETPUNCTUATION + // EM_GETTHUMB + + // Not supported but should be: + // EM_GETEVENTMASK + // EM_SETEVENTMASK + // For printing: + // EM_DISPLAYBAND + // EM_SETTARGETDEVICE + + case EM_CANUNDO: + return pdoc->CanUndo() ? TRUE : FALSE; + + case EM_UNDO: + Undo(); + break; + + case EM_EMPTYUNDOBUFFER: + pdoc->DeleteUndoHistory(); + return 0; + + case EM_GETFIRSTVISIBLELINE: + return topLine; + + case EM_GETLINE: { + if (lParam == 0) + return 0; + int lineStart = pdoc->LineStart(wParam); + int lineEnd = pdoc->LineStart(wParam + 1); + char *ptr = reinterpret_cast<char *>(lParam); + WORD *pBufSize = reinterpret_cast<WORD *>(lParam); + if (*pBufSize < lineEnd - lineStart) { + ptr[0] = '\0'; // If no characters copied have to put a NUL into buffer + return 0; + } + int iPlace = 0; + for (int iChar = lineStart; iChar < lineEnd; iChar++) + ptr[iPlace++] = pdoc->CharAt(iChar); + return iPlace; + } + + case EM_GETLINECOUNT: + if (pdoc->LinesTotal() == 0) + return 1; + else + return pdoc->LinesTotal(); + + case EM_GETMODIFY: + return !pdoc->IsSavePoint(); + + case EM_SETMODIFY: + // Not really supported now that there is the save point stuff + //pdoc->isModified = wParam; + //return pdoc->isModified; + return false; + + case EM_GETRECT: + if (lParam == 0) + return 0; + *(reinterpret_cast<PRectangle *>(lParam)) = GetClientRectangle(); + break; + + case EM_GETSEL: + if (wParam) + *reinterpret_cast<int *>(wParam) = SelectionStart(); + if (lParam) + *reinterpret_cast<int *>(lParam) = SelectionEnd(); + return MAKELONG(SelectionStart(), SelectionEnd()); + + case EM_EXGETSEL: { + if (lParam == 0) + return 0; + CHARRANGE *pCR = reinterpret_cast<CHARRANGE *>(lParam); + pCR->cpMin = SelectionStart(); + pCR->cpMax = SelectionEnd(); + } + break; + + case EM_SETSEL: { + int nStart = static_cast<int>(wParam); + int nEnd = static_cast<int>(lParam); + if (nEnd < 0) + nEnd = pdoc->Length(); + if (nStart < 0) + nStart = nEnd; // Remove selection + SetSelection(nEnd, nStart); + EnsureCaretVisible(); + } + break; + + case EM_EXSETSEL: { + if (lParam == 0) + return 0; + CHARRANGE *pCR = reinterpret_cast<CHARRANGE *>(lParam); + if (pCR->cpMax == -1) { + SetSelection(pCR->cpMin, pdoc->Length()); + } else { + SetSelection(pCR->cpMin, pCR->cpMax); + } + EnsureCaretVisible(); + return pdoc->LineFromPosition(SelectionStart()); + } + + case EM_GETSELTEXT: { + if (lParam == 0) + return 0; + char *ptr = reinterpret_cast<char *>(lParam); + int selSize = SelectionRangeLength(); + char *text = CopySelectionRange(); + int iChar = 0; + if (text) { + for (; iChar < selSize; iChar++) + ptr[iChar] = text[iChar]; + ptr[iChar] = '\0'; + delete []text; + } + return iChar; + } + + case EM_GETWORDBREAKPROC: + return 0; + + case EM_SETWORDBREAKPROC: + break; + + case EM_LIMITTEXT: + // wParam holds the number of characters control should be limited to + break; + + case EM_GETLIMITTEXT: + return 0xffffffff; + + case EM_GETOLEINTERFACE: + return 0; + + case EM_LINEFROMCHAR: + if (static_cast<int>(wParam) < 0) + wParam = SelectionStart(); + return pdoc->LineFromPosition(wParam); + + case EM_EXLINEFROMCHAR: + if (static_cast<int>(lParam) < 0) + lParam = SelectionStart(); // Not specified, but probably OK + return pdoc->LineFromPosition(lParam); + + case EM_LINEINDEX: + if (static_cast<int>(wParam) < 0) + wParam = pdoc->LineFromPosition(SelectionStart()); + if (wParam == 0) + return 0; // Even if there is no text, there is a first line that starts at 0 + if (static_cast<int>(wParam) > pdoc->LinesTotal()) + return - 1; + //if (wParam > pdoc->LineFromPosition(pdoc->Length())) // Useful test, anyway... + // return -1; + return pdoc->LineStart(wParam); + + case EM_LINELENGTH: + { + if (static_cast<int>(wParam) < 0) // Who use this anyway? + return 0; // Should be... Too complex to describe here, see MS specs! + if (static_cast<int>(wParam) > pdoc->Length()) // Useful test, anyway... + return 0; + int line = pdoc->LineFromPosition(wParam); + int charsOnLine = 0; + for (int pos = pdoc->LineStart(line); pos < pdoc->LineStart(line + 1); pos++) { + if ((pdoc->CharAt(pos) != '\r') && (pdoc->CharAt(pos) != '\n')) + charsOnLine++; + } + return charsOnLine; + } + + // Replacement of the old Scintilla interpretation of EM_LINELENGTH + case SCI_LINELENGTH: + if ((static_cast<int>(wParam) < 0) || + (static_cast<int>(wParam) > pdoc->LineFromPosition(pdoc->Length()))) + return 0; + return pdoc->LineStart(wParam + 1) - pdoc->LineStart(wParam); + + case EM_REPLACESEL: { + if (lParam == 0) + return 0; + pdoc->BeginUndoAction(); + ClearSelection(); + char *replacement = reinterpret_cast<char *>(lParam); + pdoc->InsertString(currentPos, replacement); + pdoc->EndUndoAction(); + SetEmptySelection(currentPos + strlen(replacement)); + EnsureCaretVisible(); + } + break; + + case EM_LINESCROLL: + ScrollTo(topLine + lParam); + HorizontalScrollTo(xOffset + wParam * vs.spaceWidth); + return TRUE; + + case EM_SCROLLCARET: + EnsureCaretVisible(); + break; + + case EM_SETREADONLY: + pdoc->SetReadOnly(wParam); + return TRUE; + + case EM_SETRECT: + break; + + case EM_CANPASTE: + return 1; + + case EM_CHARFROMPOS: { + if (lParam == 0) + return 0; + Point *ppt = reinterpret_cast<Point *>(lParam); + int pos = PositionFromLocation(*ppt); + int line = pdoc->LineFromPosition(pos); + return MAKELONG(pos, line); + } + + case EM_POSFROMCHAR: { + // The MS specs for this have changed 3 times: using the RichEdit 3 version + if (wParam == 0) + return 0; + Point *ppt = reinterpret_cast<Point *>(wParam); + if (lParam < 0) { + *ppt = Point(0, 0); + } else { + *ppt = LocationFromPosition(lParam); + } + return 0; + } + + case EM_FINDTEXT: + return FindText(iMessage, wParam, lParam); + + case EM_FINDTEXTEX: + return FindText(iMessage, wParam, lParam); + + case EM_GETTEXTRANGE: { + if (lParam == 0) + return 0; + TEXTRANGE *tr = reinterpret_cast<TEXTRANGE *>(lParam); + int cpMax = tr->chrg.cpMax; + if (cpMax == -1) + cpMax = pdoc->Length(); + int len = cpMax - tr->chrg.cpMin; // No -1 as cpMin and cpMax are referring to inter character positions + pdoc->GetCharRange(tr->lpstrText, tr->chrg.cpMin, len); + // Spec says copied text is terminated with a NUL + tr->lpstrText[len] = '\0'; + return len; // Not including NUL + } + + case EM_SELECTIONTYPE: + if (currentPos == anchor) + return SEL_EMPTY; + else + return SEL_TEXT; + + case EM_HIDESELECTION: + hideSelection = wParam; + Redraw(); + break; + + case EM_FORMATRANGE: + return FormatRange(wParam, reinterpret_cast<FORMATRANGE *>(lParam)); + + case EM_GETMARGINS: + return MAKELONG(vs.leftMarginWidth, vs.rightMarginWidth); + + case EM_SETMARGINS: + if (wParam & EC_LEFTMARGIN) { + vs.leftMarginWidth = LOWORD(lParam); + } + if (wParam & EC_RIGHTMARGIN) { + vs.rightMarginWidth = HIWORD(lParam); + } + if (wParam == EC_USEFONTINFO) { + vs.leftMarginWidth = vs.aveCharWidth / 2; + vs.rightMarginWidth = vs.aveCharWidth / 2; + } + InvalidateStyleRedraw(); + break; + + // Control specific mesages + + case SCI_ADDTEXT: { + if (lParam == 0) + return 0; + pdoc->InsertString(CurrentPosition(), reinterpret_cast<char *>(lParam), wParam); + SetEmptySelection(currentPos + wParam); + return 0; + } + + case SCI_ADDSTYLEDTEXT: { + if (lParam == 0) + return 0; + pdoc->InsertStyledString(CurrentPosition() * 2, reinterpret_cast<char *>(lParam), wParam); + SetEmptySelection(currentPos + wParam / 2); + return 0; + } + + case SCI_INSERTTEXT: { + if (lParam == 0) + return 0; + int insertPos = wParam; + if (static_cast<short>(wParam) == -1) + insertPos = CurrentPosition(); + int newCurrent = CurrentPosition(); + int newAnchor = anchor; + char *sz = reinterpret_cast<char *>(lParam); + pdoc->InsertString(insertPos, sz); + if (newCurrent > insertPos) + newCurrent += strlen(sz); + if (newAnchor > insertPos) + newAnchor += strlen(sz); + SetEmptySelection(newCurrent); + return 0; + } + + case SCI_CLEARALL: + ClearAll(); + return 0; + + case SCI_SETUNDOCOLLECTION: + pdoc->SetUndoCollection(static_cast<enum undoCollectionType>(wParam)); + return 0; + +#ifdef INCLUDE_DEPRECATED_FEATURES + case SCI_APPENDUNDOSTARTACTION: + pdoc->AppendUndoStartAction(); + return 0; +#endif + + case SCI_BEGINUNDOACTION: + pdoc->BeginUndoAction(); + return 0; + + case SCI_ENDUNDOACTION: + pdoc->EndUndoAction(); + return 0; + + case SCI_GETCARETPERIOD: + return caret.period; + + case SCI_SETCARETPERIOD: + caret.period = wParam; + break; + + case SCI_SETWORDCHARS: { + if (lParam == 0) + return 0; + pdoc->SetWordChars(reinterpret_cast<unsigned char *>(lParam)); + } + break; + + case SCI_GETLENGTH: + return pdoc->Length(); + + case SCI_GETCHARAT: + return pdoc->CharAt(wParam); + + case SCI_GETCURRENTPOS: + return currentPos; + + case SCI_GETANCHOR: + return anchor; + + case SCI_GETSTYLEAT: + if (static_cast<short>(wParam) >= pdoc->Length()) + return 0; + else + return pdoc->StyleAt(wParam); + + case SCI_REDO: + Redo(); + break; + + case SCI_SELECTALL: + SelectAll(); + break; + + case SCI_SETSAVEPOINT: + pdoc->SetSavePoint(); + NotifySavePoint(true); + break; + + case SCI_GETSTYLEDTEXT: { + if (lParam == 0) + return 0; + TEXTRANGE *tr = reinterpret_cast<TEXTRANGE *>(lParam); + int iPlace = 0; + for (int iChar = tr->chrg.cpMin; iChar < tr->chrg.cpMax; iChar++) { + tr->lpstrText[iPlace++] = pdoc->CharAt(iChar); + tr->lpstrText[iPlace++] = pdoc->StyleAt(iChar); + } + tr->lpstrText[iPlace] = '\0'; + tr->lpstrText[iPlace + 1] = '\0'; + return iPlace; + } + + case SCI_CANREDO: + return pdoc->CanRedo() ? TRUE : FALSE; + + case SCI_MARKERLINEFROMHANDLE: + return pdoc->LineFromHandle(wParam); + + case SCI_MARKERDELETEHANDLE: + pdoc->DeleteMarkFromHandle(wParam); + break; + + case SCI_GETVIEWWS: + return vs.viewWhitespace; + + case SCI_SETVIEWWS: + vs.viewWhitespace = wParam; + Redraw(); + break; + + case SCI_GOTOLINE: + GoToLine(wParam); + break; + + case SCI_GOTOPOS: + SetEmptySelection(wParam); + EnsureCaretVisible(); + Redraw(); + break; + + case SCI_SETANCHOR: + SetSelection(currentPos, wParam); + break; + + case SCI_GETCURLINE: { + if (lParam == 0) + return 0; + int lineCurrentPos = pdoc->LineFromPosition(currentPos); + int lineStart = pdoc->LineStart(lineCurrentPos); + unsigned int lineEnd = pdoc->LineStart(lineCurrentPos + 1); + char *ptr = reinterpret_cast<char *>(lParam); + unsigned int iPlace = 0; + for (unsigned int iChar = lineStart; iChar < lineEnd && iPlace < wParam; iChar++) + ptr[iPlace++] = pdoc->CharAt(iChar); + ptr[iPlace] = '\0'; + return currentPos - lineStart; + } + + case SCI_GETENDSTYLED: + return pdoc->GetEndStyled(); + + case SCI_GETEOLMODE: + return pdoc->eolMode; + + case SCI_SETEOLMODE: + pdoc->eolMode = wParam; + break; + + case SCI_STARTSTYLING: + pdoc->StartStyling(wParam, lParam); + break; + + case SCI_SETSTYLING: + pdoc->SetStyleFor(wParam, lParam); + break; + + case SCI_SETSTYLINGEX: // Specify a complete styling buffer + if (lParam == 0) + return 0; + pdoc->SetStyles(wParam, reinterpret_cast<char *>(lParam)); + break; + +#ifdef INCLUDE_DEPRECATED_FEATURES + case SCI_SETMARGINWIDTH: + if (wParam < 100) { + vs.ms[1].width = wParam; + } + InvalidateStyleRedraw(); + break; +#endif + + case SCI_SETBUFFEREDDRAW: + bufferedDraw = wParam; + break; + + case SCI_SETTABWIDTH: + if (wParam > 0) + pdoc->tabInChars = wParam; + InvalidateStyleRedraw(); + break; + + case SCI_SETCODEPAGE: + pdoc->dbcsCodePage = wParam; + break; + +#ifdef INCLUDE_DEPRECATED_FEATURES + case SCI_SETLINENUMBERWIDTH: + if (wParam < 200) { + vs.ms[0].width = wParam; + } + InvalidateStyleRedraw(); + break; +#endif + + case SCI_SETUSEPALETTE: + palette.allowRealization = wParam; + InvalidateStyleRedraw(); + break; + + // Marker definition and setting + case SCI_MARKERDEFINE: + if (wParam <= MARKER_MAX) + vs.markers[wParam].markType = lParam; + InvalidateStyleData(); + RedrawSelMargin(); + break; + case SCI_MARKERSETFORE: + if (wParam <= MARKER_MAX) + vs.markers[wParam].fore.desired = Colour(lParam); + InvalidateStyleData(); + RedrawSelMargin(); + break; + case SCI_MARKERSETBACK: + if (wParam <= MARKER_MAX) + vs.markers[wParam].back.desired = Colour(lParam); + InvalidateStyleData(); + RedrawSelMargin(); + break; + case SCI_MARKERADD: { + int markerID = pdoc->AddMark(wParam, lParam); + RedrawSelMargin(); + return markerID; + } + + case SCI_MARKERDELETE: + pdoc->DeleteMark(wParam, lParam); + RedrawSelMargin(); + break; + + case SCI_MARKERDELETEALL: + pdoc->DeleteAllMarks(static_cast<int>(wParam)); + RedrawSelMargin(); + break; + + case SCI_MARKERGET: + return pdoc->GetMark(wParam); + + case SCI_MARKERNEXT: { + int lt = pdoc->LinesTotal(); + for (int iLine = wParam; iLine < lt; iLine++) { + if ((pdoc->GetMark(iLine) & lParam) != 0) + return iLine; + } + } + return -1; + + case SCI_MARKERPREVIOUS: { + for (int iLine = wParam; iLine >= 0; iLine--) { + if ((pdoc->GetMark(iLine) & lParam) != 0) + return iLine; + } + } + return -1; + + case SCI_SETMARGINTYPEN: + if (wParam >= 0 && wParam < ViewStyle::margins) { + vs.ms[wParam].symbol = (lParam == SC_MARGIN_SYMBOL); + InvalidateStyleRedraw(); + } + break; + + case SCI_GETMARGINTYPEN: + if (wParam >= 0 && wParam < ViewStyle::margins) + return vs.ms[wParam].symbol ? SC_MARGIN_SYMBOL : SC_MARGIN_NUMBER; + else + return 0; + + case SCI_SETMARGINWIDTHN: + if (wParam >= 0 && wParam < ViewStyle::margins) { + vs.ms[wParam].width = lParam; + InvalidateStyleRedraw(); + } + break; + + case SCI_GETMARGINWIDTHN: + if (wParam >= 0 && wParam < ViewStyle::margins) + return vs.ms[wParam].width; + else + return 0; + + case SCI_SETMARGINMASKN: + if (wParam >= 0 && wParam < ViewStyle::margins) { + vs.ms[wParam].mask = lParam; + InvalidateStyleRedraw(); + } + break; + + case SCI_GETMARGINMASKN: + if (wParam >= 0 && wParam < ViewStyle::margins) + return vs.ms[wParam].mask; + else + return 0; + + case SCI_SETMARGINSENSITIVEN: + if (wParam >= 0 && wParam < ViewStyle::margins) { + vs.ms[wParam].sensitive = lParam; + InvalidateStyleRedraw(); + } + break; + + case SCI_GETMARGINSENSITIVEN: + if (wParam >= 0 && wParam < ViewStyle::margins) + return vs.ms[wParam].sensitive ? 1 : 0; + else + return 0; + + case SCI_STYLECLEARALL: + vs.ClearStyles(); + InvalidateStyleRedraw(); + break; + + case SCI_STYLESETFORE: + if (wParam <= STYLE_MAX) { + vs.styles[wParam].fore.desired = Colour(lParam); + InvalidateStyleRedraw(); + } + break; + case SCI_STYLESETBACK: + if (wParam <= STYLE_MAX) { + vs.styles[wParam].back.desired = Colour(lParam); + InvalidateStyleRedraw(); + } + break; + case SCI_STYLESETBOLD: + if (wParam <= STYLE_MAX) { + vs.styles[wParam].bold = lParam; + InvalidateStyleRedraw(); + } + break; + case SCI_STYLESETITALIC: + if (wParam <= STYLE_MAX) { + vs.styles[wParam].italic = lParam; + InvalidateStyleRedraw(); + } + break; + case SCI_STYLESETEOLFILLED: + if (wParam <= STYLE_MAX) { + vs.styles[wParam].eolFilled = lParam; + InvalidateStyleRedraw(); + } + break; + case SCI_STYLESETSIZE: + if (wParam <= STYLE_MAX) { + vs.styles[wParam].size = lParam; + InvalidateStyleRedraw(); + } + break; + case SCI_STYLESETFONT: + if (lParam == 0) + return 0; + if (wParam <= STYLE_MAX) { + vs.SetStyleFontName(wParam, reinterpret_cast<const char *>(lParam)); + InvalidateStyleRedraw(); + } + break; + + case SCI_STYLERESETDEFAULT: + vs.ResetDefaultStyle(); + InvalidateStyleRedraw(); + break; + + case SCI_SETSTYLEBITS: + pdoc->SetStylingBits(wParam); + break; + + case SCI_GETSTYLEBITS: + return pdoc->stylingBits; + + case SCI_SETLINESTATE: + return pdoc->SetLineState(wParam, lParam); + + case SCI_GETLINESTATE: + return pdoc->GetLineState(wParam); + + case SCI_GETMAXLINESTATE: + return pdoc->GetMaxLineState(); + + // Folding messages + + case SCI_VISIBLEFROMDOCLINE: + return cs.DisplayFromDoc(wParam); + + case SCI_DOCLINEFROMVISIBLE: + return cs.DocFromDisplay(wParam); + + case SCI_SETFOLDLEVEL: { + int prev = pdoc->SetLevel(wParam, lParam); + if (prev != lParam) + RedrawSelMargin(); + return prev; + } + + case SCI_GETFOLDLEVEL: + return pdoc->GetLevel(wParam); + + case SCI_GETLASTCHILD: + return pdoc->GetLastChild(wParam, lParam); + + case SCI_GETFOLDPARENT: + return pdoc->GetFoldParent(wParam); + + case SCI_SHOWLINES: + cs.SetVisible(wParam, lParam, true); + SetScrollBars(); + Redraw(); + break; + + case SCI_HIDELINES: + cs.SetVisible(wParam, lParam, false); + SetScrollBars(); + Redraw(); + break; + + case SCI_GETLINEVISIBLE: + return cs.GetVisible(wParam); + + case SCI_SETFOLDEXPANDED: + if (cs.SetExpanded(wParam, lParam)) { + RedrawSelMargin(); + } + break; + + case SCI_GETFOLDEXPANDED: + return cs.GetExpanded(wParam); + + case SCI_SETFOLDFLAGS: + foldFlags = wParam; + Redraw(); + break; + + case SCI_TOGGLEFOLD: + ToggleContraction(wParam); + break; + + case SCI_ENSUREVISIBLE: + EnsureLineVisible(wParam); + break; + + case SCI_SEARCHANCHOR: + SearchAnchor(); + break; + + case SCI_SEARCHNEXT: + case SCI_SEARCHPREV: + return SearchText(iMessage, wParam, lParam); + break; + + case SCI_SETCARETPOLICY: + caretPolicy = wParam; + caretSlop = lParam; + break; + +#ifdef INCLUDE_DEPRECATED_FEATURES + case SCI_SETFORE: + vs.styles[STYLE_DEFAULT].fore.desired = Colour(wParam); + InvalidateStyleRedraw(); + break; + + case SCI_SETBACK: + vs.styles[STYLE_DEFAULT].back.desired = Colour(wParam); + InvalidateStyleRedraw(); + break; + + case SCI_SETBOLD: + vs.styles[STYLE_DEFAULT].bold = wParam; + InvalidateStyleRedraw(); + break; + + case SCI_SETITALIC: + vs.styles[STYLE_DEFAULT].italic = wParam; + InvalidateStyleRedraw(); + break; + + case SCI_SETSIZE: + vs.styles[STYLE_DEFAULT].size = wParam; + InvalidateStyleRedraw(); + break; + + case SCI_SETFONT: + if (wParam == 0) + return 0; + strcpy(vs.styles[STYLE_DEFAULT].fontName, reinterpret_cast<char *>(wParam)); + InvalidateStyleRedraw(); + break; +#endif + + case SCI_SETSELFORE: + vs.selforeset = wParam; + vs.selforeground.desired = Colour(lParam); + InvalidateStyleRedraw(); + break; + + case SCI_SETSELBACK: + vs.selbackset = wParam; + vs.selbackground.desired = Colour(lParam); + InvalidateStyleRedraw(); + break; + + case SCI_SETCARETFORE: + vs.caretcolour.desired = Colour(wParam); + InvalidateStyleRedraw(); + break; + + case SCI_ASSIGNCMDKEY: + kmap.AssignCmdKey(LOWORD(wParam), HIWORD(wParam), lParam); + break; + + case SCI_CLEARCMDKEY: + kmap.AssignCmdKey(LOWORD(wParam), HIWORD(wParam), WM_NULL); + break; + + case SCI_CLEARALLCMDKEYS: + kmap.Clear(); + break; + + case SCI_INDICSETSTYLE: + if (wParam <= INDIC_MAX) { + vs.indicators[wParam].style = lParam; + InvalidateStyleRedraw(); + } + break; + + case SCI_INDICGETSTYLE: + return (wParam <= INDIC_MAX) ? vs.indicators[wParam].style : 0; + + case SCI_INDICSETFORE: + if (wParam <= INDIC_MAX) { + vs.indicators[wParam].fore.desired = Colour(lParam); + InvalidateStyleRedraw(); + } + break; + + case SCI_INDICGETFORE: + return (wParam <= INDIC_MAX) ? vs.indicators[wParam].fore.desired.AsLong() : 0; + + case SCI_LINEDOWN: + case SCI_LINEDOWNEXTEND: + case SCI_LINEUP: + case SCI_LINEUPEXTEND: + case SCI_CHARLEFT: + case SCI_CHARLEFTEXTEND: + case SCI_CHARRIGHT: + case SCI_CHARRIGHTEXTEND: + case SCI_WORDLEFT: + case SCI_WORDLEFTEXTEND: + case SCI_WORDRIGHT: + case SCI_WORDRIGHTEXTEND: + case SCI_HOME: + case SCI_HOMEEXTEND: + case SCI_LINEEND: + case SCI_LINEENDEXTEND: + case SCI_DOCUMENTSTART: + case SCI_DOCUMENTSTARTEXTEND: + case SCI_DOCUMENTEND: + case SCI_DOCUMENTENDEXTEND: + case SCI_PAGEUP: + case SCI_PAGEUPEXTEND: + case SCI_PAGEDOWN: + case SCI_PAGEDOWNEXTEND: + case SCI_EDITTOGGLEOVERTYPE: + case SCI_CANCEL: + case SCI_DELETEBACK: + case SCI_TAB: + case SCI_BACKTAB: + case SCI_NEWLINE: + case SCI_FORMFEED: + case SCI_VCHOME: + case SCI_VCHOMEEXTEND: + case SCI_ZOOMIN: + case SCI_ZOOMOUT: + case SCI_DELWORDLEFT: + case SCI_DELWORDRIGHT: + return KeyCommand(iMessage); + + case SCI_BRACEHIGHLIGHT: + SetBraceHighlight(static_cast<int>(wParam), lParam, STYLE_BRACELIGHT); + break; + + case SCI_BRACEBADLIGHT: + SetBraceHighlight(static_cast<int>(wParam), -1, STYLE_BRACEBAD); + break; + + case SCI_BRACEMATCH: + // wParam is position of char to find brace for, + // lParam is maximum amount of text to restyle to find it + return BraceMatch(wParam, lParam); + + case SCI_GETVIEWEOL: + return vs.viewEOL; + + case SCI_SETVIEWEOL: + vs.viewEOL = wParam; + Redraw(); + break; + + case SCI_GETEDGECOLUMN: + return theEdge; + + case SCI_SETEDGECOLUMN: + theEdge = wParam; + InvalidateStyleRedraw(); + break; + + case SCI_GETEDGEMODE: + return edgeState; + + case SCI_SETEDGEMODE: + edgeState = wParam; + InvalidateStyleRedraw(); + break; + + case SCI_GETEDGECOLOUR: + return vs.edgecolour.desired.AsLong(); + + case SCI_SETEDGECOLOUR: + vs.edgecolour.desired = Colour(wParam); + InvalidateStyleRedraw(); + break; + + case SCI_GETDOCPOINTER: + return reinterpret_cast<LRESULT>(pdoc); + + case SCI_SETDOCPOINTER: + SetDocPointer(reinterpret_cast<Document *>(lParam)); + return 0; + + case SCI_SETMODEVENTMASK: + modEventMask = wParam; + return 0; + + case SCI_CONVERTEOLS: + pdoc->ConvertLineEnds(wParam); + SetSelection(currentPos, anchor); // Ensure selection inside document + return 0; + +#ifdef MACRO_SUPPORT + case SCI_STARTRECORD: + recordingMacro = 1; + return 0; + + case SCI_STOPRECORD: + recordingMacro = 0; + return 0; +#endif + + default: + return DefWndProc(iMessage, wParam, lParam); + } + //Platform::DebugPrintf("end wnd proc\n"); + return 0l; +} diff --git a/src/Editor.h b/src/Editor.h new file mode 100644 index 000000000..4ff334767 --- /dev/null +++ b/src/Editor.h @@ -0,0 +1,281 @@ +// Scintilla source code edit control +// Editor.h - defines the main editor class +// Copyright 1998-2000 by Neil Hodgson <neilh@scintilla.org> +// The License.txt file describes the conditions under which this software may be distributed. + +#ifndef EDITOR_H +#define EDITOR_H + +class Caret { +public: + bool active; + bool on; + int period; + + Caret(); +}; + +class Timer { + +public: + bool ticking; + int ticksToWait; + enum {tickSize = 100}; + int tickerID; + + Timer(); +}; + +class LineLayout { +public: + // Drawing is only performed for maxLineLength characters on each line. + enum {maxLineLength = 4000}; + int numCharsInLine; + char chars[maxLineLength]; + char styles[maxLineLength]; + char indicators[maxLineLength]; + int positions[maxLineLength]; +}; + +class Editor : public DocWatcher { +protected: // ScintillaBase subclass needs access to much of Editor + + // On GTK+, Scintilla is a container widget holding two scroll bars and a drawing area + // whereas on Windows there is just one window with both scroll bars turned on. + // Therefore, on GTK+ the following are separate windows but only one window on Windows. + Window wMain; // The Scintilla parent window + Window wDraw; // The text drawing area + + // Style resources may be expensive to allocate so are cached between uses. + // When a style attribute is changed, this cache is flushed. + bool stylesValid; + ViewStyle vs; + Palette palette; + + bool hideSelection; + bool inOverstrike; + + // In bufferedDraw mode, graphics operations are drawn to a pixmap and then copied to + // the screen. This avoids flashing but is about 30% slower. + bool bufferedDraw; + + int xOffset; // Horizontal scrolled amount in pixels + int xCaretMargin; // Ensure this many pixels visible on both sides of caret + + Surface pixmapLine; + Surface pixmapSelMargin; + Surface pixmapSelPattern; + // Intellimouse support - currently only implemented for Windows + unsigned int ucWheelScrollLines; + short cWheelDelta; //wheel delta from roll + + KeyMap kmap; + + Caret caret; + Timer timer; + + Point lastClick; + unsigned int lastClickTime; + enum { selChar, selWord, selLine } selectionType; + Point ptMouseLast; + bool firstExpose; + bool inDragDrop; + bool dropWentOutside; + int posDrag; + int posDrop; + int lastXChosen; + int lineAnchor; + int originalAnchorPos; + int currentPos; + int anchor; + int topLine; + int posTopLine; + + bool needUpdateUI; + Position braces[2]; + int bracesMatchStyle; + + int edgeState; + int theEdge; + + enum { notPainting, painting, paintAbandoned } paintState; + PRectangle rcPaint; + bool paintingAllText; + + int modEventMask; + + char *dragChars; + int lenDrag; + bool dragIsRectangle; + enum { selStream, selRectangle, selRectangleFixed } selType; + int xStartSelect; + int xEndSelect; + + int caretPolicy; + int caretSlop; + + int searchAnchor; + +#ifdef MACRO_SUPPORT + int recordingMacro; +#endif + + int foldFlags; + ContractionState cs; + + Document *pdoc; + + Editor(); + virtual ~Editor(); + virtual void Initialise() = 0; + virtual void Finalise(); + + void InvalidateStyleData(); + void InvalidateStyleRedraw(); + virtual void RefreshColourPalette(Palette &pal, bool want); + void RefreshStyleData(); + void DropGraphics(); + + PRectangle GetClientRectangle(); + PRectangle GetTextRectangle(); + + int LinesOnScreen(); + int LinesToScroll(); + int MaxScrollPos(); + Point LocationFromPosition(unsigned int pos); + int XFromPosition(unsigned int pos); + int PositionFromLocation(Point pt); + int PositionFromLineX(int line, int x); + int LineFromLocation(Point pt); + void SetTopLine(int topLineNew); + + void RedrawRect(PRectangle rc); + void Redraw(); + void RedrawSelMargin(); + PRectangle RectangleFromRange(int start, int end); + void InvalidateRange(int start, int end); + + int CurrentPosition(); + bool SelectionEmpty(); + int SelectionStart(int line=-1); + int SelectionEnd(int line=-1); + void SetSelection(int currentPos_, int anchor_); + void SetSelection(int currentPos_); + void SetEmptySelection(int currentPos_); + int MovePositionTo(int newPos, bool extend = false); + int MovePositionSoVisible(int pos, int moveDir); + void SetLastXChosen(); + + void ScrollTo(int line); + virtual void ScrollText(int linesToMove); + void HorizontalScrollTo(int xPos); + void EnsureCaretVisible(bool useMargin=true); + void ShowCaretAtCurrentPosition(); + void DropCaret(); + void InvalidateCaret(); + + void PaintSelMargin(Surface *surface, PRectangle &rc); + void LayoutLine(int line, Surface *surface, ViewStyle &vstyle, LineLayout &ll); + void DrawLine(Surface *surface, ViewStyle &vsDraw, int line, int xStart, + PRectangle rcLine, LineLayout &ll); + void Paint(Surface *surfaceWindow, PRectangle rcArea); + long FormatRange(bool draw, FORMATRANGE *pfr); + + virtual void SetVerticalScrollPos() = 0; + virtual void SetHorizontalScrollPos() = 0; + virtual bool ModifyScrollBars(int nMax, int nPage) = 0; + void SetScrollBarsTo(PRectangle rsClient); + void SetScrollBars(); + + virtual void AddChar(char ch); + void ClearSelection(); + void ClearAll(); + void Cut(); + void PasteRectangular(int pos, const char *ptr, int len); + virtual void Copy() = 0; + virtual void Paste() = 0; + void Clear(); + void SelectAll(); + void Undo(); + void Redo(); + void DelChar(); + void DelCharBack(); + virtual void ClaimSelection() = 0; + + virtual void NotifyChange() = 0; + virtual void NotifyFocus(bool focus); + virtual void NotifyParent(SCNotification scn) = 0; + virtual void NotifyStyleNeeded(int endStyleNeeded); + void NotifyChar(char ch); + void NotifySavePoint(bool isSavePoint); + void NotifyModifyAttempt(); + virtual void NotifyDoubleClick(Point pt, bool shift); + void NotifyUpdateUI(); + bool NotifyMarginClick(Point pt, bool shift, bool ctrl, bool alt); + void NotifyNeedShown(int pos, int len); + + void NotifyModifyAttempt(Document *document, void *userData); + void NotifySavePoint(Document *document, void *userData, bool atSavePoint); + void NotifyModified(Document *document, DocModification mh, void *userData); + void NotifyDeleted(Document *document, void *userData); + +#ifdef MACRO_SUPPORT + void NotifyMacroRecord(UINT iMessage, WPARAM wParam, LPARAM lParam); +#endif + + void PageMove(int direction, bool extend=false); + virtual int KeyCommand(UINT iMessage); + virtual int KeyDefault(int /* key */, int /*modifiers*/); + int KeyDown(int key, bool shift, bool ctrl, bool alt); + + bool GetWhitespaceVisible(); + void SetWhitespaceVisible(bool view); + + void Indent(bool forwards); + + long FindText(UINT iMessage,WPARAM wParam,LPARAM lParam); + void SearchAnchor(); + long SearchText(UINT iMessage,WPARAM wParam,LPARAM lParam); + void GoToLine(int lineNo); + + char *CopyRange(int start, int end); + int SelectionRangeLength(); + char *CopySelectionRange(); + void CopySelectionIntoDrag(); + void SetDragPosition(int newPos); + virtual void StartDrag(); + void DropAt(int position, const char *value, bool moving, bool rectangular); + // PositionInSelection returns 0 if position in selection, -1 if position before selection, and 1 if after. + // Before means either before any line of selection or before selection on its line, with a similar meaning to after + int PositionInSelection(int pos); + bool PointInSelection(Point pt); + bool PointInSelMargin(Point pt); + virtual void ButtonDown(Point pt, unsigned int curTime, bool shift, bool ctrl, bool alt); + void ButtonMove(Point pt); + void ButtonUp(Point pt, unsigned int curTime, bool ctrl); + + void Tick(); + virtual void SetTicking(bool on) = 0; + virtual void SetMouseCapture(bool on) = 0; + virtual bool HaveMouseCapture() = 0; + + void CheckForChangeOutsidePaint(Range r); + int BraceMatch(int position, int maxReStyle); + void SetBraceHighlight(Position pos0, Position pos1, int matchStyle); + + void SetDocPointer(Document *document); + + void Expand(int &line, bool doExpand); + void ToggleContraction(int line); + void EnsureLineVisible(int line); + + virtual LRESULT DefWndProc(UINT iMessage, WPARAM wParam, LPARAM lParam) = 0; + +public: + // Public so scintilla_send_message can use it + virtual LRESULT WndProc(UINT iMessage, WPARAM wParam, LPARAM lParam); + // Public so scintilla_set_id can use it + int ctrlID; +}; + +#endif diff --git a/src/Indicator.cxx b/src/Indicator.cxx new file mode 100644 index 000000000..fb6ad0915 --- /dev/null +++ b/src/Indicator.cxx @@ -0,0 +1,45 @@ +// Scintilla source code edit control +// Indicator.cxx - defines the style of indicators which are text decorations such as underlining +// Copyright 1998-2000 by Neil Hodgson <neilh@scintilla.org> +// The License.txt file describes the conditions under which this software may be distributed. + +#include "Platform.h" + +#include "Scintilla.h" +#include "Indicator.h" + +void Indicator::Draw(Surface *surface, PRectangle &rc) { + surface->PenColour(fore.allocated); + int ymid = (rc.bottom + rc.top) / 2; + if (style == INDIC_SQUIGGLE) { + surface->MoveTo(rc.left, rc.top); + int x = rc.left + 2; + int y = 2; + while (x < rc.right) { + surface->LineTo(x, rc.top + y); + x += 2; + y = 2 - y; + } + surface->LineTo(rc.right, rc.top + y); // Finish the line + } else if (style == INDIC_TT) { + surface->MoveTo(rc.left, ymid); + int x = rc.left + 5; + while (x < rc.right) { + surface->LineTo(x, ymid); + surface->MoveTo(x-3, ymid); + surface->LineTo(x-3, ymid+2); + x++; + surface->MoveTo(x, ymid); + x += 5; + } + surface->LineTo(rc.right, ymid); // Finish the line + if (x - 3 <= rc.right) { + surface->MoveTo(x-3, ymid); + surface->LineTo(x-3, ymid+2); + } + } else { // Either INDIC_PLAIN or unknown + surface->MoveTo(rc.left, ymid); + surface->LineTo(rc.right, ymid); + } +} + diff --git a/src/Indicator.h b/src/Indicator.h new file mode 100644 index 000000000..2472cedc1 --- /dev/null +++ b/src/Indicator.h @@ -0,0 +1,18 @@ +// Scintilla source code edit control +// Indicator.h - defines the style of indicators which are text decorations such as underlining +// Copyright 1998-2000 by Neil Hodgson <neilh@scintilla.org> +// The License.txt file describes the conditions under which this software may be distributed. + +#ifndef INDICATOR_H +#define INDICATOR_H + +class Indicator { +public: + int style; + ColourPair fore; + Indicator() : style(INDIC_PLAIN), fore(Colour(0,0,0)) { + } + void Draw(Surface *surface, PRectangle &rc); +}; + +#endif diff --git a/src/KeyMap.cxx b/src/KeyMap.cxx new file mode 100644 index 000000000..f339cd275 --- /dev/null +++ b/src/KeyMap.cxx @@ -0,0 +1,111 @@ +// Scintilla source code edit control +// KeyMap.cxx - defines a mapping between keystrokes and commands +// Copyright 1998-2000 by Neil Hodgson <neilh@scintilla.org> +// The License.txt file describes the conditions under which this software may be distributed. + +#include "Platform.h" + +#include "Scintilla.h" + +#include "KeyMap.h" + +KeyMap::KeyMap() : kmap(0), len(0), alloc(0) { + for (int i = 0; MapDefault[i].key; i++) { + AssignCmdKey(MapDefault[i].key, + MapDefault[i].modifiers, + MapDefault[i].msg); + } +} + +KeyMap::~KeyMap() { + Clear(); +} + +void KeyMap::Clear() { + delete []kmap; + kmap = 0; + len = 0; + alloc = 0; +} + +void KeyMap::AssignCmdKey(int key, int modifiers, UINT msg) { + if ((len+1) >= alloc) { + KeyToCommand *ktcNew = new KeyToCommand[alloc + 5]; + if (!ktcNew) + return; + for (int k=0;k<len;k++) + ktcNew[k] = kmap[k]; + alloc += 5; + delete []kmap; + kmap = ktcNew; + } + for (int keyIndex = 0; keyIndex < len; keyIndex++) { + if ((key == kmap[keyIndex].key) && (modifiers == kmap[keyIndex].modifiers)) { + kmap[keyIndex].msg = msg; + return; + } + } + kmap[len].key = key; + kmap[len].modifiers = modifiers; + kmap[len].msg = msg; + len++; +} + +UINT KeyMap::Find(int key, int modifiers) { + for (int i=0; i < len; i++) { + if ((key == kmap[i].key) && (modifiers == kmap[i].modifiers)) { + return kmap[i].msg; + } + } + return 0; +} + +KeyToCommand KeyMap::MapDefault[] = { + VK_DOWN, SCI_NORM, SCI_LINEDOWN, + VK_DOWN, SCI_SHIFT, SCI_LINEDOWNEXTEND, + VK_UP, SCI_NORM, SCI_LINEUP, + VK_UP, SCI_SHIFT, SCI_LINEUPEXTEND, + VK_LEFT, SCI_NORM, SCI_CHARLEFT, + VK_LEFT, SCI_SHIFT, SCI_CHARLEFTEXTEND, + VK_LEFT, SCI_CTRL, SCI_WORDLEFT, + VK_LEFT, SCI_CSHIFT, SCI_WORDLEFTEXTEND, + VK_RIGHT, SCI_NORM, SCI_CHARRIGHT, + VK_RIGHT, SCI_SHIFT, SCI_CHARRIGHTEXTEND, + VK_RIGHT, SCI_CTRL, SCI_WORDRIGHT, + VK_RIGHT, SCI_CSHIFT, SCI_WORDRIGHTEXTEND, + VK_HOME, SCI_NORM, SCI_VCHOME, + VK_HOME, SCI_SHIFT, SCI_VCHOMEEXTEND, + VK_HOME, SCI_CTRL, SCI_DOCUMENTSTART, + VK_HOME, SCI_CSHIFT, SCI_DOCUMENTSTARTEXTEND, + VK_END, SCI_NORM, SCI_LINEEND, + VK_END, SCI_SHIFT, SCI_LINEENDEXTEND, + VK_END, SCI_CTRL, SCI_DOCUMENTEND, + VK_END, SCI_CSHIFT, SCI_DOCUMENTENDEXTEND, + VK_PRIOR, SCI_NORM, SCI_PAGEUP, + VK_PRIOR, SCI_SHIFT, SCI_PAGEUPEXTEND, + VK_NEXT, SCI_NORM, SCI_PAGEDOWN, + VK_NEXT, SCI_SHIFT, SCI_PAGEDOWNEXTEND, + VK_DELETE, SCI_NORM, WM_CLEAR, + VK_DELETE, SCI_SHIFT, WM_CUT, + VK_DELETE, SCI_CTRL, SCI_DELWORDRIGHT, + VK_INSERT, SCI_NORM, SCI_EDITTOGGLEOVERTYPE, + VK_INSERT, SCI_SHIFT, WM_PASTE, + VK_INSERT, SCI_CTRL, WM_COPY, + VK_ESCAPE, SCI_NORM, SCI_CANCEL, + VK_BACK, SCI_NORM, SCI_DELETEBACK, + VK_BACK, SCI_CTRL, SCI_DELWORDLEFT, + 'Z', SCI_CTRL, WM_UNDO, + 'Y', SCI_CTRL, SCI_REDO, + 'X', SCI_CTRL, WM_CUT, + 'C', SCI_CTRL, WM_COPY, + 'V', SCI_CTRL, WM_PASTE, + 'A', SCI_CTRL, SCI_SELECTALL, + VK_TAB, SCI_NORM, SCI_TAB, + VK_TAB, SCI_SHIFT, SCI_BACKTAB, + VK_RETURN, SCI_NORM, SCI_NEWLINE, + 'L', SCI_CTRL, SCI_FORMFEED, + VK_ADD, SCI_CTRL, SCI_ZOOMIN, + VK_SUBTRACT, SCI_CTRL, SCI_ZOOMOUT, + 0,0,0, +}; + diff --git a/src/KeyMap.h b/src/KeyMap.h new file mode 100644 index 000000000..814f3aa3b --- /dev/null +++ b/src/KeyMap.h @@ -0,0 +1,35 @@ +// Scintilla source code edit control +// KeyMap.h - defines a mapping between keystrokes and commands +// Copyright 1998-2000 by Neil Hodgson <neilh@scintilla.org> +// The License.txt file describes the conditions under which this software may be distributed. + +#ifndef KEYTOCOMMAND_H +#define KEYTOCOMMAND_H + +#define SCI_NORM 0 +#define SCI_SHIFT SHIFT_PRESSED +#define SCI_CTRL LEFT_CTRL_PRESSED +#define SCI_ALT LEFT_ALT_PRESSED +#define SCI_CSHIFT (SCI_CTRL | SCI_SHIFT) + +class KeyToCommand { +public: + int key; + int modifiers; + UINT msg; +}; + +class KeyMap { + KeyToCommand *kmap; + int len; + int alloc; + static KeyToCommand MapDefault[]; +public: + KeyMap(); + ~KeyMap(); + void Clear(); + void AssignCmdKey(int key, int modifiers, UINT msg); + UINT Find(int key, int modifiers); // 0 returned on failure +}; + +#endif diff --git a/src/KeyWords.cxx b/src/KeyWords.cxx new file mode 100644 index 000000000..7a6784f73 --- /dev/null +++ b/src/KeyWords.cxx @@ -0,0 +1,2648 @@ +// SciTE - Scintilla based Text Editor +// KeyWords.cxx - colourise for particular languages +// Copyright 1998-2000 by Neil Hodgson <neilh@scintilla.org> +// The License.txt file describes the conditions under which this software may be distributed. + +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <stdio.h> +#include <stdarg.h> + +#include "Platform.h" + +#include "PropSet.h" +#include "Accessor.h" +#include "KeyWords.h" +#include "Scintilla.h" +#include "SciLexer.h" + +inline bool IsLeadByte(int codePage, char ch) { +#if PLAT_GTK + // TODO: support DBCS under GTK+ + return false; +#elif PLAT_WIN + return codePage && IsDBCSLeadByteEx(codePage, ch); +#elif PLAT_WX + return false; +#endif +} + +inline bool iswordchar(char ch) { + return isalnum(ch) || ch == '.' || ch == '_'; +} + +inline bool iswordstart(char ch) { + return isalnum(ch) || ch == '_'; +} + +enum { wsSpace = 1, wsTab = 2, wsSpaceTab = 4, wsInconsistent=8}; + +typedef bool (*PFNIsCommentLeader)(StylingContext &styler, int pos, int len); + +static int IndentAmount(StylingContext &styler, int line, int *flags, PFNIsCommentLeader pfnIsCommentLeader = 0) { + int end = styler.Length(); + int spaceFlags = 0; + + // Determines the indentation level of the current line and also checks for consistent + // indentation compared to the previous line. + // Indentation is judged consistent when the indentation whitespace of each line lines + // the same or the indentation of one line is a prefix of the other. + + int pos = styler.LineStart(line); + char ch = styler[pos]; + int indent = 0; + bool inPrevPrefix = line > 0; + int posPrev = inPrevPrefix ? styler.LineStart(line-1) : 0; + while ((ch == ' ' || ch == '\t') && (pos < end)) { + if (inPrevPrefix) { + char chPrev = styler[posPrev++]; + if (chPrev == ' ' || chPrev == '\t') { + if (chPrev != ch) + spaceFlags |= wsInconsistent; + } else { + inPrevPrefix = false; + } + } + if (ch == ' ') { + spaceFlags |= wsSpace; + indent++; + } else { // Tab + spaceFlags |= wsTab; + if (spaceFlags & wsSpace) + spaceFlags |= wsSpaceTab; + indent = (indent / 8 + 1) * 8; + } + ch = styler[++pos]; + } + + *flags = spaceFlags; + indent += SC_FOLDLEVELBASE; + // if completely empty line or the start of a comment... + if ( isspace(ch) || (pfnIsCommentLeader && (*pfnIsCommentLeader)(styler, pos, end-pos)) ) + return indent | SC_FOLDLEVELWHITEFLAG; + else + return indent; +} + +inline bool isoperator(char ch) { + if (isalnum(ch)) + return false; + // '.' left out as it is used to make up numbers + if (ch == '%' || ch == '^' || ch == '&' || ch == '*' || + ch == '(' || ch == ')' || ch == '-' || ch == '+' || + ch == '=' || ch == '|' || ch == '{' || ch == '}' || + ch == '[' || ch == ']' || ch == ':' || ch == ';' || + ch == '<' || ch == '>' || ch == ',' || ch == '/' || + ch == '?' || ch == '!' || ch == '.' || ch == '~') + return true; + return false; +} + +static void classifyWordCpp(unsigned int start, unsigned int end, WordList &keywords, StylingContext &styler) { + char s[100]; + bool wordIsNumber = isdigit(styler[start]) || (styler[start] == '.'); + for (unsigned int i = 0; i < end - start + 1 && i < 30; i++) { + s[i] = styler[start + i]; + s[i + 1] = '\0'; + } + char chAttr = SCE_C_IDENTIFIER; + if (wordIsNumber) + chAttr = SCE_C_NUMBER; + else { + if (keywords.InList(s)) + chAttr = SCE_C_WORD; + } + styler.ColourSegment(start, end, chAttr); +} + +static void ColouriseCppDoc(int codePage, int startPos, int length, + int initStyle, WordList &keywords, StylingContext &styler) { + + bool fold = styler.GetPropSet().GetInt("fold"); + int lineCurrent = styler.GetLine(startPos); + int levelPrev = styler.LevelAt(lineCurrent) & SC_FOLDLEVELNUMBERMASK; + int levelCurrent = levelPrev; + + int state = initStyle; + char chPrev = ' '; + char chNext = styler[startPos]; + int startSeg = startPos; + int lengthDoc = startPos + length; + int visChars = 0; + for (unsigned int i = startPos; i <= lengthDoc; i++) { + char ch = chNext; + chNext = styler.SafeGetCharAt(i + 1); + + if ((fold) && ((ch == '\r' && chNext != '\n') || (ch == '\n'))) { + int lev = levelPrev; + if (visChars == 0) + lev |= SC_FOLDLEVELWHITEFLAG; + if ((levelCurrent > levelPrev) && (visChars > 0)) + lev |= SC_FOLDLEVELHEADERFLAG; + styler.SetLevel(lineCurrent, lev); + lineCurrent++; + visChars = 0; + levelPrev = levelCurrent; + } + if (!isspace(ch)) + visChars++; + + if (IsLeadByte(codePage, ch)) { // dbcs + chNext = styler.SafeGetCharAt(i + 2); + chPrev = ' '; + i += 1; + continue; + } + + if (state == SCE_C_STRINGEOL) { + if (ch != '\r' && ch != '\n') { + styler.ColourSegment(startSeg, i - 1, state); + state = SCE_C_DEFAULT; + startSeg = i; + } + } + if (state == SCE_C_DEFAULT) { + if (iswordstart(ch)) { + styler.ColourSegment(startSeg, i - 1, state); + state = SCE_C_WORD; + startSeg = i; + } else if (ch == '/' && chNext == '*') { + styler.ColourSegment(startSeg, i - 1, state); + if (styler.SafeGetCharAt(i + 2) == '*') + state = SCE_C_COMMENTDOC; + else + state = SCE_C_COMMENT; + startSeg = i; + } else if (ch == '/' && chNext == '/') { + styler.ColourSegment(startSeg, i - 1, state); + state = SCE_C_COMMENTLINE; + startSeg = i; + } else if (ch == '\"') { + styler.ColourSegment(startSeg, i - 1, state); + state = SCE_C_STRING; + startSeg = i; + } else if (ch == '\'') { + styler.ColourSegment(startSeg, i - 1, state); + state = SCE_C_CHARACTER; + startSeg = i; + } else if (ch == '#') { + styler.ColourSegment(startSeg, i - 1, state); + state = SCE_C_PREPROCESSOR; + startSeg = i; + } else if (isoperator(ch)) { + styler.ColourSegment(startSeg, i - 1, state); + styler.ColourSegment(i, i, SCE_C_OPERATOR); + startSeg = i + 1; + if ((ch == '{') || (ch == '}')) { + levelCurrent += (ch == '{') ? 1 : -1; + } + } + } else if (state == SCE_C_WORD) { + if (!iswordchar(ch)) { + classifyWordCpp(startSeg, i - 1, keywords, styler); + state = SCE_C_DEFAULT; + startSeg = i; + if (ch == '/' && chNext == '*') { + if (styler.SafeGetCharAt(i + 2) == '*') + state = SCE_C_COMMENTDOC; + else + state = SCE_C_COMMENT; + } else if (ch == '/' && chNext == '/') { + state = SCE_C_COMMENTLINE; + } else if (ch == '\"') { + state = SCE_C_STRING; + } else if (ch == '\'') { + state = SCE_C_CHARACTER; + } else if (ch == '#') { + state = SCE_C_PREPROCESSOR; + } else if (isoperator(ch)) { + styler.ColourSegment(startSeg, i, SCE_C_OPERATOR); + startSeg = i + 1; + if ((ch == '{') || (ch == '}')) { + levelCurrent += (ch == '{') ? 1 : -1; + } + } + } + } else { + if (state == SCE_C_PREPROCESSOR) { + if ((ch == '\r' || ch == '\n') && (chPrev != '\\')) { + styler.ColourSegment(startSeg, i - 1, state); + state = SCE_C_DEFAULT; + startSeg = i; + } + } else if (state == SCE_C_COMMENT) { + if (ch == '/' && chPrev == '*' && ( + (i > startSeg + 2) || ((initStyle == SCE_C_COMMENT) && (startSeg == startPos)))) { + styler.ColourSegment(startSeg, i, state); + state = SCE_C_DEFAULT; + startSeg = i + 1; + } + } else if (state == SCE_C_COMMENTDOC) { + if (ch == '/' && chPrev == '*' && ( + (i > startSeg + 3) || ((initStyle == SCE_C_COMMENTDOC) && (startSeg == startPos)))) { + styler.ColourSegment(startSeg, i, state); + state = SCE_C_DEFAULT; + startSeg = i + 1; + } + } else if (state == SCE_C_COMMENTLINE) { + if (ch == '\r' || ch == '\n') { + styler.ColourSegment(startSeg, i - 1, state); + state = SCE_C_DEFAULT; + startSeg = i; + } + } else if (state == SCE_C_STRING) { + if ((ch == '\r' || ch == '\n') && (chPrev != '\\')) { + styler.ColourSegment(startSeg, i - 1, state); + state = SCE_C_STRINGEOL; + startSeg = i; + } else if (ch == '\\') { + if (chNext == '\"' || chNext == '\'' || chNext == '\\') { + i++; + ch = chNext; + chNext = styler.SafeGetCharAt(i + 1); + } + } else if (ch == '\"') { + styler.ColourSegment(startSeg, i, state); + state = SCE_C_DEFAULT; + i++; + ch = chNext; + chNext = styler.SafeGetCharAt(i + 1); + startSeg = i; + } + } else if (state == SCE_C_CHARACTER) { + if ((ch == '\r' || ch == '\n') && (chPrev != '\\')) { + styler.ColourSegment(startSeg, i - 1, state); + state = SCE_C_STRINGEOL; + startSeg = i; + } else if (ch == '\\') { + if (chNext == '\"' || chNext == '\'' || chNext == '\\') { + i++; + ch = chNext; + chNext = styler.SafeGetCharAt(i + 1); + } + } else if (ch == '\'') { + styler.ColourSegment(startSeg, i, state); + state = SCE_C_DEFAULT; + i++; + ch = chNext; + chNext = styler.SafeGetCharAt(i + 1); + startSeg = i; + } + } + if (state == SCE_C_DEFAULT) { // One of the above succeeded + if (ch == '/' && chNext == '*') { + if (styler.SafeGetCharAt(i + 2) == '*') + state = SCE_C_COMMENTDOC; + else + state = SCE_C_COMMENT; + } else if (ch == '/' && chNext == '/') { + state = SCE_C_COMMENTLINE; + } else if (ch == '\"') { + state = SCE_C_STRING; + } else if (ch == '\'') { + state = SCE_C_CHARACTER; + } else if (ch == '#') { + state = SCE_C_PREPROCESSOR; + } else if (iswordstart(ch)) { + state = SCE_C_WORD; + } else if (isoperator(ch)) { + styler.ColourSegment(startSeg, i, SCE_C_OPERATOR); + startSeg = i + 1; + if ((ch == '{') || (ch == '}')) { + levelCurrent += (ch == '{') ? 1 : -1; + } + } + } + } + chPrev = ch; + } + if (startSeg < lengthDoc) + styler.ColourSegment(startSeg, lengthDoc - 1, state); + // Fill in the real level of the next line, keeping the current flags as they will be filled in later + if (fold) { + int flagsNext = styler.LevelAt(lineCurrent) & ~SC_FOLDLEVELNUMBERMASK; + //styler.SetLevel(lineCurrent, levelCurrent | flagsNext); + styler.SetLevel(lineCurrent, levelPrev | flagsNext); + + } +} + +inline bool isPerlOperator(char ch) { + if (isalnum(ch)) + return false; + // '.' left out as it is used to make up numbers + if (ch == '%' || ch == '^' || ch == '&' || ch == '*' || ch == '\\' || + ch == '(' || ch == ')' || ch == '-' || ch == '+' || + ch == '=' || ch == '|' || ch == '{' || ch == '}' || + ch == '[' || ch == ']' || ch == ':' || ch == ';' || + ch == '<' || ch == '>' || ch == ',' || ch == '/' || + ch == '?' || ch == '!' || ch == '.' || ch == '~') + return true; + return false; +} + +static int classifyWordPerl(unsigned int start, unsigned int end, WordList &keywords, StylingContext &styler) { + char s[100]; + bool wordIsNumber = isdigit(styler[start]) || (styler[start] == '.'); + for (unsigned int i = 0; i < end - start + 1 && i < 30; i++) { + s[i] = styler[start + i]; + s[i + 1] = '\0'; + } + char chAttr = SCE_PL_IDENTIFIER; + if (wordIsNumber) + chAttr = SCE_PL_NUMBER; + else { + if (keywords.InList(s)) + chAttr = SCE_PL_WORD; + } + styler.ColourSegment(start, end, chAttr); + return chAttr; +} + +static bool isEndVar(char ch) { + return !isalnum(ch) && ch != '#' && ch != '$' && + ch != '_' && ch != '\''; +} + +static bool isMatch(StylingContext &styler, int lengthDoc, int pos, const char *val) { + if ((pos + static_cast<int>(strlen(val))) >= lengthDoc) { + return false; + } + while (*val) { + if (*val != styler[pos++]) { + return false; + } + val++; + } + return true; +} + +static bool isOKQuote(char ch) { + if (isalnum(ch)) + return false; + if (isspace(ch)) + return false; + if (iscntrl(ch)) + return false; + return true; +} + +static char opposite(char ch) { + if (ch == '(') + return ')'; + if (ch == '[') + return ']'; + if (ch == '{') + return '}'; + if (ch == '<') + return '>'; + return ch; +} + +static void ColourisePerlDoc(int codePage, int startPos, int length, int initStyle, + WordList &keywords, StylingContext &styler) { + char sooked[100]; + int quotes = 0; + char quoteDown = 'd'; + char quoteUp = 'd'; + int quoteRep = 1; + int sookedpos = 0; + bool preferRE = true; + sooked[sookedpos] = '\0'; + int state = initStyle; + int lengthDoc = startPos + length; + // If in a long distance lexical state, seek to the beginning to find quote characters + if (state == SCE_PL_HERE || state == SCE_PL_REGEX || + state == SCE_PL_REGSUBST || state == SCE_PL_LONGQUOTE) { + while ((startPos > 1) && (styler.StyleAt(startPos - 1) == state)) { + startPos--; + } + state = SCE_PL_DEFAULT; + } + styler.StartAt(startPos); + char chPrev = ' '; + char chNext = styler[startPos]; + int startSeg = startPos; + for (int i = startPos; i <= lengthDoc; i++) { + char ch = chNext; + chNext = styler.SafeGetCharAt(i + 1); + char chNext2 = styler.SafeGetCharAt(i + 2); + + if (IsLeadByte(codePage, ch)) { // dbcs + chNext = styler.SafeGetCharAt(i + 2); + chPrev = ' '; + i += 1; + continue; + } + + if (state == SCE_PL_DEFAULT) { + if (iswordstart(ch)) { + styler.ColourSegment(startSeg, i - 1, state); + if (ch == 's' && !isalnum(chNext)) { + state = SCE_PL_REGSUBST; + quotes = 0; + quoteUp = '\0'; + quoteDown = '\0'; + quoteRep = 2; + startSeg = i; + } else if (ch == 'm' && !isalnum(chNext)) { + state = SCE_PL_REGEX; + quotes = 0; + quoteUp = '\0'; + quoteDown = '\0'; + quoteRep = 1; + startSeg = i; + } else if (ch == 't' && chNext == 'r' && !isalnum(chNext2)) { + state = SCE_PL_REGSUBST; + quotes = 0; + quoteUp = '\0'; + quoteDown = '\0'; + quoteRep = 2; + startSeg = i; + i++; + chNext = chNext2; + } else if (ch == 'q' && (chNext == 'q' || chNext == 'r' || chNext == 'w' || chNext == 'x') && !isalnum(chNext2)) { + state = SCE_PL_LONGQUOTE; + startSeg = i; + i++; + chNext = chNext2; + quotes = 0; + quoteUp = '\0'; + quoteDown = '\0'; + quoteRep = 1; + } else { + state = SCE_PL_WORD; + startSeg = i; + preferRE = false; + } + } else if (ch == '#') { + styler.ColourSegment(startSeg, i - 1, state); + state = SCE_PL_COMMENTLINE; + startSeg = i; + } else if (ch == '\"') { + styler.ColourSegment(startSeg, i - 1, state); + state = SCE_PL_STRING; + startSeg = i; + } else if (ch == '\'') { + if (chPrev == '&') { + // Archaic call + styler.ColourSegment(i, i, state); + startSeg = i + 1; + } else { + styler.ColourSegment(startSeg, i - 1, state); + state = SCE_PL_CHARACTER; + startSeg = i; + } + } else if (ch == '`') { + styler.ColourSegment(startSeg, i - 1, state); + state = SCE_PL_BACKTICKS; + startSeg = i; + } else if (ch == '$') { + preferRE = false; + styler.ColourSegment(startSeg, i - 1, state); + if (isalnum(chNext) || chNext == '#' || chNext == '$' || chNext == '_') { + state = SCE_PL_SCALAR; + startSeg = i; + } else if (chNext != '{' && chNext != '[') { + styler.ColourSegment(i - 1, i, SCE_PL_SCALAR); + i++; + startSeg = i + 1; + ch = ' '; + chNext = ' '; + } else { + styler.ColourSegment(i, i, SCE_PL_SCALAR); + startSeg = i + 1; + } + } else if (ch == '@') { + preferRE = false; + styler.ColourSegment(startSeg, i - 1, state); + if (isalpha(chNext) || chNext == '#' || chNext == '$' || chNext == '_') { + state = SCE_PL_ARRAY; + startSeg = i; + } else if (chNext != '{' && chNext != '[') { + styler.ColourSegment(i - 1, i, SCE_PL_ARRAY); + i++; + startSeg = i + 1; + ch = ' '; + } else { + styler.ColourSegment(i, i, SCE_PL_ARRAY); + startSeg = i + 1; + } + } else if (ch == '%') { + preferRE = false; + styler.ColourSegment(startSeg, i - 1, state); + if (isalpha(chNext) || chNext == '#' || chNext == '$' || chNext == '_') { + state = SCE_PL_HASH; + startSeg = i; + } else if (chNext != '{' && chNext != '[') { + styler.ColourSegment(i - 1, i, SCE_PL_HASH); + i++; + startSeg = i + 1; + ch = ' '; + } else { + styler.ColourSegment(i, i, SCE_PL_HASH); + startSeg = i + 1; + } + } else if (ch == '*') { + styler.ColourSegment(startSeg, i - 1, state); + state = SCE_PL_SYMBOLTABLE; + startSeg = i; + } else if (ch == '/' && preferRE) { + styler.ColourSegment(startSeg, i - 1, state); + state = SCE_PL_REGEX; + quoteUp = '/'; + quoteDown = '/'; + quotes = 1; + quoteRep = 1; + startSeg = i; + } else if (ch == '<' && chNext == '<') { + styler.ColourSegment(startSeg, i - 1, state); + state = SCE_PL_HERE; + startSeg = i; + i++; + ch = chNext; + chNext = chNext2; + quotes = 0; + sookedpos = 0; + sooked[sookedpos] = '\0'; + } else if (ch == '=' && isalpha(chNext)) { + styler.ColourSegment(startSeg, i - 1, state); + state = SCE_PL_POD; + startSeg = i; + quotes = 0; + sookedpos = 0; + sooked[sookedpos] = '\0'; + } else if (isPerlOperator(ch)) { + if (ch == ')' || ch == ']') + preferRE = false; + else + preferRE = true; + styler.ColourSegment(startSeg, i - 1, state); + styler.ColourSegment(i, i, SCE_PL_OPERATOR); + startSeg = i + 1; + } + } else if (state == SCE_PL_WORD) { + if (!iswordchar(ch) && ch != '\'') { // Archaic Perl has quotes inside names + if (isMatch(styler, lengthDoc, startSeg, "__DATA__")) { + styler.ColourSegment(startSeg, i, SCE_PL_DATASECTION); + state = SCE_PL_DATASECTION; + } else if (isMatch(styler, lengthDoc, startSeg, "__END__")) { + styler.ColourSegment(startSeg, i, SCE_PL_DATASECTION); + state = SCE_PL_DATASECTION; + } else { + if (classifyWordPerl(startSeg, i - 1, keywords, styler) == SCE_PL_WORD) + preferRE = true; + state = SCE_PL_DEFAULT; + startSeg = i; + if (ch == '#') { + state = SCE_PL_COMMENTLINE; + } else if (ch == '\"') { + state = SCE_PL_STRING; + } else if (ch == '\'') { + state = SCE_PL_CHARACTER; + } else if (ch == '<' && chNext == '<') { + state = SCE_PL_HERE; + quotes = 0; + startSeg = i; + sookedpos = 0; + sooked[sookedpos] = '\0'; + } else if (isPerlOperator(ch)) { + if (ch == ')' || ch == ']') + preferRE = false; + else + preferRE = true; + styler.ColourSegment(startSeg, i, SCE_PL_OPERATOR); + state = SCE_PL_DEFAULT; + startSeg = i + 1; + } + } + } + } else { + if (state == SCE_PL_COMMENTLINE) { + if (ch == '\r' || ch == '\n') { + styler.ColourSegment(startSeg, i - 1, state); + state = SCE_PL_DEFAULT; + startSeg = i; + } + } else if (state == SCE_PL_HERE) { + if (isalnum(ch) && quotes < 2) { + sooked[sookedpos++] = ch; + sooked[sookedpos] = '\0'; + if (quotes == 0) + quotes = 1; + } else { + quotes++; + } + + if (quotes > 1 && isMatch(styler, lengthDoc, i, sooked)) { + styler.ColourSegment(startSeg, i + sookedpos - 1, SCE_PL_HERE); + state = SCE_PL_DEFAULT; + i += sookedpos; + startSeg = i; + chNext = ' '; + } + } else if (state == SCE_PL_STRING) { + if (ch == '\\') { + if (chNext == '\"' || chNext == '\'' || chNext == '\\') { + i++; + ch = chNext; + chNext = styler.SafeGetCharAt(i + 1); + } + } else if (ch == '\"') { + styler.ColourSegment(startSeg, i, state); + state = SCE_PL_DEFAULT; + i++; + ch = chNext; + chNext = styler.SafeGetCharAt(i + 1); + startSeg = i; + } + } else if (state == SCE_PL_CHARACTER) { + if (ch == '\\') { + if (chNext == '\"' || chNext == '\'' || chNext == '\\') { + i++; + ch = chNext; + chNext = styler.SafeGetCharAt(i + 1); + } + } else if (ch == '\'') { + styler.ColourSegment(startSeg, i, state); + state = SCE_PL_DEFAULT; + i++; + ch = chNext; + chNext = styler.SafeGetCharAt(i + 1); + startSeg = i; + } + } else if (state == SCE_PL_BACKTICKS) { + if (ch == '`') { + styler.ColourSegment(startSeg, i, state); + state = SCE_PL_DEFAULT; + i++; + ch = chNext; + chNext = styler.SafeGetCharAt(i + 1); + startSeg = i; + } + } else if (state == SCE_PL_POD) { + if (ch == '=') { + if (isMatch(styler, lengthDoc, i, "=cut")) { + styler.ColourSegment(startSeg, i - 1 + 4, state); + i += 4; + startSeg = i; + state = SCE_PL_DEFAULT; + chNext = ' '; + ch = ' '; + } + } + } else if (state == SCE_PL_SCALAR) { + if (isEndVar(ch)) { + styler.ColourSegment(startSeg, i - 1, state); + startSeg = i; + state = SCE_PL_DEFAULT; + } + } else if (state == SCE_PL_ARRAY) { + if (isEndVar(ch)) { + styler.ColourSegment(startSeg, i - 1, state); + startSeg = i; + state = SCE_PL_DEFAULT; + } + } else if (state == SCE_PL_HASH) { + if (isEndVar(ch)) { + styler.ColourSegment(startSeg, i - 1, state); + startSeg = i; + state = SCE_PL_DEFAULT; + } + } else if (state == SCE_PL_SYMBOLTABLE) { + if (isEndVar(ch)) { + styler.ColourSegment(startSeg, i - 1, state); + startSeg = i; + state = SCE_PL_DEFAULT; + } + } else if (state == SCE_PL_REF) { + if (isEndVar(ch)) { + styler.ColourSegment(startSeg, i - 1, state); + startSeg = i; + state = SCE_PL_DEFAULT; + } + } else if (state == SCE_PL_REGEX) { + if (!quoteUp && !isspace(ch)) { + quoteUp = ch; + quoteDown = opposite(ch); + quotes++; + } else { + if (ch == quoteDown && chPrev != '\\') { + quotes--; + if (quotes == 0) { + quoteRep--; + if (quoteUp == quoteDown) { + quotes++; + } + } + if (!isalpha(chNext)) { + if (quoteRep <= 0) { + styler.ColourSegment(startSeg, i, state); + startSeg = i + 1; + state = SCE_PL_DEFAULT; + ch = ' '; + } + } + } else if (ch == quoteUp && chPrev != '\\') { + quotes++; + } else if (!isalpha(chNext)) { + if (quoteRep <= 0) { + styler.ColourSegment(startSeg, i, state); + startSeg = i + 1; + state = SCE_PL_DEFAULT; + ch = ' '; + } + } + } + } else if (state == SCE_PL_REGSUBST) { + if (!quoteUp && !isspace(ch)) { + quoteUp = ch; + quoteDown = opposite(ch); + quotes++; + } else { + if (ch == quoteDown && chPrev != '\\') { + quotes--; + if (quotes == 0) { + quoteRep--; + } + if (!isalpha(chNext)) { + if (quoteRep <= 0) { + styler.ColourSegment(startSeg, i, state); + startSeg = i + 1; + state = SCE_PL_DEFAULT; + ch = ' '; + } + } + if (quoteUp == quoteDown) { + quotes++; + } + } else if (ch == quoteUp && chPrev != '\\') { + quotes++; + } else if (!isalpha(chNext)) { + if (quoteRep <= 0) { + styler.ColourSegment(startSeg, i, state); + startSeg = i + 1; + state = SCE_PL_DEFAULT; + ch = ' '; + } + } + } + } else if (state == SCE_PL_LONGQUOTE) { + if (!quoteDown && !isspace(ch)) { + quoteUp = ch; + quoteDown = opposite(quoteUp); + quotes++; + } else if (ch == quoteDown) { + quotes--; + if (quotes == 0) { + quoteRep--; + if (quoteRep <= 0) { + styler.ColourSegment(startSeg, i, state); + startSeg = i + 1; + state = SCE_PL_DEFAULT; + ch = ' '; + } + if (quoteUp == quoteDown) { + quotes++; + } + } + } else if (ch == quoteUp) { + quotes++; + } + } + + if (state == SCE_PL_DEFAULT) { // One of the above succeeded + if (ch == '#') { + state = SCE_PL_COMMENTLINE; + } else if (ch == '\"') { + state = SCE_PL_STRING; + } else if (ch == '\'') { + state = SCE_PL_CHARACTER; + } else if (iswordstart(ch)) { + state = SCE_PL_WORD; + preferRE = false; + } else if (isoperator(ch)) { + styler.ColourSegment(startSeg, i, SCE_PL_OPERATOR); + startSeg = i + 1; + } + } + } + chPrev = ch; + } + if (startSeg < lengthDoc) + styler.ColourSegment(startSeg, lengthDoc, state); +} + + +static int classifyWordVB(unsigned int start, unsigned int end, WordList &keywords, StylingContext &styler) { + char s[100]; + bool wordIsNumber = isdigit(styler[start]) || (styler[start] == '.'); + for (unsigned int i = 0; i < end - start + 1 && i < 30; i++) { + s[i] = tolower(styler[start + i]); + s[i + 1] = '\0'; + } + char chAttr = SCE_C_DEFAULT; + if (wordIsNumber) + chAttr = SCE_C_NUMBER; + else { + if (keywords.InList(s)) { + chAttr = SCE_C_WORD; + if (strcmp(s, "rem") == 0) + chAttr = SCE_C_COMMENTLINE; + } + } + styler.ColourSegment(start, end, chAttr); + if (chAttr == SCE_C_COMMENTLINE) + return SCE_C_COMMENTLINE; + else + return SCE_C_DEFAULT; +} + +static void ColouriseVBDoc(int codePage, int startPos, int length, int initStyle, + WordList &keywords, StylingContext &styler) { + int state = initStyle; + char chNext = styler[startPos]; + int startSeg = startPos; + int lengthDoc = startPos + length; + for (int i = startPos; i < lengthDoc; i++) { + char ch = chNext; + chNext = styler.SafeGetCharAt(i + 1); + + if (IsLeadByte(codePage, ch)) { // dbcs + chNext = styler.SafeGetCharAt(i + 2); + i += 1; + continue; + } + + if (state == SCE_C_DEFAULT) { + if (iswordstart(ch)) { + styler.ColourSegment(startSeg, i - 1, state); + state = SCE_C_WORD; + startSeg = i; + } else if (ch == '\'') { + styler.ColourSegment(startSeg, i - 1, state); + state = SCE_C_COMMENTLINE; + startSeg = i; + } else if (ch == '\"') { + styler.ColourSegment(startSeg, i - 1, state); + state = SCE_C_STRING; + startSeg = i; + } + } else if (state == SCE_C_WORD) { + if (!iswordchar(ch)) { + state = classifyWordVB(startSeg, i - 1, keywords, styler); + if (state == SCE_C_DEFAULT) { + startSeg = i; + if (ch == '\'') { + state = SCE_C_COMMENTLINE; + } else if (ch == '\"') { + state = SCE_C_STRING; + } + } + } + } else { + if (state == SCE_C_COMMENTLINE) { + if (ch == '\r' || ch == '\n') { + styler.ColourSegment(startSeg, i - 1, state); + state = SCE_C_DEFAULT; + startSeg = i; + } + } else if (state == SCE_C_STRING) { + // VB doubles quotes to preserve them + if (ch == '\"') { + styler.ColourSegment(startSeg, i, state); + state = SCE_C_DEFAULT; + i++; + ch = chNext; + chNext = styler.SafeGetCharAt(i + 1); + startSeg = i; + } + } + if (state == SCE_C_DEFAULT) { // One of the above succeeded + if (ch == '\'') { + state = SCE_C_COMMENTLINE; + } else if (ch == '\"') { + state = SCE_C_STRING; + } else if (iswordstart(ch)) { + state = SCE_C_WORD; + } + } + } + } + if (startSeg < lengthDoc) + styler.ColourSegment(startSeg, lengthDoc, state); +} + +static void classifyWordPy(unsigned int start, unsigned int end, WordList &keywords, StylingContext &styler, char *prevWord) { + char s[100]; + bool wordIsNumber = isdigit(styler[start]); + for (unsigned int i = 0; i < end - start + 1 && i < 30; i++) { + s[i] = styler[start + i]; + s[i + 1] = '\0'; + } + char chAttr = SCE_P_IDENTIFIER; + if (0 == strcmp(prevWord, "class")) + chAttr = SCE_P_CLASSNAME; + else if (0 == strcmp(prevWord, "def")) + chAttr = SCE_P_DEFNAME; + else if (wordIsNumber) + chAttr = SCE_P_NUMBER; + else if (keywords.InList(s)) + chAttr = SCE_P_WORD; + styler.ColourSegment(start, end, chAttr); + strcpy(prevWord, s); +} + +static bool IsPyComment(StylingContext &styler, int pos, int len) { + return len>0 && styler[pos]=='#'; +} + +static void ColourisePyDoc(int codePage, int startPos, int length, int initStyle, WordList &keywords, StylingContext &styler) { + //Platform::DebugPrintf("Python coloured\n"); + bool fold = styler.GetPropSet().GetInt("fold"); + int whingeLevel = styler.GetPropSet().GetInt("tab.timmy.whinge.level"); + char prevWord[200]; + prevWord[0] = '\0'; + if (length == 0) + return ; + int lineCurrent = styler.GetLine(startPos); + int spaceFlags = 0; + // TODO: Need to check previous line for indentation for both folding and bad indentation + int indentCurrent = IndentAmount(styler, lineCurrent, &spaceFlags, IsPyComment); + + int state = initStyle & 31; + char chPrev = ' '; + char chPrev2 = ' '; + char chNext = styler[startPos]; + char chNext2 = styler[startPos]; + int startSeg = startPos; + int lengthDoc = startPos + length; + bool atStartLine = true; + for (int i = startPos; i <= lengthDoc; i++) { + + if (atStartLine) { + if (whingeLevel == 1) { + styler.SetFlags((spaceFlags & wsInconsistent) ? 64 : 0, state); + } else if (whingeLevel == 2) { + styler.SetFlags((spaceFlags & wsSpaceTab) ? 64 : 0, state); + } else if (whingeLevel == 3) { + styler.SetFlags((spaceFlags & wsSpace) ? 64 : 0, state); + } else if (whingeLevel == 4) { + styler.SetFlags((spaceFlags & wsTab) ? 64 : 0, state); + } + atStartLine = false; + } + + char ch = chNext; + chNext = styler.SafeGetCharAt(i + 1); + chNext2 = styler.SafeGetCharAt(i + 2); + + if ((ch == '\r' && chNext != '\n') || (ch == '\n')) { + if ((state == SCE_P_DEFAULT) || (state == SCE_P_TRIPLE) || (state == SCE_P_TRIPLEDOUBLE)) { + // Perform colourisation of white space and triple quoted strings at end of each line to allow + // tab marking to work inside white space and triple quoted strings + styler.ColourSegment(startSeg, i, state); + startSeg = i + 1; + } + + int lev = indentCurrent; + int indentNext = IndentAmount(styler, lineCurrent + 1, &spaceFlags, IsPyComment); + if (!(indentCurrent & SC_FOLDLEVELWHITEFLAG)) { + // Only non whitespace lines can be headers + if ((indentCurrent & SC_FOLDLEVELNUMBERMASK) < (indentNext & SC_FOLDLEVELNUMBERMASK)) { + lev |= SC_FOLDLEVELHEADERFLAG; + } + } + indentCurrent = indentNext; + if (fold) { + styler.SetLevel(lineCurrent, lev); + } + lineCurrent++; + atStartLine = true; + } + + if (IsLeadByte(codePage, ch)) { // dbcs + chNext = styler.SafeGetCharAt(i + 2); + chPrev = ' '; + chPrev2 = ' '; + i += 1; + continue; + } + + if (state == SCE_P_STRINGEOL) { + if (ch != '\r' && ch != '\n') { + styler.ColourSegment(startSeg, i - 1, state); + state = SCE_P_DEFAULT; + startSeg = i; + } + } + if (state == SCE_P_DEFAULT) { + if (iswordstart(ch)) { + styler.ColourSegment(startSeg, i - 1, state); + state = SCE_P_WORD; + startSeg = i; + } else if (ch == '#') { + styler.ColourSegment(startSeg, i - 1, state); + state = chNext == '#' ? SCE_P_COMMENTBLOCK : SCE_P_COMMENTLINE; + startSeg = i; + } else if (ch == '\"') { + styler.ColourSegment(startSeg, i - 1, state); + startSeg = i; + if (chNext == '\"' && chNext2 == '\"') { + i += 2; + state = SCE_P_TRIPLEDOUBLE; + ch = ' '; + chPrev = ' '; + chNext = styler.SafeGetCharAt(i + 1); + } else { + state = SCE_P_STRING; + } + } else if (ch == '\'') { + styler.ColourSegment(startSeg, i - 1, state); + startSeg = i; + if (chNext == '\'' && chNext2 == '\'') { + i += 2; + state = SCE_P_TRIPLE; + ch = ' '; + chPrev = ' '; + chNext = styler.SafeGetCharAt(i + 1); + } else { + state = SCE_P_CHARACTER; + } + } else if (isoperator(ch)) { + styler.ColourSegment(startSeg, i - 1, state); + styler.ColourSegment(i, i, SCE_P_OPERATOR); + startSeg = i + 1; + } + } else if (state == SCE_P_WORD) { + if (!iswordchar(ch)) { + classifyWordPy(startSeg, i - 1, keywords, styler, prevWord); + state = SCE_P_DEFAULT; + startSeg = i; + if (ch == '#') { + state = chNext == '#' ? SCE_P_COMMENTBLOCK : SCE_P_COMMENTLINE; + } else if (ch == '\"') { + if (chNext == '\"' && chNext2 == '\"') { + i += 2; + state = SCE_P_TRIPLEDOUBLE; + ch = ' '; + chPrev = ' '; + chNext = styler.SafeGetCharAt(i + 1); + } else { + state = SCE_P_STRING; + } + } else if (ch == '\'') { + if (chNext == '\'' && chNext2 == '\'') { + i += 2; + state = SCE_P_TRIPLE; + ch = ' '; + chPrev = ' '; + chNext = styler.SafeGetCharAt(i + 1); + } else { + state = SCE_P_CHARACTER; + } + } else if (isoperator(ch)) { + styler.ColourSegment(startSeg, i, SCE_P_OPERATOR); + startSeg = i + 1; + } + } + } else { + if (state == SCE_P_COMMENTLINE || state == SCE_P_COMMENTBLOCK) { + if (ch == '\r' || ch == '\n') { + styler.ColourSegment(startSeg, i - 1, state); + state = SCE_P_DEFAULT; + startSeg = i; + } + } else if (state == SCE_P_STRING) { + if ((ch == '\r' || ch == '\n') && (chPrev != '\\')) { + styler.ColourSegment(startSeg, i - 1, state); + state = SCE_P_STRINGEOL; + startSeg = i; + } else if (ch == '\\') { + if (chNext == '\"' || chNext == '\'' || chNext == '\\') { + i++; + ch = chNext; + chNext = styler.SafeGetCharAt(i + 1); + } + } else if (ch == '\"') { + styler.ColourSegment(startSeg, i, state); + state = SCE_P_DEFAULT; + startSeg = i + 1; + } + } else if (state == SCE_P_CHARACTER) { + if ((ch == '\r' || ch == '\n') && (chPrev != '\\')) { + styler.ColourSegment(startSeg, i - 1, state); + state = SCE_P_STRINGEOL; + startSeg = i; + } else if (ch == '\\') { + if (chNext == '\"' || chNext == '\'' || chNext == '\\') { + i++; + ch = chNext; + chNext = styler.SafeGetCharAt(i + 1); + } + } else if (ch == '\'') { + styler.ColourSegment(startSeg, i, state); + state = SCE_P_DEFAULT; + startSeg = i + 1; + } + } else if (state == SCE_P_TRIPLE) { + if (ch == '\'' && chPrev == '\'' && chPrev2 == '\'') { + styler.ColourSegment(startSeg, i, state); + state = SCE_P_DEFAULT; + startSeg = i + 1; + } + } else if (state == SCE_P_TRIPLEDOUBLE) { + if (ch == '\"' && chPrev == '\"' && chPrev2 == '\"') { + styler.ColourSegment(startSeg, i, state); + state = SCE_P_DEFAULT; + startSeg = i + 1; + } + } + } + chPrev2 = chPrev; + chPrev = ch; + } + if (startSeg <= lengthDoc) { + if (state == SCE_P_DEFAULT) { + styler.ColourSegment(startSeg, lengthDoc, state); + } else if (state == SCE_P_WORD) { + classifyWordPy(startSeg, lengthDoc, keywords, styler, prevWord); + } else if (state == SCE_P_COMMENTLINE) { + styler.ColourSegment(startSeg, lengthDoc, state); + } else if (state == SCE_P_COMMENTBLOCK) { + styler.ColourSegment(startSeg, lengthDoc, state); + } else if (state == SCE_P_STRING) { + styler.ColourSegment(startSeg, lengthDoc, state); + } else if (state == SCE_P_CHARACTER) { + styler.ColourSegment(startSeg, lengthDoc, state); + } else if (state == SCE_P_TRIPLE) { + styler.ColourSegment(startSeg, lengthDoc, state); + } else if (state == SCE_P_TRIPLEDOUBLE) { + styler.ColourSegment(startSeg, lengthDoc, state); + } + } +} + +static void ColouriseBatchLine(char *lineBuffer, int lengthLine, StylingContext &styler) { + if (0 == strncmp(lineBuffer, "REM", 3)) { + styler.ColourSegment(0, lengthLine - 1, 1); + } else if (0 == strncmp(lineBuffer, "rem", 3)) { + styler.ColourSegment(0, lengthLine - 1, 1); + } else if (0 == strncmp(lineBuffer, "SET", 3)) { + styler.ColourSegment(0, lengthLine - 1, 2); + } else if (0 == strncmp(lineBuffer, "set", 3)) { + styler.ColourSegment(0, lengthLine - 1, 2); + } else if (lineBuffer[0] == ':') { + styler.ColourSegment(0, lengthLine - 1, 3); + } else { + styler.ColourSegment(0, lengthLine - 1, 0); + } +} + +static void ColouriseBatchDoc(int startPos, int length, int, StylingContext &styler) { + char lineBuffer[1024]; + unsigned int linePos = 0; + for (int i = startPos; i < startPos + length; i++) { + lineBuffer[linePos++] = styler[i]; + if (styler[i] == '\r' || styler[i] == '\n' || (linePos >= sizeof(lineBuffer) - 1)) { + ColouriseBatchLine(lineBuffer, linePos, styler); + linePos = 0; + } + } + if (linePos > 0) + ColouriseBatchLine(lineBuffer, linePos, styler); +} + +enum { eScriptNone, eScriptJS, eScriptVBS, eScriptPython }; +static int segIsScriptingIndicator(StylingContext &styler, unsigned int start, unsigned int end, int prevValue) { + char s[100]; + s[0] = '\0'; + for (unsigned int i = 0; i < end - start + 1 && i < 30; i++) { + s[i] = tolower(styler[start + i]); + s[i + 1] = '\0'; + } +Platform::DebugPrintf("Scripting indicator [%s]\n", s); + if (strstr(s, "vbs")) + return eScriptVBS; + if (strstr(s, "pyth")) + return eScriptPython; + if (strstr(s, "javas")) + return eScriptJS; + if (strstr(s, "jscr")) + return eScriptJS; + + return prevValue; +} + +static void classifyAttribHTML(unsigned int start, unsigned int end, WordList &keywords, StylingContext &styler) { + bool wordIsNumber = isdigit(styler[start]) || (styler[start] == '.') || + (styler[start] == '-') || (styler[start] == '#'); + char chAttr = SCE_H_ATTRIBUTEUNKNOWN; + if (wordIsNumber) { + chAttr = SCE_H_NUMBER; + } else { + char s[100]; + s[0] = '\0'; + for (unsigned int i = 0; i < end - start + 1 && i < 30; i++) { + s[i] = tolower(styler[start + i]); + s[i + 1] = '\0'; + } + if (keywords.InList(s)) + chAttr = SCE_H_ATTRIBUTE; + } + styler.ColourTo(end, chAttr); +} + +static int classifyTagHTML(unsigned int start, unsigned int end, + WordList &keywords, StylingContext &styler) { + char s[100]; + // Copy after the '<' + unsigned int i = 0; + for (int cPos=start; cPos <= end && i < 30; cPos++) { + char ch = styler[cPos]; + if (ch != '<') + s[i++] = tolower(ch); + } + s[i] = '\0'; + char chAttr = SCE_H_TAGUNKNOWN; + if (s[0] == '!' && s[1] == '-' && s[2] == '-') { //Comment + chAttr = SCE_H_COMMENT; + } else if (s[0] == '/') { // Closing tag + if (keywords.InList(s + 1)) + chAttr = SCE_H_TAG; + } else { + if (keywords.InList(s)) { + chAttr = SCE_H_TAG; + if (0 == strcmp(s, "script")) + chAttr = SCE_H_SCRIPT; + } + } + styler.ColourTo(end, chAttr); + return chAttr; +} + +static void classifyWordHTJS(unsigned int start, unsigned int end, + WordList &keywords, StylingContext &styler) { + char s[100]; + bool wordIsNumber = isdigit(styler[start]) || (styler[start] == '.'); + for (unsigned int i = 0; i < end - start + 1 && i < 30; i++) { + s[i] = styler[start + i]; + s[i + 1] = '\0'; + } + char chAttr = SCE_HJ_WORD; + if (wordIsNumber) + chAttr = SCE_HJ_NUMBER; + else { + if (keywords.InList(s)) + chAttr = SCE_HJ_KEYWORD; + } + styler.ColourTo(end, chAttr); +} + +static void classifyWordHTJSA(unsigned int start, unsigned int end, + WordList &keywords, StylingContext &styler) { + char s[100]; + bool wordIsNumber = isdigit(styler[start]) || (styler[start] == '.'); + for (unsigned int i = 0; i < end - start + 1 && i < 30; i++) { + s[i] = styler[start + i]; + s[i + 1] = '\0'; + } + char chAttr = SCE_HJA_WORD; + if (wordIsNumber) + chAttr = SCE_HJA_NUMBER; + else { + if (keywords.InList(s)) + chAttr = SCE_HJA_KEYWORD; + } + styler.ColourTo(end, chAttr); +} + +static int classifyWordHTVB(unsigned int start, unsigned int end, WordList &keywords, StylingContext &styler) { + char s[100]; + bool wordIsNumber = isdigit(styler[start]) || (styler[start] == '.'); + for (unsigned int i = 0; i < end - start + 1 && i < 30; i++) { + s[i] = tolower(styler[start + i]); + s[i + 1] = '\0'; + } + char chAttr = SCE_HB_IDENTIFIER; + if (wordIsNumber) + chAttr = SCE_HB_NUMBER; + else { + if (keywords.InList(s)) { + chAttr = SCE_HB_WORD; + if (strcmp(s, "rem") == 0) + chAttr = SCE_HB_COMMENTLINE; + } + } + styler.ColourTo(end, chAttr); + if (chAttr == SCE_HB_COMMENTLINE) + return SCE_HB_COMMENTLINE; + else + return SCE_HB_DEFAULT; +} + +static int classifyWordHTVBA(unsigned int start, unsigned int end, WordList &keywords, StylingContext &styler) { + char s[100]; + bool wordIsNumber = isdigit(styler[start]) || (styler[start] == '.'); + for (unsigned int i = 0; i < end - start + 1 && i < 30; i++) { + s[i] = tolower(styler[start + i]); + s[i + 1] = '\0'; + } + char chAttr = SCE_HBA_IDENTIFIER; + if (wordIsNumber) + chAttr = SCE_HBA_NUMBER; + else { + if (keywords.InList(s)) { + chAttr = SCE_HBA_WORD; + if (strcmp(s, "rem") == 0) + chAttr = SCE_HBA_COMMENTLINE; + } + } + styler.ColourTo(end, chAttr); + if (chAttr == SCE_HBA_COMMENTLINE) + return SCE_HBA_COMMENTLINE; + else + return SCE_HBA_DEFAULT; +} + +static void classifyWordHTPy(unsigned int start, unsigned int end, WordList &keywords, StylingContext &styler, char *prevWord) { + char s[100]; + bool wordIsNumber = isdigit(styler[start]); + for (unsigned int i = 0; i < end - start + 1 && i < 30; i++) { + s[i] = styler[start + i]; + s[i + 1] = '\0'; + } + char chAttr = SCE_HP_IDENTIFIER; + if (0 == strcmp(prevWord, "class")) + chAttr = SCE_HP_CLASSNAME; + else if (0 == strcmp(prevWord, "def")) + chAttr = SCE_HP_DEFNAME; + else if (wordIsNumber) + chAttr = SCE_HP_NUMBER; + else if (keywords.InList(s)) + chAttr = SCE_HP_WORD; + styler.ColourTo(end, chAttr); + strcpy(prevWord, s); +} + +static void classifyWordHTPyA(unsigned int start, unsigned int end, WordList &keywords, StylingContext &styler, char *prevWord) { + char s[100]; + bool wordIsNumber = isdigit(styler[start]); + for (unsigned int i = 0; i < end - start + 1 && i < 30; i++) { + s[i] = styler[start + i]; + s[i + 1] = '\0'; + } + char chAttr = SCE_HPA_IDENTIFIER; + if (0 == strcmp(prevWord, "class")) + chAttr = SCE_HPA_CLASSNAME; + else if (0 == strcmp(prevWord, "def")) + chAttr = SCE_HPA_DEFNAME; + else if (wordIsNumber) + chAttr = SCE_HPA_NUMBER; + else if (keywords.InList(s)) + chAttr = SCE_HPA_WORD; + styler.ColourTo(end, chAttr); + strcpy(prevWord, s); +} + +inline bool ishtmlwordchar(char ch) { + return isalnum(ch) || ch == '.' || ch == '-' || ch == '_' || ch == ':' || ch == '!' || ch == '#'; +} + +static bool InTagState(int state) { + return state == SCE_H_TAG || state == SCE_H_TAGUNKNOWN || + state == SCE_H_SCRIPT || + state == SCE_H_ATTRIBUTE || state == SCE_H_ATTRIBUTEUNKNOWN || + state == SCE_H_NUMBER || state == SCE_H_OTHER || + state == SCE_H_DOUBLESTRING || state == SCE_H_SINGLESTRING; +} + +static bool isLineEnd(char ch) { + return ch == '\r' || ch == '\n'; +} + +static void ColouriseHyperTextDoc(int codePage, int startPos, int length, + int initStyle, WordList &keywords, WordList &keywords2, WordList &keywords3, WordList &keywords4, + StylingContext &styler) { + + styler.StartAt(startPos, 127); + bool lastTagWasScript = false; + char prevWord[200]; + prevWord[0] = '\0'; + int scriptLanguage = eScriptJS; + int state = initStyle; + // If inside a tag, it may be a script tage, so reread from the start to ensure any language tas are seen + if (InTagState(state)) { + while ((startPos > 1) && (InTagState(styler.StyleAt(startPos - 1)))) { + startPos--; + } + state = SCE_H_DEFAULT; + } + styler.StartAt(startPos, 127); + + int lineState = eScriptVBS; + int lineCurrent = styler.GetLine(startPos); + if (lineCurrent > 0) + lineState = styler.GetLineState(lineCurrent); + int defaultScript = lineState &0xff; + int beforeASP = (lineState >> 8) &0xff; + int inASP = (lineState >> 16) &0xff; + + char chPrev = ' '; + char chPrev2 = ' '; + styler.StartSegment(startPos); + int lengthDoc = startPos + length; + for (int i = startPos; i <= lengthDoc; i++) { + char ch = styler[i]; + char chNext = styler.SafeGetCharAt(i + 1); + char chNext2 = styler.SafeGetCharAt(i + 2); + + if (IsLeadByte(codePage, ch)) { // dbcs + chPrev2 = ' '; + chPrev = ' '; + i += 1; + continue; + } + + if ((ch == '\r' && chNext != '\n') || (ch == '\n')) { + // New line -> record any line state onto /next/ line + lineCurrent++; + styler.SetLineState(lineCurrent, + defaultScript | (beforeASP << 8) | (inASP << 16)); + } + + // Handle ASP even within other constructs as it is a preprocessor + if ((ch == '<') && (chNext == '%')) { + beforeASP = state; + styler.ColourTo(i - 1, state); + if (chNext2 == '@') { + styler.ColourTo(i + 2, SCE_H_ASP); + state = SCE_H_ASPAT; + i+=2; + } else { + if (defaultScript == eScriptVBS) + state = SCE_HBA_START; + else if (defaultScript == eScriptPython) + state = SCE_HPA_START; + else + state = SCE_HJA_START; + if (chNext2 == '=') { + styler.ColourTo(i + 2, SCE_H_ASP); + i+=2; + } else { + styler.ColourTo(i + 1, SCE_H_ASP); + i++; + } + } + inASP = 1; + continue; + } + if (inASP && (ch == '%') && (chNext == '>')) { + if (state == SCE_H_ASPAT) + defaultScript = segIsScriptingIndicator(styler, styler.GetStartSegment(), i-1, defaultScript); + // Bounce out of any ASP mode + if (state == SCE_HJA_WORD) { + classifyWordHTJSA(styler.GetStartSegment(), i - 1, keywords2, styler); + } else if (state == SCE_HBA_WORD) { + classifyWordHTVBA(styler.GetStartSegment(), i - 1, keywords3, styler); + } else if (state == SCE_HPA_WORD) { + classifyWordHTPyA(styler.GetStartSegment(), i - 1, keywords4, styler, prevWord); + } else { + styler.ColourTo(i - 1, state); + } + //if (state == SCE_H_ASPAT) + // styler.ColourTo(i+1, SCE_H_ASPAT); + //else + styler.ColourTo(i+1, SCE_H_ASP); + i++; + state = beforeASP; + beforeASP = SCE_H_DEFAULT; + inASP = 0; + continue; + } + + if (state == SCE_H_DEFAULT) { + if (ch == '<') { + styler.ColourTo(i - 1, state); + state = SCE_H_TAGUNKNOWN; + if (chNext == '?') { + styler.ColourTo(i + 1, SCE_H_XMLSTART); + i++; + ch = chNext; + } + } else if (ch == '&') { + styler.ColourTo(i - 1, SCE_H_DEFAULT); + state = SCE_H_ENTITY; + } + } else if (state == SCE_H_COMMENT) { + if ((ch == '>') && (chPrev == '-')) { + styler.ColourTo(i, state); + state = SCE_H_DEFAULT; + } + } else if (state == SCE_H_ENTITY) { + if (ch == ';') { + styler.ColourTo(i, state); + state = SCE_H_DEFAULT; + } + } else if (state == SCE_H_TAGUNKNOWN) { + if (!ishtmlwordchar(ch) && ch != '/' && ch != '-') { + int eClass = classifyTagHTML(styler.GetStartSegment(), i - 1, keywords, styler); + lastTagWasScript = eClass == SCE_H_SCRIPT; + if (lastTagWasScript) { + scriptLanguage = eScriptJS; + eClass = SCE_H_TAG; + } + if (ch == '>') { + styler.ColourTo(i, SCE_H_TAG); + if (lastTagWasScript) { + if (scriptLanguage == eScriptVBS) + state = SCE_HB_START; + else if (scriptLanguage == eScriptPython) + state = SCE_HP_START; + else + state = SCE_HJ_START; + } else { + state = SCE_H_DEFAULT; + } + } else { + if (eClass == SCE_H_COMMENT) { + state = SCE_H_COMMENT; + } else { + state = SCE_H_OTHER; + } + } + } + } else if (state == SCE_H_ATTRIBUTE) { + if (!ishtmlwordchar(ch) && ch != '/' && ch != '-') { + if (lastTagWasScript) + scriptLanguage = segIsScriptingIndicator(styler, styler.GetStartSegment(), i-1, scriptLanguage); + classifyAttribHTML(styler.GetStartSegment(), i - 1, keywords, styler); + if (ch == '>') { + styler.ColourTo(i, SCE_H_TAG); + if (lastTagWasScript) { + if (scriptLanguage == eScriptVBS) + state = SCE_HB_START; + else if (scriptLanguage == eScriptPython) + state = SCE_HP_START; + else + state = SCE_HJ_START; + } else { + state = SCE_H_DEFAULT; + } + } else { + state = SCE_H_OTHER; + } + } + } else if (state == SCE_H_ASP) { + if ((ch == '>') && (chPrev == '%')) { + styler.ColourTo(i, state); + state = SCE_H_DEFAULT; + } + } else if (state == SCE_H_ASPAT) { + if ((ch == '>') && (chPrev == '%')) { + styler.ColourTo(i, state); + state = SCE_H_DEFAULT; + } + } else if (state == SCE_H_OTHER) { + if (ch == '>') { + styler.ColourTo(i - 1, state); + styler.ColourTo(i, SCE_H_TAG); + if (lastTagWasScript) { + if (scriptLanguage == eScriptVBS) + state = SCE_HB_START; + else if (scriptLanguage == eScriptPython) + state = SCE_HP_START; + else + state = SCE_HJ_START; + } else { + state = SCE_H_DEFAULT; + } + } else if (ch == '\"') { + styler.ColourTo(i - 1, state); + state = SCE_H_DOUBLESTRING; + } else if (ch == '\'') { + styler.ColourTo(i - 1, state); + state = SCE_H_SINGLESTRING; + } else if (ch == '/' && chNext == '>') { + styler.ColourTo(i - 1, state); + styler.ColourTo(i + 1, SCE_H_TAGEND); + i++; + ch = chNext; + state = SCE_H_DEFAULT; + } else if (ch == '?' && chNext == '>') { + styler.ColourTo(i - 1, state); + styler.ColourTo(i + 1, SCE_H_XMLEND); + i++; + ch = chNext; + state = SCE_H_DEFAULT; + } else if (ishtmlwordchar(ch)) { + styler.ColourTo(i - 1, state); + state = SCE_H_ATTRIBUTE; + } + } else if (state == SCE_H_DOUBLESTRING) { + if (ch == '\"') { + if (lastTagWasScript) + scriptLanguage = segIsScriptingIndicator(styler, styler.GetStartSegment(), i, scriptLanguage); + styler.ColourTo(i, SCE_H_DOUBLESTRING); + state = SCE_H_OTHER; + } + } else if (state == SCE_H_SINGLESTRING) { + if (ch == '\'') { + if (lastTagWasScript) + scriptLanguage = segIsScriptingIndicator(styler, styler.GetStartSegment(), i, scriptLanguage); + styler.ColourTo(i, SCE_H_SINGLESTRING); + state = SCE_H_OTHER; + } + } else if (state == SCE_HJ_DEFAULT || state == SCE_HJ_START) { + if (iswordstart(ch)) { + styler.ColourTo(i - 1, state); + state = SCE_HJ_WORD; + } else if (ch == '/' && chNext == '*') { + styler.ColourTo(i - 1, state); + if (chNext2 == '*') + state = SCE_HJ_COMMENTDOC; + else + state = SCE_HJ_COMMENT; + } else if (ch == '/' && chNext == '/') { + styler.ColourTo(i - 1, state); + state = SCE_HJ_COMMENTLINE; + } else if (ch == '\"') { + styler.ColourTo(i - 1, state); + state = SCE_HJ_DOUBLESTRING; + } else if (ch == '\'') { + styler.ColourTo(i - 1, state); + state = SCE_HJ_SINGLESTRING; + } else if ((ch == '<') && (chNext == '/')) { + styler.ColourTo(i - 1, state); + state = SCE_H_TAGUNKNOWN; + } else if ((ch == '<') && (chNext == '!') && (chNext2 == '-') && + styler.SafeGetCharAt(i + 3) == '-') { + styler.ColourTo(i - 1, state); + state = SCE_HJ_COMMENTLINE; + } else if (isoperator(ch)) { + styler.ColourTo(i - 1, state); + styler.ColourTo(i, SCE_HJ_SYMBOLS); + state = SCE_HJ_DEFAULT; + } else if ((ch == ' ') || (ch == '\t')) { + if (state == SCE_HJ_START) { + styler.ColourTo(i - 1, state); + state = SCE_HJ_DEFAULT; + } + } + } else if (state == SCE_HJ_WORD) { + if (!iswordchar(ch)) { + classifyWordHTJS(styler.GetStartSegment(), i - 1, keywords2, styler); + //styler.ColourTo(i - 1, eHTJSKeyword); + state = SCE_HJ_DEFAULT; + if (ch == '/' && chNext == '*') { + if (chNext2 == '*') + state = SCE_HJ_COMMENTDOC; + else + state = SCE_HJ_COMMENT; + } else if (ch == '/' && chNext == '/') { + state = SCE_HJ_COMMENTLINE; + } else if (ch == '\"') { + state = SCE_HJ_DOUBLESTRING; + } else if (ch == '\'') { + state = SCE_HJ_SINGLESTRING; + } else if (isoperator(ch)) { + styler.ColourTo(i, SCE_HJ_SYMBOLS); + state = SCE_HJ_DEFAULT; + } + } + } else if (state == SCE_HJ_COMMENT) { + if (ch == '/' && chPrev == '*') { + state = SCE_HJ_DEFAULT; + styler.ColourTo(i, SCE_HJ_COMMENT); + } else if ((ch == '<') && (chNext == '/')) { + styler.ColourTo(i - 1, state); + styler.ColourTo(i + 1, SCE_H_TAGEND); + i++; + ch = chNext; + state = SCE_H_DEFAULT; + } + } else if (state == SCE_HJ_COMMENTDOC) { + if (ch == '/' && chPrev == '*') { + state = SCE_HJ_DEFAULT; + styler.ColourTo(i, SCE_HJ_COMMENTDOC); + } else if ((ch == '<') && (chNext == '/')) { + styler.ColourTo(i - 1, state); + styler.ColourTo(i + 1, SCE_H_TAGEND); + i++; + ch = chNext; + state = SCE_H_DEFAULT; + } + } else if (state == SCE_HJ_COMMENTLINE) { + if (ch == '\r' || ch == '\n') { + styler.ColourTo(i - 1, SCE_HJ_COMMENTLINE); + state = SCE_HJ_DEFAULT; + } else if ((ch == '<') && (chNext == '/')) { + // Common to hide end script tag in comment + styler.ColourTo(i - 1, SCE_HJ_COMMENTLINE); + state = SCE_H_TAGUNKNOWN; + } + } else if (state == SCE_HJ_DOUBLESTRING) { + if (ch == '\\') { + if (chNext == '\"' || chNext == '\'' || chNext == '\\') { + i++; + } + } else if (ch == '\"') { + styler.ColourTo(i, SCE_HJ_DOUBLESTRING); + state = SCE_HJ_DEFAULT; + i++; + ch = chNext; + } else if (isLineEnd(ch)) { + styler.ColourTo(i-1, state); + state = SCE_HJ_STRINGEOL; + } + } else if (state == SCE_HJ_SINGLESTRING) { + if (ch == '\\') { + if (chNext == '\"' || chNext == '\'' || chNext == '\\') { + i++; + } + } else if (ch == '\'') { + styler.ColourTo(i, SCE_HJ_SINGLESTRING); + state = SCE_HJ_DEFAULT; + i++; + ch = chNext; + } else if (isLineEnd(ch)) { + styler.ColourTo(i-1, state); + state = SCE_HJ_STRINGEOL; + } + } else if (state == SCE_HJ_STRINGEOL) { + if (!isLineEnd(ch)) { + styler.ColourTo(i - 1, state); + state = SCE_HJ_DEFAULT; + } else if (!isLineEnd(chNext)) { + styler.ColourTo(i, state); + state = SCE_HJ_DEFAULT; + } + } else if (state == SCE_HJA_DEFAULT || state == SCE_HJA_START) { + if (iswordstart(ch)) { + styler.ColourTo(i - 1, state); + state = SCE_HJA_WORD; + } else if (ch == '/' && chNext == '*') { + styler.ColourTo(i - 1, state); + if (chNext2 == '*') + state = SCE_HJA_COMMENTDOC; + else + state = SCE_HJA_COMMENT; + } else if (ch == '/' && chNext == '/') { + styler.ColourTo(i - 1, state); + state = SCE_HJA_COMMENTLINE; + } else if (ch == '\"') { + styler.ColourTo(i - 1, state); + state = SCE_HJA_DOUBLESTRING; + } else if (ch == '\'') { + styler.ColourTo(i - 1, state); + state = SCE_HJA_SINGLESTRING; + } else if ((ch == '<') && (chNext == '/')) { + styler.ColourTo(i - 1, state); + state = SCE_H_TAGUNKNOWN; + } else if ((ch == '<') && (chNext == '!') && (chNext2 == '-') && + styler.SafeGetCharAt(i + 3) == '-') { + styler.ColourTo(i - 1, state); + state = SCE_HJA_COMMENTLINE; + } else if (isoperator(ch)) { + styler.ColourTo(i - 1, state); + styler.ColourTo(i, SCE_HJA_SYMBOLS); + state = SCE_HJA_DEFAULT; + } else if ((ch == ' ') || (ch == '\t')) { + if (state == SCE_HJA_START) { + styler.ColourTo(i - 1, state); + state = SCE_HJA_DEFAULT; + } + } + } else if (state == SCE_HJA_WORD) { + if (!iswordchar(ch)) { + classifyWordHTJSA(styler.GetStartSegment(), i - 1, keywords2, styler); + //styler.ColourTo(i - 1, eHTJSKeyword); + state = SCE_HJA_DEFAULT; + if (ch == '/' && chNext == '*') { + if (chNext2 == '*') + state = SCE_HJA_COMMENTDOC; + else + state = SCE_HJA_COMMENT; + } else if (ch == '/' && chNext == '/') { + state = SCE_HJA_COMMENTLINE; + } else if (ch == '\"') { + state = SCE_HJA_DOUBLESTRING; + } else if (ch == '\'') { + state = SCE_HJA_SINGLESTRING; + } else if (isoperator(ch)) { + styler.ColourTo(i, SCE_HJA_SYMBOLS); + state = SCE_HJA_DEFAULT; + } + } + } else if (state == SCE_HJA_COMMENT) { + if (ch == '/' && chPrev == '*') { + state = SCE_HJA_DEFAULT; + styler.ColourTo(i, SCE_HJA_COMMENT); + } else if ((ch == '<') && (chNext == '/')) { + styler.ColourTo(i - 1, state); + styler.ColourTo(i + 1, SCE_H_TAGEND); + i++; + ch = chNext; + state = SCE_H_DEFAULT; + } + } else if (state == SCE_HJA_COMMENTDOC) { + if (ch == '/' && chPrev == '*') { + state = SCE_HJA_DEFAULT; + styler.ColourTo(i, SCE_HJA_COMMENTDOC); + } else if ((ch == '<') && (chNext == '/')) { + styler.ColourTo(i - 1, state); + styler.ColourTo(i + 1, SCE_H_TAGEND); + i++; + ch = chNext; + state = SCE_H_DEFAULT; + } + } else if (state == SCE_HJA_COMMENTLINE) { + if (ch == '\r' || ch == '\n') { + styler.ColourTo(i - 1, SCE_HJA_COMMENTLINE); + state = SCE_HJA_DEFAULT; + } else if ((ch == '<') && (chNext == '/')) { + // Common to hide end script tag in comment + styler.ColourTo(i - 1, SCE_HJA_COMMENTLINE); + state = SCE_H_TAGUNKNOWN; + } + } else if (state == SCE_HJA_DOUBLESTRING) { + if (ch == '\\') { + if (chNext == '\"' || chNext == '\'' || chNext == '\\') { + i++; + } + } else if (ch == '\"') { + styler.ColourTo(i, SCE_HJA_DOUBLESTRING); + state = SCE_HJA_DEFAULT; + i++; + ch = chNext; + } else if (isLineEnd(ch)) { + styler.ColourTo(i-1, state); + state = SCE_HJA_STRINGEOL; + } + } else if (state == SCE_HJA_SINGLESTRING) { + if (ch == '\\') { + if (chNext == '\"' || chNext == '\'' || chNext == '\\') { + i++; + } + } else if (ch == '\'') { + styler.ColourTo(i, SCE_HJA_SINGLESTRING); + state = SCE_HJA_DEFAULT; + i++; + ch = chNext; + } else if (isLineEnd(ch)) { + styler.ColourTo(i-1, state); + state = SCE_HJA_STRINGEOL; + } + } else if (state == SCE_HJA_STRINGEOL) { + if (!isLineEnd(ch)) { + styler.ColourTo(i - 1, state); + state = SCE_HJA_DEFAULT; + } else if (!isLineEnd(chNext)) { + styler.ColourTo(i, state); + state = SCE_HJA_DEFAULT; + } + } else if (state == SCE_HB_DEFAULT || state == SCE_HB_START) { + if (iswordstart(ch)) { + styler.ColourTo(i - 1, state); + state = SCE_HB_WORD; + } else if (ch == '\'') { + styler.ColourTo(i - 1, state); + state = SCE_HB_COMMENTLINE; + } else if (ch == '\"') { + styler.ColourTo(i - 1, state); + state = SCE_HB_STRING; + } else if ((ch == '<') && (chNext == '/')) { + styler.ColourTo(i - 1, state); + state = SCE_H_TAGUNKNOWN; + } else if ((ch == '<') && (chNext == '!') && (chNext2 == '-') && + styler.SafeGetCharAt(i + 3) == '-') { + styler.ColourTo(i - 1, state); + state = SCE_HB_COMMENTLINE; + } else if (isoperator(ch)) { + styler.ColourTo(i - 1, state); + styler.ColourTo(i, SCE_HB_DEFAULT); + state = SCE_HB_DEFAULT; + } else if ((ch == ' ') || (ch == '\t')) { + if (state == SCE_HB_START) { + styler.ColourTo(i - 1, state); + state = SCE_HB_DEFAULT; + } + } + } else if (state == SCE_HB_WORD) { + if (!iswordchar(ch)) { + state = classifyWordHTVB(styler.GetStartSegment(), i - 1, keywords3, styler); + if (state == SCE_HB_DEFAULT) { + if (ch == '\"') { + state = SCE_HB_STRING; + } else if (ch == '\'') { + state = SCE_HB_COMMENTLINE; + } else if (isoperator(ch)) { + styler.ColourTo(i, SCE_HB_DEFAULT); + state = SCE_HB_DEFAULT; + } + } + } + } else if (state == SCE_HB_STRING) { + if (ch == '\"') { + styler.ColourTo(i, state); + state = SCE_HB_DEFAULT; + i++; + ch = chNext; + } else if (ch == '\r' || ch == '\n') { + styler.ColourTo(i-1, state); + state = SCE_HB_STRINGEOL; + } + } else if (state == SCE_HB_COMMENTLINE) { + if (ch == '\r' || ch == '\n') { + styler.ColourTo(i - 1, state); + state = SCE_HB_DEFAULT; + } else if ((ch == '<') && (chNext == '/')) { + // Common to hide end script tag in comment + styler.ColourTo(i - 1, state); + state = SCE_H_TAGUNKNOWN; + } + } else if (state == SCE_HB_STRINGEOL) { + if (!isLineEnd(ch)) { + styler.ColourTo(i - 1, state); + state = SCE_HB_DEFAULT; + } else if (!isLineEnd(chNext)) { + styler.ColourTo(i, state); + state = SCE_HB_DEFAULT; + } + } else if (state == SCE_HBA_DEFAULT || state == SCE_HBA_START) { + if (iswordstart(ch)) { + styler.ColourTo(i - 1, state); + state = SCE_HBA_WORD; + } else if (ch == '\'') { + styler.ColourTo(i - 1, state); + state = SCE_HBA_COMMENTLINE; + } else if (ch == '\"') { + styler.ColourTo(i - 1, state); + state = SCE_HBA_STRING; + } else if ((ch == '<') && (chNext == '/')) { + styler.ColourTo(i - 1, state); + state = SCE_H_TAGUNKNOWN; + } else if ((ch == '<') && (chNext == '!') && (chNext2 == '-') && + styler.SafeGetCharAt(i + 3) == '-') { + styler.ColourTo(i - 1, state); + state = SCE_HBA_COMMENTLINE; + } else if (isoperator(ch)) { + styler.ColourTo(i - 1, state); + styler.ColourTo(i, SCE_HBA_DEFAULT); + state = SCE_HBA_DEFAULT; + } else if ((ch == ' ') || (ch == '\t')) { + if (state == SCE_HBA_START) { + styler.ColourTo(i - 1, state); + state = SCE_HBA_DEFAULT; + } + } + } else if (state == SCE_HBA_WORD) { + if (!iswordchar(ch)) { + state = classifyWordHTVBA(styler.GetStartSegment(), i - 1, keywords3, styler); + if (state == SCE_HBA_DEFAULT) { + if (ch == '\"') { + state = SCE_HBA_STRING; + } else if (ch == '\'') { + state = SCE_HBA_COMMENTLINE; + } else if (isoperator(ch)) { + styler.ColourTo(i, SCE_HBA_DEFAULT); + state = SCE_HBA_DEFAULT; + } + } + } + } else if (state == SCE_HBA_STRING) { + if (ch == '\"') { + styler.ColourTo(i, state); + state = SCE_HBA_DEFAULT; + i++; + ch = chNext; + } else if (ch == '\r' || ch == '\n') { + styler.ColourTo(i-1, state); + state = SCE_HBA_STRINGEOL; + } + } else if (state == SCE_HBA_COMMENTLINE) { + if (ch == '\r' || ch == '\n') { + styler.ColourTo(i - 1, state); + state = SCE_HBA_DEFAULT; + } else if ((ch == '<') && (chNext == '/')) { + // Common to hide end script tag in comment + styler.ColourTo(i - 1, state); + state = SCE_H_TAGUNKNOWN; + } + } else if (state == SCE_HBA_STRINGEOL) { + if (!isLineEnd(ch)) { + styler.ColourTo(i - 1, state); + state = SCE_HBA_DEFAULT; + } else if (!isLineEnd(chNext)) { + styler.ColourTo(i, state); + state = SCE_HBA_DEFAULT; + } + } else if (state == SCE_HP_DEFAULT || state == SCE_HP_START) { + if (iswordstart(ch)) { + styler.ColourTo(i - 1, state); + state = SCE_HP_WORD; + } else if ((ch == '<') && (chNext == '/')) { + styler.ColourTo(i - 1, state); + state = SCE_H_TAGUNKNOWN; + } else if ((ch == '<') && (chNext == '!') && (chNext2 == '-') && + styler.SafeGetCharAt(i + 3) == '-') { + styler.ColourTo(i - 1, state); + state = SCE_HP_COMMENTLINE; + } else if (ch == '#') { + styler.ColourTo(i - 1, state); + state = SCE_HP_COMMENTLINE; + } else if (ch == '\"') { + styler.ColourTo(i - 1, state); + if (chNext == '\"' && chNext2 == '\"') { + i += 2; + state = SCE_HP_TRIPLEDOUBLE; + ch = ' '; + chPrev = ' '; + chNext = styler.SafeGetCharAt(i + 1); + } else { + state = SCE_HP_STRING; + } + } else if (ch == '\'') { + styler.ColourTo(i - 1, state); + if (chNext == '\'' && chNext2 == '\'') { + i += 2; + state = SCE_HP_TRIPLE; + ch = ' '; + chPrev = ' '; + chNext = styler.SafeGetCharAt(i + 1); + } else { + state = SCE_HP_CHARACTER; + } + } else if (isoperator(ch)) { + styler.ColourTo(i - 1, state); + styler.ColourTo(i, SCE_HP_OPERATOR); + } else if ((ch == ' ') || (ch == '\t')) { + if (state == SCE_HP_START) { + styler.ColourTo(i - 1, state); + state = SCE_HP_DEFAULT; + } + } + } else if (state == SCE_HP_WORD) { + if (!iswordchar(ch)) { + classifyWordHTPy(styler.GetStartSegment(), i - 1, keywords4, styler, prevWord); + state = SCE_HP_DEFAULT; + if (ch == '#') { + state = SCE_HP_COMMENTLINE; + } else if (ch == '\"') { + if (chNext == '\"' && chNext2 == '\"') { + i += 2; + state = SCE_HP_TRIPLEDOUBLE; + ch = ' '; + chPrev = ' '; + chNext = styler.SafeGetCharAt(i + 1); + } else { + state = SCE_HP_STRING; + } + } else if (ch == '\'') { + if (chNext == '\'' && chNext2 == '\'') { + i += 2; + state = SCE_HP_TRIPLE; + ch = ' '; + chPrev = ' '; + chNext = styler.SafeGetCharAt(i + 1); + } else { + state = SCE_HP_CHARACTER; + } + } else if (isoperator(ch)) { + styler.ColourTo(i, SCE_HP_OPERATOR); + } + } + } else if (state == SCE_HP_COMMENTLINE) { + if (ch == '\r' || ch == '\n') { + styler.ColourTo(i - 1, state); + state = SCE_HP_DEFAULT; + } + } else if (state == SCE_HP_STRING) { + if (ch == '\\') { + if (chNext == '\"' || chNext == '\'' || chNext == '\\') { + i++; + ch = chNext; + chNext = styler.SafeGetCharAt(i + 1); + } + } else if (ch == '\"') { + styler.ColourTo(i, state); + state = SCE_HP_DEFAULT; + } + } else if (state == SCE_HP_CHARACTER) { + if (ch == '\\') { + if (chNext == '\"' || chNext == '\'' || chNext == '\\') { + i++; + ch = chNext; + chNext = styler.SafeGetCharAt(i + 1); + } + } else if (ch == '\'') { + styler.ColourTo(i, state); + state = SCE_HP_DEFAULT; + } + } else if (state == SCE_HP_TRIPLE) { + if (ch == '\'' && chPrev == '\'' && chPrev2 == '\'') { + styler.ColourTo(i, state); + state = SCE_HP_DEFAULT; + } + } else if (state == SCE_HP_TRIPLEDOUBLE) { + if (ch == '\"' && chPrev == '\"' && chPrev2 == '\"') { + styler.ColourTo(i, state); + state = SCE_HP_DEFAULT; + } + } else if (state == SCE_HPA_DEFAULT || state == SCE_HPA_START) { + if (iswordstart(ch)) { + styler.ColourTo(i - 1, state); + state = SCE_HPA_WORD; + } else if ((ch == '<') && (chNext == '/')) { + styler.ColourTo(i - 1, state); + state = SCE_H_TAGUNKNOWN; + } else if ((ch == '<') && (chNext == '!') && (chNext2 == '-') && + styler.SafeGetCharAt(i + 3) == '-') { + styler.ColourTo(i - 1, state); + state = SCE_HPA_COMMENTLINE; + } else if (ch == '#') { + styler.ColourTo(i - 1, state); + state = SCE_HPA_COMMENTLINE; + } else if (ch == '\"') { + styler.ColourTo(i - 1, state); + if (chNext == '\"' && chNext2 == '\"') { + i += 2; + state = SCE_HPA_TRIPLEDOUBLE; + ch = ' '; + chPrev = ' '; + chNext = styler.SafeGetCharAt(i + 1); + } else { + state = SCE_HPA_STRING; + } + } else if (ch == '\'') { + styler.ColourTo(i - 1, state); + if (chNext == '\'' && chNext2 == '\'') { + i += 2; + state = SCE_HPA_TRIPLE; + ch = ' '; + chPrev = ' '; + chNext = styler.SafeGetCharAt(i + 1); + } else { + state = SCE_HPA_CHARACTER; + } + } else if (isoperator(ch)) { + styler.ColourTo(i - 1, state); + styler.ColourTo(i, SCE_HPA_OPERATOR); + } else if ((ch == ' ') || (ch == '\t')) { + if (state == SCE_HPA_START) { + styler.ColourTo(i - 1, state); + state = SCE_HPA_DEFAULT; + } + } + } else if (state == SCE_HPA_WORD) { + if (!iswordchar(ch)) { + classifyWordHTPyA(styler.GetStartSegment(), i - 1, keywords4, styler, prevWord); + state = SCE_HPA_DEFAULT; + if (ch == '#') { + state = SCE_HPA_COMMENTLINE; + } else if (ch == '\"') { + if (chNext == '\"' && chNext2 == '\"') { + i += 2; + state = SCE_HPA_TRIPLEDOUBLE; + ch = ' '; + chPrev = ' '; + chNext = styler.SafeGetCharAt(i + 1); + } else { + state = SCE_HPA_STRING; + } + } else if (ch == '\'') { + if (chNext == '\'' && chNext2 == '\'') { + i += 2; + state = SCE_HPA_TRIPLE; + ch = ' '; + chPrev = ' '; + chNext = styler.SafeGetCharAt(i + 1); + } else { + state = SCE_HPA_CHARACTER; + } + } else if (isoperator(ch)) { + styler.ColourTo(i, SCE_HPA_OPERATOR); + } + } + } else if (state == SCE_HPA_COMMENTLINE) { + if (ch == '\r' || ch == '\n') { + styler.ColourTo(i - 1, state); + state = SCE_HPA_DEFAULT; + } + } else if (state == SCE_HPA_STRING) { + if (ch == '\\') { + if (chNext == '\"' || chNext == '\'' || chNext == '\\') { + i++; + ch = chNext; + chNext = styler.SafeGetCharAt(i + 1); + } + } else if (ch == '\"') { + styler.ColourTo(i, state); + state = SCE_HPA_DEFAULT; + } + } else if (state == SCE_HPA_CHARACTER) { + if (ch == '\\') { + if (chNext == '\"' || chNext == '\'' || chNext == '\\') { + i++; + ch = chNext; + chNext = styler.SafeGetCharAt(i + 1); + } + } else if (ch == '\'') { + styler.ColourTo(i, state); + state = SCE_HPA_DEFAULT; + } + } else if (state == SCE_HPA_TRIPLE) { + if (ch == '\'' && chPrev == '\'' && chPrev2 == '\'') { + styler.ColourTo(i, state); + state = SCE_HPA_DEFAULT; + } + } else if (state == SCE_HPA_TRIPLEDOUBLE) { + if (ch == '\"' && chPrev == '\"' && chPrev2 == '\"') { + styler.ColourTo(i, state); + state = SCE_HPA_DEFAULT; + } + } + if (state == SCE_HB_DEFAULT) { // One of the above succeeded + if (ch == '\"') { + state = SCE_HB_STRING; + } else if (ch == '\'') { + state = SCE_HB_COMMENTLINE; + } else if (iswordstart(ch)) { + state = SCE_HB_WORD; + } else if (isoperator(ch)) { + styler.ColourTo(i, SCE_HB_DEFAULT); + } + } + if (state == SCE_HBA_DEFAULT) { // One of the above succeeded + if (ch == '\"') { + state = SCE_HBA_STRING; + } else if (ch == '\'') { + state = SCE_HBA_COMMENTLINE; + } else if (iswordstart(ch)) { + state = SCE_HBA_WORD; + } else if (isoperator(ch)) { + styler.ColourTo(i, SCE_HBA_DEFAULT); + } + } + if (state == SCE_HJ_DEFAULT) { // One of the above succeeded + if (ch == '/' && chNext == '*') { + if (styler.SafeGetCharAt(i + 2) == '*') + state = SCE_HJ_COMMENTDOC; + else + state = SCE_HJ_COMMENT; + } else if (ch == '/' && chNext == '/') { + state = SCE_HJ_COMMENTLINE; + } else if (ch == '\"') { + state = SCE_HJ_DOUBLESTRING; + } else if (ch == '\'') { + state = SCE_HJ_SINGLESTRING; + } else if (iswordstart(ch)) { + state = SCE_HJ_WORD; + } else if (isoperator(ch)) { + styler.ColourTo(i, SCE_HJ_SYMBOLS); + } + } + if (state == SCE_HJA_DEFAULT) { // One of the above succeeded + if (ch == '/' && chNext == '*') { + if (styler.SafeGetCharAt(i + 2) == '*') + state = SCE_HJA_COMMENTDOC; + else + state = SCE_HJA_COMMENT; + } else if (ch == '/' && chNext == '/') { + state = SCE_HJA_COMMENTLINE; + } else if (ch == '\"') { + state = SCE_HJA_DOUBLESTRING; + } else if (ch == '\'') { + state = SCE_HJA_SINGLESTRING; + } else if (iswordstart(ch)) { + state = SCE_HJA_WORD; + } else if (isoperator(ch)) { + styler.ColourTo(i, SCE_HJA_SYMBOLS); + } + } + chPrev2 = chPrev; + chPrev = ch; + } + styler.ColourTo(lengthDoc - 1, state); +} + +static void ColourisePropsLine(char *lineBuffer, int lengthLine, StylingContext &styler) { + int i = 0; + while (isspace(lineBuffer[i]) && (i < lengthLine)) // Skip initial spaces + i++; + if (lineBuffer[i] == '#' || lineBuffer[i] == '!' || lineBuffer[i] == ';') { + styler.ColourSegment(0, lengthLine - 1, 1); + } else if (lineBuffer[i] == '[') { + styler.ColourSegment(0, lengthLine - 1, 2); + } else if (lineBuffer[i] == '@') { + styler.ColourSegment(0, i, 4); + if (lineBuffer[++i] == '=') + styler.ColourSegment(i, i, 3); + if (++i < lengthLine) + styler.ColourSegment(i, lengthLine - 1, 0); + } else { + while (lineBuffer[i] != '=' && (i < lengthLine)) // Search the '=' character + i++; + if (lineBuffer[i] == '=') { + styler.ColourSegment(0, i - 1, 0); + styler.ColourSegment(i, i, 3); + if (++i < lengthLine) + styler.ColourSegment(i, lengthLine - 1, 0); + } else + styler.ColourSegment(0, lengthLine - 1, 0); + } +} + +static void ColourisePropsDoc(int startPos, int length, int, StylingContext &styler) { + char lineBuffer[1024]; + unsigned int linePos = 0; + for (int i = startPos; i <= startPos + length; i++) { + lineBuffer[linePos++] = styler[i]; + if (styler[i] == '\r' || styler[i] == '\n' || (linePos >= sizeof(lineBuffer) - 1)) { + lineBuffer[linePos] = '\0'; + ColourisePropsLine(lineBuffer, linePos, styler); + linePos = 0; + } + } + if (linePos > 0) + ColourisePropsLine(lineBuffer, linePos, styler); +} + +static void ColouriseMakeLine(char *lineBuffer, int lengthLine, StylingContext &styler) { + int i = 0; + while (isspace(lineBuffer[i]) && (i < lengthLine)) + i++; + if (lineBuffer[i] == '#' || lineBuffer[i] == '!') { + styler.ColourSegment(0, lengthLine - 1, 1); + } else { + styler.ColourSegment(0, lengthLine - 1, 0); + } +} + +static void ColouriseMakeDoc(int startPos, int length, int, StylingContext &styler) { + char lineBuffer[1024]; + unsigned int linePos = 0; + for (int i = startPos; i <= startPos + length; i++) { + lineBuffer[linePos++] = styler[i]; + if (styler[i] == '\r' || styler[i] == '\n' || (linePos >= sizeof(lineBuffer) - 1)) { + ColouriseMakeLine(lineBuffer, linePos, styler); + linePos = 0; + } + } + if (linePos > 0) + ColouriseMakeLine(lineBuffer, linePos, styler); +} + +static void ColouriseErrorListLine(char *lineBuffer, int lengthLine, StylingContext &styler) { + if (lineBuffer[0] == '>') { + // Command or return status + styler.ColourSegment(0, lengthLine - 1, 4); + } else if (strstr(lineBuffer, "File \"") && strstr(lineBuffer, ", line ")) { + styler.ColourSegment(0, lengthLine - 1, 1); + } else if (0 == strncmp(lineBuffer, "Error ", strlen("Error "))) { + // Borland error message + styler.ColourSegment(0, lengthLine - 1, 5); + } else if (0 == strncmp(lineBuffer, "Warning ", strlen("Warning "))) { + // Borland warning message + styler.ColourSegment(0, lengthLine - 1, 5); + } else { + // Look for <filename>:<line>:message + // Look for <filename>(line)message + // Look for <filename>(line,pos)message + int state = 0; + for (int i = 0; i < lengthLine; i++) { + if (state == 0 && lineBuffer[i] == ':' && isdigit(lineBuffer[i + 1])) { + state = 1; + } else if (state == 0 && lineBuffer[i] == '(') { + state = 10; + } else if (state == 1 && isdigit(lineBuffer[i])) { + state = 2; + } else if (state == 2 && lineBuffer[i] == ':') { + state = 3; + break; + } else if (state == 2 && !isdigit(lineBuffer[i])) { + state = 99; + } else if (state == 10 && isdigit(lineBuffer[i])) { + state = 11; + } else if (state == 11 && lineBuffer[i] == ',') { + state = 14; + } else if (state == 11 && lineBuffer[i] == ')') { + state = 12; + break; + } else if (state == 12 && lineBuffer[i] == ':') { + state = 13; + } else if (state == 14 && lineBuffer[i] == ')') { + state = 15; + break; + } else if (((state == 11) || (state == 14)) && !((lineBuffer[i] == ' ') || isdigit(lineBuffer[i]))) { + state = 99; + } + } + if (state == 3) { + styler.ColourSegment(0, lengthLine - 1, 2); + } else if ((state == 14) || (state == 15)) { + styler.ColourSegment(0, lengthLine - 1, 3); + } else { + styler.ColourSegment(0, lengthLine - 1, 0); + } + } +} + +static void ColouriseErrorListDoc(int startPos, int length, int, StylingContext &styler) { + char lineBuffer[1024]; + unsigned int linePos = 0; + for (int i = startPos; i <= startPos + length; i++) { + lineBuffer[linePos++] = styler[i]; + if (styler[i] == '\r' || styler[i] == '\n' || (linePos >= sizeof(lineBuffer) - 1)) { + ColouriseErrorListLine(lineBuffer, linePos, styler); + linePos = 0; + } + } + if (linePos > 0) + ColouriseErrorListLine(lineBuffer, linePos, styler); +} + +static void classifyWordSQL(unsigned int start, unsigned int end, WordList &keywords, StylingContext &styler) { + char s[100]; + bool wordIsNumber = isdigit(styler[start]) || (styler[start] == '.'); + for (unsigned int i = 0; i < end - start + 1 && i < 30; i++) { + s[i] = toupper(styler[start + i]); + s[i + 1] = '\0'; + } + char chAttr = SCE_C_IDENTIFIER; + if (wordIsNumber) + chAttr = SCE_C_NUMBER; + else { + if (keywords.InList(s)) + chAttr = SCE_C_WORD; + } + styler.ColourSegment(start, end, chAttr); +} + +static void ColouriseSQLDoc(int codePage, int startPos, int length, + int initStyle, WordList &keywords, StylingContext &styler) { + + bool fold = styler.GetPropSet().GetInt("fold"); + int lineCurrent = styler.GetLine(startPos); + int spaceFlags = 0; + int indentCurrent = 0; + + int state = initStyle; + char chPrev = ' '; + char chNext = styler[startPos]; + int startSeg = startPos; + int lengthDoc = startPos + length; + bool prevCr = false; + for (int i = startPos; i <= lengthDoc; i++) { + char ch = chNext; + chNext = styler.SafeGetCharAt(i + 1); + + if ((ch == '\r' && chNext != '\n') || (ch == '\n')) { + indentCurrent = IndentAmount(styler, lineCurrent, &spaceFlags); + int lev = indentCurrent; + if (!(indentCurrent & SC_FOLDLEVELWHITEFLAG)) { + // Only non whitespace lines can be headers + int indentNext = IndentAmount(styler, lineCurrent + 1, &spaceFlags); + if (indentCurrent < (indentNext & ~SC_FOLDLEVELWHITEFLAG)) { + lev |= SC_FOLDLEVELHEADERFLAG; + } + } + if (fold) { + styler.SetLevel(lineCurrent, lev); + } + } + + if (IsLeadByte(codePage, ch)) { // dbcs + chNext = styler.SafeGetCharAt(i + 2); + chPrev = ' '; + i += 1; + continue; + } + + if (state == SCE_C_DEFAULT) { + if (iswordstart(ch)) { + styler.ColourSegment(startSeg, i - 1, state); + state = SCE_C_WORD; + startSeg = i; + } else if (ch == '/' && chNext == '*') { + styler.ColourSegment(startSeg, i - 1, state); + state = SCE_C_COMMENT; + startSeg = i; + } else if (ch == '-' && chNext == '-') { + styler.ColourSegment(startSeg, i - 1, state); + state = SCE_C_COMMENTLINE; + startSeg = i; + } else if (ch == '\'') { + styler.ColourSegment(startSeg, i - 1, state); + state = SCE_C_STRING; + startSeg = i; + } else if (isoperator(ch)) { + styler.ColourSegment(startSeg, i - 1, state); + styler.ColourSegment(i, i, SCE_C_OPERATOR); + startSeg = i + 1; + } + } else if (state == SCE_C_WORD) { + if (!iswordchar(ch)) { + classifyWordSQL(startSeg, i - 1, keywords, styler); + state = SCE_C_DEFAULT; + startSeg = i; + if (ch == '/' && chNext == '*') { + state = SCE_C_COMMENT; + } else if (ch == '-' && chNext == '-') { + state = SCE_C_COMMENTLINE; + } else if (ch == '\'') { + state = SCE_C_STRING; + } else if (isoperator(ch)) { + styler.ColourSegment(startSeg, i, SCE_C_OPERATOR); + startSeg = i + 1; + } + } + } else { + if (state == SCE_C_COMMENT) { + if (ch == '/' && chPrev == '*' && ( + (i > startSeg + 2) || ((initStyle == SCE_C_COMMENT) && (startSeg == startPos)))) { + state = SCE_C_DEFAULT; + styler.ColourSegment(startSeg, i, state); + startSeg = i + 1; + } + } else if (state == SCE_C_COMMENTLINE) { + if (ch == '\r' || ch == '\n') { + styler.ColourSegment(startSeg, i - 1, state); + state = SCE_C_DEFAULT; + startSeg = i; + } + } else if (state == SCE_C_STRING) { + if (ch == '\'') { + if ( chNext == '\'' ) { + i++; + } else { + styler.ColourSegment(startSeg, i, state); + state = SCE_C_DEFAULT; + i++; + startSeg = i; + } + ch = chNext; + chNext = styler.SafeGetCharAt(i + 1); + } + } + if (state == SCE_C_DEFAULT) { // One of the above succeeded + if (ch == '/' && chNext == '*') { + state = SCE_C_COMMENT; + } else if (ch == '-' && chNext == '-') { + state = SCE_C_COMMENTLINE; + } else if (ch == '\'') { + state = SCE_C_STRING; + } else if (iswordstart(ch)) { + state = SCE_C_WORD; + } else if (isoperator(ch)) { + styler.ColourSegment(startSeg, i, SCE_C_OPERATOR); + startSeg = i + 1; + } + } + } + chPrev = ch; + } + if (startSeg < lengthDoc) + styler.ColourSegment(startSeg, lengthDoc - 1, state); +} + +void ColouriseDoc(int codePage, int startPos, int lengthDoc, int initStyle, + int language, WordList *keywordlists[], StylingContext &styler) { + //Platform::DebugPrintf("ColouriseDoc <%s>\n", language); + if (language == SCLEX_PYTHON) { + // Python uses a different mask because bad indentation is marked by oring with 32 + styler.StartAt(startPos, 127); + ColourisePyDoc(codePage, startPos, lengthDoc, initStyle, *keywordlists[0], styler); + } else if (language == SCLEX_PERL) { + // Lexer for perl often has to backtrack to start of current style to determine + // which characters are being used as quotes, how deeply nested is the + // start position and what the termination string is for here documents + ColourisePerlDoc(codePage, startPos, lengthDoc, initStyle, *keywordlists[0], styler); + } else if ((language == SCLEX_HTML) || (language == SCLEX_XML)) { + // Lexer for HTML requires more lexical states (6 bits worth) than most lexers + ColouriseHyperTextDoc(codePage, startPos, lengthDoc, initStyle, + *keywordlists[0], *keywordlists[1], *keywordlists[2], *keywordlists[3], styler); + } else { + styler.StartAt(startPos); + if (language == SCLEX_CPP) { + ColouriseCppDoc(codePage, startPos, lengthDoc, initStyle, *keywordlists[0], styler); + } else if (language == SCLEX_SQL) { + ColouriseSQLDoc(codePage, startPos, lengthDoc, initStyle, *keywordlists[0], styler); + } else if (language == SCLEX_VB) { + ColouriseVBDoc(codePage, startPos, lengthDoc, initStyle, *keywordlists[0], styler); + } else if (language == SCLEX_PROPERTIES) { + ColourisePropsDoc(startPos, lengthDoc, initStyle, styler); + } else if (language == SCLEX_ERRORLIST) { + ColouriseErrorListDoc(startPos, lengthDoc, initStyle, styler); + } else if (language == SCLEX_MAKEFILE) { + ColouriseMakeDoc(startPos, lengthDoc, initStyle, styler); + } else if (language == SCLEX_BATCH) { + ColouriseBatchDoc(startPos, lengthDoc, initStyle, styler); + } else { + // Null language means all style bytes are 0 so just mark the end - no need to fill in. + styler.StartAt(startPos + lengthDoc - 1); + styler.ColourSegment(0, 0, 0); + } + } +} diff --git a/src/LineMarker.cxx b/src/LineMarker.cxx new file mode 100644 index 000000000..9afccb822 --- /dev/null +++ b/src/LineMarker.cxx @@ -0,0 +1,125 @@ +// Scintilla source code edit control +// LineMarker.cxx - defines the look of a line marker in the margin +// Copyright 1998-2000 by Neil Hodgson <neilh@scintilla.org> +// The License.txt file describes the conditions under which this software may be distributed. + +#include "Platform.h" + +#include "Scintilla.h" +#include "LineMarker.h" + +void LineMarker::Draw(Surface *surface, PRectangle &rc) { + int minDim = Platform::Minimum(rc.Width(), rc.Height()); + minDim--; // Ensure does not go beyond edge + int centreX = (rc.right + rc.left) / 2; + int centreY = (rc.bottom + rc.top) / 2; + int dimOn2 = minDim / 2; + int dimOn4 = minDim / 4; + if (rc.Width() > (rc.Height() * 2)) { + // Wide column is line number so move to left to try to avoid overlapping number + centreX = rc.left + dimOn2 + 1; + } + if (markType == SC_MARK_ROUNDRECT) { + PRectangle rcRounded = rc; + rcRounded.left = rc.left + 1; + rcRounded.right = rc.right - 1; + surface->RoundedRectangle(rcRounded, fore.allocated, back.allocated); + } else if (markType == SC_MARK_CIRCLE) { + PRectangle rcCircle; + rcCircle.left = centreX - dimOn2; + rcCircle.top = centreY - dimOn2; + rcCircle.right = centreX + dimOn2; + rcCircle.bottom = centreY + dimOn2; + surface->Ellipse(rcCircle, fore.allocated, back.allocated); + } else if (markType == SC_MARK_ARROW) { + Point pts[] = { + Point(centreX - dimOn4, centreY - dimOn2), + Point(centreX - dimOn4, centreY + dimOn2), + Point(centreX + dimOn2 - dimOn4, centreY), + }; + surface->Polygon(pts, sizeof(pts) / sizeof(pts[0]), + fore.allocated, back.allocated); + + } else if (markType == SC_MARK_ARROWDOWN) { + Point pts[] = { + Point(centreX - dimOn2, centreY - dimOn4), + Point(centreX + dimOn2, centreY - dimOn4), + Point(centreX, centreY + dimOn2 - dimOn4), + }; + surface->Polygon(pts, sizeof(pts) / sizeof(pts[0]), + fore.allocated, back.allocated); + + } else if (markType == SC_MARK_PLUS) { + int armSize = dimOn2-2; + Point xpts[] = { + Point(centreX - armSize, centreY), + Point(centreX, centreY), + Point(centreX, centreY - armSize), + Point(centreX, centreY - armSize), + Point(centreX, centreY), + Point(centreX + armSize, centreY), + Point(centreX + armSize, centreY), + Point(centreX, centreY), + Point(centreX, centreY + armSize), + Point(centreX, centreY + armSize), + Point(centreX, centreY), + Point(centreX - armSize, centreY), + }; + Point pts[] = { + Point(centreX - armSize, centreY - 1), + Point(centreX - 1, centreY - 1), + Point(centreX - 1, centreY - armSize), + Point(centreX + 1, centreY - armSize), + Point(centreX + 1, centreY - 1), + Point(centreX + armSize, centreY -1), + Point(centreX + armSize, centreY +1), + Point(centreX + 1, centreY + 1), + Point(centreX + 1, centreY + armSize), + Point(centreX - 1, centreY + armSize), + Point(centreX - 1, centreY + 1), + Point(centreX - armSize, centreY + 1), + }; + surface->Polygon(pts, sizeof(pts) / sizeof(pts[0]), + fore.allocated, back.allocated); + + } else if (markType == SC_MARK_MINUS) { + int armSize = dimOn2-2; + Point pts[] = { + Point(centreX - armSize, centreY - 1), + Point(centreX + armSize, centreY -1), + Point(centreX + armSize, centreY +1), + Point(centreX - armSize, centreY + 1), + }; + Point xpts[] = { + Point(centreX - armSize, centreY), + Point(centreX + armSize, centreY), + Point(centreX + armSize, centreY), + Point(centreX - armSize, centreY), + }; + surface->Polygon(pts, sizeof(pts) / sizeof(pts[0]), + fore.allocated, back.allocated); + + } else if (markType == SC_MARK_SMALLRECT) { + PRectangle rcSmall; + rcSmall.left = rc.left + 1; + rcSmall.top = rc.top + 2; + rcSmall.right = rc.right - 1; + rcSmall.bottom = rc.bottom - 2; + surface->RectangleDraw(rcSmall, fore.allocated, back.allocated); + } else if (markType == SC_MARK_EMPTY) { + // An invisible marker so don't draw anything + } else { // SC_MARK_SHORTARROW + Point pts[] = { + Point(centreX, centreY + dimOn2), + Point(centreX + dimOn2, centreY), + Point(centreX, centreY - dimOn2), + Point(centreX, centreY - dimOn4), + Point(centreX - dimOn4, centreY - dimOn4), + Point(centreX - dimOn4, centreY + dimOn4), + Point(centreX, centreY + dimOn4), + Point(centreX, centreY + dimOn2), + }; + surface->Polygon(pts, sizeof(pts) / sizeof(pts[0]), + fore.allocated, back.allocated); + } +} diff --git a/src/LineMarker.h b/src/LineMarker.h new file mode 100644 index 000000000..f22241bb1 --- /dev/null +++ b/src/LineMarker.h @@ -0,0 +1,22 @@ +// Scintilla source code edit control +// LineMarker.h - defines the look of a line marker in the margin +// Copyright 1998-2000 by Neil Hodgson <neilh@scintilla.org> +// The License.txt file describes the conditions under which this software may be distributed. + +#ifndef LINEMARKER_H +#define LINEMARKER_H + +class LineMarker { +public: + int markType; + ColourPair fore; + ColourPair back; + LineMarker() { + markType = SC_MARK_CIRCLE; + fore = Colour(0,0,0); + back = Colour(0xff,0xff,0xff); + } + void Draw(Surface *surface, PRectangle &rc); +}; + +#endif diff --git a/src/PropSet.cxx b/src/PropSet.cxx new file mode 100644 index 000000000..7e2a906a4 --- /dev/null +++ b/src/PropSet.cxx @@ -0,0 +1,399 @@ +// SciTE - Scintilla based Text Editor +// PropSet.cxx - a java style properties file module +// Copyright 1998-2000 by Neil Hodgson <neilh@scintilla.org> +// The License.txt file describes the conditions under which this software may be distributed. + +// Maintain a dictionary of properties + +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <stdio.h> + +#include "Platform.h" + +#include "PropSet.h" + +bool EqualCaseInsensitive(const char *a, const char *b) { +#if PLAT_GTK + return 0 == strcasecmp(a, b); +#elif PLAT_WIN + return 0 == stricmp(a, b); +#elif PLAT_WX + return 0 == wxStricmp(a, b); +#endif +} + +// Get a line of input. If end of line escaped with '\\' then continue reading. +static bool GetFullLine(const char *&fpc, int &lenData, char *s, int len) { + bool continuation = true; + while ((len > 1) && lenData > 0) { + char ch = *fpc; + fpc++; + lenData--; + if ((ch == '\r') || (ch == '\n')) { + if (!continuation) { + if ((lenData > 0) && (ch == '\r') && ((*fpc) == '\n')) { + // munch the second half of a crlf + fpc++; + lenData--; + } + *s++ = '\0'; + return true; + } + } else if ((ch == '\\') && (lenData > 0) && ((*fpc == '\r') || (*fpc == '\n'))) { + continuation = true; + } else { + continuation = false; + *s++ = ch; + len--; + } + } + return false; +} + +PropSet::PropSet() { + superPS = 0; + size = 10; + used = 0; + vals = new char * [size]; +} + +PropSet::~PropSet() { + superPS = 0; + Clear(); + delete []vals; +} + +void PropSet::EnsureCanAddEntry() { + if (used >= size - 2) { + int newsize = size + 10; + char **newvals = new char * [newsize]; + + for (int i = 0; i < used; i++) { + newvals[i] = vals[i]; + } + delete []vals; + vals = newvals; + size = newsize; + } +} + +void PropSet::Set(const char *key, const char *val) { + EnsureCanAddEntry(); + for (int i = 0; i < used; i += 2) { + if (EqualCaseInsensitive(vals[i], key)) { + // Replace current value + delete [](vals[i + 1]); + vals[i + 1] = StringDup(val); + return; + } + } + // Not found + vals[used++] = StringDup(key); + vals[used++] = StringDup(val); +} + +void PropSet::Set(char *keyval) { + char *eqat = strchr(keyval, '='); + if (eqat) { + *eqat = '\0'; + Set(keyval, eqat + 1); + *eqat = '='; + } +} + +SString PropSet::Get(const char *key) { + for (int i = 0; i < used; i += 2) { + if (EqualCaseInsensitive(vals[i], key)) { + return vals[i + 1]; + } + } + if (superPS) { + // Failed here, so try in base property set + return superPS->Get(key); + } else { + return ""; + } +} + +int PropSet::GetInt(const char *key, int defaultValue) { + SString val = Get(key); + if (val.length()) + return Get(key).value(); + else + return defaultValue; +} + +bool isprefix(const char *target, const char *prefix) { + while (*target && *prefix) { + if (toupper(*target) != toupper(*prefix)) + return false; + target++; + prefix++; + } + if (*prefix) + return false; + else + return true; +} + +bool issuffix(const char *target, const char *suffix) { + int lentarget = strlen(target); + int lensuffix = strlen(suffix); + if (lensuffix > lentarget) + return false; + for (int i = lensuffix - 1; i >= 0; i--) { + if (toupper(target[i + lentarget - lensuffix]) != toupper(suffix[i])) + return false; + } + return true; +} + +SString PropSet::GetWild(const char *keybase, const char *filename) { + for (int i = 0; i < used; i += 2) { + if (isprefix(vals[i], keybase)) { + char *orgkeyfile = vals[i] + strlen(keybase); + char *keyfile = NULL; + + if (strstr(orgkeyfile, "$(") == orgkeyfile) { + char *cpendvar = strchr(orgkeyfile, ')'); + if (cpendvar) { + int lenvar = cpendvar - orgkeyfile - 2; // Subtract the $() + char *var = static_cast<char *>(malloc(lenvar + 1)); + strncpy(var, orgkeyfile + 2, lenvar); + var[lenvar] = '\0'; + SString s = Get(var); + free(var); + keyfile = strdup(s.c_str()); + } + } + char *keyptr = keyfile; + + if (keyfile == NULL) + keyfile = orgkeyfile; + + for (; ; ) { + char *del = strchr(keyfile, ';'); + if (del == NULL) + del = keyfile + strlen(keyfile); + char delchr = *del; + *del = '\0'; + if (*keyfile == '*') { + if (issuffix(filename, keyfile + 1)) { + *del = delchr; + free(keyptr); + return vals[i + 1]; + } + } else if (EqualCaseInsensitive(keyfile, filename)) { + *del = delchr; + free(keyptr); + return vals[i + 1]; + } + if (delchr == '\0') + break; + *del = delchr; + keyfile = del + 1; + } + free(keyptr); + + if (EqualCaseInsensitive(vals[i], keybase)) { + return vals[i + 1]; + } + } + } + if (superPS) { + // Failed here, so try in base property set + return superPS->GetWild(keybase, filename); + } else { + return ""; + } +} + +SString PropSet::GetNewExpand(const char *keybase, const char *filename) { + char *base = StringDup(GetWild(keybase, filename).c_str()); + char *cpvar = strstr(base, "$("); + while (cpvar) { + char *cpendvar = strchr(cpvar, ')'); + if (cpendvar) { + int lenvar = cpendvar - cpvar - 2; // Subtract the $() + char *var = new char[lenvar + 1]; + strncpy(var, cpvar + 2, lenvar); + var[lenvar] = '\0'; + SString val = GetWild(var, filename); + int newlenbase = strlen(base) + val.length() - lenvar; + char *newbase = new char[newlenbase]; + strncpy(newbase, base, cpvar - base); + strcpy(newbase + (cpvar - base), val.c_str()); + strcpy(newbase + (cpvar - base) + val.length(), cpendvar + 1); + delete []var; + delete []base; + base = newbase; + } + cpvar = strstr(base, "$("); + } + SString sret = base; + delete []base; + return sret; +} + +void PropSet::Clear() { + for (int i = 0; i < used; i++) { + delete [](vals[i]); + vals[i] = 0; + } + used = 0; +} + +void PropSet::ReadFromMemory(const char *data, int len) { + if (len > 0) { + const char *pd = data; + char linebuf[60000]; + while (GetFullLine(pd, len, linebuf, sizeof(linebuf))) { + if (isalpha(linebuf[0])) + Set(linebuf); + } + } +} + +void PropSet::Read(const char *filename) { + //printf("Opening properties <%s>\n", filename); + Clear(); + char propsData[60000]; + FILE *rcfile = fopen(filename, "rb"); + if (rcfile) { + int lenFile = fread(propsData, 1, sizeof(propsData), rcfile); + fclose(rcfile); + ReadFromMemory(propsData, lenFile); + } else { + //printf("Could not open <%s>\n", filename); + } +} + +static bool iswordsep(char ch, bool onlyLineEnds) { + if (!isspace(ch)) + return false; + if (!onlyLineEnds) + return true; + return ch == '\r' || ch == '\n'; +} + +// Creates an array that points into each word in the string and puts \0 terminators +// after each word. +static char **ArrayFromWordList(char *wordlist, bool onlyLineEnds = false) { + char prev = '\n'; + int words = 0; + for (int j = 0; wordlist[j]; j++) { + if (!iswordsep(wordlist[j], onlyLineEnds) && iswordsep(prev, onlyLineEnds)) + words++; + prev = wordlist[j]; + } + char **keywords = new char * [words + 1]; + if (keywords) { + words = 0; + prev = '\0'; + int len = strlen(wordlist); + for (int k = 0; k < len; k++) { + if (!iswordsep(wordlist[k], onlyLineEnds)) { + if (!prev) { + keywords[words] = &wordlist[k]; + words++; + } + } else { + wordlist[k] = '\0'; + } + prev = wordlist[k]; + } + keywords[words] = &wordlist[len]; + } + return keywords; +} + +void WordList::Clear() { + if (words) { + delete []words; + delete []list; + } + words = 0; + list = 0; + len = 0; +} + +void WordList::Set(const char *s) { + len = 0; + list = StringDup(s); + words = ArrayFromWordList(list, onlyLineEnds); +} + +char *WordList::Allocate(int size) { + list = new char[size + 1]; + list[size] = '\0'; + return list; +} + +void WordList::SetFromAllocated() { + len = 0; + words = ArrayFromWordList(list, onlyLineEnds); +} + +// Shell sort based upon public domain C implementation by Raymond Gardner 1991 +// Used here because of problems with mingw qsort. +static void SortWordList(char **words, unsigned int len) { + unsigned int gap = len / 2; + + while (gap > 0) { + unsigned int i = gap; + while (i < len) { + unsigned int j = i; + char **a = words + j; + do { + j -= gap; + char **b = a; + a -= gap; + if (strcmp(*a, *b) > 0) { + char *tmp = *a; + *a = *b; + *b = tmp; + } else { + break; + } + } while (j >= gap); + i++; + } + gap = gap / 2; + } +} + +bool WordList::InList(const char *s) { + if (0 == words) + return false; + if (len == 0) { + for (int i = 0; words[i][0]; i++) + len++; + SortWordList(words, len); + for (int k = 0; k < (sizeof(starts) / sizeof(starts[0])); k++) + starts[k] = -1; + for (int l = len - 1; l >= 0; l--) { + unsigned char indexChar = words[l][0]; + starts[indexChar] = l; + } + } + unsigned char firstChar = s[0]; + int j = starts[firstChar]; + if (j >= 0) { + while (words[j][0] == firstChar) { + if (s[1] == words[j][1]) { + const char *a = words[j] + 1; + const char *b = s + 1; + while (*a && *a == *b) { + a++; + b++; + } + if (!*a && !*b) + return true; + } + j++; + } + } + return false; +} diff --git a/src/SVector.h b/src/SVector.h new file mode 100644 index 000000000..7bc948738 --- /dev/null +++ b/src/SVector.h @@ -0,0 +1,110 @@ +// Scintilla source code edit control +// SVector.h - a simple expandable vector +// Copyright 1998-1999 by Neil Hodgson <neilh@hare.net.au> +// The License.txt file describes the conditions under which this software may be distributed. + +#ifndef SVECTOR_H +#define SVECTOR_H + +// A simple expandable vector. +// T must support assignment. +// Storage not allocated for elements until an element is used. +// This makes it very lightweight unless used so is a good match for optional features. +template<class T, int sizeIncrement> +class SVector { + T *v; + unsigned int size; // Number of elements allocated + unsigned int len; // Number of elements in vector + bool allocFailure; // A memory allocation call has failed + + // Internally allocate more elements than the user wants to avoid + // thrashng the memory allocator + void SizeTo(int newSize) { + if (newSize < sizeIncrement) + newSize += sizeIncrement; + else + newSize = (newSize * 3) / 2; + T* newv = new T[newSize]; + if (!newv) { + allocFailure = true; + return; + } + size = newSize; + for (int i=0; i<len; i++) { + newv[i] = v[i]; + } + delete []v; + v = newv; + } + +public: + SVector() { + allocFailure = false; + v = 0; + len = 0; + size = 0; + } + ~SVector() { + Free(); + } + SVector(const SVector &other) { + allocFailure = false; + v = 0; + len = 0; + size = 0; + if (other.Length() > 0) { + SizeTo(other.Length()); + if (!allocFailure) { + for (int i=0;i<other.Length();i++) + v[i] = other.v[i]; + len = other.Length(); + } + } + } + SVector &operator=(const SVector &other) { + if (this != &other) { + delete []v; + allocFailure = false; + v = 0; + len = 0; + size = 0; + if (other.Length() > 0) { + SizeTo(other.Length()); + if (!allocFailure) { + for (int i=0;i<other.Length();i++) + v[i] = other.v[i]; + } + len = other.Length(); + } + } + return *this; + } + T &operator[](unsigned int i) { + if (i >= len) { + if (i >= size) { + SizeTo(i); + } + len = i+1; + } + return v[i]; + } + void Free() { + delete []v; + v = 0; + size = 0; + len = 0; + } + void SetLength(int newLen) { + if (newLength > len) { + if (newLength >= size) { + SizeTo(newLength); + } + } + len = newLen; + } + int Length() const { + return len; + } +}; + +#endif diff --git a/src/ScintillaBase.cxx b/src/ScintillaBase.cxx new file mode 100644 index 000000000..0ca299bd2 --- /dev/null +++ b/src/ScintillaBase.cxx @@ -0,0 +1,397 @@ +// Scintilla source code edit control +// ScintillaBase.cxx - an enhanced subclass of Editor with calltips, autocomplete and context menu +// Copyright 1998-2000 by Neil Hodgson <neilh@scintilla.org> +// The License.txt file describes the conditions under which this software may be distributed. + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <ctype.h> + +#include "Platform.h" + +#include "Scintilla.h" +#ifdef SCI_LEXER +#include "SciLexer.h" +#include "PropSet.h" +#include "Accessor.h" +#include "KeyWords.h" +#endif +#include "ContractionState.h" +#include "SVector.h" +#include "CellBuffer.h" +#include "CallTip.h" +#include "KeyMap.h" +#include "Indicator.h" +#include "LineMarker.h" +#include "Style.h" +#include "ViewStyle.h" +#include "AutoComplete.h" +#include "Document.h" +#include "Editor.h" +#include "ScintillaBase.h" + +ScintillaBase::ScintillaBase() { +#ifdef SCI_LEXER + lexLanguage = SCLEX_CONTAINER; + for (int wl=0;wl<numWordLists;wl++) + keyWordLists[wl] = new WordList; +#endif +} + +ScintillaBase::~ScintillaBase() {} + +void ScintillaBase::Finalise() { + popup.Destroy(); +} + +void ScintillaBase::RefreshColourPalette(Palette &pal, bool want) { + Editor::RefreshColourPalette(pal, want); + ct.RefreshColourPalette(pal, want); +} + +void ScintillaBase::AddChar(char ch) { + bool acActiveBeforeCharAdded = ac.Active(); + Editor::AddChar(ch); + if (acActiveBeforeCharAdded) + AutoCompleteChanged(ch); +} + +void ScintillaBase::Command(int cmdId) { + + switch (cmdId) { + + case idAutoComplete: // Nothing to do + break; + + case idCallTip: // Nothing to do + break; + + case idcmdUndo: + WndProc(WM_UNDO, 0, 0); + break; + + case idcmdRedo: + WndProc(SCI_REDO, 0, 0); + break; + + case idcmdCut: + WndProc(WM_CUT, 0, 0); + break; + + case idcmdCopy: + WndProc(WM_COPY, 0, 0); + break; + + case idcmdPaste: + WndProc(WM_PASTE, 0, 0); + break; + + case idcmdDelete: + WndProc(WM_CLEAR, 0, 0); + break; + + case idcmdSelectAll: + WndProc(SCI_SELECTALL, 0, 0); + break; + } +} + +int ScintillaBase::KeyCommand(UINT iMessage) { + // Most key commands cancel autocompletion mode + if (ac.Active()) { + switch (iMessage) { + // Except for these + case SCI_LINEDOWN: + AutoCompleteMove(1); + return 0; + case SCI_LINEUP: + AutoCompleteMove( -1); + return 0; + case SCI_PAGEDOWN: + AutoCompleteMove(5); + return 0; + case SCI_PAGEUP: + AutoCompleteMove( -5); + return 0; + case SCI_VCHOME: + AutoCompleteMove( -5000); + return 0; + case SCI_LINEEND: + AutoCompleteMove(5000); + return 0; + case SCI_DELETEBACK: + DelCharBack(); + AutoCompleteChanged(); + EnsureCaretVisible(); + return 0; + case SCI_TAB: + AutoCompleteCompleted(); + return 0; + + default: + ac.Cancel(); + } + } + + if (ct.inCallTipMode) { + if ( + (iMessage != SCI_CHARLEFT) && + (iMessage != SCI_CHARLEFTEXTEND) && + (iMessage != SCI_CHARRIGHT) && + (iMessage != SCI_CHARLEFTEXTEND) && + (iMessage != SCI_EDITTOGGLEOVERTYPE) && + (iMessage != SCI_DELETEBACK) + ) { + ct.CallTipCancel(); + } + if (iMessage == SCI_DELETEBACK) { + if (currentPos <= ct.posStartCallTip) { + ct.CallTipCancel(); + } + } + } + return Editor::KeyCommand(iMessage); +} + +void ScintillaBase::AutoCompleteStart(int lenEntered, const char *list) { + //Platform::DebugPrintf("AutoCOmplete %s\n", list); + ct.CallTipCancel(); + + ac.Start(wDraw, idAutoComplete, currentPos, lenEntered); + + PRectangle rcClient = GetClientRectangle(); + Point pt = LocationFromPosition(currentPos-lenEntered); + + //Platform::DebugPrintf("Auto complete %x\n", lbAutoComplete); + int heightLB = 100; + int widthLB = 100; + if (pt.x >= rcClient.right - widthLB) { + HorizontalScrollTo(xOffset + pt.x - rcClient.right + widthLB); + Redraw(); + pt = LocationFromPosition(currentPos); + } + PRectangle rcac; + rcac.left = pt.x - 5; + if (pt.y >= rcClient.bottom - heightLB && // Wont fit below. + pt.y >= (rcClient.bottom + rcClient.top) / 2) { // and there is more room above. + rcac.top = pt.y - heightLB; + if (rcac.top < 0) { + heightLB += rcac.top; + rcac.top = 0; + } + } else { + rcac.top = pt.y + vs.lineHeight; + } + rcac.right = rcac.left + widthLB; + rcac.bottom = Platform::Minimum(rcac.top + heightLB, rcClient.bottom); + ac.lb.SetPositionRelative(rcac, wMain); + ac.lb.SetFont(vs.styles[0].font); + + int maxStrLen = ac.SetList(list); + + // Fiddle the position of the list so it is right next to the target and wide enough for all its strings + PRectangle rcList = ac.lb.GetPosition(); + int heightAlloced = rcList.bottom - rcList.top; + // Make an allowance for large strings in list + rcList.left = pt.x - 5; + rcList.right = rcList.left + Platform::Maximum(widthLB, maxStrLen * 8 + 16); + if (pt.y >= rcClient.bottom - heightLB && // Wont fit below. + pt.y >= (rcClient.bottom + rcClient.top) / 2) { // and there is more room above. + rcList.top = pt.y - heightAlloced; + } else { + rcList.top = pt.y + vs.lineHeight; + } + rcList.bottom = rcList.top + heightAlloced; + ac.lb.SetPositionRelative(rcList, wMain); + //lbAutoComplete.SetPosition(rcList); + ac.Show(); +} + +void ScintillaBase::AutoCompleteCancel() { + ac.Cancel(); +} + +void ScintillaBase::AutoCompleteMove(int delta) { + ac.Move(delta); +} + +void ScintillaBase::AutoCompleteChanged(char ch) { + if (currentPos <= ac.posStart) { + ac.Cancel(); + } else if (ac.IsStopChar(ch)) { + ac.Cancel(); + } else { + char wordCurrent[1000]; + int i; + int startWord = ac.posStart - ac.startLen; + for (i = startWord; i < currentPos; i++) + wordCurrent[i - startWord] = pdoc->CharAt(i); + wordCurrent[i - startWord] = '\0'; + ac.Select(wordCurrent); + } +} + +void ScintillaBase::AutoCompleteCompleted() { + int item = ac.lb.GetSelection(); + char selected[200]; + if (item != -1) { + ac.lb.GetValue(item, selected, sizeof(selected)); + } + ac.Cancel(); + if (currentPos != ac.posStart) { + pdoc->DeleteChars(ac.posStart, currentPos - ac.posStart); + } + SetEmptySelection(ac.posStart); + if (item != -1) { + pdoc->InsertString(currentPos, selected + ac.startLen); + SetEmptySelection(currentPos + strlen(selected + ac.startLen)); + } +} + +void ScintillaBase::ContextMenu(Point pt) { + popup.CreatePopUp(); + AddToPopUp("Undo", idcmdUndo, pdoc->CanUndo()); + AddToPopUp("Redo", idcmdRedo, pdoc->CanRedo()); + AddToPopUp(""); + AddToPopUp("Cut", idcmdCut, currentPos != anchor); + AddToPopUp("Copy", idcmdCopy, currentPos != anchor); + AddToPopUp("Paste", idcmdPaste, WndProc(EM_CANPASTE, 0, 0)); + AddToPopUp("Delete", idcmdDelete, currentPos != anchor); + AddToPopUp(""); + AddToPopUp("Select All", idcmdSelectAll); + popup.Show(pt, wMain); +} + +void ScintillaBase::ButtonDown(Point pt, unsigned int curTime, bool shift, bool ctrl, bool alt) { + AutoCompleteCancel(); + ct.CallTipCancel(); + Editor::ButtonDown(pt, curTime, shift, ctrl, alt); +} + +#ifdef SCI_LEXER +void ScintillaBase::Colourise(int start, int end) { + int lengthDoc = Platform::SendScintilla(wMain.GetID(), SCI_GETLENGTH, 0, 0); + if (end == -1) + end = lengthDoc; + int len = end - start; + + StylingContext styler(wMain.GetID(), props); + + int styleStart = 0; + if (start > 0) + styleStart = styler.StyleAt(start - 1); + + ColouriseDoc(pdoc->dbcsCodePage, start, len, styleStart, lexLanguage, keyWordLists, styler); + styler.Flush(); +} +#endif + +void ScintillaBase::NotifyStyleNeeded(int endStyleNeeded) { +#ifdef SCI_LEXER + if (lexLanguage != SCLEX_CONTAINER) { + int endStyled = Platform::SendScintilla(wMain.GetID(), SCI_GETENDSTYLED, 0, 0); + int lineEndStyled = Platform::SendScintilla(wMain.GetID(), EM_LINEFROMCHAR, endStyled, 0); + endStyled = Platform::SendScintilla(wMain.GetID(), EM_LINEINDEX, lineEndStyled, 0); + Colourise(endStyled, endStyleNeeded); + return; + } +#endif + Editor::NotifyStyleNeeded(endStyleNeeded); +} + +LRESULT ScintillaBase::WndProc(UINT iMessage, WPARAM wParam, LPARAM lParam) { + switch (iMessage) { + case SCI_AUTOCSHOW: + AutoCompleteStart(wParam, reinterpret_cast<const char *>(lParam)); + break; + + case SCI_AUTOCCANCEL: + AutoCompleteCancel(); + break; + + case SCI_AUTOCACTIVE: + return ac.Active(); + + case SCI_AUTOCPOSSTART: + return ac.posStart; + + case SCI_AUTOCCOMPLETE: + AutoCompleteCompleted(); + break; + + case SCI_AUTOCSTOPS: + ac.SetStopChars(reinterpret_cast<char *>(lParam)); + break; + + case SCI_CALLTIPSHOW: { + AutoCompleteCancel(); + if (!ct.wCallTip.Created()) { + PRectangle rc = ct.CallTipStart(currentPos, LocationFromPosition(wParam), + reinterpret_cast<char *>(lParam), + vs.styles[0].fontName, vs.styles[0].size); + // If the call-tip window would be out of the client + // space, adjust so it displays above the text. + PRectangle rcClient = GetClientRectangle(); + if (rc.bottom > rcClient.bottom) { + int offset = vs.lineHeight + rc.Height(); + rc.top -= offset; + rc.bottom -= offset; + } + // Now display the window. + CreateCallTipWindow(rc); + ct.wCallTip.SetPositionRelative(rc, wDraw); + ct.wCallTip.Show(); + } + } + break; + + case SCI_CALLTIPCANCEL: + ct.CallTipCancel(); + break; + + case SCI_CALLTIPACTIVE: + return ct.inCallTipMode; + + case SCI_CALLTIPPOSSTART: + return ct.posStartCallTip; + + case SCI_CALLTIPSETHLT: + ct.SetHighlight(wParam, lParam); + break; + + case SCI_CALLTIPSETBACK: + ct.colourBG = Colour(wParam); + InvalidateStyleRedraw(); + break; + +#ifdef SCI_LEXER + case SCI_SETLEXER: + lexLanguage = wParam; + break; + + case SCI_GETLEXER: + return lexLanguage; + + case SCI_COLOURISE: + Colourise(wParam, lParam); + break; + + case SCI_SETPROPERTY: + props.Set(reinterpret_cast<const char *>(wParam), + reinterpret_cast<const char *>(lParam)); + break; + + case SCI_SETKEYWORDS: + if ((wParam >= 0) && (wParam < numWordLists)) { + keyWordLists[wParam]->Clear(); + keyWordLists[wParam]->Set(reinterpret_cast<const char *>(lParam)); + } + break; +#endif + + default: + return Editor::WndProc(iMessage, wParam, lParam); + } + return 0l; +} diff --git a/src/ScintillaBase.h b/src/ScintillaBase.h new file mode 100644 index 000000000..e9f8f28d0 --- /dev/null +++ b/src/ScintillaBase.h @@ -0,0 +1,68 @@ +// Scintilla source code edit control +// ScintillaBase.h - defines an enhanced subclass of Editor with calltips, autocomplete and context menu +// Copyright 1998-2000 by Neil Hodgson <neilh@scintilla.org> +// The License.txt file describes the conditions under which this software may be distributed. + +#ifndef SCINTILLABASE_H +#define SCINTILLABASE_H + +class ScintillaBase : public Editor { +protected: + // Enumeration of commands and child windows + enum { + idCallTip=1, + idAutoComplete=2, + + idcmdUndo=10, + idcmdRedo=11, + idcmdCut=12, + idcmdCopy=13, + idcmdPaste=14, + idcmdDelete=15, + idcmdSelectAll=16 + }; + + Menu popup; + AutoComplete ac; + + CallTip ct; + +#ifdef SCI_LEXER + int lexLanguage; + PropSet props; + enum {numWordLists=5}; + WordList *keyWordLists[numWordLists]; + void Colourise(int start, int end); +#endif + + ScintillaBase(); + virtual ~ScintillaBase(); + virtual void Initialise() = 0; + virtual void Finalise() = 0; + + virtual void RefreshColourPalette(Palette &pal, bool want); + + virtual void AddChar(char ch); + void Command(int cmdId); + virtual int KeyCommand(UINT iMessage); + + void AutoCompleteStart(int lenEntered, const char *list); + void AutoCompleteCancel(); + void AutoCompleteMove(int delta); + void AutoCompleteChanged(char ch=0); + void AutoCompleteCompleted(); + + virtual void CreateCallTipWindow(PRectangle rc) = 0; + + virtual void AddToPopUp(const char *label, int cmd=0, bool enabled=true) = 0; + void ContextMenu(Point pt); + + virtual void ButtonDown(Point pt, unsigned int curTime, bool shift, bool ctrl, bool alt); + + virtual void NotifyStyleNeeded(int endStyleNeeded); +public: + // Public so scintilla_send_message can use it + virtual LRESULT WndProc(UINT iMessage, WPARAM wParam, LPARAM lParam); +}; + +#endif diff --git a/src/Style.cxx b/src/Style.cxx new file mode 100644 index 000000000..271d98fc2 --- /dev/null +++ b/src/Style.cxx @@ -0,0 +1,89 @@ +// Scintilla source code edit control +// Style.cxx - defines the font and colour style for a class of text +// Copyright 1998-2000 by Neil Hodgson <neilh@scintilla.org> +// The License.txt file describes the conditions under which this software may be distributed. + +#include <string.h> + +#include "Platform.h" + +#include "Style.h" + +Style::Style() { + Clear(Colour(0,0,0), Colour(0xff,0xff,0xff), + Platform::DefaultFontSize(), 0, + false, false, false); +} + +Style::~Style() { + if (!aliasOfDefaultFont) + font.Release(); +} + +Style &Style::operator=(const Style &source) { + if (this == &source) + return *this; + Clear(Colour(0,0,0), Colour(0xff,0xff,0xff), + Platform::DefaultFontSize(), 0, + false, false, false); + fore.desired = source.fore.desired; + back.desired = source.back.desired; + bold = source.bold; + italic = source.italic; + size = source.size; + fontName = source.fontName; + eolFilled = source.eolFilled; + return *this; +} + +void Style::Clear(Colour fore_, Colour back_, int size_, const char *fontName_, + bool bold_, bool italic_, bool eolFilled_) { + fore.desired = fore_; + back.desired = back_; + bold = bold_; + italic = italic_; + size = size_; + fontName = fontName_; + eolFilled = eolFilled_; + if (!aliasOfDefaultFont) + font.Release(); + aliasOfDefaultFont = false; +} + +bool Style::EquivalentFontTo(const Style *other) const { + if (bold != other->bold || + italic != other->italic || + size != other->size) + return false; + if (fontName == other->fontName) + return true; + if (!fontName) + return false; + if (!other->fontName) + return false; + return strcmp(fontName, other->fontName) == 0; +} + +void Style::Realise(Surface &surface, int zoomLevel, Style *defaultStyle) { + int sizeZoomed = size + zoomLevel; + if (sizeZoomed <= 2) // Hangs if sizeZoomed <= 1 + sizeZoomed = 2; + + int deviceHeight = (sizeZoomed * surface.LogPixelsY()) / 72; + aliasOfDefaultFont = defaultStyle && EquivalentFontTo(defaultStyle); + if (aliasOfDefaultFont) { + font.SetID(defaultStyle->font.GetID()); + } else if (fontName) { + font.Create(fontName, deviceHeight, bold, italic); + } + + ascent = surface.Ascent(font); + descent = surface.Descent(font); + // Probably more typographically correct to include leading + // but that means more complex drawing as leading must be erased + //lineHeight = surface.ExternalLeading() + surface.Height(); + externalLeading = surface.ExternalLeading(font); + lineHeight = surface.Height(font); + aveCharWidth = surface.AverageCharWidth(font); + spaceWidth = surface.WidthChar(font, ' '); +} diff --git a/src/Style.h b/src/Style.h new file mode 100644 index 000000000..a610ff8ba --- /dev/null +++ b/src/Style.h @@ -0,0 +1,39 @@ +// Scintilla source code edit control +// Style.h - defines the font and colour style for a class of text +// Copyright 1998-2000 by Neil Hodgson <neilh@scintilla.org> +// The License.txt file describes the conditions under which this software may be distributed. + +#ifndef STYLE_H +#define STYLE_H + +class Style { +public: + ColourPair fore; + ColourPair back; + bool aliasOfDefaultFont; + bool bold; + bool italic; + int size; + const char *fontName; + bool eolFilled; + + Font font; + unsigned int lineHeight; + unsigned int ascent; + unsigned int descent; + unsigned int externalLeading; + unsigned int aveCharWidth; + unsigned int spaceWidth; + + Style(); + ~Style(); + Style &operator=(const Style &source); + void Clear(Colour fore_, Colour back_, + int size_, + const char *fontName_, + bool bold_, bool italic_, bool eolFilled_); + bool EquivalentFontTo(const Style *other) const; + void Realise(Surface &surface, int zoomLevel, Style *defaultStyle=0); +}; + +#endif diff --git a/src/ViewStyle.cxx b/src/ViewStyle.cxx new file mode 100644 index 000000000..122db6a00 --- /dev/null +++ b/src/ViewStyle.cxx @@ -0,0 +1,226 @@ +// Scintilla source code edit control +// ViewStyle.cxx - store information on how the document is to be viewed +// Copyright 1998-2000 by Neil Hodgson <neilh@scintilla.org> +// The License.txt file describes the conditions under which this software may be distributed. + +#include <string.h> + +#include "Platform.h" + +#include "Scintilla.h" +#include "Indicator.h" +#include "LineMarker.h" +#include "Style.h" +#include "ViewStyle.h" + +MarginStyle::MarginStyle() : + symbol(false), width(16), mask(0xffffffff), sensitive(false) { +} + +// A list of the fontnames - avoids wasting space in each style +FontNames::FontNames() { + max = 0; +} + +FontNames::~FontNames() { + Clear(); +} + +void FontNames::Clear() { + for (int i=0;i<max;i++) { + delete []names[i]; + } + max = 0; +} + +const char *FontNames::Save(const char *name) { + for (int i=0;i<max;i++) { + if (strcmp(names[i], name) == 0) { + return names[i]; + } + } + names[max] = new char[strlen(name) + 1]; + strcpy(names[max], name); + max++; + return names[max-1]; +} + +ViewStyle::ViewStyle() { + Init(); +} + +ViewStyle::ViewStyle(const ViewStyle &source) { + Init(); + for (int sty=0;sty<=STYLE_MAX;sty++) { + styles[sty] = source.styles[sty]; + } + for (int mrk=0;mrk<=MARKER_MAX;mrk++) { + markers[mrk] = source.markers[mrk]; + } + for (int ind=0;ind<=INDIC_MAX;ind++) { + indicators[ind] = source.indicators[ind]; + } + + selforeset = source.selforeset; + selforeground.desired = source.selforeground.desired; + selbackset = source.selbackset; + selbackground.desired = source.selbackground.desired; + selbar.desired = source.selbar.desired; + selbarlight.desired = source.selbarlight.desired; + caretcolour.desired = source.caretcolour.desired; + edgecolour.desired = source.edgecolour.desired; + leftMarginWidth = source.leftMarginWidth; + rightMarginWidth = source.rightMarginWidth; + for (int i=0;i < margins; i++) { + ms[i] = source.ms[i]; + } + symbolMargin = source.symbolMargin; + maskInLine = source.maskInLine; + fixedColumnWidth = source.fixedColumnWidth; + zoomLevel = source.zoomLevel; + viewWhitespace = source.viewWhitespace; + viewEOL = source.viewEOL; + showMarkedLines = source.showMarkedLines; +} + +ViewStyle::~ViewStyle() { +} + +void ViewStyle::Init() { + fontNames.Clear(); + + indicators[0].style = INDIC_SQUIGGLE; + indicators[0].fore = Colour(0, 0x7f, 0); + indicators[1].style = INDIC_TT; + indicators[1].fore = Colour(0, 0, 0xff); + indicators[2].style = INDIC_PLAIN; + indicators[2].fore = Colour(0xff, 0, 0); + + lineHeight = 1; + maxAscent = 1; + maxDescent = 1; + aveCharWidth = 8; + spaceWidth = 8; + + selforeset = false; + selforeground.desired = Colour(0xff, 0, 0); + selbackset = true; + selbackground.desired = Colour(0xc0, 0xc0, 0xc0); + selbar.desired = Platform::Chrome(); + selbarlight.desired = Platform::ChromeHighlight(); + styles[STYLE_LINENUMBER].fore.desired = Colour(0, 0, 0); + styles[STYLE_LINENUMBER].back.desired = Platform::Chrome(); + //caretcolour.desired = Colour(0xff, 0, 0); + caretcolour.desired = Colour(0, 0, 0); + edgecolour.desired = Colour(0xc0, 0xc0, 0xc0); + + leftMarginWidth = 1; + rightMarginWidth = 1; + ms[0].symbol = false; + ms[0].width = 0; + ms[0].mask = 0; + ms[1].symbol = true; + ms[1].width = 16; + ms[1].mask = ~SC_MASK_FOLDERS; + ms[2].symbol = true; + ms[2].width = 14; // Nice width for arrows + ms[2].mask = SC_MASK_FOLDERS; + ms[2].width = 0; // Nice width for arrows + ms[2].mask = 0; + fixedColumnWidth = leftMarginWidth; + symbolMargin = false; + maskInLine = 0xffffffff; + for (int margin=0; margin < margins; margin++) { + fixedColumnWidth += ms[margin].width; + symbolMargin = symbolMargin || ms[margin].symbol; + if (ms[margin].width > 0) + maskInLine &= ~ms[margin].mask; + } + zoomLevel = 0; + viewWhitespace = false; + viewEOL = false; + showMarkedLines = true; +} + +void ViewStyle::RefreshColourPalette(Palette &pal, bool want) { + unsigned int i; + for (i=0;i<(sizeof(styles)/sizeof(styles[0]));i++) { + pal.WantFind(styles[i].fore, want); + pal.WantFind(styles[i].back, want); + } + for (i=0;i<(sizeof(indicators)/sizeof(indicators[0]));i++) { + pal.WantFind(indicators[i].fore, want); + } + for (i=0;i<(sizeof(markers)/sizeof(markers[0]));i++) { + pal.WantFind(markers[i].fore, want); + pal.WantFind(markers[i].back, want); + } + pal.WantFind(selforeground, want); + pal.WantFind(selbackground, want); + pal.WantFind(selbar, want); + pal.WantFind(selbarlight, want); + pal.WantFind(caretcolour, want); + pal.WantFind(edgecolour, want); +} + +#include <mmsystem.h> +void ViewStyle::Refresh(Surface &surface) { +DWORD dwStart = timeGetTime(); + selbar.desired = Platform::Chrome(); + selbarlight.desired = Platform::ChromeHighlight(); + maxAscent = 1; + maxDescent = 1; + styles[STYLE_DEFAULT].Realise(surface, zoomLevel); + for (unsigned int i=0;i<(sizeof(styles)/sizeof(styles[0]));i++) { + if (i != STYLE_DEFAULT) { + styles[i].Realise(surface, zoomLevel, &styles[STYLE_DEFAULT]); + if (maxAscent < styles[i].ascent) + maxAscent = styles[i].ascent; + if (maxDescent < styles[i].descent) + maxDescent = styles[i].descent; + } + } + + lineHeight = maxAscent + maxDescent; + aveCharWidth = styles[STYLE_DEFAULT].aveCharWidth; + spaceWidth = styles[STYLE_DEFAULT].spaceWidth; + + fixedColumnWidth = leftMarginWidth; + symbolMargin = false; + maskInLine = 0xffffffff; + for (int margin=0; margin < margins; margin++) { + fixedColumnWidth += ms[margin].width; + symbolMargin = symbolMargin || ms[margin].symbol; + if (ms[margin].width > 0) + maskInLine &= ~ms[margin].mask; + } +DWORD dwEnd = timeGetTime(); +Platform::DebugPrintf("Refresh took %d\n", dwEnd - dwStart); +} + +void ViewStyle::ResetDefaultStyle() { + styles[STYLE_DEFAULT].Clear(Colour(0,0,0), Colour(0xff,0xff,0xff), + Platform::DefaultFontSize(), fontNames.Save(Platform::DefaultFont()), + false, false, false); +} + +void ViewStyle::ClearStyles() { + // Reset all styles to be like the default style + for (int i=0; i<=STYLE_MAX; i++) { + if (i != STYLE_DEFAULT) { + styles[i].Clear( + styles[STYLE_DEFAULT].fore.desired, + styles[STYLE_DEFAULT].back.desired, + styles[STYLE_DEFAULT].size, + styles[STYLE_DEFAULT].fontName, + styles[STYLE_DEFAULT].bold, + styles[STYLE_DEFAULT].italic, + styles[STYLE_DEFAULT].eolFilled); + } + } + styles[STYLE_LINENUMBER].back.desired = Platform::Chrome(); +} + +void ViewStyle::SetStyleFontName(int styleIndex, const char *name) { + styles[styleIndex].fontName = fontNames.Save(name); +} diff --git a/src/ViewStyle.h b/src/ViewStyle.h new file mode 100644 index 000000000..4436e83ff --- /dev/null +++ b/src/ViewStyle.h @@ -0,0 +1,72 @@ +// Scintilla source code edit control +// ViewStyle.h - store information on how the document is to be viewed +// Copyright 1998-2000 by Neil Hodgson <neilh@scintilla.org> +// The License.txt file describes the conditions under which this software may be distributed. + +#ifndef VIEWSTYLE_H +#define VIEWSTYLE_H + +class MarginStyle { +public: + bool symbol; + int width; + int mask; + bool sensitive; + MarginStyle(); +}; + +class FontNames { +private: + char *names[STYLE_MAX + 1]; + int max; +public: + FontNames(); + ~FontNames(); + void Clear(); + const char *Save(const char *name); +}; + +class ViewStyle { +public: + FontNames fontNames; + Style styles[STYLE_MAX + 1]; + LineMarker markers[MARKER_MAX + 1]; + Indicator indicators[INDIC_MAX + 1]; + int lineHeight; + unsigned int maxAscent; + unsigned int maxDescent; + unsigned int aveCharWidth; + unsigned int spaceWidth; + bool selforeset; + ColourPair selforeground; + bool selbackset; + ColourPair selbackground; + ColourPair selbar; + ColourPair selbarlight; + // Margins are ordered: Line Numbers, Selection Margin, Spacing Margin + int leftMarginWidth; // Spacing margin on left of text + int rightMarginWidth; // Spacing margin on left of text + enum { margins=3 }; + bool symbolMargin; + int maskInLine; // Mask for markers to be put into text because there is nowhere for them to go in margin + MarginStyle ms[margins]; + int fixedColumnWidth; + int zoomLevel; + bool viewWhitespace; + bool viewEOL; + bool showMarkedLines; + ColourPair caretcolour; + ColourPair edgecolour; + + ViewStyle(); + ViewStyle(const ViewStyle &source); + ~ViewStyle(); + void Init(); + void RefreshColourPalette(Palette &pal, bool want); + void Refresh(Surface &surface); + void ResetDefaultStyle(); + void ClearStyles(); + void SetStyleFontName(int styleIndex, const char *name); +}; + +#endif diff --git a/win32/Margin.cur b/win32/Margin.cur Binary files differnew file mode 100644 index 000000000..9e0e79066 --- /dev/null +++ b/win32/Margin.cur diff --git a/win32/PlatWin.cxx b/win32/PlatWin.cxx new file mode 100644 index 000000000..9fde8a272 --- /dev/null +++ b/win32/PlatWin.cxx @@ -0,0 +1,673 @@ +// Scintilla source code edit control +// PlatWin.cxx - implementation of platform facilities on Windows +// Copyright 1998-2000 by Neil Hodgson <neilh@scintilla.org> +// The License.txt file describes the conditions under which this software may be distributed. + +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <stdarg.h> +#include <stdio.h> + +#include "Platform.h" +#include "PlatformRes.h" + +Point Point::FromLong(long lpoint) { + return Point(static_cast<short>(LOWORD(lpoint)), static_cast<short>(HIWORD(lpoint))); +} + +static RECT RectFromPRectangle(PRectangle prc) { + RECT rc = {prc.left, prc.top, prc.right, prc.bottom}; + return rc; +} + +Colour::Colour(long lcol) { + co = lcol; +} + +Colour::Colour(unsigned int red, unsigned int green, unsigned int blue) { + co = RGB(red, green, blue); +} + +bool Colour::operator==(const Colour &other) const { + return co == other.co; +} + +long Colour::AsLong() const { + return co; +} + +unsigned int Colour::GetRed() { + return co & 0xff; +} + +unsigned int Colour::GetGreen() { + return (co >> 8) & 0xff; +} + +unsigned int Colour::GetBlue() { + return (co >> 16) & 0xff; +} + +Palette::Palette() { + used = 0; + allowRealization = false; + hpal = 0; +} + +Palette::~Palette() { + Release(); +} + +void Palette::Release() { + used = 0; + if (hpal) + ::DeleteObject(hpal); + hpal = 0; +} + +// This method either adds a colour to the list of wanted colours (want==true) +// or retrieves the allocated colour back to the ColourPair. +// This is one method to make it easier to keep the code for wanting and retrieving in sync. +void Palette::WantFind(ColourPair &cp, bool want) { + if (want) { + for (int i=0; i < used; i++) { + if (entries[i].desired == cp.desired) + return; + } + + if (used < numEntries) { + entries[used].desired = cp.desired; + entries[used].allocated = cp.desired; + used++; + } + } else { + for (int i=0; i < used; i++) { + if (entries[i].desired == cp.desired) { + cp.allocated = entries[i].allocated; + return; + } + } + cp.allocated = cp.desired; + } +} + +void Palette::Allocate(Window &) { + if (hpal) + ::DeleteObject(hpal); + hpal = 0; + + if (allowRealization) { + char *pal = new char[sizeof(LOGPALETTE) + (used-1) * sizeof(PALETTEENTRY)]; + LOGPALETTE *logpal = reinterpret_cast<LOGPALETTE *>(pal); + logpal->palVersion = 0x300; + logpal->palNumEntries = used; + for (int iPal=0;iPal<used;iPal++) { + Colour desired = entries[iPal].desired; + logpal->palPalEntry[iPal].peRed = desired.GetRed(); + logpal->palPalEntry[iPal].peGreen = desired.GetGreen(); + logpal->palPalEntry[iPal].peBlue = desired.GetBlue(); + entries[iPal].allocated = + PALETTERGB(desired.GetRed(), desired.GetGreen(), desired.GetBlue()); + // PC_NOCOLLAPSE means exact colours allocated even when in background this means other windows + // are less likely to get their colours and also flashes more when switching windows + logpal->palPalEntry[iPal].peFlags = PC_NOCOLLAPSE; + // 0 allows approximate colours when in background, yielding moe colours to other windows + //logpal->palPalEntry[iPal].peFlags = 0; + } + hpal = ::CreatePalette(logpal); + delete []pal; + } +} + +Font::Font() { + id = 0; +} + +Font::~Font() { +} + +void Font::Create(const char *faceName, int size, bool bold, bool italic) { + Release(); + + LOGFONT lf; + memset(&lf, 0, sizeof(lf)); + // The negative is to allow for leading + lf.lfHeight = -(abs(size)); + lf.lfWeight = bold ? FW_BOLD : FW_NORMAL; + lf.lfItalic = italic ? 1 : 0; + lf.lfCharSet = DEFAULT_CHARSET; + strcpy(lf.lfFaceName, faceName); + + id = ::CreateFontIndirect(&lf); +} + +void Font::Release() { + if (id) + ::DeleteObject(id); + id = 0; +} + +Surface::Surface() : + hdc(0), hdcOwned(false), + pen(0), penOld(0), + brush(0), brushOld(0), + font(0), fontOld(0), + bitmap(0), bitmapOld(0), + paletteOld(0) { +} + +Surface::~Surface() { + Release(); +} + +void Surface::Release() { + if (penOld) { + ::SelectObject(hdc, penOld); + ::DeleteObject(pen); + penOld = 0; + } + pen = 0; + if (brushOld) { + ::SelectObject(hdc, brushOld); + ::DeleteObject(brush); + brushOld = 0; + } + brush = 0; + if (fontOld) { + // Fonts are not deleted as they are owned by a Font object + ::SelectObject(hdc, fontOld); + fontOld = 0; + } + font =0; + if (bitmapOld) { + ::SelectObject(hdc, bitmapOld); + ::DeleteObject(bitmap); + bitmapOld = 0; + } + bitmap = 0; + if (paletteOld) { + // Fonts are not deleted as they are owned by a Palette object + ::SelectPalette(hdc, paletteOld, TRUE); + paletteOld = 0; + } + if (hdcOwned) { + ::DeleteDC(hdc); + hdc = 0; + hdcOwned = false; + } +} + +bool Surface::Initialised() { + return hdc; +} + +void Surface::Init() { + Release(); + hdc = ::CreateCompatibleDC(NULL); + hdcOwned = true; + ::SetTextAlign(hdc, TA_BASELINE); +} + +void Surface::Init(HDC hdc_) { + Release(); + hdc = hdc_; + ::SetTextAlign(hdc, TA_BASELINE); +} + +void Surface::InitPixMap(int width, int height, Surface *surface_) { + Release(); + hdc = ::CreateCompatibleDC(surface_->hdc); + hdcOwned = true; + bitmap = ::CreateCompatibleBitmap(surface_->hdc, width, height); + bitmapOld = static_cast<HBITMAP>(::SelectObject(hdc, bitmap)); + ::SetTextAlign(hdc, TA_BASELINE); +} + +void Surface::PenColour(Colour fore) { + if (pen) { + ::SelectObject(hdc, penOld); + ::DeleteObject(pen); + pen = 0; + penOld = 0; + } + pen = ::CreatePen(0,1,fore.AsLong()); + penOld = static_cast<HPEN>(::SelectObject(hdc, pen)); +} + +void Surface::BrushColor(Colour back) { + if (brush) { + ::SelectObject(hdc, brushOld); + ::DeleteObject(brush); + brush = 0; + brushOld = 0; + } + // Only ever want pure, non-dithered brushes + Colour colourNearest = ::GetNearestColor(hdc, back.AsLong()); + brush = ::CreateSolidBrush(colourNearest.AsLong()); + brushOld = static_cast<HBRUSH>(::SelectObject(hdc, brush)); +} + +void Surface::SetFont(Font &font_) { + if (font_.GetID() != font) { + if (fontOld) { + ::SelectObject(hdc, font_.GetID()); + } else { + fontOld = static_cast<HFONT>(::SelectObject(hdc, font_.GetID())); + } + font = font_.GetID(); + } +} + +int Surface::LogPixelsY() { + return ::GetDeviceCaps(hdc, LOGPIXELSY); +} + +void Surface::MoveTo(int x_, int y_) { + ::MoveToEx(hdc, x_, y_, 0); +} + +void Surface::LineTo(int x_, int y_) { + ::LineTo(hdc, x_, y_); +} + +void Surface::Polygon(Point *pts, int npts, Colour fore, + Colour back) { + PenColour(fore); + BrushColor(back); + ::Polygon(hdc, reinterpret_cast<POINT *>(pts), npts); +} + +void Surface::RectangleDraw(PRectangle rc, Colour fore, Colour back) { + PenColour(fore); + BrushColor(back); + ::Rectangle(hdc, rc.left, rc.top, rc.right, rc.bottom); +} + +void Surface::FillRectangle(PRectangle rc, Colour back) { + // Using ExtTextOut rather than a FillRect ensures that no dithering occurs. + // There is no need to allocate a brush either. + RECT rcw = RectFromPRectangle(rc); + ::SetBkColor(hdc, back.AsLong()); + ::ExtTextOut(hdc, rc.left, rc.top, ETO_OPAQUE, &rcw, "", 0, NULL); +} + +void Surface::FillRectangle(PRectangle rc, Surface &surfacePattern) { + HBRUSH br = 0; + if (surfacePattern.bitmap) + br = ::CreatePatternBrush(surfacePattern.bitmap); + else // Something is wrong so display in red + br = ::CreateSolidBrush(RGB(0xff, 0, 0)); + RECT rcw = RectFromPRectangle(rc); + ::FillRect(hdc, &rcw, br); + ::DeleteObject(br); +} + +void Surface::RoundedRectangle(PRectangle rc, Colour fore, Colour back) { + PenColour(fore); + BrushColor(back); + ::RoundRect(hdc, + rc.left + 1, rc.top, + rc.right - 1, rc.bottom, + 8, 8 ); +} + +void Surface::Ellipse(PRectangle rc, Colour fore, Colour back) { + PenColour(fore); + BrushColor(back); + ::Ellipse(hdc, rc.left, rc.top, rc.right, rc.bottom); +} + +void Surface::Copy(PRectangle rc, Point from, Surface &surfaceSource) { + ::BitBlt(hdc, rc.left, rc.top, rc.Width(), rc.Height(), + surfaceSource.hdc, from.x, from.y, SRCCOPY); +} + +void Surface::DrawText(PRectangle rc, Font &font_, int ybase, const char *s, int len, Colour fore, Colour back) { + SetFont(font_); + ::SetTextColor(hdc, fore.AsLong()); + ::SetBkColor(hdc, back.AsLong()); + RECT rcw = RectFromPRectangle(rc); + ::ExtTextOut(hdc, rc.left, ybase, ETO_OPAQUE, &rcw, s, len, NULL); +} + +void Surface::DrawTextClipped(PRectangle rc, Font &font_, int ybase, const char *s, int len, Colour fore, Colour back) { + SetFont(font_); + ::SetTextColor(hdc, fore.AsLong()); + ::SetBkColor(hdc, back.AsLong()); + RECT rcw = RectFromPRectangle(rc); + ::ExtTextOut(hdc, rc.left, ybase, ETO_OPAQUE | ETO_CLIPPED, &rcw, s, len, NULL); +} + +int Surface::WidthText(Font &font_, const char *s, int len) { + SetFont(font_); + SIZE sz; + ::GetTextExtentPoint32(hdc, s, len, &sz); + return sz.cx; +} + +void Surface::MeasureWidths(Font &font_, const char *s, int len, int *positions) { + SetFont(font_); + SIZE sz; + int fit = 0; + ::GetTextExtentExPoint(hdc, s, len, 30000, &fit, positions, &sz); +} + +int Surface::WidthChar(Font &font_, char ch) { + SetFont(font_); + SIZE sz; + ::GetTextExtentPoint32(hdc, &ch, 1, &sz); + return sz.cx; +} + +int Surface::Ascent(Font &font_) { + SetFont(font_); + TEXTMETRIC tm; + ::GetTextMetrics(hdc, &tm); + return tm.tmAscent; +} + +int Surface::Descent(Font &font_) { + SetFont(font_); + TEXTMETRIC tm; + ::GetTextMetrics(hdc, &tm); + return tm.tmDescent; +} + +int Surface::InternalLeading(Font &font_) { + SetFont(font_); + TEXTMETRIC tm; + ::GetTextMetrics(hdc, &tm); + return tm.tmInternalLeading; +} + +int Surface::ExternalLeading(Font &font_) { + SetFont(font_); + TEXTMETRIC tm; + ::GetTextMetrics(hdc, &tm); + return tm.tmExternalLeading; +} + +int Surface::Height(Font &font_) { + SetFont(font_); + TEXTMETRIC tm; + ::GetTextMetrics(hdc, &tm); + return tm.tmHeight; +} + +int Surface::AverageCharWidth(Font &font_) { + SetFont(font_); + TEXTMETRIC tm; + ::GetTextMetrics(hdc, &tm); + return tm.tmAveCharWidth; +} + +int Surface::SetPalette(Palette *pal, bool inBackGround) { + if (paletteOld) { + ::SelectPalette(hdc,paletteOld,TRUE); + } + paletteOld = 0; + int changes = 0; + if (pal->allowRealization) { + paletteOld = ::SelectPalette(hdc, pal->hpal, inBackGround); + changes = ::RealizePalette(hdc); + } + return changes; +} + +void Surface::SetClip(PRectangle rc) { + ::IntersectClipRect(hdc, rc.left, rc.top, rc.right, rc.bottom); +} + +Window::~Window() { +} + +void Window::Destroy() { + if (id) + ::DestroyWindow(id); + id = 0; +} + +bool Window::HasFocus() { + return ::GetFocus() == id; +} + +PRectangle Window::GetPosition() { + RECT rc; + ::GetWindowRect(id, &rc); + return PRectangle(rc.left, rc.top, rc.right, rc.bottom); +} + +void Window::SetPosition(PRectangle rc) { + ::SetWindowPos(id, 0, rc.left, rc.top, rc.Width(), rc.Height(), 0); +} + +void Window::SetPositionRelative(PRectangle rc, Window) { + SetPosition(rc); +} + +PRectangle Window::GetClientPosition() { + RECT rc; + ::GetClientRect(id, &rc); + return PRectangle(rc.left, rc.top, rc.right, rc.bottom); +} + +void Window::Show(bool show) { + if (show) + ::ShowWindow(id, SW_SHOWNORMAL); + else + ::ShowWindow(id, SW_HIDE); +} + +void Window::InvalidateAll() { + ::InvalidateRect(id, NULL, FALSE); +} + +void Window::InvalidateRectangle(PRectangle rc) { + RECT rcw = RectFromPRectangle(rc); + ::InvalidateRect(id, &rcw, FALSE); +} + +void Window::SetFont(Font &font) { + SendMessage(WM_SETFONT, + reinterpret_cast<WPARAM>(font.GetID()), 0); +} + +static HINSTANCE hinstPlatformRes = 0; + +void Window::SetCursor(Cursor curs) { + switch (curs) { + case cursorText: + ::SetCursor(::LoadCursor(NULL,IDC_IBEAM)); + break; + case cursorArrow: + ::SetCursor(::LoadCursor(NULL,IDC_ARROW)); + break; + case cursorUp: + ::SetCursor(::LoadCursor(NULL,IDC_UPARROW)); + break; + case cursorWait: + ::SetCursor(::LoadCursor(NULL,IDC_WAIT)); + break; + case cursorHoriz: + ::SetCursor(::LoadCursor(NULL,IDC_SIZEWE)); + break; + case cursorVert: + ::SetCursor(::LoadCursor(NULL,IDC_SIZENS)); + break; + case cursorReverseArrow: { + if (!hinstPlatformRes) + hinstPlatformRes = GetModuleHandle("Scintilla"); + if (!hinstPlatformRes) + hinstPlatformRes = GetModuleHandle("SciLexer"); + if (!hinstPlatformRes) + hinstPlatformRes = GetModuleHandle(NULL); + ::SetCursor(::LoadCursor(hinstPlatformRes, MAKEINTRESOURCE(IDC_MARGIN))); + } + break; + } +} + +void Window::SetTitle(const char *s) { + ::SetWindowText(id, s); +} + +LRESULT Window::SendMessage(UINT msg, WPARAM wParam, LPARAM lParam) { + return ::SendMessage(id, msg, wParam, lParam); +} + +int Window::GetDlgCtrlID() { + return ::GetDlgCtrlID(id); +} + +HINSTANCE Window::GetInstance() { + return reinterpret_cast<HINSTANCE>( + ::GetWindowLong(id,GWL_HINSTANCE)); +} + +ListBox::ListBox() { +} + +ListBox::~ListBox() { +} + +void ListBox::Create(Window &parent, int ctrlID) { + id = ::CreateWindowEx( + WS_EX_CLIENTEDGE, "listbox", "", + WS_CHILD|WS_BORDER|WS_VSCROLL|LBS_SORT|LBS_NOTIFY, + 100,100, 150,80, parent.GetID(), reinterpret_cast<HMENU>(ctrlID), + parent.GetInstance(), 0); +} + +void ListBox::Clear() { + SendMessage(LB_RESETCONTENT); +} + +void ListBox::Append(char *s) { + SendMessage(LB_ADDSTRING, 0, reinterpret_cast<LPARAM>(s)); +} + +int ListBox::Length() { + return SendMessage(LB_GETCOUNT); +} + +void ListBox::Select(int n) { + SendMessage(LB_SETCURSEL, n); +} + +int ListBox::GetSelection() { + return SendMessage(LB_GETCURSEL); +} + +int ListBox::Find(const char *prefix) { + return SendMessage(LB_FINDSTRING, 0, reinterpret_cast<LPARAM>(prefix)); +} + +void ListBox::GetValue(int n, char *value, int len) { + int lenText = SendMessage(LB_GETTEXTLEN, n); + if ((len > 0) && (lenText > 0)){ + char *text = new char[len+1]; + if (text) { + SendMessage(LB_GETTEXT, n, reinterpret_cast<LPARAM>(text)); + strncpy(value, text, len); + value[len-1] = '\0'; + delete []text; + } else { + value[0] = '\0'; + } + } else { + value[0] = '\0'; + } +} + +void ListBox::Sort() { + // Windows keeps sorted so no need to sort +} + +Menu::Menu() : id(0) { +} + +void Menu::CreatePopUp() { + Destroy(); + id = ::CreatePopupMenu(); +} + +void Menu::Destroy() { + if (id) + ::DestroyMenu(id); + id = 0; +} + +void Menu::Show(Point pt, Window &w) { + ::TrackPopupMenu(id, 0, pt.x - 4, pt.y, 0, w.GetID(), NULL); + Destroy(); +} + +Colour Platform::Chrome() { + return ::GetSysColor(COLOR_3DFACE); +} + +Colour Platform::ChromeHighlight() { + return ::GetSysColor(COLOR_3DHIGHLIGHT); +} + +const char *Platform::DefaultFont() { + return "Verdana"; +} + +int Platform::DefaultFontSize() { + return 8; +} + +unsigned int Platform::DoubleClickTime() { + return ::GetDoubleClickTime(); +} + +void Platform::DebugDisplay(const char *s) { + ::OutputDebugString(s); +} + +bool Platform::IsKeyDown(int key) { + return ::GetKeyState(key) & 0x80000000; +} + +long Platform::SendScintilla(WindowID w, unsigned int msg, unsigned long wParam, long lParam) { + return ::SendMessage(w, msg, wParam, lParam); +} + +// These are utility functions not really tied to a platform + +int Platform::Minimum(int a, int b) { + if (a < b) + return a; + else + return b; +} + +int Platform::Maximum(int a, int b) { + if (a > b) + return a; + else + return b; +} + +#define TRACE + +void Platform::DebugPrintf(const char *format, ...) { +#ifdef TRACE + char buffer[2000]; + va_list pArguments; + va_start(pArguments, format); + vsprintf(buffer,format,pArguments); + va_end(pArguments); + Platform::DebugDisplay(buffer); +#endif +} + +int Platform::Clamp(int val, int minVal, int maxVal) { + if (val > maxVal) + val = maxVal; + if (val < minVal) + val = minVal; + return val; +} diff --git a/win32/PlatformRes.h b/win32/PlatformRes.h new file mode 100644 index 000000000..a4361dcaa --- /dev/null +++ b/win32/PlatformRes.h @@ -0,0 +1,6 @@ +// Scintilla source code edit control +// PlatformRes.h - defines IDs of resources used by Scintilla on Windows platform +// Copyright 1998-2000 by Neil Hodgson <neilh@scintilla.org> +// The License.txt file describes the conditions under which this software may be distributed. + +#define IDC_MARGIN 400 diff --git a/win32/ScintRes.rc b/win32/ScintRes.rc new file mode 100644 index 000000000..96c93751e --- /dev/null +++ b/win32/ScintRes.rc @@ -0,0 +1,40 @@ +// Resource file for Scintilla +// Copyright 1998-2000 by Neil Hodgson <neilh@scintilla.org> +// The License.txt file describes the conditions under which this software may be distributed. + +#ifndef __BORLANDC__ +#include <windows.h> +#endif + +#include "PlatformRes.h" + +VS_VERSION_INFO VERSIONINFO +FILEVERSION 1, 2, 2, 0 +PRODUCTVERSION 1, 2, 2, 0 +FILEFLAGSMASK 0x3fL +FILEFLAGS 0 +FILEOS VOS_NT_WINDOWS32 +FILETYPE VFT_APP +FILESUBTYPE VFT2_UNKNOWN +BEGIN + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "Neil Hodgson neilh@scintilla.org\0" + VALUE "FileDescription", "Scintilla.DLL - a Source Editing Component\0" + VALUE "FileVersion", "1.22\0" + VALUE "InternalName", "Scintilla\0" + VALUE "LegalCopyright", "Copyright 1998-2000 by Neil Hodgson\0" + VALUE "OriginalFilename", "Scintilla.DLL\0" + VALUE "ProductName", "Scintilla\0" + VALUE "ProductVersion", "1.22\0" + END + END +END + +IDC_MARGIN CURSOR DISCARDABLE "Margin.cur" diff --git a/win32/ScintillaWin.cxx b/win32/ScintillaWin.cxx new file mode 100644 index 000000000..fd4126249 --- /dev/null +++ b/win32/ScintillaWin.cxx @@ -0,0 +1,1349 @@ +// Scintilla source code edit control +// ScintillaWin.cxx - Windows specific subclass of ScintillaBase +// Copyright 1998-2000 by Neil Hodgson <neilh@scintilla.org> +// The License.txt file describes the conditions under which this software may be distributed. + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <ctype.h> + +#include "Platform.h" + +#include "Scintilla.h" +#ifdef SCI_LEXER +#include "SciLexer.h" +#include "PropSet.h" +#include "Accessor.h" +#include "KeyWords.h" +#endif +#include "ContractionState.h" +#include "SVector.h" +#include "CellBuffer.h" +#include "CallTip.h" +#include "KeyMap.h" +#include "Indicator.h" +#include "LineMarker.h" +#include "Style.h" +#include "AutoComplete.h" +#include "ViewStyle.h" +#include "Document.h" +#include "Editor.h" +#include "ScintillaBase.h" + +//#include "CElapsed.h" + +#ifndef SPI_GETWHEELSCROLLLINES +#define SPI_GETWHEELSCROLLLINES 104 +#endif + +#ifndef WM_IME_STARTCOMPOSITION +#include <imm.h> +#endif + +#include <commctrl.h> +#ifndef __BORLANDC__ +#include <zmouse.h> +#endif +#include <ole2.h> + +#ifndef MK_ALT +#define MK_ALT 32 +#endif + +// TOTAL_CONTROL ifdef surrounds code that will only work when ScintillaWin +// is derived from ScintillaBase (all features) rather than directly from Editor (lightweight editor). +#define TOTAL_CONTROL + +// GCC has trouble with the standard COM ABI so do it the old C way with explicit vtables. + +class ScintillaWin; // Forward declaration for COM interface subobjects + +class FormatEnumerator { +public: + void **vtbl; + int ref; + int pos; + FormatEnumerator(int pos_); +}; + +class DropSource { +public: + void **vtbl; + ScintillaWin *sci; + DropSource(); +}; + +class DataObject { +public: + void **vtbl; + ScintillaWin *sci; + DataObject(); +}; + +class DropTarget { +public: + void **vtbl; + ScintillaWin *sci; + DropTarget(); +}; + +class ScintillaWin : + public ScintillaBase { + + bool capturedMouse; + + UINT cfColumnSelect; + + DropSource ds; + DataObject dob; + DropTarget dt; + + static HINSTANCE hInstance; + + ScintillaWin(HWND hwnd); + virtual ~ScintillaWin(); + + virtual void Initialise(); + virtual void Finalise(); + + static LRESULT PASCAL SWndProc( + HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam); + static LRESULT PASCAL CTWndProc( + HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam); + + virtual void StartDrag(); + virtual LRESULT WndProc(UINT iMessage, WPARAM wParam, LPARAM lParam); + virtual LRESULT DefWndProc(UINT iMessage, WPARAM wParam, LPARAM lParam); + virtual void SetTicking(bool on); + virtual void SetMouseCapture(bool on); + virtual bool HaveMouseCapture(); + virtual void ScrollText(int linesToMove); + virtual void SetVerticalScrollPos(); + virtual void SetHorizontalScrollPos(); + virtual bool ModifyScrollBars(int nMax, int nPage); + virtual void NotifyChange(); + virtual void NotifyFocus(bool focus); + virtual void NotifyParent(SCNotification scn); + virtual void NotifyDoubleClick(Point pt, bool shift); + virtual void Copy(); + virtual void Paste(); + virtual void CreateCallTipWindow(PRectangle rc); + virtual void AddToPopUp(const char *label, int cmd = 0, bool enabled = true); + virtual void ClaimSelection(); + + // DBCS + void ImeStartComposition(); + void ImeEndComposition(); + + void GetIntelliMouseParameters(); + HGLOBAL GetSelText(); + void ScrollMessage(WPARAM wParam); + void HorizontalScrollMessage(WPARAM wParam); + void RealizeWindowPalette(bool inBackGround); + void FullPaint(); + +public: + // Implement IUnknown + STDMETHODIMP QueryInterface(REFIID riid, PVOID *ppv); + STDMETHODIMP_(ULONG)AddRef(); + STDMETHODIMP_(ULONG)Release(); + + // Implement IDropTarget + STDMETHODIMP DragEnter(LPDATAOBJECT pIDataSource, DWORD grfKeyState, + POINTL pt, PDWORD pdwEffect); + STDMETHODIMP DragOver(DWORD grfKeyState, POINTL pt, PDWORD pdwEffect); + STDMETHODIMP DragLeave(); + STDMETHODIMP Drop(LPDATAOBJECT pIDataSource, DWORD grfKeyState, + POINTL pt, PDWORD pdwEffect); + + // Implement important part of IDataObject + STDMETHODIMP GetData(FORMATETC *pFEIn, STGMEDIUM *pSTM); + + static void Register(HINSTANCE hInstance_); + friend class DropSource; + friend class DataObject; + friend class DropTarget; + bool DragIsRectangularOK(UINT fmt) { + return dragIsRectangle && (fmt == cfColumnSelect); + } +}; + +HINSTANCE ScintillaWin::hInstance = 0; + +ScintillaWin::ScintillaWin(HWND hwnd) { + + capturedMouse = false; + + // There does not seem to be a real standard for indicating that the clipboard contains a rectangular + // selection, so copy Developer Studio. + cfColumnSelect = ::RegisterClipboardFormat("MSDEVColumnSelect"); + + wMain = hwnd; + wDraw = hwnd; + + dob.sci = this; + ds.sci = this; + dt.sci = this; + + Initialise(); +} + +ScintillaWin::~ScintillaWin() {} + +void ScintillaWin::Initialise() { + // Initialize COM. If the app has already done this it will have + // no effect. If the app hasnt, we really shouldnt ask them to call + // it just so this internal feature works. + OleInitialize(NULL); +} + +void ScintillaWin::Finalise() { + ScintillaBase::Finalise(); + SetTicking(false); + RevokeDragDrop(wMain.GetID()); + OleUninitialize(); +} + +void ScintillaWin::StartDrag() { + DWORD dwEffect = 0; + dropWentOutside = true; + IDataObject *pDataObject = reinterpret_cast<IDataObject *>(&dob); + IDropSource *pDropSource = reinterpret_cast<IDropSource *>(&ds); + //Platform::DebugPrintf("About to DoDragDrop %x %x\n", pDataObject, pDropSource); + HRESULT hr = DoDragDrop( + pDataObject, + pDropSource, + DROPEFFECT_COPY | DROPEFFECT_MOVE, &dwEffect); + //Platform::DebugPrintf("DoDragDrop = %x\n", hr); + if (SUCCEEDED(hr)) { + if ((hr == DRAGDROP_S_DROP) && (dwEffect == DROPEFFECT_MOVE) && dropWentOutside) { + // Remove dragged out text + ClearSelection(); + } + } + inDragDrop = false; + SetDragPosition(invalidPosition); +} + +// Avoid warnings everywhere for old style casts by conecntrating them here +static WORD LoWord(DWORD l) { + return LOWORD(l); +} + +static WORD HiWord(DWORD l) { + return HIWORD(l); +} + +LRESULT ScintillaWin::WndProc(UINT iMessage, WPARAM wParam, LPARAM lParam) { + switch (iMessage) { + + case WM_CREATE: + ctrlID = wMain.GetDlgCtrlID(); + // Get Intellimouse scroll line parameters + GetIntelliMouseParameters(); + RegisterDragDrop(wMain.GetID(), reinterpret_cast<IDropTarget *>(&dt)); + break; + + case WM_COMMAND: +#ifdef TOTAL_CONTROL + if (LoWord(wParam) == idAutoComplete) { + int cmd = HiWord(wParam); + if (cmd == LBN_DBLCLK) { + AutoCompleteCompleted(); + } else { + if (cmd != LBN_SETFOCUS) + SetFocus(wMain.GetID()); + } + } + Command(LoWord(wParam)); +#endif + break; + + case WM_PAINT: { + //CElapsed ce; ce.Begin(); + paintState = painting; + PAINTSTRUCT ps; + BeginPaint(wMain.GetID(), &ps); + Surface surfaceWindow; + surfaceWindow.Init(ps.hdc); + rcPaint = PRectangle(ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right, ps.rcPaint.bottom); + PRectangle rcText = GetTextRectangle(); + paintingAllText = rcPaint.Contains(rcText); + if (paintingAllText) { + //Platform::DebugPrintf("Performing full text paint\n"); + } else { + //Platform::DebugPrintf("Performing partial paint %d .. %d\n", rcPaint.top, rcPaint.bottom); + } + Paint(&surfaceWindow, rcPaint); + surfaceWindow.Release(); + EndPaint(wMain.GetID(), &ps); + if (paintState == paintAbandoned) { + // Painting area was insufficient to cover new styling or brace highlight positions + FullPaint(); + } + paintState = notPainting; + //Platform::DebugPrintf("Paint took %g\n", ce.End()); + } + break; + + case WM_VSCROLL: + ScrollMessage(wParam); + break; + + case WM_HSCROLL: + HorizontalScrollMessage(wParam); + break; + + case WM_SIZE: { + //Platform::DebugPrintf("S start wnd proc %d %d %d\n",iMessage, wParam, lParam); + PRectangle rsClient(0, 0, LoWord(lParam), HiWord(lParam)); + SetScrollBarsTo(rsClient); + DropGraphics(); + } + break; + + case WM_MOUSEWHEEL: + // Don't handle datazoom. + // (A good idea for datazoom would be to "fold" or "unfold" details. + // i.e. if datazoomed out only class structures are visible, when datazooming in the control + // structures appear, then eventually the individual statements...) + if (wParam & MK_SHIFT) { + return DefWindowProc(wMain.GetID(), iMessage, wParam, lParam); + } + + // Either SCROLL or ZOOM. We handle the wheel steppings calculation + cWheelDelta -= static_cast<short>(HiWord(wParam)); + if (abs(cWheelDelta) >= WHEEL_DELTA && ucWheelScrollLines > 0) { + int cLineScroll; + cLineScroll = ucWheelScrollLines; + if (cLineScroll == 0) { + cLineScroll++; + } + cLineScroll *= (cWheelDelta / WHEEL_DELTA); + cWheelDelta = cWheelDelta % WHEEL_DELTA; + + if (wParam & MK_CONTROL) { + // Zoom! We play with the font sizes in the styles. + // Number of steps/line is ignored, we just care if sizing up or down + if (cLineScroll < 0) { + KeyCommand(SCI_ZOOMIN); + } else { + KeyCommand(SCI_ZOOMOUT); + } + } else { + // Scroll + ScrollTo(topLine + cLineScroll); + } + } + return 0; + + case WM_TIMER: + Tick(); + break; + + case WM_GETMINMAXINFO: + return DefWindowProc(wMain.GetID(), iMessage, wParam, lParam); + + case WM_LBUTTONDOWN: + //Platform::DebugPrintf("Buttdown %d %x %x %x %x %x\n",iMessage, wParam, lParam, + // Platform::IsKeyDown(VK_SHIFT), + // Platform::IsKeyDown(VK_CONTROL), + // Platform::IsKeyDown(VK_MENU)); + ButtonDown(Point::FromLong(lParam), GetTickCount(), + wParam & MK_SHIFT, wParam & MK_CONTROL, Platform::IsKeyDown(VK_MENU)); + SetFocus(wMain.GetID()); + break; + + case WM_MOUSEMOVE: + ButtonMove(Point::FromLong(lParam)); + break; + + case WM_LBUTTONUP: + ButtonUp(Point::FromLong(lParam), GetTickCount(), wParam & MK_CONTROL); + break; + + case WM_SETCURSOR: + if (LoWord(lParam) == HTCLIENT) { + if (inDragDrop) { + wDraw.SetCursor(Window::cursorUp); + } else { + // Display regular (drag) cursor over selection + POINT pt; + ::GetCursorPos(&pt); + ::ScreenToClient(wMain.GetID(), &pt); + if (PointInSelMargin(Point(pt.x, pt.y))) { + wDraw.SetCursor(Window::cursorReverseArrow); + } else if (PointInSelection(Point(pt.x, pt.y))) { + wDraw.SetCursor(Window::cursorArrow); + } else { + wDraw.SetCursor(Window::cursorText); + } + } + return TRUE; + } else + return DefWindowProc(wMain.GetID(), iMessage, wParam, lParam); + + case WM_CHAR: + //Platform::DebugPrintf("S char proc %d %x %x\n",iMessage, wParam, lParam); + if (!iscntrl(wParam&0xff)) + AddChar(static_cast<char>(wParam&0xff)); + return 1; + + case WM_KEYDOWN: + //Platform::DebugPrintf("S keydown %d %x %x %x %x\n",iMessage, wParam, lParam, ::IsKeyDown(VK_SHIFT), ::IsKeyDown(VK_CONTROL)); + return KeyDown(wParam, Platform::IsKeyDown(VK_SHIFT), + Platform::IsKeyDown(VK_CONTROL), false); + + case WM_KEYUP: + //Platform::DebugPrintf("S keyup %d %x %x\n",iMessage, wParam, lParam); + break; + + case WM_SETTINGCHANGE: + //Platform::DebugPrintf("Setting Changed\n"); + InvalidateStyleData(); + // Get Intellimouse scroll line parameters + GetIntelliMouseParameters(); + break; + + case WM_GETDLGCODE: + return DLGC_HASSETSEL | DLGC_WANTALLKEYS; + + case WM_KILLFOCUS: + NotifyFocus(false); + DropCaret(); + //RealizeWindowPalette(true); + break; + + case WM_SETFOCUS: + NotifyFocus(true); + ShowCaretAtCurrentPosition(); + RealizeWindowPalette(false); + break; + + case WM_SYSCOLORCHANGE: + //Platform::DebugPrintf("Setting Changed\n"); + InvalidateStyleData(); + break; + + case WM_PALETTECHANGED: + if (wParam != reinterpret_cast<unsigned int>(wMain.GetID())) { + //Platform::DebugPrintf("** Palette Changed\n"); + RealizeWindowPalette(true); + } + break; + + case WM_QUERYNEWPALETTE: + //Platform::DebugPrintf("** Query palette\n"); + RealizeWindowPalette(false); + break; + + case WM_IME_STARTCOMPOSITION: // dbcs + ImeStartComposition(); + return DefWindowProc(wMain.GetID(), iMessage, wParam, lParam); + + case WM_IME_ENDCOMPOSITION: // dbcs + ImeEndComposition(); + return DefWindowProc(wMain.GetID(), iMessage, wParam, lParam); + + case WM_CONTEXTMENU: +#ifdef TOTAL_CONTROL + ContextMenu(Point::FromLong(lParam)); +#endif + break; + + case EM_CANPASTE: { + OpenClipboard(wMain.GetID()); + HGLOBAL hmemSelection = GetClipboardData(CF_TEXT); + if (hmemSelection) + GlobalUnlock(hmemSelection); + CloseClipboard(); + return hmemSelection != 0; + } + + case EM_SCROLL: { + int topStart = topLine; + ScrollMessage(wParam); + return MAKELONG(topLine - topStart, TRUE); + } + + default: + return ScintillaBase::WndProc(iMessage, wParam, lParam); + } + return 0l; +} + +LRESULT ScintillaWin::DefWndProc(UINT iMessage, WPARAM wParam, LPARAM lParam) { + return ::DefWindowProc(wMain.GetID(), iMessage, wParam, lParam); +} + +void ScintillaWin::SetTicking(bool on) { + if (timer.ticking != on) { + timer.ticking = on; + if (timer.ticking) { + timer.tickerID = ::SetTimer(wMain.GetID(), 1, timer.tickSize, NULL); + } else { + ::KillTimer(wMain.GetID(), timer.tickerID); + timer.tickerID = 0; + } + } + timer.ticksToWait = caret.period; +} + +void ScintillaWin::SetMouseCapture(bool on) { + if (on) { + ::SetCapture(wMain.GetID()); + } else { + ::ReleaseCapture(); + } + capturedMouse = on; +} + +bool ScintillaWin::HaveMouseCapture() { + // Cannot just see if GetCapture is this window as the scroll bar also sets capture for the window + return capturedMouse && (::GetCapture() == wMain.GetID()); +} + +void ScintillaWin::ScrollText(int linesToMove) { + //Platform::DebugPrintf("ScintillaWin::ScrollText %d\n", linesToMove); + ::ScrollWindow(wMain.GetID(), 0, + vs.lineHeight * (linesToMove), 0, 0); + ::UpdateWindow(wMain.GetID()); +} + +void ScintillaWin::SetVerticalScrollPos() { + ::SetScrollPos(wMain.GetID(), SB_VERT, topLine, TRUE); +} + +void ScintillaWin::SetHorizontalScrollPos() { + ::SetScrollPos(wMain.GetID(), SB_HORZ, xOffset, TRUE); +} + +bool ScintillaWin::ModifyScrollBars(int nMax, int nPage) { + bool modified = false; + SCROLLINFO sci = { + sizeof(sci) + }; + sci.fMask = SIF_PAGE | SIF_RANGE; + BOOL bz = ::GetScrollInfo(wMain.GetID(), SB_VERT, &sci); + if ((sci.nMin != 0) || (sci.nMax != pdoc->LinesTotal()) || + (sci.nPage != (pdoc->LinesTotal() - MaxScrollPos() + 1)) || + (sci.nPos != 0)) { + //Platform::DebugPrintf("Scroll info changed %d %d %d %d %d\n", + // sci.nMin, sci.nMax, sci.nPage, sci.nPos, sci.nTrackPos); + sci.fMask = SIF_PAGE | SIF_RANGE; + sci.nMin = 0; + sci.nMax = nMax; + sci.nPage = nPage; + sci.nPos = 0; + sci.nTrackPos = 1; + ::SetScrollInfo(wMain.GetID(), SB_VERT, &sci, TRUE); + modified = true; + } + int horizStart = 0; + int horizEnd = 2000; + if (!::GetScrollRange(wMain.GetID(), SB_HORZ, &horizStart, &horizEnd) || + horizStart != 0 || horizEnd != 2000) { + ::SetScrollRange(wMain.GetID(), SB_HORZ, 0, 2000, TRUE); + //Platform::DebugPrintf("Horiz Scroll info changed\n"); + modified = true; + } + return modified; +} + +void ScintillaWin::NotifyChange() { + ::SendMessage(GetParent(wMain.GetID()), WM_COMMAND, + MAKELONG(wMain.GetDlgCtrlID(), EN_CHANGE), + reinterpret_cast<LPARAM>(wMain.GetID())); +} + +void ScintillaWin::NotifyFocus(bool focus) { + ::SendMessage(GetParent(wMain.GetID()), WM_COMMAND, + MAKELONG(wMain.GetDlgCtrlID(), focus ? EN_SETFOCUS : EN_KILLFOCUS), + reinterpret_cast<LPARAM>(wMain.GetID())); +} + +void ScintillaWin::NotifyParent(SCNotification scn) { + scn.nmhdr.hwndFrom = wMain.GetID(); + scn.nmhdr.idFrom = ctrlID; + ::SendMessage(GetParent(wMain.GetID()), WM_NOTIFY, + wMain.GetDlgCtrlID(), reinterpret_cast<LPARAM>(&scn)); +} + +void ScintillaWin::NotifyDoubleClick(Point pt, bool shift) { + //Platform::DebugPrintf("ScintillaWin Double click 0\n"); + ScintillaBase::NotifyDoubleClick(pt, shift); + // Send myself a WM_LBUTTONDBLCLK, so the container can handle it too. + wMain.SendMessage(WM_LBUTTONDBLCLK, + shift ? MK_SHIFT : 0, + MAKELPARAM(pt.x, pt.y)); +} + +void ScintillaWin::Copy() { + //Platform::DebugPrintf("Copy\n"); + if (currentPos != anchor) { + HGLOBAL hmemSelection = GetSelText(); + ::OpenClipboard(wMain.GetID()); + ::EmptyClipboard(); + ::SetClipboardData(CF_TEXT, hmemSelection); + if (selType == selRectangle) { + ::SetClipboardData(cfColumnSelect, 0); + } + ::CloseClipboard(); + } +} + +void ScintillaWin::Paste() { + pdoc->BeginUndoAction(); + int selStart = SelectionStart(); + ClearSelection(); + ::OpenClipboard(wMain.GetID()); + bool isRectangular = ::IsClipboardFormatAvailable(cfColumnSelect); + HGLOBAL hmemSelection = ::GetClipboardData(CF_TEXT); + if (hmemSelection) { + char *ptr = static_cast<char *>( + ::GlobalLock(hmemSelection)); + if (ptr) { + unsigned int bytes = ::GlobalSize(hmemSelection); + unsigned int len = bytes; + for (unsigned int i = 0; i < bytes; i++) { + if ((len == bytes) && (0 == ptr[i])) + len = i; + } + if (isRectangular) { + PasteRectangular(selStart, ptr, len); + } else { + pdoc->InsertString(currentPos, ptr, len); + SetEmptySelection(currentPos + len); + } + } + ::GlobalUnlock(hmemSelection); + } + ::CloseClipboard(); + pdoc->EndUndoAction(); + NotifyChange(); + Redraw(); +} + +void ScintillaWin::CreateCallTipWindow(PRectangle) { +#ifdef TOTAL_CONTROL + ct.wCallTip = ::CreateWindow(callClassName, "ACallTip", + WS_VISIBLE | WS_CHILD, 100, 100, 150, 20, + wDraw.GetID(), reinterpret_cast<HMENU>(idCallTip), wDraw.GetInstance(), &ct); + ct.wDraw = ct.wCallTip; +#endif +} + +void ScintillaWin::AddToPopUp(const char *label, int cmd, bool enabled) { +#ifdef TOTAL_CONTROL + if (!label[0]) + ::AppendMenu(popup.GetID(), MF_SEPARATOR, 0, ""); + else if (enabled) + ::AppendMenu(popup.GetID(), MF_STRING, cmd, label); + else + ::AppendMenu(popup.GetID(), MF_STRING | MF_DISABLED | MF_GRAYED, cmd, label); +#endif +} + +void ScintillaWin::ClaimSelection() { + // Windows does not have a primary selection +} + +// Implement IUnknown + +STDMETHODIMP_(ULONG)FormatEnumerator_AddRef(FormatEnumerator *fe); +STDMETHODIMP FormatEnumerator_QueryInterface(FormatEnumerator *fe, REFIID riid, PVOID *ppv) { + //Platform::DebugPrintf("EFE QI"); + *ppv = NULL; + if (riid == IID_IUnknown) + *ppv = reinterpret_cast<IEnumFORMATETC *>(fe); + if (riid == IID_IEnumFORMATETC) + *ppv = reinterpret_cast<IEnumFORMATETC *>(fe); + if (!*ppv) + return E_NOINTERFACE; + FormatEnumerator_AddRef(fe); + return S_OK; +} +STDMETHODIMP_(ULONG)FormatEnumerator_AddRef(FormatEnumerator *fe) { + return ++fe->ref; +} +STDMETHODIMP_(ULONG)FormatEnumerator_Release(FormatEnumerator *fe) { + fe->ref--; + if (fe->ref > 0) + return fe->ref; + delete fe; + return 0; +} +// Implement IEnumFORMATETC +STDMETHODIMP FormatEnumerator_Next(FormatEnumerator *fe, ULONG celt, FORMATETC *rgelt, ULONG *pceltFetched) { + //Platform::DebugPrintf("EFE Next %d %d", fe->pos, celt); + if (rgelt == NULL) return E_POINTER; + // We only support one format, so this is simple. + unsigned int putPos = 0; + while ((fe->pos < 1) && (putPos < celt)) { + rgelt->cfFormat = CF_TEXT; + rgelt->ptd = 0; + rgelt->dwAspect = DVASPECT_CONTENT; + rgelt->lindex = -1; + rgelt->tymed = TYMED_HGLOBAL; + fe->pos++; + putPos++; + } + if (pceltFetched) + *pceltFetched = putPos; + return putPos ? S_OK : S_FALSE; +} +STDMETHODIMP FormatEnumerator_Skip(FormatEnumerator *fe, ULONG celt) { + fe->pos += celt; + return S_OK; +} +STDMETHODIMP FormatEnumerator_Reset(FormatEnumerator *fe) { + fe->pos = 0; + return S_OK; +} +STDMETHODIMP FormatEnumerator_Clone(FormatEnumerator *fe, IEnumFORMATETC **ppenum) { + FormatEnumerator *pfe = new FormatEnumerator(fe->pos); + return FormatEnumerator_QueryInterface(pfe, IID_IEnumFORMATETC, + reinterpret_cast<void **>(ppenum)); +} + +static void *vtFormatEnumerator[] = { + FormatEnumerator_QueryInterface, + FormatEnumerator_AddRef, + FormatEnumerator_Release, + FormatEnumerator_Next, + FormatEnumerator_Skip, + FormatEnumerator_Reset, + FormatEnumerator_Clone +}; + +FormatEnumerator::FormatEnumerator(int pos_) { + vtbl = vtFormatEnumerator; + ref = 0; // First QI adds first reference... + pos = pos_; +} + +// Implement IUnknown +STDMETHODIMP DropSource_QueryInterface(DropSource *ds, REFIID riid, PVOID *ppv) { + return ds->sci->QueryInterface(riid, ppv); +} +STDMETHODIMP_(ULONG)DropSource_AddRef(DropSource *ds) { + return ds->sci->AddRef(); +} +STDMETHODIMP_(ULONG)DropSource_Release(DropSource *ds) { + return ds->sci->Release(); +} + +// Implement IDropSource +STDMETHODIMP DropSource_QueryContinueDrag(DropSource *, BOOL fEsc, DWORD grfKeyState) { + if (fEsc) + return DRAGDROP_S_CANCEL; + if (!(grfKeyState & MK_LBUTTON)) + return DRAGDROP_S_DROP; + return S_OK; +} + +STDMETHODIMP DropSource_GiveFeedback(DropSource *, DWORD) { + return DRAGDROP_S_USEDEFAULTCURSORS; +} + +static void *vtDropSource[] = { + DropSource_QueryInterface, + DropSource_AddRef, + DropSource_Release, + DropSource_QueryContinueDrag, + DropSource_GiveFeedback + }; + +DropSource::DropSource() { + vtbl = vtDropSource; + sci = 0; +} + +// Implement IUnkown +STDMETHODIMP DataObject_QueryInterface(DataObject *pd, REFIID riid, PVOID *ppv) { + //Platform::DebugPrintf("DO QI %x\n", pd); + return pd->sci->QueryInterface(riid, ppv); +} +STDMETHODIMP_(ULONG)DataObject_AddRef(DataObject *pd) { + return pd->sci->AddRef(); +} +STDMETHODIMP_(ULONG)DataObject_Release(DataObject *pd) { + return pd->sci->Release(); +} +// Implement IDataObject +STDMETHODIMP DataObject_GetData(DataObject *pd, FORMATETC *pFEIn, STGMEDIUM *pSTM) { + return pd->sci->GetData(pFEIn, pSTM); +} + +STDMETHODIMP DataObject_GetDataHere(DataObject *, FORMATETC *, STGMEDIUM *) { + //Platform::DebugPrintf("DOB GetDataHere\n"); + return E_NOTIMPL; +} + +STDMETHODIMP DataObject_QueryGetData(DataObject *pd, FORMATETC *pFE) { + if (pd->sci->DragIsRectangularOK(pFE->cfFormat) && + pFE->ptd == 0 && + (pFE->dwAspect & DVASPECT_CONTENT) != 0 && + pFE->lindex == -1 && + (pFE->tymed & TYMED_HGLOBAL) != 0 + ) { + return S_OK; + } + + if ( + ((pFE->cfFormat != CF_TEXT) && (pFE->cfFormat != CF_HDROP)) || + pFE->ptd != 0 || + (pFE->dwAspect & DVASPECT_CONTENT) == 0 || + pFE->lindex != -1 || + (pFE->tymed & TYMED_HGLOBAL) == 0 + ) { + //Platform::DebugPrintf("DOB QueryGetData No %x\n",pFE->cfFormat); + //return DATA_E_FORMATETC; + return S_FALSE; + } + //Platform::DebugPrintf("DOB QueryGetData OK %x\n",pFE->cfFormat); + return S_OK; +} + +STDMETHODIMP DataObject_GetCanonicalFormatEtc(DataObject *, FORMATETC *, FORMATETC *pFEOut) { + //Platform::DebugPrintf("DOB GetCanon\n"); + pFEOut->cfFormat = CF_TEXT; + pFEOut->ptd = 0; + pFEOut->dwAspect = DVASPECT_CONTENT; + pFEOut->lindex = -1; + pFEOut->tymed = TYMED_HGLOBAL; + return S_OK; +} + +STDMETHODIMP DataObject_SetData(DataObject *, FORMATETC *, STGMEDIUM *, BOOL) { + //Platform::DebugPrintf("DOB SetData\n"); + return E_FAIL; +} + +STDMETHODIMP DataObject_EnumFormatEtc(DataObject *, DWORD dwDirection, IEnumFORMATETC **ppEnum) { + //Platform::DebugPrintf("DOB EnumFormatEtc %d\n", dwDirection); + if (dwDirection != DATADIR_GET) { + *ppEnum = 0; + return E_FAIL; + } + FormatEnumerator *pfe = new FormatEnumerator(0); + return FormatEnumerator_QueryInterface(pfe, IID_IEnumFORMATETC, + reinterpret_cast<void **>(ppEnum)); +} + +STDMETHODIMP DataObject_DAdvise(DataObject *, FORMATETC *, DWORD, IAdviseSink *, PDWORD) { + //Platform::DebugPrintf("DOB DAdvise\n"); + return E_FAIL; +} + +STDMETHODIMP DataObject_DUnadvise(DataObject *, DWORD) { + //Platform::DebugPrintf("DOB DUnadvise\n"); + return E_FAIL; +} + +STDMETHODIMP DataObject_EnumDAdvise(DataObject *, IEnumSTATDATA **) { + //Platform::DebugPrintf("DOB EnumDAdvise\n"); + return E_FAIL; +} + +static void *vtDataObject[] = { + DataObject_QueryInterface, + DataObject_AddRef, + DataObject_Release, + DataObject_GetData, + DataObject_GetDataHere, + DataObject_QueryGetData, + DataObject_GetCanonicalFormatEtc, + DataObject_SetData, + DataObject_EnumFormatEtc, + DataObject_DAdvise, + DataObject_DUnadvise, + DataObject_EnumDAdvise +}; + +DataObject::DataObject() { + vtbl = vtDataObject; + sci = 0; +} + +// Implement IUnknown +STDMETHODIMP DropTarget_QueryInterface(DropTarget *dt, REFIID riid, PVOID *ppv) { + //Platform::DebugPrintf("DT QI %x\n", dt); + return dt->sci->QueryInterface(riid, ppv); +} +STDMETHODIMP_(ULONG)DropTarget_AddRef(DropTarget *dt) { + return dt->sci->AddRef(); +} +STDMETHODIMP_(ULONG)DropTarget_Release(DropTarget *dt) { + return dt->sci->Release(); +} + +// Implement IDropTarget by forwarding to Scintilla +STDMETHODIMP DropTarget_DragEnter(DropTarget *dt, LPDATAOBJECT pIDataSource, DWORD grfKeyState, + POINTL pt, PDWORD pdwEffect) { + return dt->sci->DragEnter(pIDataSource, grfKeyState, pt, pdwEffect); +} +STDMETHODIMP DropTarget_DragOver(DropTarget *dt, DWORD grfKeyState, POINTL pt, PDWORD pdwEffect) { + return dt->sci->DragOver(grfKeyState, pt, pdwEffect); +} +STDMETHODIMP DropTarget_DragLeave(DropTarget *dt) { + return dt->sci->DragLeave(); +} +STDMETHODIMP DropTarget_Drop(DropTarget *dt, LPDATAOBJECT pIDataSource, DWORD grfKeyState, + POINTL pt, PDWORD pdwEffect) { + return dt->sci->Drop(pIDataSource, grfKeyState, pt, pdwEffect); +} + +static void *vtDropTarget[] = { + DropTarget_QueryInterface, + DropTarget_AddRef, + DropTarget_Release, + DropTarget_DragEnter, + DropTarget_DragOver, + DropTarget_DragLeave, + DropTarget_Drop +}; + +DropTarget::DropTarget() { + vtbl = vtDropTarget; + sci = 0; +} + +// DBCS: support Input Method Editor (IME) +// Called when IME Window opened. +void ScintillaWin::ImeStartComposition() { + if (caret.active) { + // Move IME Window to current caret position + HIMC hIMC = ::ImmGetContext(wMain.GetID()); + Point pos = LocationFromPosition(currentPos); + COMPOSITIONFORM CompForm; + CompForm.dwStyle = CFS_POINT; + CompForm.ptCurrentPos.x = pos.x; + CompForm.ptCurrentPos.y = pos.y; + + ::ImmSetCompositionWindow(hIMC, &CompForm); + + // Set font of IME window to same as surrounded text. + if (stylesValid) { + // Since the style creation code has been made platform independent, + // The logfont for the IME is recreated here. + int styleHere = (pdoc->StyleAt(currentPos)) & 31; + LOGFONT lf = {0}; + int sizeZoomed = vs.styles[styleHere].size + vs.zoomLevel; + if (sizeZoomed <= 2) // Hangs if sizeZoomed <= 1 + sizeZoomed = 2; + Surface surface; + int deviceHeight = (sizeZoomed * surface.LogPixelsY()) / 72; + // The negative is to allow for leading + lf.lfHeight = -(abs(deviceHeight)); + lf.lfWeight = vs.styles[styleHere].bold ? FW_BOLD : FW_NORMAL; + lf.lfItalic = vs.styles[styleHere].italic ? 1 : 0; + lf.lfCharSet = DEFAULT_CHARSET; + strcpy(lf.lfFaceName, vs.styles[styleHere].fontName); + + ::ImmSetCompositionFont(hIMC, &lf); + ::ImmReleaseContext(wMain.GetID(), hIMC); + } + // Caret is displayed in IME window. So, caret in Scintilla is useless. + DropCaret(); + } +} + +// Called when IME Window closed. +void ScintillaWin::ImeEndComposition() { + ShowCaretAtCurrentPosition(); +} + +void ScintillaWin::GetIntelliMouseParameters() { + // This retrieves the number of lines per scroll as configured inthe Mouse Properties sheet in Control Panel + ::SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &ucWheelScrollLines, 0); +} + +HGLOBAL ScintillaWin::GetSelText() { + int bytes = SelectionRangeLength(); + + HGLOBAL hand = ::GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, + bytes + 1); + if (hand) { + char *ptr = static_cast<char *>(::GlobalLock(hand)); + char *selChars = CopySelectionRange(); + if (selChars) { + memcpy(ptr, selChars, bytes); + delete []selChars; + //for (int i = 0; i < bytes; i++) { + // ptr[i] = pdoc->CharAt(startPos + i); + //} + } + ptr[bytes] = '\0'; + ::GlobalUnlock(hand); + } + return hand; +} + +void ScintillaWin::ScrollMessage(WPARAM wParam) { + //DWORD dwStart = timeGetTime(); + //Platform::DebugPrintf("Scroll %x %d\n", wParam, lParam); + + SCROLLINFO sci; + memset(&sci, 0, sizeof(sci)); + sci.cbSize = sizeof(sci); + sci.fMask = SIF_ALL; + + BOOL b = ::GetScrollInfo(wMain.GetID(), SB_VERT, &sci); + + //Platform::DebugPrintf("ScrollInfo %d mask=%x min=%d max=%d page=%d pos=%d track=%d\n", b,sci.fMask, + //sci.nMin, sci.nMax, sci.nPage, sci.nPos, sci.nTrackPos); + + int topLineNew = topLine; + switch (LoWord(wParam)) { + case SB_LINEUP: + topLineNew -= 1; + break; + case SB_LINEDOWN: + topLineNew += 1; + break; + case SB_PAGEUP: + topLineNew -= LinesToScroll(); break; + case SB_PAGEDOWN: topLineNew += LinesToScroll(); break; + case SB_TOP: topLineNew = 0; break; + case SB_BOTTOM: topLineNew = MaxScrollPos(); break; + case SB_THUMBPOSITION: topLineNew = sci.nTrackPos; break; + case SB_THUMBTRACK: topLineNew = sci.nTrackPos; break; + } + ScrollTo(topLineNew); +} + +void ScintillaWin::HorizontalScrollMessage(WPARAM wParam) { + int xPos = xOffset; + switch (LoWord(wParam)) { + case SB_LINEUP: + xPos -= 20; + break; + case SB_LINEDOWN: + xPos += 20; + break; + case SB_PAGEUP: + xPos -= 200; + break; + case SB_PAGEDOWN: + xPos += 200; + break; + case SB_TOP: + xPos = 0; + break; + case SB_BOTTOM: + xPos = 2000; + break; + case SB_THUMBPOSITION: + xPos = HiWord(wParam); + break; + case SB_THUMBTRACK: + xPos = HiWord(wParam); + break; + } + HorizontalScrollTo(xPos); +} + +void ScintillaWin::RealizeWindowPalette(bool inBackGround) { + RefreshStyleData(); + Surface surfaceWindow; + HDC hdc = ::GetDC(wMain.GetID()); + surfaceWindow.Init(hdc); + int changes = surfaceWindow.SetPalette(&palette, inBackGround); + if (changes > 0) + Redraw(); + surfaceWindow.Release(); + ::ReleaseDC(wMain.GetID(), hdc); +} + +// Redraw all of text area. This paint will not be abandoned. +void ScintillaWin::FullPaint() { + paintState = painting; + rcPaint = GetTextRectangle(); + paintingAllText = true; + HDC hdc = ::GetDC(wMain.GetID()); + Surface surfaceWindow; + surfaceWindow.Init(hdc); + Paint(&surfaceWindow, rcPaint); + surfaceWindow.Release(); + ::ReleaseDC(wMain.GetID(), hdc); + paintState = notPainting; +} + +// Implement IUnknown +STDMETHODIMP ScintillaWin::QueryInterface(REFIID riid, PVOID *ppv) { + *ppv = NULL; + if (riid == IID_IUnknown) + *ppv = reinterpret_cast<IDropTarget *>(&dt); + if (riid == IID_IDropSource) + *ppv = reinterpret_cast<IDropSource *>(&ds); + if (riid == IID_IDropTarget) + *ppv = reinterpret_cast<IDropTarget *>(&dt); + if (riid == IID_IDataObject) + *ppv = reinterpret_cast<IDataObject *>(&dob); + if (!*ppv) + return E_NOINTERFACE; + return S_OK; +} + +STDMETHODIMP_(ULONG) ScintillaWin::AddRef() { + return 1; +} + +STDMETHODIMP_(ULONG) ScintillaWin::Release() { + return 1; +} + +// Implement IDropTarget +STDMETHODIMP ScintillaWin::DragEnter(LPDATAOBJECT, DWORD grfKeyState, + POINTL, PDWORD pdwEffect) { + if (inDragDrop) // Internal defaults to move + *pdwEffect = DROPEFFECT_MOVE; + else + *pdwEffect = DROPEFFECT_COPY; + if (grfKeyState & MK_ALT) + *pdwEffect = DROPEFFECT_MOVE; + if (grfKeyState & MK_CONTROL) + *pdwEffect = DROPEFFECT_COPY; + return S_OK; +} + +STDMETHODIMP ScintillaWin::DragOver(DWORD grfKeyState, POINTL pt, PDWORD pdwEffect) { + // These are the Wordpad semantics. + if (inDragDrop) // Internal defaults to move + *pdwEffect = DROPEFFECT_MOVE; + else + *pdwEffect = DROPEFFECT_COPY; + if (grfKeyState & MK_ALT) + *pdwEffect = DROPEFFECT_MOVE; + if (grfKeyState & MK_CONTROL) + *pdwEffect = DROPEFFECT_COPY; + // Update the cursor. + POINT rpt = {pt.x, pt.y}; + ::ScreenToClient(wMain.GetID(), &rpt); + SetDragPosition(PositionFromLocation(Point(rpt.x, rpt.y))); + + return S_OK; +} + +STDMETHODIMP ScintillaWin::DragLeave() { + SetDragPosition(invalidPosition); + return S_OK; +} + +STDMETHODIMP ScintillaWin::Drop(LPDATAOBJECT pIDataSource, DWORD grfKeyState, + POINTL pt, PDWORD pdwEffect) { + if (inDragDrop) // Internal defaults to move + *pdwEffect = DROPEFFECT_MOVE; + else + *pdwEffect = DROPEFFECT_COPY; + if (grfKeyState & MK_ALT) + *pdwEffect = DROPEFFECT_MOVE; + if (grfKeyState & MK_CONTROL) + *pdwEffect = DROPEFFECT_COPY; + + if (pIDataSource == NULL) + return E_POINTER; + + SetDragPosition(invalidPosition); + + FORMATETC fmte = {CF_TEXT, + NULL, + DVASPECT_CONTENT, + -1, + TYMED_HGLOBAL + }; + STGMEDIUM medium; + + HRESULT hres = pIDataSource->GetData(&fmte, &medium); + if (FAILED(hres)) { + //Platform::DebugPrintf("Bad data format: 0x%x\n", hres); + return hres; + } + if (medium.hGlobal == 0) { + return E_OUTOFMEMORY; + } + char *data = static_cast<char *>(::GlobalLock(medium.hGlobal)); + + FORMATETC fmtr = {cfColumnSelect, + NULL, + DVASPECT_CONTENT, + -1, + TYMED_HGLOBAL + }; + HRESULT hrRectangular = pIDataSource->QueryGetData(&fmtr); + + POINT rpt = {pt.x, pt.y}; + ::ScreenToClient(wMain.GetID(), &rpt); + Point npt(rpt.x, rpt.y); + int movePos = PositionFromLocation(Point(rpt.x, rpt.y)); + + DropAt(movePos, data, *pdwEffect == DROPEFFECT_MOVE, hrRectangular == S_OK); + + ::GlobalUnlock(medium.hGlobal); + + // Free data + if (medium.pUnkForRelease != NULL) + medium.pUnkForRelease->Release(); + + return S_OK; +} + +// Implement important part of IDataObject +STDMETHODIMP ScintillaWin::GetData(FORMATETC *pFEIn, STGMEDIUM *pSTM) { + if ( + ((pFEIn->cfFormat != CF_TEXT) && (pFEIn->cfFormat != CF_HDROP)) || + pFEIn->ptd != 0 || + (pFEIn->dwAspect & DVASPECT_CONTENT) == 0 || + pFEIn->lindex != -1 || + (pFEIn->tymed & TYMED_HGLOBAL) == 0 + ) { + //Platform::DebugPrintf("DOB GetData No %d %x %x fmt=%x\n", lenDrag, pFEIn, pSTM, pFEIn->cfFormat); + return DATA_E_FORMATETC; + } + pSTM->tymed = TYMED_HGLOBAL; + if (pFEIn->cfFormat == CF_HDROP) { + pSTM->hGlobal = 0; + pSTM->pUnkForRelease = 0; + return S_OK; + } + //Platform::DebugPrintf("DOB GetData OK %d %x %x\n", lenDrag, pFEIn, pSTM); + + HGLOBAL hand = ::GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, + lenDrag + 1); + if (hand) { + char *ptr = static_cast<char *>(::GlobalLock(hand)); + for (int i = 0; i < lenDrag; i++) { + ptr[i] = dragChars[i]; + } + ptr[lenDrag] = '\0'; + ::GlobalUnlock(hand); + } + pSTM->hGlobal = hand; + pSTM->pUnkForRelease = 0; + return S_OK; +} + +const char scintillaClassName[] = "Scintilla"; + +void ScintillaWin::Register(HINSTANCE hInstance_) { + + hInstance = hInstance_; + + InitCommonControls(); + + WNDCLASS wndclass; + + // Register the Scintilla class + + wndclass.style = CS_GLOBALCLASS | CS_HREDRAW | CS_VREDRAW; + wndclass.lpfnWndProc = ::ScintillaWin::SWndProc; + wndclass.cbClsExtra = 0; + // Reserve extra bytes for each instance of the window; + // we will use these bytes to store a pointer to the C++ + // (ScintillaWin) object corresponding to the window. + wndclass.cbWndExtra = sizeof(ScintillaWin *); + wndclass.hInstance = hInstance; + wndclass.hIcon = NULL; + //wndclass.hCursor = LoadCursor(NULL,IDC_IBEAM); + wndclass.hCursor = NULL; + wndclass.hbrBackground = NULL; + wndclass.lpszMenuName = NULL; + wndclass.lpszClassName = scintillaClassName; + + if (!RegisterClass(&wndclass)) { + //Platform::DebugPrintf("Could not register class\n"); + // TODO: fail nicely + return; + } + + // Register the CallTip class + + wndclass.lpfnWndProc = ScintillaWin::CTWndProc; + wndclass.hCursor = LoadCursor(NULL, IDC_ARROW); + wndclass.lpszClassName = callClassName; + + if (!RegisterClass(&wndclass)) { + //Platform::DebugPrintf("Could not register class\n"); + return; + } +} + +LRESULT PASCAL ScintillaWin::CTWndProc( + HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam) { + + // Find C++ object associated with window. + CallTip *ctp = reinterpret_cast<CallTip *>(GetWindowLong(hWnd, 0)); + // ctp will be zero if WM_CREATE not seen yet + if (ctp == 0) { + if (iMessage == WM_CREATE) { + // Associate CallTip object with window + CREATESTRUCT *pCreate = reinterpret_cast<CREATESTRUCT *>(lParam); + SetWindowLong(hWnd, 0, + reinterpret_cast<LONG>(pCreate->lpCreateParams)); + return 0; + } else { + return DefWindowProc(hWnd, iMessage, wParam, lParam); + } + } else { + if (iMessage == WM_DESTROY) { + SetWindowLong(hWnd, 0, 0); + return DefWindowProc(hWnd, iMessage, wParam, lParam); + } else if (iMessage == WM_PAINT) { + PAINTSTRUCT ps; + ::BeginPaint(hWnd, &ps); + Surface surfaceWindow; + surfaceWindow.Init(ps.hdc); + ctp->PaintCT(&surfaceWindow); + surfaceWindow.Release(); + ::EndPaint(hWnd, &ps); + return 0; + } else { + return DefWindowProc(hWnd, iMessage, wParam, lParam); + } + } +} + +LRESULT PASCAL ScintillaWin::SWndProc( + HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam) { + //Platform::DebugPrintf("S W:%x M:%d WP:%x L:%x\n", hWnd, iMessage, wParam, lParam); + + // Find C++ object associated with window. + ScintillaWin *sci = reinterpret_cast<ScintillaWin *>(GetWindowLong(hWnd, 0)); + // sci will be zero if WM_CREATE not seen yet + if (sci == 0) { + if (iMessage == WM_CREATE) { + // Create C++ object associated with window + sci = new ScintillaWin(hWnd); + SetWindowLong(hWnd, 0, reinterpret_cast<LONG>(sci)); + return sci->WndProc(iMessage, wParam, lParam); + } else { + return DefWindowProc(hWnd, iMessage, wParam, lParam); + } + } else { + if (iMessage == WM_DESTROY) { + sci->Finalise(); + delete sci; + SetWindowLong(hWnd, 0, 0); + return DefWindowProc(hWnd, iMessage, wParam, lParam); + } else { + return sci->WndProc(iMessage, wParam, lParam); + } + } +} + +// This function is externally visible so it can be called from container when building statically +void Scintilla_RegisterClasses(HINSTANCE hInstance) { + ScintillaWin::Register(hInstance); +} + +#ifndef STATIC_BUILD +extern "C" int APIENTRY DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID) { + //Platform::DebugPrintf("Scintilla::DllMain %d %d\n", hInstance, dwReason); + if (dwReason == DLL_PROCESS_ATTACH) { + Scintilla_RegisterClasses(hInstance); + } + return TRUE; +} +#endif diff --git a/win32/makefile b/win32/makefile new file mode 100644 index 000000000..0dfded5a9 --- /dev/null +++ b/win32/makefile @@ -0,0 +1,98 @@ +# Make file for Scintilla on Windows +# Copyright 1998-2000 by Neil Hodgson <neilh@scintilla.org> +# The License.txt file describes the conditions under which this software may be distributed. +# This makefile assumes the mingw32 version of GCC 2.95.2 is used and changes will +# be needed to use other compilers. + +.SUFFIXES: .cxx +CC = g++ +DLLWRAP = dllwrap + +COMPONENT = ../bin/Scintilla.dll +LEXCOMPONENT = ../bin/SciLexer.dll + +vpath %.h ../src ../include +vpath %.cxx ../src + +LDFLAGS = -lkernel32 -lgdi32 -luser32 -lwinmm -lcomdlg32 -lcomctl32 -limm32 -lole32 -luuid +#CXXFLAGS = -W -Wall +# Add -MMD to get dependencies +#CXXFLAGS = -g -pg -pedantic -Os -fno-exceptions -fvtable-thunks -fno-rtti +INCLUDEDIRS=-I ../include -I ../src +CXXFLAGS = -pedantic $(INCLUDEDIRS) -Os -fno-exceptions -fvtable-thunks -fno-rtti + +.cxx.o: + $(CC) $(CXXFLAGS) -c $< -o $@ + +ALL: $(COMPONENT) $(LEXCOMPONENT) ScintillaWinS.o + +clean: + del /q *.exe *.o *.obj *.dll *.res *.map + +SOBJS = ScintillaWin.o ScintillaBase.o Editor.o Document.o \ + ContractionState.o CellBuffer.o CallTip.o \ + ScintRes.o PlatWin.o KeyMap.o Indicator.o LineMarker.o Style.o \ + ViewStyle.o AutoComplete.o +$(COMPONENT): $(SOBJS) + $(DLLWRAP) --target i386-mingw32 -o $(COMPONENT) $(SOBJS) $(LDFLAGS) -s --relocatable + +LOBJS = ScintillaWinL.o ScintillaBaseL.o Editor.o Document.o \ + ContractionState.o CellBuffer.o CallTip.o \ + ScintRes.o PlatWin.o KeyMap.o Indicator.o LineMarker.o Style.o \ + ViewStyle.o AutoComplete.o KeyWords.o Accessor.o PropSet.o +$(LEXCOMPONENT): $(LOBJS) + $(DLLWRAP) --target i386-mingw32 -o $(LEXCOMPONENT) $(LOBJS) $(LDFLAGS) -s --relocatable + +Accessor.o: Accessor.cxx Platform.h PropSet.h Accessor.h Scintilla.h +AutoComplete.o: AutoComplete.cxx Platform.h AutoComplete.h +CallTip.o: CallTip.cxx Platform.h CallTip.h +CellBuffer.o: CellBuffer.cxx Platform.h Scintilla.h CellBuffer.h +ContractionState.o: ContractionState.cxx Platform.h ContractionState.h +Document.o: Document.cxx Platform.h Scintilla.h CellBuffer.h \ + Document.h +Editor.o: Editor.cxx Platform.h Scintilla.h ContractionState.h \ + CellBuffer.h KeyMap.h Indicator.h LineMarker.h Style.h ViewStyle.h \ + Document.h Editor.h +Indicator.o: Indicator.cxx Platform.h Scintilla.h Indicator.h +KeyMap.o: KeyMap.cxx Platform.h Scintilla.h KeyMap.h +KeyWords.o: KeyWords.cxx Platform.h PropSet.h Accessor.h KeyWords.h \ + Scintilla.h SciLexer.h +LineMarker.o: LineMarker.cxx Platform.h Scintilla.h LineMarker.h +PlatWin.o: PlatWin.cxx Platform.h PlatformRes.h +PropSet.o: PropSet.cxx Platform.h PropSet.h +ScintillaBase.o: ScintillaBase.cxx Platform.h Scintilla.h \ + ContractionState.h CellBuffer.h CallTip.h KeyMap.h Indicator.h \ + LineMarker.h Style.h ViewStyle.h AutoComplete.h Document.h Editor.h \ + ScintillaBase.h +ScintillaBaseL.o: ScintillaBase.cxx Platform.h Scintilla.h SciLexer.h \ + ContractionState.h CellBuffer.h CallTip.h KeyMap.h Indicator.h \ + LineMarker.h Style.h AutoComplete.h ViewStyle.h Document.h Editor.h \ + ScintillaBase.h PropSet.h Accessor.h KeyWords.h +ScintillaWin.o: ScintillaWin.cxx Platform.h Scintilla.h \ + ContractionState.h CellBuffer.h CallTip.h KeyMap.h Indicator.h \ + LineMarker.h Style.h AutoComplete.h ViewStyle.h Document.h Editor.h \ + ScintillaBase.h +ScintillaWinL.o: ScintillaWin.cxx Platform.h Scintilla.h SciLexer.h \ + ContractionState.h CellBuffer.h CallTip.h KeyMap.h Indicator.h \ + LineMarker.h Style.h AutoComplete.h ViewStyle.h Document.h Editor.h \ + ScintillaBase.h PropSet.h Accessor.h KeyWords.h +ScintillaWinS.o: ScintillaWin.cxx Platform.h Scintilla.h \ + ContractionState.h CellBuffer.h CallTip.h KeyMap.h Indicator.h \ + LineMarker.h Style.h AutoComplete.h ViewStyle.h Document.h Editor.h \ + ScintillaBase.h +Style.o: Style.cxx Platform.h Style.h +ViewStyle.o: ViewStyle.cxx Platform.h Scintilla.h Indicator.h \ + LineMarker.h Style.h ViewStyle.h + +ScintillaBaseL.o: + $(CC) $(CXXFLAGS) -D SCI_LEXER -c $< -o ScintillaBaseL.o + +ScintillaWinS.o: + $(CC) $(CXXFLAGS) -D STATIC_BUILD -c $< -o ScintillaWinS.o + +ScintillaWinL.o: + $(CC) $(CXXFLAGS) -D SCI_LEXER -c $< -o ScintillaWinL.o + +ScintRes.o: ScintRes.rc PlatformRes.h + windres ScintRes.rc ScintRes.o + diff --git a/win32/makefile_bor b/win32/makefile_bor new file mode 100644 index 000000000..0b2cda968 --- /dev/null +++ b/win32/makefile_bor @@ -0,0 +1,120 @@ +# Make file for Scintilla on Windows Borland C++ Builder version +# Copyright 1998-2000 by Neil Hodgson <neilh@scintilla.org> +# The License.txt file describes the conditions under which this software may be distributed. +# This makefile is for using Visual C++ and nmake. +# The main makefile uses mingw32 gcc and may be more current than this file. + +.SUFFIXES: .cxx +CC = bcc32 +RC = brcc32 +LD = tlink32 + +COMPONENT = ..\bin\Scintilla.dll +LEXCOMPONENT = ..\bin\SciLexer.dll + +LDFLAGS = import32 cw32mt +INCLUDEDIRS=-I../include -I../src +CXXFLAGS = -P -tW -w -RT- -x- -v + +.cxx.obj: + $(CC) $(INCLUDEDIRS) $(CXXFLAGS) -c $*.cxx + +.rc.res: + $(RC) /r $*.rc + +ALL: $(COMPONENT) $(LEXCOMPONENT) ScintillaWinS.obj + +clean: + del /q *.exe *.o *.obj *.dll *.res *.map + +SOBJS = ScintillaWin.obj ScintillaBase.obj Editor.obj Document.obj \ + ContractionState.obj CellBuffer.obj CallTip.obj \ + PlatWin.obj KeyMap.obj Indicator.obj LineMarker.obj Style.obj \ + ViewStyle.obj AutoComplete.obj +$(COMPONENT): $(SOBJS) ScintRes.res + $(LD) -Tpd /c c0d32 $(SOBJS), $(COMPONENT), ,$(LDFLAGS), , ScintRes.res + +LOBJS = ScintillaWinL.obj ScintillaBaseL.obj Editor.obj Document.obj \ + ContractionState.obj CellBuffer.obj CallTip.obj \ + PlatWin.obj KeyMap.obj Indicator.obj LineMarker.obj Style.obj \ + ViewStyle.obj AutoComplete.obj KeyWords.obj Accessor.obj PropSet.obj +$(LEXCOMPONENT): $(LOBJS) + $(LD) -Tpd /c c0d32 $(LOBJS), $(LEXCOMPONENT), ,$(LDFLAGS), , ScintRes.res + +Accessor.obj: ..\src\Accessor.cxx ..\include\Platform.h ..\include\PropSet.h ..\include\Accessor.h ..\include\Scintilla.h + $(CC) $(INCLUDEDIRS) $(CXXFLAGS) -c ..\src\$*.cxx -o$* + +AutoComplete.obj: ..\src\AutoComplete.cxx ..\include\Platform.h ..\src\AutoComplete.h + $(CC) $(INCLUDEDIRS) $(CXXFLAGS) -c ..\src\$*.cxx -o$* + +CallTip.obj: ..\src\CallTip.cxx ..\include\Platform.h ..\src\CallTip.h + $(CC) $(INCLUDEDIRS) $(CXXFLAGS) -c ..\src\$*.cxx -o$* + +CellBuffer.obj: ..\src\CellBuffer.cxx ..\include\Platform.h ..\include\Scintilla.h ..\src\CellBuffer.h + $(CC) $(INCLUDEDIRS) $(CXXFLAGS) -c ..\src\$*.cxx -o$* + +ContractionState.obj: ..\src\ContractionState.cxx ..\include\Platform.h ..\src\ContractionState.h + $(CC) $(INCLUDEDIRS) $(CXXFLAGS) -c ..\src\$*.cxx -o$* + +Document.obj: ..\src\Document.cxx ..\include\Platform.h ..\include\Scintilla.h ..\src\CellBuffer.h \ + ..\src\Document.h + $(CC) $(INCLUDEDIRS) $(CXXFLAGS) -c ..\src\$*.cxx -o$* + +Editor.obj: ..\src\Editor.cxx ..\include\Platform.h ..\include\Scintilla.h ..\src\ContractionState.h \ + ..\src\CellBuffer.h ..\src\KeyMap.h ..\src\Indicator.h ..\src\LineMarker.h ..\src\Style.h ..\src\ViewStyle.h \ + ..\src\Document.h ..\src\Editor.h + $(CC) $(INCLUDEDIRS) $(CXXFLAGS) -c ..\src\$*.cxx -o$* + +Indicator.obj: ..\src\Indicator.cxx ..\include\Platform.h ..\include\Scintilla.h ..\src\Indicator.h + $(CC) $(INCLUDEDIRS) $(CXXFLAGS) -c ..\src\$*.cxx -o$* + +KeyMap.obj: ..\src\KeyMap.cxx ..\include\Platform.h ..\include\Scintilla.h ..\src\KeyMap.h + $(CC) $(INCLUDEDIRS) $(CXXFLAGS) -c ..\src\$*.cxx -o$* + +KeyWords.obj: ..\src\KeyWords.cxx ..\include\Platform.h ..\include\PropSet.h ..\include\Accessor.h ..\include\KeyWords.h \ + ..\include\Scintilla.h ..\include\SciLexer.h + $(CC) $(INCLUDEDIRS) $(CXXFLAGS) -c ..\src\$*.cxx -o$* + +LineMarker.obj: ..\src\LineMarker.cxx ..\include\Platform.h ..\include\Scintilla.h ..\src\LineMarker.h + $(CC) $(INCLUDEDIRS) $(CXXFLAGS) -c ..\src\$*.cxx -o$* + +PlatWin.obj: PlatWin.cxx ..\include\Platform.h PlatformRes.h + +PropSet.obj: ..\src\PropSet.cxx ..\include\Platform.h ..\include\PropSet.h + $(CC) $(INCLUDEDIRS) $(CXXFLAGS) -c ..\src\$*.cxx -o$* + +ScintillaBase.obj: ..\src\ScintillaBase.cxx ..\include\Platform.h ..\include\Scintilla.h \ + ..\src\ContractionState.h ..\src\CellBuffer.h ..\src\CallTip.h ..\src\KeyMap.h ..\src\Indicator.h \ + ..\src\LineMarker.h ..\src\Style.h ..\src\ViewStyle.h ..\src\AutoComplete.h ..\src\Document.h ..\src\Editor.h \ + ..\src\ScintillaBase.h + $(CC) $(INCLUDEDIRS) $(CXXFLAGS) -c ..\src\ScintillaBase.cxx -o$@ + +ScintillaBaseL.obj: ..\src\ScintillaBase.cxx ..\include\Platform.h ..\include\Scintilla.h ..\include\SciLexer.h \ + ..\src\ContractionState.h ..\src\CellBuffer.h ..\src\CallTip.h ..\src\KeyMap.h ..\src\Indicator.h \ + ..\src\LineMarker.h ..\src\Style.h ..\src\AutoComplete.h ..\src\ViewStyle.h ..\src\Document.h ..\src\Editor.h \ + ..\src\ScintillaBase.h ..\include\PropSet.h ..\include\Accessor.h ..\include\KeyWords.h + $(CC) $(INCLUDEDIRS) $(CXXFLAGS) /DSCI_LEXER -o$* /c ..\src\ScintillaBase.cxx + +ScintillaWin.obj: ScintillaWin.cxx ..\include\Platform.h ..\include\Scintilla.h \ + ..\src\ContractionState.h ..\src\CellBuffer.h ..\src\CallTip.h ..\src\KeyMap.h ..\src\Indicator.h \ + ..\src\LineMarker.h ..\src\Style.h ..\src\AutoComplete.h ..\src\ViewStyle.h ..\src\Document.h ..\src\Editor.h \ + ..\src\ScintillaBase.h + +ScintillaWinL.obj: ScintillaWin.cxx ..\include\Platform.h ..\include\Scintilla.h ..\include\SciLexer.h \ + ..\src\ContractionState.h ..\src\CellBuffer.h ..\src\CallTip.h ..\src\KeyMap.h ..\src\Indicator.h \ + ..\src\LineMarker.h ..\src\Style.h ..\src\AutoComplete.h ..\src\ViewStyle.h ..\src\Document.h ..\src\Editor.h \ + ..\src\ScintillaBase.h ..\include\PropSet.h ..\include\Accessor.h ..\include\KeyWords.h + $(CC) $(INCLUDEDIRS) $(CXXFLAGS) /DSCI_LEXER -o$* /c ScintillaWin.cxx + +ScintillaWinS.obj: ScintillaWin.cxx ..\include\Platform.h ..\include\Scintilla.h \ + ..\src\ContractionState.h ..\src\CellBuffer.h ..\src\CallTip.h ..\src\KeyMap.h ..\src\Indicator.h \ + ..\src\LineMarker.h ..\src\Style.h ..\src\AutoComplete.h ..\src\ViewStyle.h ..\src\Document.h ..\src\Editor.h \ + ..\src\ScintillaBase.h + $(CC) $(INCLUDEDIRS) $(CXXFLAGS) /DSTATIC_BUILD -o$* /c ScintillaWin.cxx + +Style.obj: ..\src\Style.cxx ..\include\Platform.h ..\src\Style.h + $(CC) $(INCLUDEDIRS) $(CXXFLAGS) -c ..\src\$*.cxx -o$@ + +ViewStyle.obj: ..\src\ViewStyle.cxx ..\include\Platform.h ..\include\Scintilla.h ..\src\Indicator.h \ + ..\src\LineMarker.h ..\src\Style.h ..\src\ViewStyle.h + $(CC) $(INCLUDEDIRS) $(CXXFLAGS) -c ..\src\$*.cxx -o$@ diff --git a/win32/makefile_vc b/win32/makefile_vc new file mode 100644 index 000000000..32a52a385 --- /dev/null +++ b/win32/makefile_vc @@ -0,0 +1,125 @@ +# Make file for Scintilla on Windows Visual C++ version +# Copyright 1998-2000 by Neil Hodgson <neilh@scintilla.org> +# The License.txt file describes the conditions under which this software may be distributed. +# This makefile is for using Visual C++ and nmake. +# The main makefile uses mingw32 gcc and may be more current than this file. + +.SUFFIXES: .cxx +CC = cl +RC = rc +LD = link + +COMPONENT = ../bin/Scintilla.dll +LEXCOMPONENT = ../bin/SciLexer.dll + +LDFLAGS = /NODEFAULTLIB:LIBC KERNEL32.lib USER32.lib GDI32.lib COMDLG32.lib WINMM.lib COMCTL32.lib ADVAPI32.lib IMM32.lib SHELL32.LIB OLE32.LIB +INCLUDEDIRS=-I ../include -I ../src +CXXFLAGS = /TP /MD /Ox + +!IFDEF DEBUG +CXXFLAGS=$(CXXFLAGS) /Zi +LDFLAGS=/DEBUG $(LDFLAGS) +!ENDIF + +.cxx.obj: + $(CC) $(INCLUDEDIRS) $(CXXFLAGS) -c $< -o $@ + +.rc.res: + $(RC) $*.rc + +ALL: $(COMPONENT) $(LEXCOMPONENT) ScintillaWinS.obj + +clean: + del /q *.exe *.o *.obj *.dll *.res *.map + +SOBJS = ScintillaWin.obj ScintillaBase.obj Editor.obj Document.obj \ + ContractionState.obj CellBuffer.obj CallTip.obj \ + PlatWin.obj KeyMap.obj Indicator.obj LineMarker.obj Style.obj \ + ViewStyle.obj AutoComplete.obj +$(COMPONENT): $(SOBJS) ScintRes.res + $(LD) /DLL /OUT:$(COMPONENT) $(SOBJS) ScintRes.res $(LDFLAGS) + +LOBJS = ScintillaWinL.obj ScintillaBaseL.obj Editor.obj Document.obj \ + ContractionState.obj CellBuffer.obj CallTip.obj \ + PlatWin.obj KeyMap.obj Indicator.obj LineMarker.obj Style.obj \ + ViewStyle.obj AutoComplete.obj KeyWords.obj Accessor.obj PropSet.obj +$(LEXCOMPONENT): $(LOBJS) + $(LD) /DLL /OUT:$(LEXCOMPONENT) $(LOBJS) ScintRes.res $(LDFLAGS) + +Accessor.obj: ..\src\Accessor.cxx ..\include\Platform.h ..\include\PropSet.h ..\include\Accessor.h ..\include\Scintilla.h + $(CC) $(INCLUDEDIRS) $(CXXFLAGS) -c ..\src\$*.cxx -o $@ + +AutoComplete.obj: ..\src\AutoComplete.cxx ..\include\Platform.h ..\src\AutoComplete.h + $(CC) $(INCLUDEDIRS) $(CXXFLAGS) -c ..\src\$*.cxx -o $@ + +CallTip.obj: ..\src\CallTip.cxx ..\include\Platform.h ..\src\CallTip.h + $(CC) $(INCLUDEDIRS) $(CXXFLAGS) -c ..\src\$*.cxx -o $@ + +CellBuffer.obj: ..\src\CellBuffer.cxx ..\include\Platform.h ..\include\Scintilla.h ..\src\CellBuffer.h + $(CC) $(INCLUDEDIRS) $(CXXFLAGS) -c ..\src\$*.cxx -o $@ + +ContractionState.obj: ..\src\ContractionState.cxx ..\include\Platform.h ..\src\ContractionState.h + $(CC) $(INCLUDEDIRS) $(CXXFLAGS) -c ..\src\$*.cxx -o $@ + +Document.obj: ..\src\Document.cxx ..\include\Platform.h ..\include\Scintilla.h ..\src\CellBuffer.h \ + ..\src\Document.h + $(CC) $(INCLUDEDIRS) $(CXXFLAGS) -c ..\src\$*.cxx -o $@ + +Editor.obj: ..\src\Editor.cxx ..\include\Platform.h ..\include\Scintilla.h ..\src\ContractionState.h \ + ..\src\CellBuffer.h ..\src\KeyMap.h ..\src\Indicator.h ..\src\LineMarker.h ..\src\Style.h ..\src\ViewStyle.h \ + ..\src\Document.h ..\src\Editor.h + $(CC) $(INCLUDEDIRS) $(CXXFLAGS) -c ..\src\$*.cxx -o $@ + +Indicator.obj: ..\src\Indicator.cxx ..\include\Platform.h ..\include\Scintilla.h ..\src\Indicator.h + $(CC) $(INCLUDEDIRS) $(CXXFLAGS) -c ..\src\$*.cxx -o $@ + +KeyMap.obj: ..\src\KeyMap.cxx ..\include\Platform.h ..\include\Scintilla.h ..\src\KeyMap.h + $(CC) $(INCLUDEDIRS) $(CXXFLAGS) -c ..\src\$*.cxx -o $@ + +KeyWords.obj: ..\src\KeyWords.cxx ..\include\Platform.h ..\include\PropSet.h ..\include\Accessor.h ..\include\KeyWords.h \ + ..\include\Scintilla.h ..\include\SciLexer.h + $(CC) $(INCLUDEDIRS) $(CXXFLAGS) -c ..\src\$*.cxx -o $@ + +LineMarker.obj: ..\src\LineMarker.cxx ..\include\Platform.h ..\include\Scintilla.h ..\src\LineMarker.h + $(CC) $(INCLUDEDIRS) $(CXXFLAGS) -c ..\src\$*.cxx -o $@ + +PlatWin.obj: PlatWin.cxx ..\include\Platform.h PlatformRes.h + +PropSet.obj: ..\src\PropSet.cxx ..\include\Platform.h ..\include\PropSet.h + $(CC) $(INCLUDEDIRS) $(CXXFLAGS) -c ..\src\$*.cxx -o $@ + +ScintillaBase.obj: ..\src\ScintillaBase.cxx ..\include\Platform.h ..\include\Scintilla.h \ + ..\src\ContractionState.h ..\src\CellBuffer.h ..\src\CallTip.h ..\src\KeyMap.h ..\src\Indicator.h \ + ..\src\LineMarker.h ..\src\Style.h ..\src\ViewStyle.h ..\src\AutoComplete.h ..\src\Document.h ..\src\Editor.h \ + ..\src\ScintillaBase.h + $(CC) $(INCLUDEDIRS) $(CXXFLAGS) -c ..\src\ScintillaBase.cxx -o $@ + +ScintillaBaseL.obj: ..\src\ScintillaBase.cxx ..\include\Platform.h ..\include\Scintilla.h ..\include\SciLexer.h \ + ..\src\ContractionState.h ..\src\CellBuffer.h ..\src\CallTip.h ..\src\KeyMap.h ..\src\Indicator.h \ + ..\src\LineMarker.h ..\src\Style.h ..\src\AutoComplete.h ..\src\ViewStyle.h ..\src\Document.h ..\src\Editor.h \ + ..\src\ScintillaBase.h ..\include\PropSet.h ..\include\Accessor.h ..\include\KeyWords.h + $(CC) $(INCLUDEDIRS) $(CXXFLAGS) /D SCI_LEXER /c ..\src\ScintillaBase.cxx /Fo$@ + +ScintillaWin.obj: ScintillaWin.cxx ..\include\Platform.h ..\include\Scintilla.h \ + ..\src\ContractionState.h ..\src\CellBuffer.h ..\src\CallTip.h ..\src\KeyMap.h ..\src\Indicator.h \ + ..\src\LineMarker.h ..\src\Style.h ..\src\AutoComplete.h ..\src\ViewStyle.h ..\src\Document.h ..\src\Editor.h \ + ..\src\ScintillaBase.h + +ScintillaWinL.obj: ScintillaWin.cxx ..\include\Platform.h ..\include\Scintilla.h ..\include\SciLexer.h \ + ..\src\ContractionState.h ..\src\CellBuffer.h ..\src\CallTip.h ..\src\KeyMap.h ..\src\Indicator.h \ + ..\src\LineMarker.h ..\src\Style.h ..\src\AutoComplete.h ..\src\ViewStyle.h ..\src\Document.h ..\src\Editor.h \ + ..\src\ScintillaBase.h ..\include\PropSet.h ..\include\Accessor.h ..\include\KeyWords.h + $(CC) $(INCLUDEDIRS) $(CXXFLAGS) /D SCI_LEXER /c ScintillaWin.cxx /Fo$@ + +ScintillaWinS.obj: ScintillaWin.cxx ..\include\Platform.h ..\include\Scintilla.h \ + ..\src\ContractionState.h ..\src\CellBuffer.h ..\src\CallTip.h ..\src\KeyMap.h ..\src\Indicator.h \ + ..\src\LineMarker.h ..\src\Style.h ..\src\AutoComplete.h ..\src\ViewStyle.h ..\src\Document.h ..\src\Editor.h \ + ..\src\ScintillaBase.h + $(CC) $(INCLUDEDIRS) $(CXXFLAGS) /D STATIC_BUILD /c ScintillaWin.cxx /Fo$@ + +Style.obj: ..\src\Style.cxx ..\include\Platform.h ..\src\Style.h + $(CC) $(INCLUDEDIRS) $(CXXFLAGS) -c ..\src\$*.cxx -o $@ + +ViewStyle.obj: ..\src\ViewStyle.cxx ..\include\Platform.h ..\include\Scintilla.h ..\src\Indicator.h \ + ..\src\LineMarker.h ..\src\Style.h ..\src\ViewStyle.h + $(CC) $(INCLUDEDIRS) $(CXXFLAGS) -c ..\src\$*.cxx -o $@ |