Artifact a020b84c7ac8dc1d01d7225119984beef2271313d1974d8d06ed4f28f3a58ace:
- Executable file
r38/lisp/csl/cslbase/FXMathText.cpp
— part of check-in
[f2fda60abd]
at
2011-09-02 18:13:33
on branch master
— Some historical releases purely for archival purposes
git-svn-id: https://svn.code.sf.net/p/reduce-algebra/code/trunk/historical@1375 2bfe0521-f11c-4a00-b80e-6202646ff360 (user: arthurcnorman@users.sourceforge.net, size: 151824) [annotate] [blame] [check-ins using] [more...]
// This version is starting off as just a copy of FXText.cpp that I can // patch and change to support variable-height lines. I may merge // my changes back into the original version sometime or I may fork this // as a genuine new file - I do not know yet how things will work out! // This was originally from FOX 1.2.9. // // I am really getting to think that the scope of changes I will need to make // here will make it a clone-and-hack job rather than an inherit-and-override, // if only because of delicacies about just what would remain in the base // class and what would need re-implementing because not very much is // made virtual. // // // Arthur Norman, September 2004 // January 2006 // January 2007 /* Signature: 493645c4 18-Jan-2007 */ /******************************************************************************** * * * M u l t i - L i ne T e x t O b j e c t * * * ********************************************************************************* * Copyright (C) 1998,2005 by Jeroen van der Zijp. All Rights Reserved. * ********************************************************************************* * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Lesser General Public * * License as published by the Free Software Foundation; * * version 2.1 of the License. * * * * This library is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. * ********************************************************************************* * $Id: FXMathText.cpp,v 1.299 2005/01/16 16:06:07 fox Exp $ * ********************************************************************************/ #include "config.h" #include "xincs.h" #include "fxver.h" #if FOX_MAJOR>1 || (FOX_MAJOR==1 && FOX_MINOR>=4) // Fox 1.4.x is different from 1.2.x. I have not checked which // of these the 1.3.x releases follow! #define FOX14 1 #endif #include "fxdefs.h" #include "fxkeys.h" #include "FXHash.h" #ifdef FOX14 #include "FXThread.h" #endif #include "FXStream.h" #include "FXString.h" #include "FXRex.h" #include "FXSize.h" #include "FXPoint.h" #include "FXRectangle.h" #include "FXObject.h" #include "FXRegistry.h" #include "FXAccelTable.h" #include "FXApp.h" #include "FXDCWindow.h" #include "FXFont.h" #include "FXGIFIcon.h" #include "FXScrollBar.h" #include "FXMathText.h" #include "FXInputDialog.h" #include "FXReplaceDialog.h" #include "FXSearchDialog.h" #include "FXFile.h" #include "icons.h" #if (FOX_MINOR>4) // The following could possibly turn into fxunicode.h one day. #include "fxascii.h" #define isspace(ch) Ascii::isSpace(ch) #define isdigit(ch) Ascii::isDigit(ch) #define toupper(ch) Ascii::toUpper(ch) #define tolower(ch) Ascii::toLower(ch) #endif /* ACN Notes re introduction of support for maths display: - Space for maths is made by (ab)using the existing scheme where a line of text can end up wrapped to form several rows. - mouse identification within maths is not yet addressed AT ALL. - a Maths line that (say) needs the spaec of 4 rows will be represented as 0x02 0x02 0x02 0x02 <data> 0x05 '\n' and each 0x02 is treated as starting a "row". Any draw operation steps up and draws all the rows involved. The <data> will not have and 0x02, 0x03 or '\n' bytes in it. - I will probably also want to mark the maths material with a style tag, but partly because I want to work with an existing interface that generates 0x02 and 0x05 to surround displayed maths I will start off using them rather than styles. */ /* Notes: - Line start array is one longer than number of visible lines. - We want both tab translated to spaces as well as tab-stops array. - Control characters in the buffer are OK (e.g. ^L) - Drag cursor should be same as normal one until drag starts! - Change of cursor only implies makePositionVisible() if done by user. - Breaking: Soft-hyphen 173 \xAD No break space 240 \xF0 - Buffer layout: Content : A B C . . . . . . . . D E F G Position : 0 1 2 3 4 5 6 length=7 Addresss : 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 buffersize=7+11-3=15 ^ ^ | | gapstart=3 gapend=11 gaplen=11-3=8 The gap is moved around the buffer so newly added text can be entered into the gap; when the gap becomes too small, the buffer is resized. This gapped-buffer technique minimizes the number of resizes of the buffer, and minimizes the number of block moves. The tail end of the visrows array will look like: visrow[0]= 0: "Q R S T U V W \n" visrow[1]= 8: "X Y Z" visrow[2]=11: <no text> visrow[3]=11: <no text> length = 11 The last legal position is length = 11. - While resizing window, keep track of a position which should remain visible, i.e. toppos=rowStart(position). The position is changed same as toppos, except during resize. - When changing text, if we're looking at the tail end of the buffer, avoid jumping the top lines when the content hight shrinks. - Add undo capability. (First undo will turn mod flag back off). - Add incremental search, search/replace, selection search. - Style table stuff. - Need to allow for one single routine to update style buffer same as text buffer - Suggested additional bindings: Ctl-F Find Ctl-R Replace Ctl-G Find again (Shift-Ctl-G Find again backward) Ctl-T Replace again (Shift-Ctl-G Replace again backward) Ctl-L Goto line number Ctl-E Goto selected Ctl-I Indent (shift 1 character right) Ctl-U Unindent (shift 1 character left) Ctl-Z undo Ctl-Y redo Ctl-M Goto matching (Shift-Ctl-M backward) Insert toggle overstrike mode Brace matching - Maybe put all keyboard bindings into accelerator table. - Variable cursorcol should take tabcolumns into account. - Italic fonts are bit problematic on border between selected/unselected text due to kerning. - Tab should work as tabcolumns columns when computing a column. - Need rectangular selection capability. - Perhaps split off buffer management into separate text buffer class (allows for multiple views). - Need to implement regex search/replace. - Add better support for subclassing (syntax coloring e.g.). - Add support for line numbers. - Improve book keeping based on line/column numbers, not rows/characters. - If there is a style table, the style buffer is used as index into the style table, allowing for up to 255 styles (style index==0 is the default style). The style member in the FXHiliteStyle struct is used for underlining, strikeouts, and other effects. If there is NO style table but there is a style buffer, the style buffer can still be used for underlining, strikeouts, and other effects. - Sending SEL_CHANGED is pretty useless; should only be sent AFTER text change, and void* should contain some sensible info. - When in overstrike mode and having a selection, entering a character should replace the selection, not delete the selection and then overstrike the character after the selection. - Middle mouse paste does not paste inside selection, and does not kill selection. - When pasting or dropping whole lines, insert at begin of line instead of at cursor; question:- how to know we're pasting whole lines? - Need block cursor when in overstrike mode. - Inserting lots of stuff should show cursor. */ #define MINSIZE 80 // Minimum gap size #define NVISROWS 24 // Initial visible rows #define TEXT_MASK (TEXT_FIXEDWRAP|TEXT_WORDWRAP|TEXT_COLUMNWRAP|TEXT_OVERSTRIKE|TEXT_READONLY|TEXT_NO_TABS|TEXT_AUTOINDENT|TEXT_SHOWACTIVE) using namespace FX; /*******************************************************************************/ namespace FX { // Map FXDEFMAP(FXMathText) FXMathTextMap[]={ // Well I will leave all the selectors here as the ones used with FXText... FXMAPFUNC(SEL_PAINT,0,FXMathText::onPaint), FXMAPFUNC(SEL_UPDATE,0,FXMathText::onUpdate), FXMAPFUNC(SEL_MOTION,0,FXMathText::onMotion), FXMAPFUNC(SEL_DRAGGED,0,FXMathText::onDragged), FXMAPFUNC(SEL_TIMEOUT,FXMathText::ID_BLINK,FXMathText::onBlink), FXMAPFUNC(SEL_TIMEOUT,FXMathText::ID_AUTOSCROLL,FXMathText::onAutoScroll), FXMAPFUNC(SEL_TIMEOUT,FXMathText::ID_FLASH,FXMathText::onFlash), FXMAPFUNC(SEL_FOCUSIN,0,FXMathText::onFocusIn), FXMAPFUNC(SEL_FOCUSOUT,0,FXMathText::onFocusOut), FXMAPFUNC(SEL_BEGINDRAG,0,FXMathText::onBeginDrag), FXMAPFUNC(SEL_ENDDRAG,0,FXMathText::onEndDrag), FXMAPFUNC(SEL_LEFTBUTTONPRESS,0,FXMathText::onLeftBtnPress), FXMAPFUNC(SEL_LEFTBUTTONRELEASE,0,FXMathText::onLeftBtnRelease), FXMAPFUNC(SEL_MIDDLEBUTTONPRESS,0,FXMathText::onMiddleBtnPress), FXMAPFUNC(SEL_MIDDLEBUTTONRELEASE,0,FXMathText::onMiddleBtnRelease), FXMAPFUNC(SEL_RIGHTBUTTONPRESS,0,FXMathText::onRightBtnPress), FXMAPFUNC(SEL_RIGHTBUTTONRELEASE,0,FXMathText::onRightBtnRelease), FXMAPFUNC(SEL_UNGRABBED,0,FXMathText::onUngrabbed), FXMAPFUNC(SEL_DND_ENTER,0,FXMathText::onDNDEnter), FXMAPFUNC(SEL_DND_LEAVE,0,FXMathText::onDNDLeave), FXMAPFUNC(SEL_DND_DROP,0,FXMathText::onDNDDrop), FXMAPFUNC(SEL_DND_MOTION,0,FXMathText::onDNDMotion), FXMAPFUNC(SEL_DND_REQUEST,0,FXMathText::onDNDRequest), FXMAPFUNC(SEL_SELECTION_LOST,0,FXMathText::onSelectionLost), FXMAPFUNC(SEL_SELECTION_GAINED,0,FXMathText::onSelectionGained), FXMAPFUNC(SEL_SELECTION_REQUEST,0,FXMathText::onSelectionRequest), FXMAPFUNC(SEL_CLIPBOARD_LOST,0,FXMathText::onClipboardLost), FXMAPFUNC(SEL_CLIPBOARD_GAINED,0,FXMathText::onClipboardGained), FXMAPFUNC(SEL_CLIPBOARD_REQUEST,0,FXMathText::onClipboardRequest), FXMAPFUNC(SEL_KEYPRESS,0,FXMathText::onKeyPress), FXMAPFUNC(SEL_KEYRELEASE,0,FXMathText::onKeyRelease), #ifdef FOX14 FXMAPFUNC(SEL_QUERY_TIP,0,FXMathText::onQueryTip), FXMAPFUNC(SEL_QUERY_HELP,0,FXMathText::onQueryHelp), #else FXMAPFUNC(SEL_UPDATE,FXMathText::ID_QUERY_HELP,FXMathText::onQueryHelp), FXMAPFUNC(SEL_UPDATE,FXMathText::ID_QUERY_TIP,FXMathText::onQueryTip), #endif FXMAPFUNC(SEL_UPDATE,FXMathText::ID_TOGGLE_EDITABLE,FXMathText::onUpdToggleEditable), FXMAPFUNC(SEL_UPDATE,FXMathText::ID_TOGGLE_OVERSTRIKE,FXMathText::onUpdToggleOverstrike), FXMAPFUNC(SEL_UPDATE,FXMathText::ID_CURSOR_ROW,FXMathText::onUpdCursorRow), FXMAPFUNC(SEL_UPDATE,FXMathText::ID_CURSOR_COLUMN,FXMathText::onUpdCursorColumn), FXMAPFUNC(SEL_UPDATE,FXMathText::ID_CUT_SEL,FXMathText::onUpdHaveSelection), FXMAPFUNC(SEL_UPDATE,FXMathText::ID_COPY_SEL,FXMathText::onUpdHaveSelection), FXMAPFUNC(SEL_UPDATE,FXMathText::ID_PASTE_SEL,FXMathText::onUpdYes), FXMAPFUNC(SEL_UPDATE,FXMathText::ID_DELETE_SEL,FXMathText::onUpdHaveSelection), FXMAPFUNC(SEL_UPDATE,FXMathText::ID_SELECT_ALL,FXMathText::onUpdSelectAll), FXMAPFUNC(SEL_UPDATE,FXMathText::ID_UPPER_CASE,FXMathText::onUpdHaveSelection), FXMAPFUNC(SEL_UPDATE,FXMathText::ID_LOWER_CASE,FXMathText::onUpdHaveSelection), FXMAPFUNC(SEL_COMMAND,FXMathText::ID_CURSOR_TOP,FXMathText::onCmdCursorTop), FXMAPFUNC(SEL_COMMAND,FXMathText::ID_CURSOR_BOTTOM,FXMathText::onCmdCursorBottom), FXMAPFUNC(SEL_COMMAND,FXMathText::ID_CURSOR_HOME,FXMathText::onCmdCursorHome), FXMAPFUNC(SEL_COMMAND,FXMathText::ID_CURSOR_END,FXMathText::onCmdCursorEnd), FXMAPFUNC(SEL_COMMAND,FXMathText::ID_CURSOR_RIGHT,FXMathText::onCmdCursorRight), FXMAPFUNC(SEL_COMMAND,FXMathText::ID_CURSOR_LEFT,FXMathText::onCmdCursorLeft), FXMAPFUNC(SEL_COMMAND,FXMathText::ID_CURSOR_UP,FXMathText::onCmdCursorUp), FXMAPFUNC(SEL_COMMAND,FXMathText::ID_CURSOR_DOWN,FXMathText::onCmdCursorDown), FXMAPFUNC(SEL_COMMAND,FXMathText::ID_CURSOR_WORD_LEFT,FXMathText::onCmdCursorWordLeft), FXMAPFUNC(SEL_COMMAND,FXMathText::ID_CURSOR_WORD_RIGHT,FXMathText::onCmdCursorWordRight), FXMAPFUNC(SEL_COMMAND,FXMathText::ID_CURSOR_WORD_START,FXMathText::onCmdCursorWordStart), FXMAPFUNC(SEL_COMMAND,FXMathText::ID_CURSOR_WORD_END,FXMathText::onCmdCursorWordEnd), FXMAPFUNC(SEL_COMMAND,FXMathText::ID_CURSOR_PAGEDOWN,FXMathText::onCmdCursorPageDown), FXMAPFUNC(SEL_COMMAND,FXMathText::ID_CURSOR_PAGEUP,FXMathText::onCmdCursorPageUp), FXMAPFUNC(SEL_COMMAND,FXMathText::ID_CURSOR_SCRNTOP,FXMathText::onCmdCursorScreenTop), FXMAPFUNC(SEL_COMMAND,FXMathText::ID_CURSOR_SCRNBTM,FXMathText::onCmdCursorScreenBottom), FXMAPFUNC(SEL_COMMAND,FXMathText::ID_CURSOR_SCRNCTR,FXMathText::onCmdCursorScreenCenter), FXMAPFUNC(SEL_COMMAND,FXMathText::ID_CURSOR_PAR_HOME,FXMathText::onCmdCursorParHome), FXMAPFUNC(SEL_COMMAND,FXMathText::ID_CURSOR_PAR_END,FXMathText::onCmdCursorParEnd), FXMAPFUNC(SEL_COMMAND,FXMathText::ID_SCROLL_UP,FXMathText::onCmdScrollUp), FXMAPFUNC(SEL_COMMAND,FXMathText::ID_SCROLL_DOWN,FXMathText::onCmdScrollDown), FXMAPFUNC(SEL_COMMAND,FXMathText::ID_MARK,FXMathText::onCmdMark), FXMAPFUNC(SEL_COMMAND,FXMathText::ID_EXTEND,FXMathText::onCmdExtend), FXMAPFUNC(SEL_COMMAND,FXMathText::ID_OVERST_STRING,FXMathText::onCmdOverstString), FXMAPFUNC(SEL_COMMAND,FXMathText::ID_INSERT_STRING,FXMathText::onCmdInsertString), FXMAPFUNC(SEL_COMMAND,FXMathText::ID_INSERT_NEWLINE,FXMathText::onCmdInsertNewline), FXMAPFUNC(SEL_COMMAND,FXMathText::ID_INSERT_TAB,FXMathText::onCmdInsertTab), FXMAPFUNC(SEL_COMMAND,FXMathText::ID_CUT_SEL,FXMathText::onCmdCutSel), FXMAPFUNC(SEL_COMMAND,FXMathText::ID_COPY_SEL,FXMathText::onCmdCopySel), FXMAPFUNC(SEL_COMMAND,FXMathText::ID_DELETE_SEL,FXMathText::onCmdDeleteSel), FXMAPFUNC(SEL_COMMAND,FXMathText::ID_PASTE_SEL,FXMathText::onCmdPasteSel), FXMAPFUNC(SEL_COMMAND,FXMathText::ID_PASTE_MIDDLE,FXMathText::onCmdPasteMiddle), FXMAPFUNC(SEL_COMMAND,FXMathText::ID_SELECT_CHAR,FXMathText::onCmdSelectChar), FXMAPFUNC(SEL_COMMAND,FXMathText::ID_SELECT_WORD,FXMathText::onCmdSelectWord), FXMAPFUNC(SEL_COMMAND,FXMathText::ID_SELECT_LINE,FXMathText::onCmdSelectLine), FXMAPFUNC(SEL_COMMAND,FXMathText::ID_SELECT_ALL,FXMathText::onCmdSelectAll), FXMAPFUNC(SEL_COMMAND,FXMathText::ID_DESELECT_ALL,FXMathText::onCmdDeselectAll), FXMAPFUNC(SEL_COMMAND,FXMathText::ID_BACKSPACE,FXMathText::onCmdBackspace), FXMAPFUNC(SEL_COMMAND,FXMathText::ID_BACKSPACE_WORD,FXMathText::onCmdBackspaceWord), FXMAPFUNC(SEL_COMMAND,FXMathText::ID_BACKSPACE_BOL,FXMathText::onCmdBackspaceBol), FXMAPFUNC(SEL_COMMAND,FXMathText::ID_DELETE,FXMathText::onCmdDelete), FXMAPFUNC(SEL_COMMAND,FXMathText::ID_DELETE_WORD,FXMathText::onCmdDeleteWord), FXMAPFUNC(SEL_COMMAND,FXMathText::ID_DELETE_EOL,FXMathText::onCmdDeleteEol), FXMAPFUNC(SEL_COMMAND,FXMathText::ID_DELETE_ALL,FXMathText::onCmdDeleteAll), FXMAPFUNC(SEL_COMMAND,FXMathText::ID_DELETE_LINE,FXMathText::onCmdDeleteLine), FXMAPFUNC(SEL_COMMAND,FXMathText::ID_TOGGLE_EDITABLE,FXMathText::onCmdToggleEditable), FXMAPFUNC(SEL_COMMAND,FXMathText::ID_TOGGLE_OVERSTRIKE,FXMathText::onCmdToggleOverstrike), FXMAPFUNC(SEL_COMMAND,FXMathText::ID_CURSOR_ROW,FXMathText::onCmdCursorRow), FXMAPFUNC(SEL_COMMAND,FXMathText::ID_CURSOR_COLUMN,FXMathText::onCmdCursorColumn), FXMAPFUNC(SEL_COMMAND,FXMathText::ID_SETSTRINGVALUE,FXMathText::onCmdSetStringValue), FXMAPFUNC(SEL_COMMAND,FXMathText::ID_GETSTRINGVALUE,FXMathText::onCmdGetStringValue), FXMAPFUNC(SEL_COMMAND,FXMathText::ID_UPPER_CASE,FXMathText::onCmdChangeCase), FXMAPFUNC(SEL_COMMAND,FXMathText::ID_LOWER_CASE,FXMathText::onCmdChangeCase), FXMAPFUNC(SEL_COMMAND,FXMathText::ID_GOTO_MATCHING,FXMathText::onCmdGotoMatching), FXMAPFUNC(SEL_COMMAND,FXMathText::ID_GOTO_SELECTED,FXMathText::onCmdGotoSelected), FXMAPFUNC(SEL_COMMAND,FXMathText::ID_GOTO_LINE,FXMathText::onCmdGotoLine), FXMAPFUNC(SEL_COMMAND,FXMathText::ID_SELECT_MATCHING,FXMathText::onCmdSelectMatching), FXMAPFUNC(SEL_COMMAND,FXMathText::ID_SEARCH,FXMathText::onCmdSearch), FXMAPFUNC(SEL_COMMAND,FXMathText::ID_REPLACE,FXMathText::onCmdReplace), FXMAPFUNC(SEL_COMMAND,FXMathText::ID_SEARCH_FORW,FXMathText::onCmdSearchNext), FXMAPFUNC(SEL_COMMAND,FXMathText::ID_SEARCH_BACK,FXMathText::onCmdSearchNext), FXMAPFUNC(SEL_COMMAND,FXMathText::ID_SEARCH_FORW_SEL,FXMathText::onCmdSearchSel), FXMAPFUNC(SEL_COMMAND,FXMathText::ID_SEARCH_BACK_SEL,FXMathText::onCmdSearchSel), FXMAPFUNCS(SEL_COMMAND,FXMathText::ID_SELECT_BRACE,FXMathText::ID_SELECT_ANG,FXMathText::onCmdSelectBlock), FXMAPFUNCS(SEL_COMMAND,FXMathText::ID_LEFT_BRACE,FXMathText::ID_LEFT_ANG,FXMathText::onCmdBlockBeg), FXMAPFUNCS(SEL_COMMAND,FXMathText::ID_RIGHT_BRACE,FXMathText::ID_RIGHT_ANG,FXMathText::onCmdBlockEnd), FXMAPFUNCS(SEL_COMMAND,FXMathText::ID_CLEAN_INDENT,FXMathText::ID_SHIFT_TABRIGHT,FXMathText::onCmdShiftText), }; // Object implementation FXIMPLEMENT(FXMathText,FXScrollArea,FXMathTextMap,ARRAYNUMBER(FXMathTextMap)) // Delimiters const FXchar FXMathText::textDelimiters[]="~.,/\\`'!@#$%^&*()-=+{}|[]\":;<>?"; /*******************************************************************************/ // Absolute value static inline FXint fxabs(FXint a){ return a<0?-a:a; } // For deserialization FXMathText::FXMathText(){ flags|=FLAG_ENABLED|FLAG_DROPTARGET; buffer=NULL; sbuffer=NULL; visrows=NULL; length=0; nrows=1; nvisrows=0; gapstart=0; gapend=0; toppos=0; keeppos=0; toprow=0; selstartpos=0; selendpos=0; hilitestartpos=0; hiliteendpos=0; anchorpos=0; cursorpos=0; revertpos=0; cursorstart=0; cursorend=0; cursorrow=0; cursorcol=0; prefcol=-1; wrapwidth=80; wrapcolumns=80; tabwidth=8; tabcolumns=8; barwidth=0; barcolumns=0; hilitestyles=NULL; textWidth=0; textHeight=0; searchflags=SEARCH_EXACT; delimiters=textDelimiters; clipbuffer=NULL; cliplength=0; vrows=0; vcols=0; matchtime=0; modified=FALSE; mode=MOUSE_NONE; grabx=0; graby=0; } // Text widget #ifdef FOX14 FXMathText::FXMathText(FXComposite *p,FXObject* tgt,FXSelector sel,FXuint opts,FXint x,FXint y,FXint w,FXint h,FXint pl,FXint pr,FXint pt,FXint pb): #else FXMathText::FXMathText(FXComposite *p,FXObject* tgt,FXSelector sel,FXuint opts,FXint x,FXint y,FXint w,FXint h): #endif FXScrollArea(p,opts,x,y,w,h){ flags|=FLAG_ENABLED|FLAG_DROPTARGET; target=tgt; message=sel; FXCALLOC(&buffer,FXchar,MINSIZE); sbuffer=NULL; FXCALLOC(&visrows,FXint,NVISROWS+1); length=0; nrows=1; nvisrows=NVISROWS; gapstart=0; gapend=MINSIZE; toppos=0; keeppos=0; toprow=0; selstartpos=0; selendpos=0; hilitestartpos=0; hiliteendpos=0; anchorpos=0; cursorpos=0; revertpos=0; cursorstart=0; cursorend=0; cursorrow=0; cursorcol=0; prefcol=-1; #ifdef FOX14 margintop=pt; marginbottom=pb; marginleft=pl; marginright=pr; #else margintop=2; marginbottom=2; marginleft=3; marginright=3; #endif wrapwidth=80; wrapcolumns=80; tabwidth=8; tabcolumns=8; barwidth=0; barcolumns=0; font=getApp()->getNormalFont(); hilitestyles=NULL; defaultCursor=getApp()->getDefaultCursor(DEF_TEXT_CURSOR); dragCursor=getApp()->getDefaultCursor(DEF_TEXT_CURSOR); textColor=getApp()->getForeColor(); selbackColor=getApp()->getSelbackColor(); seltextColor=getApp()->getSelforeColor(); hilitebackColor=FXRGB(255,128,128); hilitetextColor=getApp()->getForeColor(); activebackColor=backColor; cursorColor=getApp()->getForeColor(); numberColor=textColor; barColor=backColor; textWidth=0; textHeight=0; searchflags=SEARCH_EXACT; delimiters=textDelimiters; clipbuffer=NULL; cliplength=0; vrows=0; vcols=0; matchtime=0; modified=FALSE; mode=MOUSE_NONE; grabx=0; graby=0; } // Create window void FXMathText::create(){ FXScrollArea::create(); font->create(); if(!deleteType){ deleteType=getApp()->registerDragType(deleteTypeName); } if(!textType){ textType=getApp()->registerDragType(textTypeName); } if(options&TEXT_FIXEDWRAP){ wrapwidth=wrapcolumns*font->getTextWidth(" ",1); } tabwidth=tabcolumns*font->getTextWidth(" ",1); barwidth=barcolumns*font->getTextWidth("8",1); recalc(); } // Detach window void FXMathText::detach(){ FXScrollArea::detach(); font->detach(); deleteType=0; textType=0; } // If window can have focus #if (FOX_MINOR<=4) FXbool FXMathText::canFocus() const { return 1; } #else bool FXMathText::canFocus() const { return 1; } #endif // Into focus chain void FXMathText::setFocus(){ FXScrollArea::setFocus(); setDefault(TRUE); flags&=~FLAG_UPDATE; } // Out of focus chain void FXMathText::killFocus(){ FXScrollArea::killFocus(); setDefault(MAYBE); flags|=FLAG_UPDATE; } // Make a valid position FXint FXMathText::validPos(FXint pos) const { return pos<0 ? 0 : pos>length ? length : pos; } // Get default width FXint FXMathText::getDefaultWidth(){ if(0<vcols){ return marginleft+barwidth+marginright+vcols*font->getTextWidth("8",1); } return FXScrollArea::getDefaultWidth(); } // Get default height FXint FXMathText::getDefaultHeight(){ if(0<vrows){ return margintop+marginbottom+vrows*font->getFontHeight(); } return FXScrollArea::getDefaultHeight(); } // Enable the window void FXMathText::enable(){ if(!(flags&FLAG_ENABLED)){ FXScrollArea::enable(); update(0,0,viewport_w,viewport_h); } } // Disable the window void FXMathText::disable(){ if(flags&FLAG_ENABLED){ FXScrollArea::disable(); update(0,0,viewport_w,viewport_h); } } // Propagate size change void FXMathText::recalc(){ FXScrollArea::recalc(); flags|=FLAG_RECALC; } /*******************************************************************************/ // Get character FXint FXMathText::getChar(FXint pos) const { FXASSERT(0<=pos && pos<length); // @@@ an extra variant on the assertion to help with some desparate debugging if (pos>=length) // @@@@@ { printf("pos=%d >= length=%d\nAbort now!!!\n", pos, length); fflush(stdout); *(char *)0 = 10; } return (FXuchar)buffer[pos<gapstart ? pos : pos-gapstart+gapend]; } // Get style FXint FXMathText::getStyle(FXint pos) const { FXASSERT(0<=pos && pos<length); return (FXuchar)sbuffer[pos<gapstart ? pos : pos-gapstart+gapend]; } // Move the gap void FXMathText::movegap(FXint pos){ register FXint gaplen=gapend-gapstart; FXASSERT(0<=pos && pos<=length); FXASSERT(0<=gapstart && gapstart<=length); if(gapstart<pos){ memmove(&buffer[gapstart],&buffer[gapend],pos-gapstart); if(sbuffer){memmove(&sbuffer[gapstart],&sbuffer[gapend],pos-gapstart);} gapend=pos+gaplen; gapstart=pos; } else if(pos<gapstart){ memmove(&buffer[pos+gaplen],&buffer[pos],gapstart-pos); if(sbuffer){memmove(&sbuffer[pos+gaplen],&sbuffer[pos],gapstart-pos);} gapend=pos+gaplen; gapstart=pos; } } // Size gap void FXMathText::sizegap(FXint sz){ register FXint gaplen=gapend-gapstart; FXASSERT(0<=gapstart && gapstart<=length); if(sz>=gaplen){ sz+=MINSIZE; if(!FXRESIZE(&buffer,FXchar,length+sz)){ fxerror("%s::sizegap: out of memory.\n",getClassName()); } memmove(&buffer[gapstart+sz],&buffer[gapend],length-gapstart); if(sbuffer){ if(!FXRESIZE(&sbuffer,FXchar,length+sz)){ fxerror("%s::sizegap: out of memory.\n",getClassName()); } memmove(&sbuffer[gapstart+sz],&sbuffer[gapend],length-gapstart); } gapend=gapstart+sz; } } // Squeeze out the gap by moving it to the end of the buffer void FXMathText::squeezegap(){ if(gapstart!=length){ memmove(&buffer[gapstart],&buffer[gapend],length-gapstart); if(sbuffer){memmove(&sbuffer[gapstart],&sbuffer[gapend],length-gapstart);} gapend=length+gapend-gapstart; gapstart=length; } } /*******************************************************************************/ // Character width FXint FXMathText::charWidth(FXchar ch,FXint indent) const { if(' ' <= ((FXuchar)ch)) return font->getTextWidth(&ch,1); if(ch == '\t') return (tabwidth-indent%tabwidth); ch|=0x40; return font->getTextWidth("^",1)+font->getTextWidth(&ch,1); } // Start of next wrapped line FXint FXMathText::wrap(FXint start) const { register FXint lw,cw,p,s,c; FXASSERT(0<=start && start<=length); lw=0; p=s=start; // I should really only recognize maths if the style says so too! @@@@@ int math0 = (p<length && getChar(p)==0x02); int math1 = (p+1<length && getChar(p+1)==0x02); // A maths line breaks after any of the initial 0x02 bytes except the // last of them - after that it only breaks at newline. if (math0) { // If the first character after an 0x02 is ANOTHER 0x02 then I view the // next row as starting with that second character. if (math1) return p+1; // Otherwise the line just goes on until a terminating newline (or the end // of the buffer) while (p<length) // Find '\n' or end of buffer. { c = getChar(p); if (c=='\n') return p+1; p++; } return length; } while(p<length){ c=getChar(p); if(c=='\n') return p+1; // Newline always breaks cw=charWidth(c,lw); if(lw+cw>wrapwidth){ // Technically, a tab-before-wrap should be as wide as space! if(s>start) return s; // We remembered the last space we encountered; break there! if(p==start) p++; // Always at least one character on each line! return p; } lw+=cw; p++; if(isspace(c)&&!(options&TEXT_COLUMNWRAP)) s=p; // Remember potential break point! } return length; } // Count number of newlines FXint FXMathText::countLines(FXint start,FXint end) const { register FXint p,nl=0; FXASSERT(0<=start && end<=length+1); p=start; while(p<end){ if(p>=length) return nl+1; if(getChar(p)=='\n') nl++; p++; } return nl; } // Count number of rows; start and end should be on a row start // I believe that the logic in this should mirror that in wrap(), and // so the newer shorter version I have here keeps that logic in just // one place. FXint FXMathText::countRows(FXint start,FXint end) const { int nr=0; FXASSERT(0<=start && end<=length+1); while (start < end && start < length) { start = wrap(start); nr++; } return nr; } // Count number of columns // NB when applied to a region within which there is some displayed // mathematics this will record a value that reflects the number of bytes // in the internal representation of the mathematical formula. Often that will // be quote long, and also often it will not be very meaningful to the user! FXint FXMathText::countCols(FXint start,FXint end) const { register FXint nc=0,in=0,ch; FXASSERT(0<=start && end<=length); while(start<end){ ch=getChar(start); if(ch=='\n'){ if(in>nc) nc=in; in=0; } else if(ch=='\t'){ in+=(tabcolumns-nc%tabcolumns); } else{ in++; } start++; } if(in>nc) nc=in; return nc; } // Measure lines; start and end should be on a row start FXint FXMathText::measureText(FXint start,FXint end,FXint& wmax,FXint& hmax) const { register FXint nr=0,w=0,c,p; FXASSERT(0<=start && end<=length+1); if(options&TEXT_WORDWRAP){ // When WORDWRAP is enabled I always return wrapwidth as the measured width, // even if all rows are actually rather short. So the height to worry about // is just a count of the number of rows used. wmax=wrapwidth; nr = countRows(start, end); } else{ wmax=0; p=start; while(p<end){ if(p>=length){ if(w>wmax) wmax=w; nr++; break; } c=getChar(p); if(c=='\n'){ if(w>wmax) wmax=w; nr++; w=0; } else{ w+=charWidth(c,w); } p++; } } hmax=nr*font->getFontHeight(); return nr; } // Find end of previous word FXint FXMathText::leftWord(FXint pos) const { register FXint ch; if(pos>length) pos=length; if(0<pos){ ch=getChar(pos-1); if(strchr(delimiters,ch)) return pos-1; } while(0<pos){ ch=getChar(pos-1); if(strchr(delimiters,ch)) return pos; if(isspace(ch)) break; pos--; } while(0<pos){ ch=getChar(pos-1); if(!isspace(ch)) return pos; pos--; } return 0; } // Find begin of next word FXint FXMathText::rightWord(FXint pos) const { register FXint ch; if(pos<0) pos=0; if(pos<length){ ch=getChar(pos); if(strchr(delimiters,ch)) return pos+1; } while(pos<length){ ch=getChar(pos); if(strchr(delimiters,ch)) return pos; if(isspace(ch)) break; pos++; } while(pos<length){ ch=getChar(pos); if(!isspace(ch)) return pos; pos++; } return length; } // Find begin of a word FXint FXMathText::wordStart(FXint pos) const { register FXint c=' '; if(pos<=0) return 0; if(pos<length) c=getChar(pos); else pos=length; if(c==' ' || c=='\t'){ while(0<pos){ c=getChar(pos-1); if(c!=' ' && c!='\t') return pos; pos--; } } else if(strchr(delimiters,c)){ while(0<pos){ c=getChar(pos-1); if(!strchr(delimiters,c)) return pos; pos--; } } else{ while(0<pos){ c=getChar(pos-1); if(strchr(delimiters,c) || isspace(c)) return pos; pos--; } } return 0; } // Find end of word FXint FXMathText::wordEnd(FXint pos) const { register FXint c=' '; if(pos>=length) return length; if(0<=pos) c=getChar(pos); else pos=0; if(c==' ' || c=='\t'){ while(pos<length){ c=getChar(pos); if(c!=' ' && c!='\t') return pos; pos++; } } else if(strchr(delimiters,c)){ while(pos<length){ c=getChar(pos); if(!strchr(delimiters,c)) return pos; pos++; } } else{ while(pos<length){ c=getChar(pos); if(strchr(delimiters,c) || isspace(c)) return pos; pos++; } } return length; } // Return position of begin of paragraph FXint FXMathText::lineStart(FXint pos) const { FXASSERT(0<=pos && pos<=length); while(0<pos){ if(getChar(pos-1)=='\n') return pos; pos--; } return 0; } // Return position of end of paragraph FXint FXMathText::lineEnd(FXint pos) const { FXASSERT(0<=pos && pos<=length); while(pos<length){ if(getChar(pos)=='\n') return pos; pos++; } return length; } // Return start of next line FXint FXMathText::nextLine(FXint pos,FXint nl) const { FXASSERT(0<=pos && pos<=length); if(nl<=0) return pos; while(pos<length){ if(getChar(pos)=='\n'){ if(--nl==0) return pos+1; } pos++; } return length; } // Return start of previous line FXint FXMathText::prevLine(FXint pos,FXint nl) const { FXASSERT(0<=pos && pos<=length); if(nl<=0) return pos; while(0<pos){ if(getChar(pos-1)=='\n'){ if(nl--==0) return pos; } pos--; } return 0; } // Return row start FXint FXMathText::rowStart(FXint pos) const { register FXint p,t; FXASSERT(0<=pos && pos<=length); p=lineStart(pos); if(!(options&TEXT_WORDWRAP)) return p; while(p<pos && (t=wrap(p))<=pos && t<length) p=t; FXASSERT(0<=p && p<=pos); return p; } // Return row end FXint FXMathText::rowEnd(FXint pos) const { register FXint p; FXASSERT(0<=pos && pos<=length); if(!(options&TEXT_WORDWRAP)) return lineEnd(pos); p=lineStart(pos); while(p<length && p<=pos) p=wrap(p); FXASSERT(0<=p && p<=length); if(pos<p && isspace(getChar(p-1))) p--; FXASSERT(pos<=p && p<=length); return p; } // Move to next row given start of line FXint FXMathText::nextRow(FXint pos,FXint nr) const { register FXint p; FXASSERT(0<=pos && pos<=length); if(!(options&TEXT_WORDWRAP)) return nextLine(pos,nr); if(nr<=0) return pos; p=rowStart(pos); while(p<length && 0<nr){ p=wrap(p); nr--; } FXASSERT(0<=p && p<=length); return p; } // Move to previous row given start of line FXint FXMathText::prevRow(FXint pos,FXint nr) const { register FXint p,q,t; FXASSERT(0<=pos && pos<=length); if(!(options&TEXT_WORDWRAP)) return prevLine(pos,nr); if(nr<=0) return pos; while(0<pos){ p=lineStart(pos); for(q=p; q<pos && (t=wrap(q))<=pos && t<length; q=t) nr--; if(nr==0) return p; if(nr<0){ do{p=wrap(p);}while(++nr); FXASSERT(0<=p && p<=length); return p; } pos=p-1; nr--; } return 0; } // Backs up to the begin of the line preceding the line containing pos, or the // start of the line containing pos if the preceding line terminated in a newline FXint FXMathText::changeBeg(FXint pos) const { register FXint p1,p2,t; FXASSERT(0<=pos && pos<=length); p1=p2=lineStart(pos); if(!(options&TEXT_WORDWRAP)) return p1; while(p2<pos && (t=wrap(p2))<=pos){ p1=p2; p2=t; } FXASSERT(0<=p1 && p1<=length); return p1; } // Scan forward to the end of affected area, which is the start of the next // paragraph; a change can cause the rest of the paragraph to reflow. FXint FXMathText::changeEnd(FXint pos) const { FXASSERT(0<=pos && pos<=length); while(pos<length){ if(getChar(pos)=='\n') return pos+1; pos++; } return length+1; // YES, one more! } // Calculate line width // The width of a "maths" line is a matter of delicacy here. At present // I return the answer 0, but wrapWidth would also be a possibility. This // procedure is called from a number of places: // getXOfPos (makePositionVisible and hence mouse clicks, cursor // movement, text inserts and deletes) // various things that call update() to ensure that bits of the screen // get re-painted. FXint FXMathText::lineWidth(FXint pos,FXint n) const { register FXint end=pos+n,w=0; FXASSERT(0<=pos && end<=length); if (pos<end && getChar(pos) == 0x02) return 0; // Maths mode line while(pos<end){ w+=charWidth(getChar(pos),w); pos++; } return w; } // Determine indent of position pos relative to start FXint FXMathText::indentFromPos(FXint start,FXint pos) const { register FXint in=0,ch; FXASSERT(0<=start && pos<=length); if (start<pos && getChar(start)==0x02) return in; // maths mode while(start<pos){ ch=getChar(start); if(ch=='\n'){ in=0; } else if(ch=='\t'){ in+=(tabcolumns-in%tabcolumns); } else{ in+=1; } start++; } return in; } // Determine position of indent relative to start FXint FXMathText::posFromIndent(FXint start,FXint indent) const { register FXint in,pos,ch; FXASSERT(0<=start && start<=length); in=0; pos=start; if (pos<length && getChar(pos)==0x02) return pos; // maths mode while(in<indent && pos<length){ ch=getChar(pos); if(ch=='\n') break; else if(ch=='\t') in+=(tabcolumns-in%tabcolumns); else in+=1; pos++; } return pos; } // Search forward for match FXint FXMathText::matchForward(FXint pos,FXint end,FXchar l,FXchar r,FXint level) const { register FXchar c; FXASSERT(0<=end && end<=length); FXASSERT(0<=pos && pos<=length); while(pos<end){ c=getChar(pos); if(c==r){ level--; if(level<=0) return pos; } else if(c==l){ level++; } pos++; } return -1; } // Search backward for match FXint FXMathText::matchBackward(FXint pos,FXint beg,FXchar l,FXchar r,FXint level) const { register FXchar c; FXASSERT(0<=beg && beg<=length); FXASSERT(0<=pos && pos<=length); while(beg<=pos){ if (pos<length) { c=getChar(pos); if(c==l){ level--; if(level<=0) return pos; } else if(c==r){ level++; } } pos--; } return -1; } // Search for matching character FXint FXMathText::findMatching(FXint pos,FXint beg,FXint end,FXchar ch,FXint level) const { FXASSERT(0<=level); FXASSERT(0<=pos && pos<=length); switch(ch){ case '{': return matchForward(pos+1,end,'{','}',level); case '}': return matchBackward(pos-1,beg,'{','}',level); case '[': return matchForward(pos+1,end,'[',']',level); case ']': return matchBackward(pos-1,beg,'[',']',level); case '(': return matchForward(pos+1,end,'(',')',level); case ')': return matchBackward(pos-1,beg,'(',')',level); } return -1; } // Flash matching braces or parentheses, if within visible part of buffer void FXMathText::flashMatching(){ FXint matchpos; killHighlight(); getApp()->removeTimeout(this,ID_FLASH); if(matchtime && 0<cursorpos){ matchpos=findMatching(cursorpos-1,visrows[0],visrows[nvisrows],getChar(cursorpos-1),1); if(0<=matchpos){ getApp()->addTimeout(this,ID_FLASH,matchtime); setHighlight(matchpos,1); } } } // Search for text FXbool FXMathText::findText(const FXString& string,FXint* beg,FXint* end,FXint start,FXuint flgs,FXint npar){ register FXint rexmode; FXRex rex; // Compile flags rexmode=REX_VERBATIM; if(1<npar) rexmode|=REX_CAPTURE; if(flgs&SEARCH_REGEX) rexmode&=~REX_VERBATIM; if(flgs&SEARCH_IGNORECASE) rexmode|=REX_ICASE; // Try parse the regex if(rex.parse(string,rexmode)==REGERR_OK){ // Make all characters contiguous in the buffer squeezegap(); // Search backward if(flgs&SEARCH_BACKWARD){ // Search from start to begin of buffer if(rex.match(buffer,length,beg,end,REX_BACKWARD,npar,0,start)) return TRUE; if(!(flgs&SEARCH_WRAP)) return FALSE; // Search from end of buffer backwards if(rex.match(buffer,length,beg,end,REX_BACKWARD,npar,start,length)) return TRUE; } // Search forward else{ // Search from start to end of buffer if(rex.match(buffer,length,beg,end,REX_FORWARD,npar,start,length)) return TRUE; if(!(flgs&SEARCH_WRAP)) return FALSE; // Search from begin of buffer forwards if(rex.match(buffer,length,beg,end,REX_FORWARD,npar,0,start)) return TRUE; } } return FALSE; } /*******************************************************************************/ // See if pos is a visible position FXbool FXMathText::posVisible(FXint pos) const { return visrows[0]<=pos && pos<=visrows[nvisrows]; } // See if position is in the selection, and the selection is non-empty FXbool FXMathText::isPosSelected(FXint pos) const { return selstartpos<selendpos && selstartpos<=pos && pos<=selendpos; } // Find line number (well row?) from visible pos (in buffer) FXint FXMathText::posToLine(FXint pos,FXint ln) const { FXASSERT(0<=ln && ln<nvisrows); FXASSERT(visrows[ln]<=pos && pos<=visrows[nvisrows]); while(ln<nvisrows-1 && visrows[ln+1]<=pos && visrows[ln]<visrows[ln+1]) ln++; FXASSERT(0<=ln && ln<nvisrows); FXASSERT(visrows[ln]<=pos && pos<=visrows[ln+1]); return ln; } // Localize position at x,y // For a maths-mode line I just indicate the start of the row // involved, and at present I do not attempt to localise information // within the expression itself. Sometime LATER ON I may try to cope with // selection etc within formulae. Actually hooking onto the box structure // that I use will probably make identifying a location within a formula // fairly easy! FXint FXMathText::getPosAt(FXint x,FXint y) const { register FXint row,ls,le,cx,cw,ch; y=y-pos_y-margintop; row=y/font->getFontHeight(); if(row<0) return 0; // Before first row if(row>=nrows) return length; // Below last row if(row<toprow){ // Above visible area ls=prevRow(toppos,toprow-row); le=nextRow(ls,1); } else if(row>=toprow+nvisrows){ // Below visible area ls=nextRow(toppos,row-toprow); le=nextRow(ls,1); } else{ // Inside visible area ls=visrows[row-toprow]; le=visrows[row-toprow+1]; } x=x-pos_x-marginleft-barwidth; // Before begin of line if(x<0) return ls; FXASSERT(0<=ls); FXASSERT(ls<=le); FXASSERT(le<=length); if(ls<le && (((ch=getChar(le-1))=='\n') || (le<length && isspace(ch)))) le--; cx=0; if (ls<le && getChar(ls)==0x02) return ls; // maths mode selects its start while(ls<le){ ch=getChar(ls); cw=charWidth(ch,cx); if(x<=(cx+(cw>>1))) return ls; cx+=cw; ls+=1; } return le; } // Determine Y from position pos FXint FXMathText::getYOfPos(FXint pos) const { register FXint h=font->getFontHeight(); register FXint n,y; if(pos>length) pos=length; if(pos<0) pos=0; // Above visible part of buffer if(pos<visrows[0]){ n=countRows(rowStart(pos),visrows[0]); y=(toprow-n)*h; FXTRACE((150,"getYOfPos(%d < visrows[0]=%d) = %d\n",pos,visrows[0],margintop+y)); } // Below visible part of buffer else if(pos>visrows[nvisrows]){ n=countRows(visrows[nvisrows-1],pos); y=(toprow+nvisrows-1+n)*h; FXTRACE((150,"getYOfPos(%d > visrows[%d]=%d) = %d\n",pos,nvisrows,visrows[nvisrows],margintop+y)); } // In visible part of buffer else{ n=posToLine(pos,0); y=(toprow+n)*h; FXTRACE((150,"getYOfPos(visrows[0]=%d <= %d <= visrows[%d]=%d) = %d\n",visrows[0],pos,nvisrows,visrows[nvisrows],margintop+y)); } return margintop+y; } // Calculate X position of pos FXint FXMathText::getXOfPos(FXint pos) const { register FXint base=rowStart(pos); return marginleft+barwidth+lineWidth(base,pos-base); } // Force position to become fully visible void FXMathText::makePositionVisible(FXint pos){ register FXint x,y,nx,ny; // Get coordinates of position x=getXOfPos(pos); y=getYOfPos(pos); // Old scroll position ny=pos_y; nx=pos_x; // Check vertical visibility if(pos_y+y<margintop){ ny=margintop-y; nx=0; } else if(pos_y+y+font->getFontHeight()>viewport_h-marginbottom){ ny=viewport_h-font->getFontHeight()-marginbottom-y; nx=0; } // Check Horizontal visibility if(pos_x+x<marginleft+barwidth){ nx=marginleft+barwidth-x; } else if(pos_x+x>viewport_w-marginright){ nx=viewport_w-marginright-x; } // If needed, scroll if(nx!=pos_x || ny!=pos_y){ setPosition(nx,ny); } } // Return TRUE if position is visible FXbool FXMathText::isPosVisible(FXint pos) const { if(visrows[0]<=pos && pos<=visrows[nvisrows]){ register FXint h=font->getFontHeight(); register FXint y=pos_y+margintop+(toprow+posToLine(pos,0))*h; return margintop<=y && y+h<viewport_h-marginbottom; } return FALSE; } // Make line containing pos the top visible line void FXMathText::setTopLine(FXint pos){ setPosition(pos_x,margintop-getYOfPos(pos)); } // Make line containing pos the bottom visible line void FXMathText::setBottomLine(FXint pos){ setPosition(pos_x,viewport_h-font->getFontHeight()-marginbottom-getYOfPos(pos)); } // Center line of pos in the middle of the screen void FXMathText::setCenterLine(FXint pos){ setPosition(pos_x,viewport_h/2+font->getFontHeight()/2-getYOfPos(pos)); } // Get top line FXint FXMathText::getTopLine() const { return visrows[0]; } // Get bottom line FXint FXMathText::getBottomLine() const { return visrows[nvisrows-1]; } // Move content void FXMathText::moveContents(FXint x,FXint y){ register FXint delta,i,dx,dy; // Erase fragments of cursor overhanging margins eraseCursorOverhang(); // Number of lines scrolled delta=-y/font->getFontHeight() - toprow; // Scrolled up one or more lines if(delta<0){ if(toprow+delta<=0){ toppos=0; toprow=0; } else{ toppos=prevRow(toppos,-delta); toprow=toprow+delta; } if(-delta<nvisrows){ for(i=nvisrows; i>=-delta; i--) visrows[i]=visrows[delta+i]; calcVisRows(0,-delta); } else{ calcVisRows(0,nvisrows); } } // Scrolled down one or more lines else if(delta>0){ if(toprow+delta>=nrows-1){ toppos=rowStart(length); toprow=nrows-1; } else{ toppos=nextRow(toppos,delta); toprow=toprow+delta; } if(delta<nvisrows){ for(i=0; i<=nvisrows-delta; i++) visrows[i]=visrows[delta+i]; calcVisRows(nvisrows-delta,nvisrows); } else{ calcVisRows(0,nvisrows); } } // This is now the new keep position keeppos=toppos; // Hopefully, all is still in range (why test if I have done nothing?) if (delta != 0) { FXASSERT(0<=toprow && toprow<=nrows-1); FXASSERT(0<=toppos && toppos<=length); // Scroll the contents dx=x-pos_x; dy=y-pos_y; pos_x=x; pos_y=y; // Scroll stuff in the bar only vertically scroll(0,0,barwidth,viewport_h,0,dy); // Scroll the text scroll(marginleft+barwidth,margintop,viewport_w-marginleft-barwidth-marginright,viewport_h-margintop-marginbottom,dx,dy); } } /*******************************************************************************/ // Recalculate line starts void FXMathText::calcVisRows(FXint startline,FXint endline){ register FXint line,pos; FXASSERT(nvisrows>0); if(startline<0) startline=0; else if(startline>nvisrows) startline=nvisrows; if(endline<0) endline=0; else if(endline>nvisrows) endline=nvisrows; if(startline<=endline){ if(startline==0){ FXASSERT(0<=toppos && toppos<=length); visrows[0]=toppos; startline=1; } pos=visrows[startline-1]; line=startline; if(options&TEXT_WORDWRAP){ while(line<=endline && pos<length){ pos=wrap(pos); FXASSERT(0<=pos && pos<=length); visrows[line++]=pos; } } else{ while(line<=endline && pos<length){ pos=nextLine(pos); FXASSERT(0<=pos && pos<=length); visrows[line++]=pos; } } while(line<=endline){ visrows[line++]=length; } } } // There has been a mutation in the buffer // "math display" lines measure rather as if they had been empty lines, and so // any changes in them will not be reflected by a change in their length. // However I am going to be treating math display as read-only once it is // in the buffer and hence mutations will only happen within non-maths // sections (apart I suppose from when maths is initially inserted) and I // hope that no problems will arise here. void FXMathText::mutation(FXint pos,FXint ncins,FXint ncdel,FXint nrins,FXint nrdel){ register FXint ncdelta=ncins-ncdel; register FXint nrdelta=nrins-nrdel; register FXint line,i,x,y; FXTRACE((150,"BEFORE: pos=%d ncins=%d ncdel=%d nrins=%d nrdel=%d toppos=%d toprow=%d nrows=%d nvisrows=%d\n",pos,ncins,ncdel,nrins,nrdel,toppos,toprow,nrows,nvisrows)); // All of the change is below the last visible line if(visrows[nvisrows]<pos){ FXTRACE((150,"change below visible\n")); nrows+=nrdelta; } // All change above first visible line else if(pos+ncdel<=visrows[0]){ FXTRACE((150,"change above visible\n")); nrows+=nrdelta; toprow+=nrdelta; toppos+=ncdelta; keeppos=toppos; for(i=0; i<=nvisrows; i++) visrows[i]+=ncdelta; pos_y-=nrdelta*font->getFontHeight(); FXASSERT(0<=toppos && toppos<=length); if(nrdelta) update(0,0,barwidth,height); } // Top visible part unchanged else if(visrows[0]<=pos){ line=posToLine(pos,0); FXTRACE((150,"change below visible line %d\n",line)); // More lines means paint the bottom half if(nrdelta>0){ FXTRACE((150,"inserted %d rows\n",nrdelta)); nrows+=nrdelta; for(i=nvisrows; i>line+nrdelta; i--) visrows[i]=visrows[i-nrdelta]+ncdelta; calcVisRows(line+1,line+nrins); FXASSERT(0<=toppos && toppos<=length); y=pos_y+margintop+(toprow+line)*font->getFontHeight(); update(barwidth,y,width-barwidth,height-y); } // Less lines means paint bottom half also else if(nrdelta<0){ FXTRACE((150,"deleted %d rows\n",-nrdelta)); nrows+=nrdelta; for(i=line+1; i<=nvisrows+nrdelta; i++) visrows[i]=visrows[i-nrdelta]+ncdelta; calcVisRows(nvisrows+nrdelta,nvisrows); calcVisRows(line+1,line+nrins); FXASSERT(0<=toppos && toppos<=length); y=pos_y+margintop+(toprow+line)*font->getFontHeight(); update(barwidth,y,width-barwidth,height-y); } // Same lines means paint the changed area only else{ FXTRACE((150,"same number of rows\n")); for(i=line+1; i<=nvisrows; i++) visrows[i]=visrows[i]+ncdelta; calcVisRows(line+1,line+nrins); FXASSERT(0<=toppos && toppos<=length); if(nrins==0){ x=pos_x+marginleft+barwidth+lineWidth(visrows[line],pos-visrows[line]); y=pos_y+margintop+(toprow+line)*font->getFontHeight(); update(x,y,width-x,font->getFontHeight()); FXTRACE((150,"update(%d,%d,%d,%d)\n",x,y,width-x,font->getFontHeight())); } else{ y=pos_y+margintop+(toprow+line)*font->getFontHeight(); update(barwidth,y,width-barwidth,nrins*font->getFontHeight()); FXTRACE((150,"update(%d,%d,%d,%d)\n",0,y,width,nrins*font->getFontHeight())); } } } // Bottom visible part unchanged else if(pos+ncdel<visrows[nvisrows-1]){ nrows+=nrdelta; line=1+posToLine(pos+ncdel,0); FXASSERT(0<=line && line<nvisrows); FXASSERT(pos+ncdel<=visrows[line]); FXTRACE((150,"change above visible line %d\n",line)); // Too few lines left to display if(toprow+nrdelta<=line){ FXTRACE((150,"reset to top\n")); toprow=0; toppos=0; keeppos=0; pos_y=0; calcVisRows(0,nvisrows); FXASSERT(0<=toppos && toppos<=length); update(); } // Redisplay only the top else{ FXTRACE((150,"redraw top %d lines\n",line)); toprow+=nrdelta; toppos=prevRow(visrows[line]+ncdelta,line); keeppos=toppos; pos_y-=nrdelta*font->getFontHeight(); calcVisRows(0,nvisrows); FXASSERT(0<=toppos && toppos<=length); update(barwidth,0,width-barwidth,pos_y+margintop+(toprow+line)*font->getFontHeight()); if(nrdelta) update(0,0,barwidth,height); } } // All visible text changed else{ FXTRACE((150,"change all visible lines\n")); nrows+=nrdelta; // Reset to top because too few lines left if(toprow>=nrows){ FXTRACE((150,"reset to top\n")); toprow=0; toppos=0; keeppos=0; FXASSERT(0<=toppos && toppos<=length); pos_y=0; } // Maintain same row as before else{ FXTRACE((150,"set to same row %d\n",toprow)); toppos=nextRow(0,toprow); keeppos=toppos; FXASSERT(0<=toppos && toppos<=length); } calcVisRows(0,nvisrows); update(); } FXTRACE((150,"AFTER : pos=%d ncins=%d ncdel=%d nrins=%d nrdel=%d toppos=%d toprow=%d nrows=%d\n",pos,ncins,ncdel,nrins,nrdel,toppos,toprow,nrows)); } // Replace m characters at pos by n characters void FXMathText::replace(FXint pos,FXint m,const FXchar *text,FXint n,FXint style){ register FXint nrdel,nrins,ncdel,ncins,wbeg,wend,del; FXint wdel,hdel,wins,hins; drawCursor(0); // FIXME can we do without this? FXTRACE((150,"pos=%d mdel=%d nins=%d\n",pos,m,n)); // Delta in characters del=n-m; // Bracket potentially affected character range for wrapping purposes wbeg=changeBeg(pos); wend=changeEnd(pos+m); // Measure stuff prior to change nrdel=measureText(wbeg,wend,wdel,hdel); ncdel=wend-wbeg; FXTRACE((150,"wbeg=%d wend=%d nrdel=%d ncdel=%d length=%d wdel=%d hdel=%d\n",wbeg,wend,nrdel,ncdel,length,wdel,hdel)); // Modify the buffer sizegap(del); movegap(pos); memcpy(&buffer[pos],text,n); if(sbuffer){memset(&sbuffer[pos],style,n);} gapstart+=n; gapend+=m; length+=del; // Measure stuff after change nrins=measureText(wbeg,wend+n-m,wins,hins); ncins=wend+n-m-wbeg; FXTRACE((150,"wbeg=%d wend+n-m=%d nrins=%d ncins=%d length=%d wins=%d hins=%d\n",wbeg,wend+n-m,nrins,ncins,length,wins,hins)); // Update stuff mutation(wbeg,ncins,ncdel,nrins,nrdel); // Fix text metrics textHeight=textHeight+hins-hdel; textWidth=FXMAX(textWidth,wins); // Fix selection range FXASSERT(selstartpos<=selendpos); if(pos+m<=selstartpos){ selstartpos+=del; selendpos+=del; } else if(pos<selendpos){ if(selendpos<=pos+m) selendpos=pos+n; else selendpos+=del; if(pos<=selstartpos) selstartpos=pos+n; } // Fix highlight range FXASSERT(hilitestartpos<=hiliteendpos); if(pos+m<=hilitestartpos){ hilitestartpos+=del; hiliteendpos+=del; } else if(pos<hiliteendpos){ if(hiliteendpos<=pos+m) hiliteendpos=pos+n; else hiliteendpos+=del; if(pos<=hilitestartpos) hilitestartpos=pos+n; } // Fix anchor position if(pos+m<=anchorpos) anchorpos+=del; else if(pos<=anchorpos) anchorpos=pos+n; // Update cursor position variables if(wend<=cursorpos){ cursorpos+=del; cursorstart+=del; cursorend+=del; cursorrow+=nrins-nrdel; } else if(wbeg<=cursorpos){ if(pos+m<=cursorpos) cursorpos+=del; else if(pos<=cursorpos) cursorpos=pos+n; cursorstart=rowStart(cursorpos); cursorend=nextRow(cursorstart); cursorcol=indentFromPos(cursorstart,cursorpos); if(cursorstart<toppos){ cursorrow=toprow-countRows(cursorstart,toppos); } else{ cursorrow=toprow+countRows(toppos,cursorstart); } } // Reconcile scrollbars FXScrollArea::layout(); // FIXME:- scrollbars, but no layout // Forget preferred column prefcol=-1; } // Replace m characters at pos by n characters void FXMathText::replaceStyledText(FXint pos,FXint m,const FXchar *text,FXint n,FXint style,FXbool notify){ FXTextChange textchange; if(n<0 || m<0 || pos<0 || length<pos+m){ fxerror("%s::replaceStyledText: bad argument range.\n",getClassName()); } FXTRACE((130,"replaceStyledText(%d,%d,text,%d)\n",pos,m,n)); textchange.pos=pos; textchange.ndel=m; textchange.nins=n; textchange.ins=(FXchar*)text; FXMALLOC(&textchange.del,FXchar,m); extractText(textchange.del,pos,m); replace(pos,m,text,n,style); if(notify && target){ target->tryHandle(this,FXSEL(SEL_REPLACED,message),(void*)&textchange); target->tryHandle(this,FXSEL(SEL_CHANGED,message),(void*)(FXival)cursorpos); } FXFREE(&textchange.del); } // Replace text by other text void FXMathText::replaceText(FXint pos,FXint m,const FXchar *text,FXint n,FXbool notify){ replaceStyledText(pos,m,text,n,0,notify); } // Add text at the end void FXMathText::appendStyledText(const FXchar *text,FXint n,FXint style,FXbool notify){ FXTextChange textchange; if(n<0){ fxerror("%s::appendStyledText: bad argument range.\n",getClassName()); } FXTRACE((130,"appendStyledText(text,%d)\n",n)); textchange.pos=length; textchange.ndel=0; textchange.nins=n; textchange.ins=(FXchar*)text; textchange.del=(FXchar*)""; replace(length,0,text,n,style); if(notify && target){ target->tryHandle(this,FXSEL(SEL_INSERTED,message),(void*)&textchange); target->tryHandle(this,FXSEL(SEL_CHANGED,message),(void*)(FXival)cursorpos); } } // Add text at the end void FXMathText::appendText(const FXchar *text,FXint n,FXbool notify){ appendStyledText(text,n,0,notify); } // Insert some text at pos void FXMathText::insertStyledText(FXint pos,const FXchar *text,FXint n,FXint style,FXbool notify){ FXTextChange textchange; if(n<0 || pos<0 || length<pos){ fxerror("%s::insertStyledText: bad argument range.\n",getClassName()); } FXTRACE((130,"insertStyledText(%d,text,%d)\n",pos,n)); textchange.pos=pos; textchange.ndel=0; textchange.nins=n; textchange.ins=(FXchar*)text; textchange.del=(FXchar*)""; replace(pos,0,text,n,style); if(notify && target){ target->tryHandle(this,FXSEL(SEL_INSERTED,message),(void*)&textchange); target->tryHandle(this,FXSEL(SEL_CHANGED,message),(void*)(FXival)cursorpos); } } // Insert some text at pos void FXMathText::insertText(FXint pos,const FXchar *text,FXint n,FXbool notify){ insertStyledText(pos,text,n,0,notify); } // Remove some text at pos void FXMathText::removeText(FXint pos,FXint n,FXbool notify){ FXTextChange textchange; if(n<0 || pos<0 || length<pos+n){ fxerror("%s::removeText: bad argument range.\n",getClassName()); } FXTRACE((130,"removeText(%d,%d)\n",pos,n)); textchange.pos=pos; textchange.ndel=n; textchange.nins=0; textchange.ins=(FXchar*)""; FXMALLOC(&textchange.del,FXchar,n); extractText(textchange.del,pos,n); replace(pos,n,NULL,0,0); if(notify && target){ target->tryHandle(this,FXSEL(SEL_DELETED,message),(void*)&textchange); target->tryHandle(this,FXSEL(SEL_CHANGED,message),(void*)(FXival)cursorpos); } FXFREE(&textchange.del); } // Grab range of text void FXMathText::extractText(FXchar *text,FXint pos,FXint n) const { if(n<0 || pos<0 || length<pos+n){ fxerror("%s::extractText: bad argument.\n",getClassName()); } FXASSERT(0<=n && 0<=pos && pos+n<=length); if(pos+n<=gapstart){ memcpy(text,&buffer[pos],n); } else if(pos>=gapstart){ memcpy(text,&buffer[pos-gapstart+gapend],n); } else{ memcpy(text,&buffer[pos],gapstart-pos); memcpy(&text[gapstart-pos],&buffer[gapend],pos+n-gapstart); } } // Grab range of style void FXMathText::extractStyle(FXchar *style,FXint pos,FXint n) const { if(n<0 || pos<0 || length<pos+n){ fxerror("%s::extractStyle: bad argument.\n",getClassName()); } FXASSERT(0<=n && 0<=pos && pos+n<=length); if(sbuffer){ if(pos+n<=gapstart){ memcpy(style,&sbuffer[pos],n); } else if(pos>=gapstart){ memcpy(style,&sbuffer[pos-gapstart+gapend],n); } else{ memcpy(style,&sbuffer[pos],gapstart-pos); memcpy(&style[gapstart-pos],&sbuffer[gapend],pos+n-gapstart); } } } // Change style of text range void FXMathText::changeStyle(FXint pos,FXint n,FXint style){ if(n<0 || pos<0 || length<pos+n){ fxerror("%s::changeStyle: bad argument range.\n",getClassName()); } if(sbuffer){ if(pos+n<=gapstart){ memset(&sbuffer[pos],style,n); } else if(pos>=gapstart){ memset(&sbuffer[pos-gapstart+gapend],style,n); } else{ memset(&sbuffer[pos],style,gapstart-pos); memset(&sbuffer[gapend],style,pos+n-gapstart); } updateRange(pos,pos+n); } } // Change style of text range from style-array void FXMathText::changeStyle(FXint pos,FXint n,const FXchar* style){ if(n<0 || pos<0 || length<pos+n){ fxerror("%s::changeStyle: bad argument range.\n",getClassName()); } if(sbuffer && style){ if(pos+n<=gapstart){ memcpy(&sbuffer[pos],style,n); } else if(pos>=gapstart){ memcpy(&sbuffer[pos-gapstart+gapend],style,n); } else{ memcpy(&sbuffer[pos],style,gapstart-pos); memcpy(&sbuffer[gapend],&style[gapstart-pos],pos+n-gapstart); } updateRange(pos,pos+n); } } // Change the text in the buffer to new text void FXMathText::setStyledText(const FXchar* text,FXint n,FXint style,FXbool notify){ FXTextChange textchange; if(n<0){ fxerror("%s::setStyledText: bad argument range.\n",getClassName()); } if(!FXRESIZE(&buffer,FXchar,n+MINSIZE)){ fxerror("%s::setStyledText: out of memory.\n",getClassName()); } memcpy(buffer,text,n); if(sbuffer){ if(!FXRESIZE(&sbuffer,FXchar,n+MINSIZE)){ fxerror("%s::setStyledText: out of memory.\n",getClassName()); } memset(sbuffer,style,n); } gapstart=n; gapend=gapstart+MINSIZE; length=n; toppos=0; toprow=0; keeppos=0; selstartpos=0; selendpos=0; hilitestartpos=0; hiliteendpos=0; anchorpos=0; cursorpos=0; cursorstart=0; cursorend=0; cursorrow=0; cursorcol=0; prefcol=-1; pos_x=0; pos_y=0; textchange.pos=0; textchange.ndel=0; textchange.nins=n; textchange.ins=(FXchar*)text; textchange.del=(FXchar*)""; if(notify && target){ target->tryHandle(this,FXSEL(SEL_INSERTED,message),(void*)&textchange); target->tryHandle(this,FXSEL(SEL_CHANGED,message),(void*)(FXival)cursorpos); } recalc(); layout(); update(); } // Change the text in the buffer to new text void FXMathText::setText(const FXchar* text,FXint n,FXbool notify){ setStyledText(text,n,0,notify); } // Retrieve text into buffer void FXMathText::getText(FXchar* text,FXint n) const { extractText(text,0,n); } // Change all of the text void FXMathText::setStyledText(const FXString& text,FXint style,FXbool notify){ setStyledText(text.text(),text.length(),style,notify); } // Change all of the text void FXMathText::setText(const FXString& text,FXbool notify){ setStyledText(text.text(),text.length(),0,notify); } // We return a constant copy of the buffer FXString FXMathText::getText() const { FXString value; FXASSERT(0<=gapstart && gapstart<=length); value.append(buffer,gapstart); value.append(&buffer[gapend],length-gapstart); return value; } // Perform belated layout long FXMathText::onUpdate(FXObject* sender,FXSelector sel,void* ptr){ FXScrollArea::onUpdate(sender,sel,ptr); // FIXME full text reflow should be done by delayed layout, // rather than immediately. return 1; } // Completely reflow the text, because font, wrapwidth, or all of the // text may have changed and everything needs to be recomputed void FXMathText::recompute(){ FXint hh=font->getFontHeight(); FXint ww1,ww2,ww3,hh1,hh2,hh3; // Major recalc if(flags&FLAG_RECALC){ // Make it point somewhere sensible if(keeppos<0) keeppos=0; if(keeppos>length) keeppos=length; // Make sure we're pointing to the start of a row again toppos=rowStart(keeppos); // Get start cursorstart=rowStart(cursorpos); cursorend=nextRow(cursorstart); cursorcol=indentFromPos(cursorstart,cursorpos); // Avoid measuring huge chunks of text twice! if(cursorstart<toprow){ cursorrow=measureText(0,cursorstart,ww1,hh1); toprow=cursorrow+measureText(cursorstart,toppos,ww2,hh2); nrows=toprow+measureText(toppos,length+1,ww3,hh3); } else{ toprow=measureText(0,toppos,ww1,hh1); cursorrow=toprow+measureText(toppos,cursorstart,ww2,hh2); nrows=cursorrow+measureText(cursorstart,length+1,ww3,hh3); } textWidth=FXMAX3(ww1,ww2,ww3); textHeight=hh1+hh2+hh3; // Adjust position; we keep the same fractional position pos_y=-toprow*hh-(-pos_y%hh); } // Number of visible lines may have changed nvisrows=(height-margintop-marginbottom+hh+hh-1)/hh; if(nvisrows<1) nvisrows=1; // Number of visible lines changed; lines is 1 longer than nvisrows, // so we can find the end of a line faster for every visible line FXRESIZE(&visrows,FXint,nvisrows+1); // Recompute line starts calcVisRows(0,nvisrows); FXTRACE((150,"recompute : toprow=%d toppos=%d nrows=%d nvisrows=%d textWidth=%d textHeight=%d length=%d cursorrow=%d cursorcol=%d\n",toprow,toppos,nrows,nvisrows,textWidth,textHeight,length,cursorrow,cursorcol)); // Done with that flags&=~(FLAG_RECALC|FLAG_DIRTY); } /*******************************************************************************/ // Determine content width of scroll area FXint FXMathText::getContentWidth(){ if(flags&FLAG_DIRTY) recompute(); return marginleft+barwidth+marginright+textWidth; } // Determine content height of scroll area FXint FXMathText::getContentHeight(){ if(flags&FLAG_DIRTY) recompute(); return margintop+marginbottom+textHeight; } // Recalculate layout void FXMathText::layout(){ // Compute new wrap width if(!(options&TEXT_FIXEDWRAP)){ wrapwidth=width-marginleft-barwidth-marginright; if(!(options&VSCROLLER_NEVER)) wrapwidth-=vertical->getDefaultWidth(); } else{ wrapwidth=wrapcolumns*font->getTextWidth(" ",1); } // Scrollbars adjusted FXScrollArea::layout(); // Set line size based on font vertical->setLine(font->getFontHeight()); horizontal->setLine(font->getTextWidth(" ",1)); // Force repaint update(); // Done flags&=~FLAG_DIRTY; } // The widget is resized void FXMathText::resize(FXint w,FXint h){ FXint hh=font->getFontHeight(); FXint nv=(h-margintop-marginbottom+hh+hh-1)/hh; if(nv<1) nv=1; // In wrap mode, a width change causes a content recalculation if((options&TEXT_WORDWRAP) && !(options&TEXT_FIXEDWRAP) && (width!=w)) flags|=(FLAG_RECALC|FLAG_DIRTY); // Need to redo line starts if(nv!=nvisrows) flags|=FLAG_DIRTY; // Resize the window, and do layout FXScrollArea::resize(w,h); } // The widget is moved and possibly resized void FXMathText::position(FXint x,FXint y,FXint w,FXint h){ FXint hh=font->getFontHeight(); FXint nv=(h-margintop-marginbottom+hh+hh-1)/hh; if(nv<1) nv=1; //FXTRACE((100,"FXMathText::position width=%d height=%d w=%d h=%d hh=%d nv=%d\n",width,height,w,h,hh,nv)); // In wrap mode, a width change causes a content recalculation if((options&TEXT_WORDWRAP) && !(options&TEXT_FIXEDWRAP) && (width!=w)) flags|=(FLAG_RECALC|FLAG_DIRTY); // Need to redo line starts if(nv!=nvisrows) flags|=FLAG_DIRTY; // Place the window, and do layout FXScrollArea::position(x,y,w,h); } /*******************************************************************************/ // Blink the cursor long FXMathText::onBlink(FXObject*,FXSelector,void*){ drawCursor(flags^FLAG_CARET); getApp()->addTimeout(this,ID_BLINK,getApp()->getBlinkSpeed()); return 0; } // Gained focus long FXMathText::onFocusIn(FXObject* sender,FXSelector sel,void* ptr){ FXScrollArea::onFocusIn(sender,sel,ptr); getApp()->addTimeout(this,ID_BLINK,getApp()->getBlinkSpeed()); drawCursor(FLAG_CARET); return 1; } // Lost focus long FXMathText::onFocusOut(FXObject* sender,FXSelector sel,void* ptr){ FXScrollArea::onFocusOut(sender,sel,ptr); getApp()->removeTimeout(this,ID_BLINK); drawCursor(0); flags|=FLAG_UPDATE; return 1; } // We were asked about tip text long FXMathText::onQueryTip(FXObject* sender,FXSelector sel,void* ptr){ if(FXWindow::onQueryTip(sender,sel,ptr)) return 1; if((flags&FLAG_TIP) && !tip.empty()){ sender->handle(this,FXSEL(SEL_COMMAND,ID_SETSTRINGVALUE),(void*)&tip); return 1; } return 0; } // We were asked about status text long FXMathText::onQueryHelp(FXObject* sender,FXSelector sel,void* ptr){ if(FXWindow::onQueryHelp(sender,sel,ptr)) return 1; if((flags&FLAG_HELP) && !help.empty()){ sender->handle(this,FXSEL(SEL_COMMAND,ID_SETSTRINGVALUE),(void*)&help); return 1; } return 0; } // Flash matching brace long FXMathText::onFlash(FXObject*,FXSelector,void*){ killHighlight(); return 0; } // Pressed left button long FXMathText::onLeftBtnPress(FXObject*,FXSelector,void* ptr){ FXEvent* event=(FXEvent*)ptr; FXint pos; flags&=~FLAG_TIP; handle(this,FXSEL(SEL_FOCUS_SELF,0),ptr); if(isEnabled()){ grab(); if(target && target->tryHandle(this,FXSEL(SEL_LEFTBUTTONPRESS,message),ptr)) return 1; flags&=~FLAG_UPDATE; // Select characters if(event->click_count==1){ pos=getPosAt(event->win_x,event->win_y); FXTRACE((150,"getPosAt(%d,%d) = %d getYOfPos(%d) = %d getXOfPos(%d)=%d\n",event->win_x,event->win_y,pos,pos,getYOfPos(pos),pos,getXOfPos(pos))); setCursorPos(pos,TRUE); makePositionVisible(pos); if(event->state&SHIFTMASK){ extendSelection(pos,SELECT_CHARS,TRUE); } else{ killSelection(TRUE); setAnchorPos(pos); flashMatching(); } mode=MOUSE_CHARS; } // Select words else if(event->click_count==2){ setAnchorPos(cursorpos); extendSelection(cursorpos,SELECT_WORDS,TRUE); mode=MOUSE_WORDS; } // Select lines else{ setAnchorPos(cursorpos); extendSelection(cursorpos,SELECT_LINES,TRUE); mode=MOUSE_LINES; } return 1; } return 0; } // Released left button long FXMathText::onLeftBtnRelease(FXObject*,FXSelector,void* ptr){ if(isEnabled()){ ungrab(); mode=MOUSE_NONE; stopAutoScroll(); if(target && target->tryHandle(this,FXSEL(SEL_LEFTBUTTONRELEASE,message),ptr)) return 1; return 1; } return 0; } // Pressed middle button long FXMathText::onMiddleBtnPress(FXObject*,FXSelector,void* ptr){ FXEvent* event=(FXEvent*)ptr; FXint pos; flags&=~FLAG_TIP; handle(this,FXSEL(SEL_FOCUS_SELF,0),ptr); if(isEnabled()){ grab(); if(target && target->tryHandle(this,FXSEL(SEL_MIDDLEBUTTONPRESS,message),ptr)) return 1; pos=getPosAt(event->win_x,event->win_y); // Move over setCursorPos(pos,TRUE); makePositionVisible(pos); // Start text drag if(isPosSelected(pos)){ mode=MOUSE_TRYDRAG; } flags&=~FLAG_UPDATE; return 1; } return 0; } // Released middle button long FXMathText::onMiddleBtnRelease(FXObject*,FXSelector,void* ptr){ FXuint md=mode; if(isEnabled()){ ungrab(); stopAutoScroll(); mode=MOUSE_NONE; if(target && target->tryHandle(this,FXSEL(SEL_MIDDLEBUTTONRELEASE,message),ptr)) return 1; // Drop text somewhere if(md==MOUSE_DRAG){ handle(this,FXSEL(SEL_ENDDRAG,0),ptr); } // Paste selection else{ handle(this,FXSEL(SEL_COMMAND,ID_PASTE_MIDDLE),NULL); } return 1; } return 0; } // Pressed right button long FXMathText::onRightBtnPress(FXObject*,FXSelector,void* ptr){ FXEvent* event=(FXEvent*)ptr; flags&=~FLAG_TIP; handle(this,FXSEL(SEL_FOCUS_SELF,0),ptr); if(isEnabled()){ grab(); if(target && target->tryHandle(this,FXSEL(SEL_RIGHTBUTTONPRESS,message),ptr)) return 1; mode=MOUSE_SCROLL; grabx=event->win_x-pos_x; graby=event->win_y-pos_y; flags&=~FLAG_UPDATE; return 1; } return 0; } // Released right button long FXMathText::onRightBtnRelease(FXObject*,FXSelector,void* ptr){ if(isEnabled()){ ungrab(); mode=MOUSE_NONE; if(target && target->tryHandle(this,FXSEL(SEL_RIGHTBUTTONRELEASE,message),ptr)) return 1; return 1; } return 0; } // The widget lost the grab for some reason long FXMathText::onUngrabbed(FXObject* sender,FXSelector sel,void* ptr){ FXScrollArea::onUngrabbed(sender,sel,ptr); mode=MOUSE_NONE; flags|=FLAG_UPDATE; stopAutoScroll(); return 1; } // Autoscroll timer fired long FXMathText::onAutoScroll(FXObject* sender,FXSelector sel,void* ptr){ FXEvent* event=(FXEvent*)ptr; FXint pos; FXScrollArea::onAutoScroll(sender,sel,ptr); switch(mode){ case MOUSE_CHARS: if((fxabs(event->win_x-event->click_x)>getApp()->getDragDelta())||(fxabs(event->win_y-event->click_y)>getApp()->getDragDelta())){ pos=getPosAt(event->win_x,event->win_y); extendSelection(pos,SELECT_CHARS,TRUE); setCursorPos(pos,TRUE); } return 1; case MOUSE_WORDS: if((fxabs(event->win_x-event->click_x)>getApp()->getDragDelta())||(fxabs(event->win_y-event->click_y)>getApp()->getDragDelta())){ pos=getPosAt(event->win_x,event->win_y); extendSelection(pos,SELECT_WORDS,TRUE); setCursorPos(pos,TRUE); } return 1; case MOUSE_LINES: if((fxabs(event->win_x-event->click_x)>getApp()->getDragDelta())||(fxabs(event->win_y-event->click_y)>getApp()->getDragDelta())){ pos=getPosAt(event->win_x,event->win_y); extendSelection(pos,SELECT_LINES,TRUE); setCursorPos(pos,TRUE); } return 1; } return 0; } // Handle real or simulated mouse motion long FXMathText::onMotion(FXObject*,FXSelector,void* ptr){ FXEvent* event=(FXEvent*)ptr; FXint pos; switch(mode){ case MOUSE_CHARS: if(startAutoScroll(event,FALSE)) return 1; if((fxabs(event->win_x-event->click_x)>getApp()->getDragDelta())||(fxabs(event->win_y-event->click_y)>getApp()->getDragDelta())){ pos=getPosAt(event->win_x,event->win_y); extendSelection(pos,SELECT_CHARS,TRUE); setCursorPos(pos,TRUE); } return 1; case MOUSE_WORDS: if(startAutoScroll(event,FALSE)) return 1; if((fxabs(event->win_x-event->click_x)>getApp()->getDragDelta())||(fxabs(event->win_y-event->click_y)>getApp()->getDragDelta())){ pos=getPosAt(event->win_x,event->win_y); extendSelection(pos,SELECT_WORDS,TRUE); setCursorPos(pos,TRUE); } return 1; case MOUSE_LINES: if(startAutoScroll(event,FALSE)) return 1; if((fxabs(event->win_x-event->click_x)>getApp()->getDragDelta())||(fxabs(event->win_y-event->click_y)>getApp()->getDragDelta())){ pos=getPosAt(event->win_x,event->win_y); extendSelection(pos,SELECT_LINES,TRUE); setCursorPos(pos,TRUE); } return 1; case MOUSE_SCROLL: setPosition(event->win_x-grabx,event->win_y-graby); return 1; case MOUSE_DRAG: handle(this,FXSEL(SEL_DRAGGED,0),ptr); return 1; case MOUSE_TRYDRAG: if(event->moved){ mode=MOUSE_NONE; if(handle(this,FXSEL(SEL_BEGINDRAG,0),ptr)){ mode=MOUSE_DRAG; } } return 1; } return 0; } /*******************************************************************************/ // Start a drag operation long FXMathText::onBeginDrag(FXObject* sender,FXSelector sel,void* ptr){ if(FXScrollArea::onBeginDrag(sender,sel,ptr)) return 1; beginDrag(&textType,1); setDragCursor(getApp()->getDefaultCursor(DEF_DNDSTOP_CURSOR)); return 1; } // End drag operation long FXMathText::onEndDrag(FXObject* sender,FXSelector sel,void* ptr){ if(FXScrollArea::onEndDrag(sender,sel,ptr)) return 1; endDrag((didAccept()!=DRAG_REJECT)); setDragCursor(getApp()->getDefaultCursor(DEF_TEXT_CURSOR)); return 1; } // Dragged stuff around long FXMathText::onDragged(FXObject* sender,FXSelector sel,void* ptr){ FXEvent* event=(FXEvent*)ptr; FXDragAction action; if(FXScrollArea::onDragged(sender,sel,ptr)) return 1; action=DRAG_COPY; if(isEditable()){ if(isDropTarget()) action=DRAG_MOVE; if(event->state&CONTROLMASK) action=DRAG_COPY; if(event->state&SHIFTMASK) action=DRAG_MOVE; } handleDrag(event->root_x,event->root_y,action); if(didAccept()!=DRAG_REJECT){ if(action==DRAG_MOVE) setDragCursor(getApp()->getDefaultCursor(DEF_DNDMOVE_CURSOR)); else setDragCursor(getApp()->getDefaultCursor(DEF_DNDCOPY_CURSOR)); } else{ setDragCursor(getApp()->getDefaultCursor(DEF_DNDSTOP_CURSOR)); } return 1; } // Handle drag-and-drop enter long FXMathText::onDNDEnter(FXObject* sender,FXSelector sel,void* ptr){ FXScrollArea::onDNDEnter(sender,sel,ptr); drawCursor(FLAG_CARET); revertpos=cursorpos; return 1; } // Handle drag-and-drop leave long FXMathText::onDNDLeave(FXObject* sender,FXSelector sel,void* ptr){ FXScrollArea::onDNDLeave(sender,sel,ptr); stopAutoScroll(); drawCursor(0); setCursorPos(revertpos,TRUE); return 1; } // Handle drag-and-drop motion long FXMathText::onDNDMotion(FXObject* sender,FXSelector sel,void* ptr){ FXEvent* event=(FXEvent*)ptr; FXDragAction action; FXint pos; // Scroll into view if(startAutoScroll(event,TRUE)) return 1; // Handled elsewhere if(FXScrollArea::onDNDMotion(sender,sel,ptr)) return 1; // Correct drop type if(offeredDNDType(FROM_DRAGNDROP,textType)){ // Is target editable? if(isEditable()){ action=inquireDNDAction(); // Check for legal DND action if(action==DRAG_COPY || action==DRAG_MOVE){ // Get the suggested drop position pos=getPosAt(event->win_x,event->win_y); // Move cursor to new position setCursorPos(pos,TRUE); makePositionVisible(pos); // We don't accept a drop on the selection if(!isPosSelected(pos)){ acceptDrop(DRAG_ACCEPT); } } } return 1; } // Didn't handle it here return 0; } // Handle drag-and-drop drop long FXMathText::onDNDDrop(FXObject* sender,FXSelector sel,void* ptr){ FXuchar *data,*junk; FXuint len,dum; // Stop scrolling stopAutoScroll(); drawCursor(0); // Try handling it in base class first if(FXScrollArea::onDNDDrop(sender,sel,ptr)) return 1; // Should really not have gotten this if non-editable if(isEditable()){ // Try handle here if(getDNDData(FROM_DRAGNDROP,textType,data,len)){ FXRESIZE(&data,FXchar,len+1); data[len]='\0'; // Need to ask the source to delete his copy if(inquireDNDAction()==DRAG_MOVE){ getDNDData(FROM_DRAGNDROP,deleteType,junk,dum); FXASSERT(!junk); } // Insert the new text handle(this,FXSEL(SEL_COMMAND,ID_INSERT_STRING),(void*)data); FXFREE(&data); } return 1; } return 0; } // Service requested DND data long FXMathText::onDNDRequest(FXObject* sender,FXSelector sel,void* ptr){ FXEvent *event=(FXEvent*)ptr; FXuchar *data; FXuint len; // Perhaps the target wants to supply its own data if(FXScrollArea::onDNDRequest(sender,sel,ptr)) return 1; // Return dragged text if(event->target==textType){ len=selendpos-selstartpos; FXMALLOC(&data,FXuchar,len); extractText((FXchar*)data,selstartpos,len); setDNDData(FROM_DRAGNDROP,textType,data,len); return 1; } // Delete dragged text if(event->target==deleteType){ if(isEditable()){ handle(this,FXSEL(SEL_COMMAND,ID_DELETE_SEL),NULL); } return 1; } return 0; } /*******************************************************************************/ // We now really do have the selection long FXMathText::onSelectionGained(FXObject* sender,FXSelector sel,void* ptr){ FXScrollArea::onSelectionGained(sender,sel,ptr); return 1; } // We lost the selection somehow long FXMathText::onSelectionLost(FXObject* sender,FXSelector sel,void* ptr){ FXint what[2]; FXScrollArea::onSelectionLost(sender,sel,ptr); if(target){ what[0]=selstartpos; what[1]=selendpos-selstartpos; target->tryHandle(this,FXSEL(SEL_DESELECTED,message),(void*)what); } updateRange(selstartpos,selendpos); selstartpos=0; selendpos=0; return 1; } // Somebody wants our selection long FXMathText::onSelectionRequest(FXObject* sender,FXSelector sel,void* ptr){ FXEvent *event=(FXEvent*)ptr; FXchar *data; FXint len; // Perhaps the target wants to supply its own data for the selection if(FXScrollArea::onSelectionRequest(sender,sel,ptr)) return 1; // Return text of the selection if(event->target==stringType || event->target==textType){ len=selendpos-selstartpos; FXMALLOC(&data,FXchar,len); extractText(data,selstartpos,len); #ifdef WIN32 fxtoDOS(data,len); #endif setDNDData(FROM_SELECTION,event->target,(FXuchar*)data,(FXuint)len); return 1; } return 0; } /*******************************************************************************/ // We now really do have the selection long FXMathText::onClipboardGained(FXObject* sender,FXSelector sel,void* ptr){ FXScrollArea::onClipboardGained(sender,sel,ptr); return 1; } // We lost the selection somehow long FXMathText::onClipboardLost(FXObject* sender,FXSelector sel,void* ptr){ FXScrollArea::onClipboardLost(sender,sel,ptr); FXFREE(&clipbuffer); clipbuffer=NULL; cliplength=0; return 1; } // Somebody wants our selection long FXMathText::onClipboardRequest(FXObject* sender,FXSelector sel,void* ptr){ FXEvent *event=(FXEvent*)ptr; FXchar *data; FXint len; // Try handling it in base class first if(FXScrollArea::onClipboardRequest(sender,sel,ptr)) return 1; // Requested data from clipboard if(event->target==stringType || event->target==textType){ len=cliplength; FXMALLOC(&data,FXchar,len); memcpy(data,clipbuffer,len); #ifdef WIN32 fxtoDOS(data,len); #endif setDNDData(FROM_CLIPBOARD,event->target,(FXuchar*)data,(FXuint)len); return 1; } return 0; } /*******************************************************************************/ // Keyboard press long FXMathText::onKeyPress(FXObject*,FXSelector,void* ptr){ FXEvent* event=(FXEvent*)ptr; flags&=~FLAG_TIP; if(!isEnabled()) return 0; FXTRACE((200,"%s::onKeyPress keysym=0x%04x state=%04x\n",getClassName(),event->code,event->state)); if(target && target->tryHandle(this,FXSEL(SEL_KEYPRESS,message),ptr)) return 1; flags&=~FLAG_UPDATE; switch(event->code){ case KEY_Shift_L: case KEY_Shift_R: case KEY_Control_L: case KEY_Control_R: if(mode==MOUSE_DRAG){handle(this,FXSEL(SEL_DRAGGED,0),ptr);} return 1; case KEY_Up: case KEY_KP_Up: if(event->state&CONTROLMASK){ handle(this,FXSEL(SEL_COMMAND,ID_SCROLL_UP),NULL); } else{ if(!(event->state&SHIFTMASK)){ handle(this,FXSEL(SEL_COMMAND,ID_DESELECT_ALL),NULL); } handle(this,FXSEL(SEL_COMMAND,ID_CURSOR_UP),NULL); if(event->state&SHIFTMASK){ handle(this,FXSEL(SEL_COMMAND,ID_EXTEND),NULL); } else{ handle(this,FXSEL(SEL_COMMAND,ID_MARK),NULL); } } return 1; case KEY_Down: case KEY_KP_Down: if(event->state&CONTROLMASK){ handle(this,FXSEL(SEL_COMMAND,ID_SCROLL_DOWN),NULL); } else{ if(!(event->state&SHIFTMASK)){ handle(this,FXSEL(SEL_COMMAND,ID_DESELECT_ALL),NULL); } handle(this,FXSEL(SEL_COMMAND,ID_CURSOR_DOWN),NULL); if(event->state&SHIFTMASK){ handle(this,FXSEL(SEL_COMMAND,ID_EXTEND),NULL); } else{ handle(this,FXSEL(SEL_COMMAND,ID_MARK),NULL); } } return 1; case KEY_Left: case KEY_KP_Left: if(!(event->state&SHIFTMASK)){ handle(this,FXSEL(SEL_COMMAND,ID_DESELECT_ALL),NULL); } if(event->state&CONTROLMASK){ handle(this,FXSEL(SEL_COMMAND,ID_CURSOR_WORD_LEFT),NULL); } else{ handle(this,FXSEL(SEL_COMMAND,ID_CURSOR_LEFT),NULL); } if(event->state&SHIFTMASK){ handle(this,FXSEL(SEL_COMMAND,ID_EXTEND),NULL); } else{ handle(this,FXSEL(SEL_COMMAND,ID_MARK),NULL); } return 1; case KEY_Right: case KEY_KP_Right: if(!(event->state&SHIFTMASK)){ handle(this,FXSEL(SEL_COMMAND,ID_DESELECT_ALL),NULL); } if(event->state&CONTROLMASK){ handle(this,FXSEL(SEL_COMMAND,ID_CURSOR_WORD_RIGHT),NULL); } else{ handle(this,FXSEL(SEL_COMMAND,ID_CURSOR_RIGHT),NULL); } if(event->state&SHIFTMASK){ handle(this,FXSEL(SEL_COMMAND,ID_EXTEND),NULL); } else{ handle(this,FXSEL(SEL_COMMAND,ID_MARK),NULL); } return 1; case KEY_Home: case KEY_KP_Home: if(!(event->state&SHIFTMASK)){ handle(this,FXSEL(SEL_COMMAND,ID_DESELECT_ALL),NULL); } if(event->state&CONTROLMASK){ handle(this,FXSEL(SEL_COMMAND,ID_CURSOR_TOP),NULL); } else{ handle(this,FXSEL(SEL_COMMAND,ID_CURSOR_HOME),NULL); } if(event->state&SHIFTMASK){ handle(this,FXSEL(SEL_COMMAND,ID_EXTEND),NULL); } else{ handle(this,FXSEL(SEL_COMMAND,ID_MARK),NULL); } return 1; case KEY_End: case KEY_KP_End: if(!(event->state&SHIFTMASK)){ handle(this,FXSEL(SEL_COMMAND,ID_DESELECT_ALL),NULL); } if(event->state&CONTROLMASK){ handle(this,FXSEL(SEL_COMMAND,ID_CURSOR_BOTTOM),NULL); } else{ handle(this,FXSEL(SEL_COMMAND,ID_CURSOR_END),NULL); } if(event->state&SHIFTMASK){ handle(this,FXSEL(SEL_COMMAND,ID_EXTEND),NULL); } else{ handle(this,FXSEL(SEL_COMMAND,ID_MARK),NULL); } return 1; case KEY_Page_Up: case KEY_KP_Page_Up: if(!(event->state&SHIFTMASK)){ handle(this,FXSEL(SEL_COMMAND,ID_DESELECT_ALL),NULL); } handle(this,FXSEL(SEL_COMMAND,ID_CURSOR_PAGEUP),NULL); if(event->state&SHIFTMASK){ handle(this,FXSEL(SEL_COMMAND,ID_EXTEND),NULL); } else{ handle(this,FXSEL(SEL_COMMAND,ID_MARK),NULL); } return 1; case KEY_Page_Down: case KEY_KP_Page_Down: if(!(event->state&SHIFTMASK)){ handle(this,FXSEL(SEL_COMMAND,ID_DESELECT_ALL),NULL); } handle(this,FXSEL(SEL_COMMAND,ID_CURSOR_PAGEDOWN),NULL); if(event->state&SHIFTMASK){ handle(this,FXSEL(SEL_COMMAND,ID_EXTEND),NULL); } else{ handle(this,FXSEL(SEL_COMMAND,ID_MARK),NULL); } return 1; case KEY_Insert: case KEY_KP_Insert: if(event->state&CONTROLMASK){ handle(this,FXSEL(SEL_COMMAND,ID_COPY_SEL),NULL); } else if(event->state&SHIFTMASK){ if(isEditable()){ handle(this,FXSEL(SEL_COMMAND,ID_PASTE_SEL),NULL); } else{ getApp()->beep(); } } else{ handle(this,FXSEL(SEL_COMMAND,ID_TOGGLE_OVERSTRIKE),NULL); } return 1; case KEY_Delete: case KEY_KP_Delete: if(isEditable()){ if(isPosSelected(cursorpos)){ if(event->state&SHIFTMASK){ handle(this,FXSEL(SEL_COMMAND,ID_CUT_SEL),NULL); } else{ handle(this,FXSEL(SEL_COMMAND,ID_DELETE_SEL),NULL); } } else{ handle(this,FXSEL(SEL_COMMAND,ID_DESELECT_ALL),NULL); if(event->state&CONTROLMASK){ handle(this,FXSEL(SEL_COMMAND,ID_DELETE_WORD),NULL); } else if(event->state&SHIFTMASK){ handle(this,FXSEL(SEL_COMMAND,ID_DELETE_EOL),NULL); } else{ handle(this,FXSEL(SEL_COMMAND,ID_DELETE),NULL); } } } else{ getApp()->beep(); } return 1; case KEY_BackSpace: if(isEditable()){ if(isPosSelected(cursorpos)){ handle(this,FXSEL(SEL_COMMAND,ID_DELETE_SEL),NULL); } else{ handle(this,FXSEL(SEL_COMMAND,ID_DESELECT_ALL),NULL); if(event->state&CONTROLMASK){ handle(this,FXSEL(SEL_COMMAND,ID_BACKSPACE_WORD),NULL); } else if(event->state&SHIFTMASK){ handle(this,FXSEL(SEL_COMMAND,ID_BACKSPACE_BOL),NULL); } else{ handle(this,FXSEL(SEL_COMMAND,ID_BACKSPACE),NULL); } } } else{ getApp()->beep(); } return 1; case KEY_Return: case KEY_KP_Enter: if(isEditable()){ handle(this,FXSEL(SEL_COMMAND,ID_INSERT_NEWLINE),NULL); } else{ getApp()->beep(); } return 1; case KEY_Tab: case KEY_KP_Tab: if(isEditable()){ if(event->state&CONTROLMASK){ handle(this,FXSEL(SEL_COMMAND,ID_INSERT_STRING),(void*)"\t"); } else{ handle(this,FXSEL(SEL_COMMAND,ID_INSERT_TAB),NULL); } } else{ getApp()->beep(); } return 1; case KEY_a: if(!(event->state&CONTROLMASK)) goto ins; handle(this,FXSEL(SEL_COMMAND,ID_SELECT_ALL),NULL); return 1; case KEY_x: if(!(event->state&CONTROLMASK)) goto ins; case KEY_F20: // Sun Cut key if(isEditable()){ handle(this,FXSEL(SEL_COMMAND,ID_CUT_SEL),NULL); } else{ getApp()->beep(); } return 1; case KEY_c: if(!(event->state&CONTROLMASK)) goto ins; case KEY_F16: // Sun Copy key handle(this,FXSEL(SEL_COMMAND,ID_COPY_SEL),NULL); return 1; case KEY_v: if(!(event->state&CONTROLMASK)) goto ins; case KEY_F18: // Sun Paste key if(isEditable()){ handle(this,FXSEL(SEL_COMMAND,ID_PASTE_SEL),NULL); } else{ getApp()->beep(); } return 1; default: ins: if((event->state&(CONTROLMASK|ALTMASK)) || ((FXuchar)event->text[0]<32)) return 0; if(isEditable()){ if(options&TEXT_OVERSTRIKE){ handle(this,FXSEL(SEL_COMMAND,ID_OVERST_STRING),(void*)event->text.text()); } else{ handle(this,FXSEL(SEL_COMMAND,ID_INSERT_STRING),(void*)event->text.text()); } } else{ getApp()->beep(); } return 1; } return 0; } // Keyboard release long FXMathText::onKeyRelease(FXObject*,FXSelector,void* ptr){ FXEvent* event=(FXEvent*)ptr; if(!isEnabled()) return 0; FXTRACE((200,"%s::onKeyRelease keysym=0x%04x state=%04x\n",getClassName(),event->code,event->state)); if(target && target->tryHandle(this,FXSEL(SEL_KEYRELEASE,message),ptr)) return 1; switch(event->code){ case KEY_Shift_L: case KEY_Shift_R: case KEY_Control_L: case KEY_Control_R: if(mode==MOUSE_DRAG){handle(this,FXSEL(SEL_DRAGGED,0),ptr);} return 1; } return 0; } /*******************************************************************************/ // Move cursor to top of buffer long FXMathText::onCmdCursorTop(FXObject*,FXSelector,void*){ setCursorPos(0,TRUE); makePositionVisible(cursorpos); flashMatching(); return 1; } // Move cursor to bottom of buffer long FXMathText::onCmdCursorBottom(FXObject*,FXSelector,void*){ setCursorPos(length,TRUE); makePositionVisible(cursorpos); flashMatching(); return 1; } // Move cursor to begin of line (well, beginning of ROW?) long FXMathText::onCmdCursorHome(FXObject*,FXSelector,void*){ setCursorPos(rowStart(cursorpos),TRUE); makePositionVisible(cursorpos); flashMatching(); return 1; } // Move cursor to end of line (ROW?) long FXMathText::onCmdCursorEnd(FXObject*,FXSelector,void*){ setCursorPos(rowEnd(cursorpos),TRUE); makePositionVisible(cursorpos); flashMatching(); return 1; } // Move cursor right long FXMathText::onCmdCursorRight(FXObject*,FXSelector,void*){ if(cursorpos>=length) return 1; // skip over body of a maths line... if(getChar(cursorpos)==0x02) cursorpos=lineEnd(cursorpos); setCursorPos(cursorpos+1,TRUE); makePositionVisible(cursorpos); flashMatching(); return 1; } // Move cursor left // @@@@@ move back onto maths long FXMathText::onCmdCursorLeft(FXObject*,FXSelector,void*){ if(cursorpos<=0) return 1; setCursorPos(cursorpos-1,TRUE); makePositionVisible(cursorpos); flashMatching(); return 1; } // Move cursor to previous line (or ROW???) long FXMathText::onCmdCursorUp(FXObject*,FXSelector,void*){ FXint newrow,newpos,col; col=(0<=prefcol) ? prefcol : cursorcol; newrow=prevRow(cursorpos); newpos=posFromIndent(newrow,col); setCursorPos(newpos,TRUE); makePositionVisible(cursorpos); flashMatching(); prefcol=col; return 1; } // Move cursor to next line (or ROW???) long FXMathText::onCmdCursorDown(FXObject*,FXSelector,void*){ FXint newrow,newpos,col; col=(0<=prefcol) ? prefcol : cursorcol; newrow=nextRow(cursorpos); newpos=posFromIndent(newrow,col); setCursorPos(newpos,TRUE); makePositionVisible(cursorpos); flashMatching(); prefcol=col; return 1; } // Page down long FXMathText::onCmdCursorPageDown(FXObject*,FXSelector,void*){ FXint newrow,newpos,col; col=(0<=prefcol) ? prefcol : cursorcol; newrow=nextRow(cursorpos,(viewport_h)/font->getFontHeight()); newpos=posFromIndent(newrow,col); setTopLine(nextRow(toppos,viewport_h/font->getFontHeight())); setCursorPos(newpos,TRUE); makePositionVisible(cursorpos); prefcol=col; return 1; } // Page up long FXMathText::onCmdCursorPageUp(FXObject*,FXSelector,void*){ FXint newrow,newpos,col; col=(0<=prefcol) ? prefcol : cursorcol; newrow=prevRow(cursorpos,(viewport_h)/font->getFontHeight()); newpos=posFromIndent(newrow,col); setTopLine(prevRow(toppos,viewport_h/font->getFontHeight())); setCursorPos(newpos,TRUE); makePositionVisible(cursorpos); prefcol=col; return 1; } // Word Left // @@@@@ over line boundary into maths? long FXMathText::onCmdCursorWordLeft(FXObject*,FXSelector,void*){ setCursorPos(leftWord(cursorpos),TRUE); makePositionVisible(cursorpos); flashMatching(); return 1; } // Word Right // @@@@@ over line boundary into maths? long FXMathText::onCmdCursorWordRight(FXObject*,FXSelector,void*){ setCursorPos(rightWord(cursorpos),TRUE); makePositionVisible(cursorpos); flashMatching(); return 1; } // Word Start long FXMathText::onCmdCursorWordStart(FXObject*,FXSelector,void*){ setCursorPos(wordStart(cursorpos),TRUE); makePositionVisible(cursorpos); flashMatching(); return 1; } // Word End long FXMathText::onCmdCursorWordEnd(FXObject*,FXSelector,void*){ setCursorPos(wordEnd(cursorpos),TRUE); makePositionVisible(cursorpos); flashMatching(); return 1; } // Cursor pos to top of screen long FXMathText::onCmdCursorScreenTop(FXObject*,FXSelector,void*){ setTopLine(cursorpos); return 1; } // Cursor pos to bottom of screen long FXMathText::onCmdCursorScreenBottom(FXObject*,FXSelector,void*){ setBottomLine(cursorpos); return 1; } // Cursor pos to center of screen long FXMathText::onCmdCursorScreenCenter(FXObject*,FXSelector,void*){ setCenterLine(cursorpos); return 1; } // Scroll up one line, leaving cursor in place long FXMathText::onCmdScrollUp(FXObject*,FXSelector,void*){ setTopLine(prevRow(toppos,1)); return 1; } // Scroll down one line, leaving cursor in place long FXMathText::onCmdScrollDown(FXObject*,FXSelector,void*){ setTopLine(nextRow(toppos,1)); return 1; } // Move cursor to begin of paragraph long FXMathText::onCmdCursorParHome(FXObject*,FXSelector,void*){ setCursorPos(lineStart(cursorpos),TRUE); makePositionVisible(cursorpos); return 1; } // Move cursor to end of paragraph long FXMathText::onCmdCursorParEnd(FXObject*,FXSelector,void*){ setCursorPos(lineEnd(cursorpos),TRUE); makePositionVisible(cursorpos); return 1; } // Mark long FXMathText::onCmdMark(FXObject*,FXSelector,void*){ setAnchorPos(cursorpos); return 1; } // Extend long FXMathText::onCmdExtend(FXObject*,FXSelector,void*){ extendSelection(cursorpos,SELECT_CHARS,TRUE); return 1; } // Overstrike a string long FXMathText::onCmdOverstString(FXObject*,FXSelector,void* ptr){ FXint sindent,oindent,nindent,pos,ch,reppos,replen; FXchar* string=(FXchar*)ptr; FXint len=strlen(string); if(!isEditable()) return 1; if(isPosSelected(cursorpos)){ reppos=selstartpos; replen=selendpos-selstartpos; } else{ sindent=0; pos=lineStart(cursorpos); while(pos<cursorpos){ // Measure indent of reppos if(getChar(pos)=='\t') sindent+=(tabcolumns-sindent%tabcolumns); else sindent+=1; pos++; } nindent=sindent; pos=0; while(pos<len){ // Measure indent of new string if(string[pos]=='\t') nindent+=(tabcolumns-nindent%tabcolumns); else nindent+=1; pos++; } oindent=sindent; pos=cursorpos; while(pos<length && (ch=getChar(pos))!='\n'){ // Measure indent of old string if(ch=='\t') oindent+=(tabcolumns-oindent%tabcolumns); else oindent+=1; if(oindent==nindent){ // Same indent pos++; // Include last character break; } if(oindent>nindent){ // Greater indent if(ch!='\t') pos++; // Don't include last character if it was a tab break; } pos++; } reppos=cursorpos; replen=pos-reppos; } replaceText(reppos,replen,string,len,TRUE); killSelection(TRUE); setCursorPos(reppos+len,TRUE); makePositionVisible(cursorpos); flashMatching(); flags|=FLAG_CHANGED; modified=TRUE; return 1; } // Insert a string long FXMathText::onCmdInsertString(FXObject*,FXSelector,void* ptr){ FXchar* string=(FXchar*)ptr; FXint len=strlen(string); FXint reppos=cursorpos; FXint replen=0; if(!isEditable()) return 1; if(isPosSelected(cursorpos)){ reppos=selstartpos; replen=selendpos-selstartpos; } replaceText(reppos,replen,string,len,TRUE); killSelection(TRUE); setCursorPos(reppos+len,TRUE); makePositionVisible(cursorpos); flashMatching(); flags|=FLAG_CHANGED; modified=TRUE; return 1; } // Insert a character long FXMathText::onCmdInsertNewline(FXObject*,FXSelector,void*){ FXint reppos=cursorpos; FXint replen=0; FXint len=1; if(!isEditable()) return 1; if(isPosSelected(cursorpos)){ reppos=selstartpos; replen=selendpos-selstartpos; } if(options&TEXT_AUTOINDENT){ FXint start=lineStart(reppos); FXint end=start; FXchar *string; while(end<reppos){ if(!isspace(getChar(end))) break; end++; } len=end-start+1; FXMALLOC(&string,FXchar,len); string[0]='\n'; extractText(&string[1],start,end-start); replaceText(reppos,replen,string,len,TRUE); FXFREE(&string); } else{ replaceText(reppos,replen,"\n",1,TRUE); } setCursorPos(reppos+len,TRUE); makePositionVisible(cursorpos); flags|=FLAG_CHANGED; modified=TRUE; return 1; } // Insert a character long FXMathText::onCmdInsertTab(FXObject*,FXSelector,void*){ FXint reppos=cursorpos; FXint replen=0; FXint len=1; if(!isEditable()) return 1; if(isPosSelected(cursorpos)){ reppos=selstartpos; replen=selendpos-selstartpos; } if(options&TEXT_NO_TABS){ FXint start=lineStart(reppos); FXint indent=0; FXchar *string; while(start<reppos){ if(getChar(start)=='\t') indent+=(tabcolumns-indent%tabcolumns); else indent+=1; start++; } len=tabcolumns-indent%tabcolumns; FXMALLOC(&string,FXchar,len); memset(string,' ',len); replaceText(reppos,replen,string,len,TRUE); FXFREE(&string); } else{ replaceText(reppos,replen,"\t",1,TRUE); } setCursorPos(reppos+len,TRUE); makePositionVisible(cursorpos); flags|=FLAG_CHANGED; modified=TRUE; return 1; } // Cut // @@@@@ cutting maths? long FXMathText::onCmdCutSel(FXObject*,FXSelector,void*){ FXDragType types[2]; if(selstartpos<selendpos){ if(isEditable()){ types[0]=stringType; types[1]=textType; if(acquireClipboard(types,2)){ FXFREE(&clipbuffer); FXASSERT(selstartpos<=selendpos); cliplength=selendpos-selstartpos; FXCALLOC(&clipbuffer,FXchar,cliplength+1); if(!clipbuffer){ fxwarning("%s::onCmdCutSel: out of memory\n",getClassName()); cliplength=0; } else{ extractText(clipbuffer,selstartpos,cliplength); handle(this,FXSEL(SEL_COMMAND,ID_DELETE_SEL),NULL); } } } else{ getApp()->beep(); } } return 1; } // Copy // @@@@@ copying maths? long FXMathText::onCmdCopySel(FXObject*,FXSelector,void*){ FXDragType types[2]; if(selstartpos<selendpos){ types[0]=stringType; types[1]=textType; if(acquireClipboard(types,2)){ FXFREE(&clipbuffer); FXASSERT(selstartpos<=selendpos); cliplength=selendpos-selstartpos; FXCALLOC(&clipbuffer,FXchar,cliplength+1); if(!clipbuffer){ fxwarning("%s::onCmdCopySel: out of memory\n",getClassName()); cliplength=0; } else{ extractText(clipbuffer,selstartpos,cliplength); } } } return 1; } // Delete selection // @@@@@ can maths be selected? long FXMathText::onCmdDeleteSel(FXObject*,FXSelector,void*){ if(selstartpos<selendpos){ if(isEditable()){ removeText(selstartpos,selendpos-selstartpos,TRUE); killSelection(TRUE); setCursorPos(cursorpos,TRUE); makePositionVisible(cursorpos); flags|=FLAG_CHANGED; modified=TRUE; } else{ getApp()->beep(); } } return 1; } // Paste clipboard long FXMathText::onCmdPasteSel(FXObject*,FXSelector,void*){ FXchar *data; FXint reppos,replen,len; if(isEditable()){ if(getDNDData(FROM_CLIPBOARD,stringType,(FXuchar*&)data,(FXuint&)len)){ #ifdef WIN32 fxfromDOS(data,len); #endif reppos=cursorpos; replen=0; if(isPosSelected(cursorpos)){ reppos=selstartpos; replen=selendpos-selstartpos; } replaceText(reppos,replen,data,len,TRUE); FXFREE(&data); killSelection(TRUE); setCursorPos(reppos+len,TRUE); makePositionVisible(cursorpos); flashMatching(); flags|=FLAG_CHANGED; modified=TRUE; } } else{ getApp()->beep(); } return 1; } // Paste selection long FXMathText::onCmdPasteMiddle(FXObject*,FXSelector,void*){ FXchar *string; FXint len; if(selstartpos==selendpos || cursorpos<=selstartpos || selendpos<=cursorpos){ // Avoid paste inside selection if(isEditable()){ if(getDNDData(FROM_SELECTION,stringType,(FXuchar*&)string,(FXuint&)len)){ #ifdef WIN32 fxfromDOS(string,len); #endif insertText(cursorpos,string,len,TRUE); // Don't kill selection; we may paste again FXFREE(&string); setCursorPos(cursorpos,TRUE); makePositionVisible(cursorpos); flashMatching(); flags|=FLAG_CHANGED; modified=TRUE; } } else{ getApp()->beep(); } } return 1; } // Select character long FXMathText::onCmdSelectChar(FXObject*,FXSelector,void*){ setAnchorPos(cursorpos); extendSelection(cursorpos+1,SELECT_CHARS,TRUE); return 1; } // Select Word long FXMathText::onCmdSelectWord(FXObject*,FXSelector,void*){ setAnchorPos(cursorpos); extendSelection(cursorpos,SELECT_WORDS,TRUE); return 1; } // Select Line long FXMathText::onCmdSelectLine(FXObject*,FXSelector,void*){ setAnchorPos(cursorpos); extendSelection(cursorpos,SELECT_LINES,TRUE); return 1; } // Select All long FXMathText::onCmdSelectAll(FXObject*,FXSelector,void*){ setAnchorPos(0); extendSelection(length,SELECT_CHARS,TRUE); return 1; } // Deselect All long FXMathText::onCmdDeselectAll(FXObject*,FXSelector,void*){ killSelection(TRUE); return 1; } // Backspace character long FXMathText::onCmdBackspace(FXObject*,FXSelector,void*){ if(!isEditable()) return 1; if(cursorpos==0){ getApp()->beep(); return 1; } removeText(cursorpos-1,1,TRUE); setCursorPos(cursorpos,TRUE); makePositionVisible(cursorpos); flags|=FLAG_CHANGED; modified=TRUE; return 1; } // Backspace word long FXMathText::onCmdBackspaceWord(FXObject*,FXSelector,void*){ if(!isEditable()) return 1; FXint pos=leftWord(cursorpos); removeText(pos,cursorpos-pos,TRUE); setCursorPos(cursorpos,TRUE); makePositionVisible(cursorpos); flags|=FLAG_CHANGED; modified=TRUE; return 1; } // Backspace bol long FXMathText::onCmdBackspaceBol(FXObject*,FXSelector,void*){ if(!isEditable()) return 1; FXint pos=rowStart(cursorpos); removeText(pos,cursorpos-pos,TRUE); setCursorPos(cursorpos,TRUE); makePositionVisible(cursorpos); flags|=FLAG_CHANGED; modified=TRUE; return 1; } // Delete character long FXMathText::onCmdDelete(FXObject*,FXSelector,void*){ if(!isEditable()) return 1; if(cursorpos==length){ getApp()->beep(); return 1; } removeText(cursorpos,1,TRUE); setCursorPos(cursorpos,TRUE); makePositionVisible(cursorpos); flags|=FLAG_CHANGED; modified=TRUE; return 1; } // Delete word long FXMathText::onCmdDeleteWord(FXObject*,FXSelector,void*){ if(!isEditable()) return 1; FXint num=rightWord(cursorpos)-cursorpos; removeText(cursorpos,num,TRUE); setCursorPos(cursorpos,TRUE); makePositionVisible(cursorpos); flags|=FLAG_CHANGED; modified=TRUE; return 1; } // Delete to end of line long FXMathText::onCmdDeleteEol(FXObject*,FXSelector,void*){ if(!isEditable()) return 1; FXint num=rowEnd(cursorpos)-cursorpos; removeText(cursorpos,num,TRUE); setCursorPos(cursorpos,TRUE); makePositionVisible(cursorpos); flags|=FLAG_CHANGED; modified=TRUE; return 1; } // Delete line long FXMathText::onCmdDeleteLine(FXObject*,FXSelector,void*){ if(!isEditable()) return 1; FXint pos=rowStart(cursorpos); FXint num=nextRow(cursorpos)-pos; removeText(pos,num,TRUE); setCursorPos(cursorpos,TRUE); makePositionVisible(cursorpos); flags|=FLAG_CHANGED; modified=TRUE; return 1; } // Delete all text long FXMathText::onCmdDeleteAll(FXObject*,FXSelector,void*){ if(!isEditable()) return 1; removeText(0,length,TRUE); setCursorPos(0,TRUE); makePositionVisible(0); flags|=FLAG_CHANGED; modified=TRUE; return 1; } // Make selected text upper case long FXMathText::onCmdChangeCase(FXObject*,FXSelector sel,void*){ register FXint i,pos,num; FXchar *text; if(!isEditable()) return 1; pos=selstartpos; num=selendpos-selstartpos; FXMALLOC(&text,FXchar,num); extractText(text,pos,num); if(FXSELID(sel)==ID_UPPER_CASE){ for(i=0; i<num; i++) text[i]=toupper((FXuchar)text[i]); } else{ for(i=0; i<num; i++) text[i]=tolower((FXuchar)text[i]); } replaceText(pos,num,text,num,TRUE); setCursorPos(cursorpos,TRUE); makePositionVisible(cursorpos); setSelection(pos,num,TRUE); FXFREE(&text); flags|=FLAG_CHANGED; modified=TRUE; return 1; } // Shift text by certain amount FXint FXMathText::shiftText(FXint start,FXint end,FXint amount,FXbool notify){ FXint white,p,len,size,c; FXchar *text; if(start<0) start=0; if(end>length) end=length; FXASSERT(0<tabcolumns); if(start<end){ p=start; white=0; size=0; while(p<end){ c=getChar(p++); if(c==' '){ white++; } else if(c=='\t'){ white+=(tabcolumns-white%tabcolumns); } else if(c=='\n'){ size++; white=0; } else{ white+=amount; if(white<0) white=0; if(!(options&TEXT_NO_TABS)){ size+=(white/tabcolumns+white%tabcolumns); } else { size+=white; } size++; while(p<end){ c=getChar(p++); size++; if(c=='\n') break; } white=0; } } FXMALLOC(&text,FXchar,size); p=start; white=0; len=0; while(p<end){ c=getChar(p++); if(c==' '){ white++; } else if(c=='\t'){ white+=(tabcolumns-white%tabcolumns); } else if(c=='\n'){ text[len++]='\n'; white=0; } else{ white+=amount; if(white<0) white=0; if(!(options&TEXT_NO_TABS)){ while(white>=tabcolumns){ text[len++]='\t'; white-=tabcolumns;} } while(white>0){ text[len++]=' '; white--; } text[len++]=c; while(p<end){ c=getChar(p++); text[len++]=c; if(c=='\n') break; } white=0; } } FXASSERT(len<=size); replaceText(start,end-start,text,len,notify); FXFREE(&text); return len; } return 0; } // Shift selected lines left or right long FXMathText::onCmdShiftText(FXObject*,FXSelector sel,void*){ FXint start,end,len,amount; if(!isEditable()) return 1; amount=0; switch(FXSELID(sel)){ case ID_SHIFT_LEFT: amount=-1; break; case ID_SHIFT_RIGHT: amount=1; break; case ID_SHIFT_TABLEFT: amount=-tabcolumns; break; case ID_SHIFT_TABRIGHT: amount=tabcolumns; break; } if(selstartpos<selendpos){ FXASSERT(0<=selstartpos && selstartpos<=length); FXASSERT(0<=selendpos && selendpos<=length); start=lineStart(selstartpos); end=selendpos; if(0<end && getChar(end-1)!='\n') end=nextLine(end); } else{ start=lineStart(cursorpos); end=lineEnd(cursorpos); if(end<length) end++; } len=shiftText(start,end,amount,TRUE); setAnchorPos(start); extendSelection(start+len,SELECT_CHARS,TRUE); setCursorPos(start,TRUE); flags|=FLAG_CHANGED; modified=TRUE; return 1; } // Goto matching character long FXMathText::onCmdGotoMatching(FXObject*,FXSelector,void*){ if(0<cursorpos){ FXchar ch=getChar(cursorpos-1); FXint pos=findMatching(cursorpos-1,0,length,ch,1); if(0<=pos){ setCursorPos(pos+1); makePositionVisible(cursorpos); return 1; } } getApp()->beep(); return 1; } // Select text till matching character long FXMathText::onCmdSelectMatching(FXObject*,FXSelector,void*){ if(0<cursorpos){ FXchar ch=getChar(cursorpos-1); FXint pos=findMatching(cursorpos-1,0,length,ch,1); if(0<=pos){ if(pos>cursorpos){ setAnchorPos(cursorpos-1); extendSelection(pos+1,SELECT_CHARS,TRUE); } else{ setAnchorPos(pos); extendSelection(cursorpos,SELECT_CHARS,TRUE); } return 1; } } getApp()->beep(); return 1; } static const FXchar righthand[]="}])>"; static const FXchar lefthand[]="{[(<"; // Select entire enclosing block long FXMathText::onCmdSelectBlock(FXObject*,FXSelector sel,void*){ FXint beg,end,what,level=1; while(1){ what=FXSELID(sel)-ID_SELECT_BRACE; beg=matchBackward(cursorpos-1,0,lefthand[what],righthand[what],level); end=matchForward(cursorpos,length,lefthand[what],righthand[what],level); if(0<=beg && beg<end){ if(isPosSelected(beg) && isPosSelected(end+1)){ level++; continue; } setAnchorPos(beg); extendSelection(end+1,SELECT_CHARS,TRUE); return 1; } getApp()->beep(); break; } return 1; } // Goto start of enclosing block long FXMathText::onCmdBlockBeg(FXObject*,FXSelector sel,void*){ FXint what=FXSELID(sel)-ID_LEFT_BRACE; FXint beg=cursorpos-1; if(0<beg){ if(getChar(beg)==lefthand[what]) beg--; FXint pos=matchBackward(beg,0,lefthand[what],righthand[what],1); if(0<=pos){ setCursorPos(pos+1); makePositionVisible(cursorpos); return 1; } } getApp()->beep(); return 1; } // Goto end of enclosing block long FXMathText::onCmdBlockEnd(FXObject*,FXSelector sel,void*){ FXint what=FXSELID(sel)-ID_RIGHT_BRACE; FXint start=cursorpos; if(start<length){ if(getChar(start)==righthand[what]) start++; FXint pos=matchForward(start,length,lefthand[what],righthand[what],1); if(0<=pos){ setCursorPos(pos); makePositionVisible(cursorpos); return 1; } } getApp()->beep(); return 1; } // Search for selected text long FXMathText::onCmdSearchSel(FXObject*,FXSelector sel,void*){ FXchar *data; FXint len; if(getDNDData(FROM_SELECTION,stringType,(FXuchar*&)data,(FXuint&)len)){ FXint pos=cursorpos; FXint beg,end; #ifdef WIN32 fxfromDOS(data,len); #endif searchstring.assign(data,len); searchflags=SEARCH_EXACT; FXFREE(&data); if(FXSELID(sel)==ID_SEARCH_FORW_SEL){ if(isPosSelected(pos)) pos=selendpos; searchflags&=~SEARCH_BACKWARD; } else{ if(isPosSelected(pos)) pos=selstartpos-1; searchflags|=SEARCH_BACKWARD; } if(findText(searchstring,&beg,&end,pos,searchflags|SEARCH_WRAP)){ if(beg!=selstartpos || end!=selendpos){ setAnchorPos(beg); extendSelection(end,SELECT_CHARS,TRUE); setCursorPos(end); makePositionVisible(beg); makePositionVisible(end); return 1; } } } getApp()->beep(); return 1; } // Search for next occurence long FXMathText::onCmdSearchNext(FXObject*,FXSelector sel,void*){ if(!searchstring.empty()){ FXint pos=cursorpos; FXint beg[10]; FXint end[10]; if(FXSELID(sel)==ID_SEARCH_FORW){ if(isPosSelected(pos)) pos=selendpos; searchflags&=~SEARCH_BACKWARD; } else{ if(isPosSelected(pos)) pos=selstartpos-1; searchflags|=SEARCH_BACKWARD; } if(findText(searchstring,beg,end,pos,searchflags|SEARCH_WRAP,10)){ if(beg[0]!=selstartpos || end[0]!=selendpos){ setAnchorPos(beg[0]); extendSelection(end[0],SELECT_CHARS,TRUE); setCursorPos(end[0]); makePositionVisible(beg[0]); makePositionVisible(end[0]); return 1; } } } getApp()->beep(); return 1; } // Search text long FXMathText::onCmdSearch(FXObject*,FXSelector,void*){ FXGIFIcon icon(getApp(),searchicon); FXSearchDialog searchdialog(this,"Search",&icon); FXint beg[10]; FXint end[10]; FXint pos; FXuint code; do{ code=searchdialog.execute(); if(code==FXSearchDialog::DONE) return 1; searchstring=searchdialog.getSearchText(); searchflags=searchdialog.getSearchMode(); pos=isPosSelected(cursorpos) ? (searchflags&SEARCH_BACKWARD) ? selstartpos-1 : selendpos : cursorpos; if(findText(searchstring,beg,end,pos,searchflags|SEARCH_WRAP,10)){ setAnchorPos(beg[0]); extendSelection(end[0],SELECT_CHARS,TRUE); setCursorPos(end[0],TRUE); makePositionVisible(beg[0]); makePositionVisible(end[0]); } else{ getApp()->beep(); } } while(code==FXSearchDialog::SEARCH_NEXT); return 1; } // Replace text; we assume that findText has called squeezegap()! long FXMathText::onCmdReplace(FXObject*,FXSelector,void*){ FXGIFIcon icon(getApp(),searchicon); FXReplaceDialog replacedialog(this,"Replace",&icon); FXint beg[10],end[10],fm,to,len,pos; FXuint searchflags,code; FXString searchstring; FXString replacestring; FXString replacevalue; do{ code=replacedialog.execute(); if(code==FXReplaceDialog::DONE) return 1; searchflags=replacedialog.getSearchMode(); searchstring=replacedialog.getSearchText(); replacestring=replacedialog.getReplaceText(); replacevalue=FXString::null; fm=-1; to=-1; if(code==FXReplaceDialog::REPLACE_ALL){ searchflags&=~SEARCH_BACKWARD; pos=0; while(findText(searchstring,beg,end,pos,searchflags,10)){ if(0<=fm) replacevalue.append(&buffer[pos],beg[0]-pos); replacevalue.append(FXRex::substitute(buffer,length,beg,end,replacestring,10)); if(fm<0) fm=beg[0]; to=end[0]; pos=end[0]; if(beg[0]==end[0]) pos++; } } else{ pos=isPosSelected(cursorpos) ? (searchflags&SEARCH_BACKWARD) ? selstartpos-1 : selendpos : cursorpos; if(findText(searchstring,beg,end,pos,searchflags|SEARCH_WRAP,10)){ replacevalue=FXRex::substitute(buffer,length,beg,end,replacestring,10); fm=beg[0]; to=end[0]; } } if(0<=fm){ len=replacevalue.length(); replaceText(fm,to-fm,replacevalue.text(),len,TRUE); setCursorPos(fm+len,TRUE); makePositionVisible(getCursorPos()); modified=TRUE; } else{ getApp()->beep(); } } while(code==FXReplaceDialog::REPLACE_NEXT); return 1; } // Goto selected line number long FXMathText::onCmdGotoSelected(FXObject*,FXSelector,void*){ FXchar *data; FXint len,row,i; if(getDNDData(FROM_SELECTION,stringType,(FXuchar*&)data,(FXuint&)len)){ #ifdef WIN32 fxfromDOS(data,len); #endif for(i=0; i<len && !isdigit((FXuchar)data[i]); i++); for(row=0; i<len && isdigit((FXuchar)data[i]); i++) row=row*10+(data[i]-'0'); FXFREE(&data); if(1<=row){ setCursorRow(row-1,TRUE); makePositionVisible(cursorpos); return 1; } } getApp()->beep(); return 1; } // Goto line number long FXMathText::onCmdGotoLine(FXObject*,FXSelector,void*){ FXGIFIcon icon(getApp(),gotoicon); FXint row=cursorrow+1; if(FXInputDialog::getInteger(row,this,"Goto Line","&Goto line number:",&icon,1,2147483647)){ update(); setCursorRow(row-1,TRUE); makePositionVisible(cursorpos); } return 1; } /*******************************************************************************/ // Editable toggle long FXMathText::onCmdToggleEditable(FXObject*,FXSelector,void*){ options^=TEXT_READONLY; return 1; } // Update editable toggle long FXMathText::onUpdToggleEditable(FXObject* sender,FXSelector,void*){ sender->handle(this,(options&TEXT_READONLY)?FXSEL(SEL_COMMAND,ID_UNCHECK):FXSEL(SEL_COMMAND,ID_CHECK),NULL); sender->handle(this,FXSEL(SEL_COMMAND,ID_SHOW),NULL); sender->handle(this,FXSEL(SEL_COMMAND,ID_ENABLE),NULL); return 1; } // Overstrike toggle long FXMathText::onCmdToggleOverstrike(FXObject*,FXSelector,void*){ options^=TEXT_OVERSTRIKE; return 1; } // Update overstrike toggle long FXMathText::onUpdToggleOverstrike(FXObject* sender,FXSelector,void*){ sender->handle(this,(options&TEXT_OVERSTRIKE)?FXSEL(SEL_COMMAND,ID_CHECK):FXSEL(SEL_COMMAND,ID_UNCHECK),NULL); sender->handle(this,FXSEL(SEL_COMMAND,ID_SHOW),NULL); sender->handle(this,FXSEL(SEL_COMMAND,ID_ENABLE),NULL); return 1; } // Move cursor to indicated row long FXMathText::onCmdCursorRow(FXObject* sender,FXSelector,void*){ FXint row=cursorrow+1; sender->handle(this,FXSEL(SEL_COMMAND,ID_GETINTVALUE),(void*)&row); setCursorRow(row-1,TRUE); makePositionVisible(cursorpos); return 1; } // Being asked about current row number long FXMathText::onUpdCursorRow(FXObject* sender,FXSelector,void*){ FXint row=cursorrow+1; sender->handle(this,FXSEL(SEL_COMMAND,ID_SETINTVALUE),(void*)&row); return 1; } // Move cursor to indicated column long FXMathText::onCmdCursorColumn(FXObject* sender,FXSelector,void*){ FXint col=cursorcol; sender->handle(this,FXSEL(SEL_COMMAND,ID_GETINTVALUE),(void*)&col); setCursorColumn(col,TRUE); makePositionVisible(cursorpos); return 1; } // Being asked about current column long FXMathText::onUpdCursorColumn(FXObject* sender,FXSelector,void*){ sender->handle(this,FXSEL(SEL_COMMAND,FXWindow::ID_SETINTVALUE),(void*)&cursorcol); return 1; } // Update somebody who works on the selection long FXMathText::onUpdHaveSelection(FXObject* sender,FXSelector,void*){ sender->handle(this,(selstartpos<selendpos)?FXSEL(SEL_COMMAND,ID_ENABLE):FXSEL(SEL_COMMAND,ID_DISABLE),NULL); return 1; } // Update somebody who works on the selection long FXMathText::onUpdSelectAll(FXObject* sender,FXSelector,void*){ sender->handle(this,(length==0)?FXSEL(SEL_COMMAND,ID_DISABLE):FXSEL(SEL_COMMAND,ID_ENABLE),NULL); return 1; } /*******************************************************************************/ // Draw fragment of text in given style // This gets overridden void FXMathText::drawBufferText(FXDCWindow& dc,FXint x,FXint y,FXint,FXint,FXint pos,FXint n,FXuint style) const { register FXuint index=(style&STYLE_MASK); register FXuint usedstyle=style; // Style flags from style buffer register FXColor color; FXchar str[2]; color=0; if(hilitestyles && index){ // Get colors from style table usedstyle=hilitestyles[index-1].style; // Style flags now from style table if(style&STYLE_SELECTED) color=hilitestyles[index-1].selectForeColor; else if(style&STYLE_HILITE) color=hilitestyles[index-1].hiliteForeColor; if(color==0) color=hilitestyles[index-1].normalForeColor; // Fall back on normal foreground color } if(color==0){ // Fall back to default style if(style&STYLE_SELECTED) color=seltextColor; else if(style&STYLE_HILITE) color=hilitetextColor; if(color==0) color=textColor; // Fall back to normal text color } dc.setForeground(color); if(style&STYLE_CONTROL){ y+=font->getFontAscent(); str[0]='^'; while(pos<gapstart && 0<n){ str[1]=buffer[pos]|0x40; dc.drawText(x,y,str,2); if(usedstyle&STYLE_BOLD) dc.drawText(x+1,y,str,2); x+=font->getTextWidth(str,2); pos++; n--; } while(0<n){ str[1]=buffer[pos-gapstart+gapend]|0x40; dc.drawText(x,y,str,2); if(usedstyle&STYLE_BOLD) dc.drawText(x+1,y,str,2); x+=font->getTextWidth(str,2); pos++; n--; } } else{ y+=font->getFontAscent(); if(pos+n<=gapstart){ dc.drawText(x,y,&buffer[pos],n); if(usedstyle&STYLE_BOLD) dc.drawText(x+1,y,&buffer[pos],n); } else if(pos>=gapstart){ dc.drawText(x,y,&buffer[pos-gapstart+gapend],n); if(usedstyle&STYLE_BOLD) dc.drawText(x+1,y,&buffer[pos-gapstart+gapend],n); } else{ dc.drawText(x,y,&buffer[pos],gapstart-pos); x+=font->getTextWidth(&buffer[pos],gapstart-pos); dc.drawText(x,y,&buffer[gapend],pos+n-gapstart); if(usedstyle&STYLE_BOLD) dc.drawText(x+1,y,&buffer[gapend],pos+n-gapstart); } } } // Fill fragment of background in given style void FXMathText::fillBufferRect(FXDCWindow& dc,FXint x,FXint y,FXint w,FXint h,FXuint style) const { register FXuint index=(style&STYLE_MASK); register FXuint usedstyle=style; // Style flags from style buffer register FXColor bgcolor,fgcolor; bgcolor=fgcolor=0; if(hilitestyles && index){ // Get colors from style table usedstyle=hilitestyles[index-1].style; // Style flags now from style table if(style&STYLE_SELECTED){ bgcolor=hilitestyles[index-1].selectBackColor; fgcolor=hilitestyles[index-1].selectForeColor; } else if(style&STYLE_HILITE){ bgcolor=hilitestyles[index-1].hiliteBackColor; fgcolor=hilitestyles[index-1].hiliteForeColor; } else if(style&STYLE_ACTIVE){ bgcolor=hilitestyles[index-1].activeBackColor; } else{ bgcolor=hilitestyles[index-1].normalBackColor; } if(fgcolor==0){ // Fall back to normal foreground color fgcolor=hilitestyles[index-1].normalForeColor; } } if(bgcolor==0){ // Fall back to default background colors if(style&STYLE_SELECTED) bgcolor=selbackColor; else if(style&STYLE_HILITE) bgcolor=hilitebackColor; else if(style&STYLE_ACTIVE) bgcolor=activebackColor; else bgcolor=backColor; } if(fgcolor==0){ // Fall back to default foreground colors if(style&STYLE_SELECTED) fgcolor=seltextColor; else if(style&STYLE_HILITE) fgcolor=hilitetextColor; if(fgcolor==0) fgcolor=textColor; // Fall back to text color } dc.setForeground(bgcolor); dc.fillRectangle(x,y,w,h); if(usedstyle&STYLE_UNDERLINE){ dc.setForeground(fgcolor); dc.fillRectangle(x,y+font->getFontAscent()+1,w,1); } if(usedstyle&STYLE_STRIKEOUT){ dc.setForeground(fgcolor); dc.fillRectangle(x,y+font->getFontAscent()/2,w,1); } } // Obtain text style at position pos; note pos may be outside of text // to allow for rectangular selections! FXuint FXMathText::style(FXint row,FXint,FXint end,FXint pos) const { register FXuint s=0; register FXchar ch; // Selected part of text if(selstartpos<=pos && pos<selendpos) s|=STYLE_SELECTED; // Highlighted part of text if(hilitestartpos<=pos && pos<hiliteendpos) s|=STYLE_HILITE; // Current active line if((row==cursorrow)&&(options&TEXT_SHOWACTIVE)) s|=STYLE_ACTIVE; // Blank part of line if(pos>=end) return s; // Special style for control characters ch=getChar(pos); // Get value from style buffer if(sbuffer) s|=getStyle(pos); // Tabs are just fill if(ch == '\t') return s; // Spaces are just fill if(ch == ' ') return s; // Newlines are just fill if(ch == '\n') return s; // Get special style for control codes if((FXuchar)ch < ' ') return s|STYLE_CONTROL|STYLE_TEXT; return s|STYLE_TEXT; } // Draw partial text line with correct style // @@@@@ N.B. that I override this in FXTerminal.cpp so the version here // is something of a dead duck. // this uses drawBufferText on each differently-styled section of a row. void FXMathText::drawTextRow(FXDCWindow& dc,FXint line,FXint left,FXint right) const { register FXint x,y,w,h,linebeg,lineend,truelineend,cw,sp,ep,row,edge; register FXuint curstyle,newstyle; linebeg=visrows[line]; lineend=truelineend=visrows[line+1]; if(linebeg<lineend && isspace(getChar(lineend-1))) lineend--; // Back off last space x=0; w=0; h=font->getFontHeight(); y=pos_y+margintop+(toprow+line)*h; edge=pos_x+marginleft+barwidth; row=toprow+line; // Scan ahead till until we hit the end or the left edge for(sp=linebeg; sp<lineend; sp++){ cw=charWidth(getChar(sp),x); if(x+edge+cw>=left) break; x+=cw; } // First style to display curstyle=style(row,linebeg,lineend,sp); // Draw until we hit the end or the right edge for(ep=sp; ep<lineend; ep++){ newstyle=style(row,linebeg,truelineend,ep); if(newstyle!=curstyle){ fillBufferRect(dc,edge+x,y,w,h,curstyle); if(curstyle&STYLE_TEXT) drawBufferText(dc,edge+x,y,w,h,sp,ep-sp,curstyle); curstyle=newstyle; sp=ep; x+=w; w=0; } cw=charWidth(getChar(ep),x+w); if(x+edge+w>=right) break; w+=cw; } // Draw unfinished fragment fillBufferRect(dc,edge+x,y,w,h,curstyle); if(curstyle&STYLE_TEXT) drawBufferText(dc,edge+x,y,w,h,sp,ep-sp,curstyle); x+=w; // Fill any left-overs outside of text if(x+edge<right){ curstyle=style(row,linebeg,truelineend,ep); fillBufferRect(dc,edge+x,y,right-edge-x,h,curstyle); } } // Draw the cursor // @@@@@ if cursorrow identified a row of maths I might have trouble here void FXMathText::drawCursor(FXuint state){ register FXint xx,yt,yb,xlo,xhi,fh; if((state^flags)&FLAG_CARET){ if(xid){ FXASSERT(0<=cursorpos && cursorpos<=length); FXASSERT(0<=cursorrow && cursorrow<=nrows); if(toprow<=cursorrow && cursorrow<toprow+nvisrows){ xx=pos_x+marginleft+barwidth+lineWidth(cursorstart,cursorpos-cursorstart)-1; if(barwidth<=xx+3 && xx-2<viewport_w){ FXDCWindow dc(this); fh=font->getFontHeight(); yt=pos_y+margintop+cursorrow*fh; yb=yt+fh-1; // Cursor can overhang margins but not line number bar dc.setClipRectangle(barwidth,0,viewport_w-barwidth,viewport_h); // Draw I beam if(state&FLAG_CARET){ // Draw I-beam dc.setForeground(cursorColor); dc.fillRectangle(xx,yt,2,yb-yt); dc.fillRectangle(xx-2,yt,6,1); dc.fillRectangle(xx-2,yb,6,1); } // Erase I-beam else{ // Erase I-beam, plus the text immediately surrounding it dc.setForeground(backColor); dc.fillRectangle(xx-2,yt,6,yb-yt+1); // Clip the text to the margins AND the rectangle that was // just erased. We don't want to overdraw any existing // characters, because of ClearType. xlo=FXMAX(xx-2,marginleft+barwidth); xhi=FXMIN(xx+4,viewport_w-marginright); dc.setClipRectangle(xlo,margintop,xhi-xlo,viewport_h-margintop-marginbottom); // Restore text dc.setFont(font); drawTextRow(dc,cursorrow-toprow,xx-3,xx+4); } } } } flags^=FLAG_CARET; } } // Erase cursor overhang outside of margins void FXMathText::eraseCursorOverhang(){ register FXint xx,yt,yb,fh; FXASSERT(0<=cursorpos && cursorpos<=length); FXASSERT(0<=cursorrow && cursorrow<=nrows); if(toprow<=cursorrow && cursorrow<toprow+nvisrows){ xx=pos_x+marginleft+barwidth+lineWidth(cursorstart,cursorpos-cursorstart)-1; if(barwidth<=xx+3 && xx-2<viewport_w){ FXDCWindow dc(this); fh=font->getFontHeight(); yt=pos_y+margintop+cursorrow*fh; yb=yt+fh-1; dc.setClipRectangle(barwidth,0,viewport_w-barwidth,viewport_h); if(xx-2<=marginleft+barwidth && barwidth<=xx+3){ dc.setForeground(backColor); dc.fillRectangle(barwidth,yt,marginleft,fh); } if(viewport_w-marginright<=xx+3 && xx-2<=viewport_w){ dc.setForeground(backColor); dc.fillRectangle(viewport_w-marginright,yt,marginright,fh); } if(yt<=margintop && 0<=yb){ dc.setForeground(backColor); dc.fillRectangle(xx-2,0,5,margintop); } if(viewport_h-marginbottom<=yb && yt<viewport_h){ dc.setForeground(backColor); dc.fillRectangle(xx-2,viewport_h-marginbottom,5,marginbottom); } } } } // Repaint lines of text // Apart from drawCursor, where for now I hope that I do not put the // cursor on a row of maths, this is the only place where drawTextRow is // called. It draws text from top to bottom. Thus the several rows that make // up one bit of maths can be spotted and handled here. void FXMathText::drawContents(FXDCWindow& dc,FXint x,FXint y,FXint w,FXint h) const { register FXint hh=font->getFontHeight(); register FXint yy=pos_y+margintop+toprow*hh; register FXint tl=(y-yy)/hh; register FXint bl=(y+h-yy)/hh; register FXint ln; if(tl<0) tl=0; if(bl>=nvisrows) bl=nvisrows-1; for(ln=tl; ln<=bl; ln++){ drawTextRow(dc,ln,x,x+w); } } // Repaint line numbers void FXMathText::drawNumbers(FXDCWindow& dc,FXint x,FXint y,FXint w,FXint h) const { register FXint hh=font->getFontHeight(); register FXint yy=pos_y+margintop+toprow*hh; register FXint tl=(y-yy)/hh; register FXint bl=(y+h-yy)/hh; register FXint ln,n,tw; FXchar lineno[20]; if(tl<0) tl=0; if(bl>=nvisrows) bl=nvisrows-1; dc.setForeground(barColor); dc.fillRectangle(x,y,w,h); dc.setForeground(numberColor); for(ln=tl; ln<=bl; ln++){ n=sprintf(lineno,"%d",toprow+ln+1); tw=font->getTextWidth(lineno,n); dc.drawText(barwidth-tw,yy+ln*hh+font->getFontAscent(),lineno,n); } } // Repaint text range void FXMathText::updateRange(FXint beg,FXint end) const { register FXint tl,bl,fc,lc,ty,by,lx,rx,t; if(beg>end){t=beg;beg=end;end=t;} if(beg<visrows[nvisrows] && visrows[0]<end && beg<end){ if(beg<visrows[0]) beg=visrows[0]; if(end>visrows[nvisrows]) end=visrows[nvisrows]; tl=posToLine(beg,0); bl=posToLine(end,tl); if(tl==bl){ fc=beg-visrows[tl]; lc=end-visrows[tl]; ty=pos_y+margintop+(toprow+tl)*font->getFontHeight(); by=ty+font->getFontHeight(); lx=pos_x+marginleft+barwidth+lineWidth(visrows[tl],fc); if(end<=(visrows[tl+1]-1)) rx=pos_x+marginleft+barwidth+lineWidth(visrows[tl],lc); else rx=width; } else{ ty=pos_y+margintop+(toprow+tl)*font->getFontHeight(); by=pos_y+margintop+(toprow+bl+1)*font->getFontHeight(); lx=barwidth; rx=width; } update(lx,ty,rx-lx,by-ty); } } // Draw item list long FXMathText::onPaint(FXObject*,FXSelector,void* ptr){ FXEvent* event=(FXEvent*)ptr; FXDCWindow dc(this,event); dc.setFont(font); //dc.setForeground(FXRGB(255,0,0)); //dc.fillRectangle(event->rect.x,event->rect.y,event->rect.w,event->rect.h); // Paint top margin if(event->rect.y<=margintop){ dc.setForeground(backColor); dc.fillRectangle(barwidth,0,viewport_w-barwidth,margintop); } // Paint bottom margin if(event->rect.y+event->rect.h>=viewport_h-marginbottom){ dc.setForeground(backColor); dc.fillRectangle(barwidth,viewport_h-marginbottom,viewport_w-barwidth,marginbottom); } // Paint left margin if(event->rect.x<barwidth+marginleft){ dc.setForeground(backColor); dc.fillRectangle(barwidth,margintop,marginleft,viewport_h-margintop-marginbottom); } // Paint right margin if(event->rect.x+event->rect.w>=viewport_w-marginright){ dc.setForeground(backColor); dc.fillRectangle(viewport_w-marginright,margintop,marginright,viewport_h-margintop-marginbottom); } // Paint line numbers if(event->rect.x<barwidth){ dc.setClipRectangle(0,0,barwidth,height); drawNumbers(dc,event->rect.x,event->rect.y,event->rect.w,event->rect.h); } // Paint text dc.setClipRectangle(marginleft+barwidth,margintop,viewport_w-marginright-marginleft-barwidth,viewport_h-margintop-marginbottom); drawContents(dc,event->rect.x,event->rect.y,event->rect.w,event->rect.h); drawCursor(flags); return 1; } /*******************************************************************************/ // Move the cursor void FXMathText::setCursorPos(FXint pos,FXbool notify){ register FXint cursorstartold,cursorendold; if(pos>length) pos=length; if(pos<0) pos=0; if(cursorpos!=pos){ drawCursor(0); if(pos<cursorstart || cursorend<=pos){ // Move to other line? cursorstartold=cursorstart; cursorendold=cursorend; cursorstart=rowStart(pos); cursorend=nextRow(cursorstart); if(cursorstart<cursorstartold){ cursorrow=cursorrow-countRows(cursorstart,cursorstartold); } else{ cursorrow=cursorrow+countRows(cursorstartold,cursorstart); } if(options&TEXT_SHOWACTIVE){ updateRange(cursorstartold,cursorendold); updateRange(cursorstart,cursorend); } } cursorcol=indentFromPos(cursorstart,pos); cursorpos=pos; drawCursor(FLAG_CARET); prefcol=-1; if(target && notify){ target->tryHandle(this,FXSEL(SEL_CHANGED,message),(void*)(FXival)cursorpos); } } } // Set cursor row void FXMathText::setCursorRow(FXint row,FXbool notify){ FXint col,newrow,newpos; if(row!=cursorrow){ if(row<0) row=0; if(row>=nrows) row=nrows-1; col=(0<=prefcol) ? prefcol : cursorcol; if(row>cursorrow){ newrow=nextRow(cursorpos,row-cursorrow); } else{ newrow=prevRow(cursorpos,cursorrow-row); } newpos=posFromIndent(newrow,col); setCursorPos(newpos,notify); prefcol=col; } } // Set cursor column void FXMathText::setCursorColumn(FXint col,FXbool notify){ FXint newpos; if(cursorcol!=col){ newpos=posFromIndent(cursorstart,col); setCursorPos(newpos,notify); } } // Set anchor position void FXMathText::setAnchorPos(FXint pos){ if(pos>length) pos=length; if(pos<0) pos=0; anchorpos=pos; } // Select all text FXbool FXMathText::selectAll(FXbool notify){ return setSelection(0,length,notify); } // Extend selection FXbool FXMathText::extendSelection(FXint pos,FXTextSelectionMode select,FXbool notify){ FXint sp,ep; // Validate position if(pos<0) pos=0; if(pos>length) pos=length; // Did position change? switch(select){ // Selecting words case SELECT_WORDS: if(pos<=anchorpos){ sp=wordStart(pos); ep=wordEnd(anchorpos); } else{ sp=wordStart(anchorpos); ep=wordEnd(pos); } break; // Selecting lines case SELECT_LINES: if(pos<=anchorpos){ sp=rowStart(pos); ep=nextRow(anchorpos); } else{ sp=rowStart(anchorpos); ep=nextRow(pos); } break; // Selecting characters default: if(pos<=anchorpos){ sp=pos; ep=anchorpos; } else{ sp=anchorpos; ep=pos; } break; } // Select the new range return setSelection(sp,ep-sp,notify); } // Set selection FXbool FXMathText::setSelection(FXint pos,FXint len,FXbool notify){ FXDragType types[2]; FXint what[2]; FXint ep=pos+len; FXint sp=pos; // Validate position if(sp<0) sp=0; if(ep<0) ep=0; if(sp>length) sp=length; if(ep>length) ep=length; // Something changed? if(selstartpos!=sp || selendpos!=ep){ // Release selection if(sp==ep){ if(notify && target){ what[0]=selstartpos; what[1]=selendpos-selstartpos; target->tryHandle(this,FXSEL(SEL_DESELECTED,message),(void*)what); } if(hasSelection()) releaseSelection(); } // Minimally update if(ep<=selstartpos || selendpos<=sp){ updateRange(selstartpos,selendpos); updateRange(sp,ep); } else{ updateRange(sp,selstartpos); updateRange(selendpos,ep); } selstartpos=sp; selendpos=ep; // Acquire selection if(sp!=ep){ types[0]=stringType; types[1]=textType; if(!hasSelection()) acquireSelection(types,2); if(notify && target){ what[0]=selstartpos; what[1]=selendpos-selstartpos; target->tryHandle(this,FXSEL(SEL_SELECTED,message),(void*)what); } } return TRUE; } return FALSE; } // Kill the selection FXbool FXMathText::killSelection(FXbool notify){ FXint what[2]; if(selstartpos<selendpos){ if(notify && target){ what[0]=selstartpos; what[1]=selendpos-selstartpos; target->tryHandle(this,FXSEL(SEL_DESELECTED,message),(void*)what); } if(hasSelection()) releaseSelection(); updateRange(selstartpos,selendpos); selstartpos=0; selendpos=0; return TRUE; } return FALSE; } // Set highlight FXbool FXMathText::setHighlight(FXint pos,FXint len){ FXint he=pos+len; FXint hs=pos; // Validate if(hs<0) hs=0; if(he<0) he=0; if(hs>length) hs=length; if(he>length) he=length; // Anything changed? if(hs!=hilitestartpos || he!=hiliteendpos){ // Minimally update if(he<=hilitestartpos || hiliteendpos<=hs){ updateRange(hilitestartpos,hiliteendpos); updateRange(hs,he); } else{ updateRange(hs,hilitestartpos); updateRange(hiliteendpos,he); } // Keep new range hilitestartpos=hs; hiliteendpos=he; return TRUE; } return FALSE; } // Unhighlight the text FXbool FXMathText::killHighlight(){ if(hilitestartpos<hiliteendpos){ updateRange(hilitestartpos,hiliteendpos); hilitestartpos=0; hiliteendpos=0; return TRUE; } return FALSE; } /*******************************************************************************/ // Change top margin void FXMathText::setMarginTop(FXint mt){ if(margintop!=mt){ margintop=mt; recalc(); update(); } } // Change bottom margin void FXMathText::setMarginBottom(FXint mb){ if(marginbottom!=mb){ marginbottom=mb; recalc(); update(); } } // Change left margin void FXMathText::setMarginLeft(FXint ml){ if(marginleft!=ml){ marginleft=ml; recalc(); update(); } } // Change right margin void FXMathText::setMarginRight(FXint mr){ if(marginright!=mr){ marginright=mr; recalc(); update(); } } // Change the font // @@@@@ at some level I will want the maths font size to scale as the // main font size. void FXMathText::setFont(FXFont* fnt){ if(!fnt){ fxerror("%s::setFont: NULL font specified.\n",getClassName()); } if(font!=fnt){ font=fnt; recalc(); tabwidth=tabcolumns*font->getTextWidth(" ",1); barwidth=barcolumns*font->getTextWidth("8",1); if(options&TEXT_FIXEDWRAP){ wrapwidth=wrapcolumns*font->getTextWidth(" ",1); } layout(); update(); } } // Set wrap columns void FXMathText::setWrapColumns(FXint cols){ if(cols<=0) cols=1; if(cols!=wrapcolumns){ wrapcolumns=cols; if(options&TEXT_FIXEDWRAP){ wrapwidth=wrapcolumns*font->getTextWidth(" ",1); } recalc(); update(); } } // Set tab columns void FXMathText::setTabColumns(FXint cols){ if(cols<=0) cols=1; if(cols!=tabcolumns){ tabcolumns=cols; tabwidth=tabcolumns*font->getTextWidth(" ",1); recalc(); update(); } } // Change number of columns used for line numbers void FXMathText::setBarColumns(FXint cols){ if(cols<=0) cols=0; if(cols!=barcolumns){ barcolumns=cols; barwidth=barcolumns*font->getTextWidth("8",1); recalc(); update(); } } // Set text color void FXMathText::setTextColor(FXColor clr){ if(clr!=textColor){ textColor=clr; update(barwidth,0,width-barwidth,height); } } // Set select background color void FXMathText::setSelBackColor(FXColor clr){ if(clr!=selbackColor){ selbackColor=clr; updateRange(selstartpos,selendpos); } } // Set selected text color void FXMathText::setSelTextColor(FXColor clr){ if(clr!=seltextColor){ seltextColor=clr; updateRange(selstartpos,selendpos); } } // Change highlighted text color void FXMathText::setHiliteTextColor(FXColor clr){ if(clr!=hilitetextColor){ hilitetextColor=clr; updateRange(hilitestartpos,hiliteendpos); } } // Change highlighted background color void FXMathText::setHiliteBackColor(FXColor clr){ if(clr!=hilitebackColor){ hilitebackColor=clr; updateRange(hilitestartpos,hiliteendpos); } } // Change active background color void FXMathText::setActiveBackColor(FXColor clr){ if(clr!=activebackColor){ activebackColor=clr; update(barwidth,0,width-barwidth,height); } } // Change line number color void FXMathText::setNumberColor(FXColor clr){ if(clr!=numberColor){ numberColor=clr; update(0,0,barwidth,height); } } // Change bar color void FXMathText::setBarColor(FXColor clr){ if(clr!=barColor){ barColor=clr; update(0,0,barwidth,height); } } // Set cursor color void FXMathText::setCursorColor(FXColor clr){ if(clr!=cursorColor){ cursorColor=clr; updateRange(cursorstart,cursorend); } } // Change text style void FXMathText::setTextStyle(FXuint style){ FXuint opts=(options&~TEXT_MASK) | (style&TEXT_MASK); if(options!=opts){ options=opts; if(options&TEXT_FIXEDWRAP){ wrapwidth=wrapcolumns*font->getTextWidth(" ",1); } recalc(); update(); } } // Get text style FXuint FXMathText::getTextStyle() const { return (options&TEXT_MASK); } // Return true if editable FXbool FXMathText::isEditable() const { return (options&TEXT_READONLY)==0; } // Set widget is editable or not void FXMathText::setEditable(FXbool edit){ if(edit) options&=~TEXT_READONLY; else options|=TEXT_READONLY; } // Set styled text mode void FXMathText::setStyled(FXbool styled){ if(styled && !sbuffer){ if(!FXCALLOC(&sbuffer,FXchar,length+gapend-gapstart)){fxerror("%s::setStyled: out of memory.\n",getClassName());} update(); } if(!styled && sbuffer){ FXFREE(&sbuffer); update(); } } // Set highlight styles void FXMathText::setHiliteStyles(const FXHiliteStyle* styles){ hilitestyles=styles; update(); } // Change number of visible rows void FXMathText::setVisibleRows(FXint rows){ if(rows<0) rows=0; if(vrows!=rows){ vrows=rows; recalc(); } } // Change number of visible columns void FXMathText::setVisibleColumns(FXint cols){ if(cols<0) cols=0; if(vcols!=cols){ vcols=cols; recalc(); } } /*******************************************************************************/ // Update value from a message long FXMathText::onCmdSetStringValue(FXObject*,FXSelector,void* ptr){ setText(*((FXString*)ptr)); return 1; } // Obtain value from text long FXMathText::onCmdGetStringValue(FXObject*,FXSelector,void* ptr){ *((FXString*)ptr)=getText(); return 1; } /*******************************************************************************/ // Save object to stream void FXMathText::save(FXStream& store) const { FXScrollArea::save(store); store << length; store.save(buffer,gapstart); store.save(buffer+gapend,length-gapstart); store << nvisrows; store.save(visrows,nvisrows+1); store << wrapcolumns; store << tabcolumns; store << margintop; store << marginbottom; store << marginleft; store << marginright; store << font; store << textColor; store << selbackColor; store << seltextColor; store << hilitebackColor; store << hilitetextColor; store << cursorColor; store << help; store << tip; store << matchtime; } // Load object from stream void FXMathText::load(FXStream& store){ FXScrollArea::load(store); store >> length; FXMALLOC(&buffer,FXchar,length+MINSIZE); // FIXME should we save text&style? store.load(buffer,length); gapstart=length; gapend=length+MINSIZE; store >> nvisrows; FXMALLOC(&visrows,FXint,nvisrows+1); store.load(visrows,nvisrows+1); store >> wrapcolumns; store >> tabcolumns; store >> margintop; store >> marginbottom; store >> marginleft; store >> marginright; store >> font; store >> textColor; store >> selbackColor; store >> seltextColor; store >> hilitebackColor; store >> hilitetextColor; store >> cursorColor; store >> help; store >> tip; store >> matchtime; } // Clean up FXMathText::~FXMathText(){ getApp()->removeTimeout(this,ID_BLINK); getApp()->removeTimeout(this,ID_FLASH); FXFREE(&buffer); FXFREE(&sbuffer); FXFREE(&visrows); FXFREE(&clipbuffer); buffer=(FXchar*)-1L; sbuffer=(FXchar*)-1L; clipbuffer=(FXchar*)-1L; visrows=(FXint*)-1L; font=(FXFont*)-1L; hilitestyles=(FXHiliteStyle*)-1L; } } // end of FXMathText.cpp