//richedit 富文本框 import win.ui.ctrl.edit; import win.ui.menu; namespace win.ui.ctrl; class richedit{ ctor(parent,tParam){ if( tParam ){ if( (!tParam.className) || (tParam.className == "RICHEDIT50W") ){ if( !_dll ) _dll = ..raw.loadDll("msftedit.dll"); tParam.cls = "RICHEDIT50W" //Windows XP SP1 RichEdit v4.1以上,支持透明背景 } else{ if( !_dll2 ) _dll2 = ..raw.loadDll("RICHED20.DLL"); tParam.cls = "RichEdit20W" //Windows XP RichEdit v3.0 以上 } if( tParam.multiline ){ tParam.style |= 4/*_ES_MULTILINE*/ tParam.style |= 0x1000/*_ES_WANTRETURN*/; } if( tParam.readonly ) tParam.style |= 0x800/*_ES_READONLY*/ if( tParam.password ) tParam.style |= 0x20/*_ES_PASSWORD*/ if( (tParam.hidesel !== null ) && (!tParam.hidesel) ) tParam.style |= 0x100/*_ES_NOHIDESEL*/ if( (tParam.savesel === null ) || tParam.savesel ) tParam.style = tParam.style | 0x8000/*_ES_SAVESEL*/ if( tParam.autoscroll !== null ){ if( tParam.autoscroll ){ tParam.style = tParam.style | 0x40/*_ES_AUTOVSCROLL*/ | 0x80/*_ES_AUTOHSCROLL*/; } } else { if( tParam.autovscroll === null || tParam.autovscroll ) { tParam.style |= 0x40/*_ES_AUTOVSCROLL*/ ; } if( tParam.autohscroll === null || tParam.autohscroll ) { tParam.style |= 0x80/*_ES_AUTOHSCROLL*/ ; } } //Win10 之后的系统,richedit 超过一定大小时,拖动滚动条然后最大化滚动条无法立即刷新。 if(!tParam.noscroll) tParam.style |= 0x2000/*_ES_DISABLENOSCROLL*/; select(tParam.align) { case "center" tParam.style |= 0x1/*_ES_CENTER*/; case "right" tParam.style |= 0x2/*_ES_RIGHT*/; else tParam.style |= 0x0/*_ES_LEFT*/; } if(tParam.edge) tParam.exstyle |= 0x200/*_WS_EX_CLIENTEDGE*/; this["(text)"] = tParam.text; tParam.text = ""; var handleKeys = {[0xD/*_VK_RETURN*/]=1;[0x1B/*_VK_ESC*/]=1;[0x9/*_VK_TAB*/]=1;} var multiline = tParam.multiline; this.defaultTranslateAccelerator = function(msg){ if( msg.message == 0x100/*_WM_KEYDOWN*/) { var vk = msg.wParam; if(!handleKeys[vk]) return; var ctrl = ::GetKeyState( 0x11/*_VK_CONTROL*/ ) & 0x8000; var alt = ::GetKeyState( 0x12/*_VK_ALT*/ ) & 0x8000; var shift = ::GetKeyState( 0x10/*_VK_SHIFT*/ ) & 0x8000; if(vk ===0xD/*_VK_RETURN*/ && this.onOk){ if(this.onOk(ctrl,alt,shift)){ return true; } } if( ctrl || alt || shift ) return; if( tParam.tabstop && ( vk == 0x9/*_VK_TAB*/ ) || (( vk == 0xD/*_VK_RETURN*/) && (!multiline)) ){ owner.tabNext(true); return true; } if( vk == 0x1B/*_VK_ESC*/ ){ if(this.onCancel && this.onCancel()){ return true; } if( this.parent.onCancel ) this.parent.onCancel(); return true; } } } this._commandFuncnames ={[0x300/*_EN_CHANGE*/]="onChange";[0x100/*_EN_SETFOCUS*/] ="onFocusGot";[0x200/*_EN_KILLFOCUS*/]="onFocusLost"} } }; onCreated = function(){ ::SendMessageInt(this.hwnd,0x478/*_EM_SETLANGOPTIONS*/,0,0); if(this["(text)"]){ this.text = this["(text)"]; this["(text)"] = null; } if(this.password){ this.disableInputMethod(); } }; @_metaProperty; } var TEXTRANGE = class { struct chrg = { int cpMin; int cpMax; }; ustring lpstrText; } var EN_CHARRANG = class { struct hdr = ::NMHDR(); int msg; int wParam; int lParam; struct chrg = { int cpMin; int cpMax; }; } class CHARFORMAT2 { INT cbSize = 116;//微软文档多写了一个 dwReserved INT mask; INT effects; int yHeight; int yOffset; INT textColor; BYTE charSet; BYTE pitchAndFamily; WORD faceName[0x20/*_LF_FACESIZE*/]; WORD weight; word spacing; INT backColor; INT lcid; INT reserved; word style; WORD kerning; BYTE underlineType; BYTE animation; BYTE revAuthor; getFont = function(){ var lf = ::LOGFONT(); if (owner.mask & 0x8000000/*_CFM_CHARSET*/) lf.charSet = owner.bCharSet; if (owner.mask & 0x400000/*_CFM_WEIGHT*/) lf.weight = owner.wWeight; if (owner.mask & 0x80000000/*_CFM_SIZE*/) lf.point = owner.yHeight / 1440 * 72; if ( owner.mask & 3 == 3) { lf.weight = owner.effects & 1/*_CFE_BOLD*/ lf.italic = owner.effects & 2/*_CFE_ITALIC*/ } if (owner.mask & 0x4000000C== 0x4000000C) { lf.underline = owner.effects & 4/*_CFE_UNDERLINE*/ lf.strike = owner.effects & 8/*_CFE_STRIKEOUT*/ } if (owner.mask & 0x20000000/*_CFM_FACE*/) { lf.family = owner.pitchAndFamily; lf.name = owner.faceName; } return lf; } } richedit._metaProperty = metaProperty( edit._metaProperty/*自edit控件继承属性元表*/,{ setCharformat = function(fm,wParam){ fm = ..table.mix( CHARFORMAT2(),fm ); if( fm.mask === null ) fm.mask = 0; if(fm.point) fm.yHeight/*twips*/ = fm.point * 20/*pt*/; if(fm.bold!==null) { fm.weight = fm.bold?700:400; if(fm.bold)fm.effects = (fm.effects:0)|1/*_CFE_BOLD*/; fm.mask = fm.mask | 1/*_CFM_BOLD*/; } if(fm.underline!==null){ if(fm.underline) fm.effects = (fm.effects:0)|4/*_CFE_UNDERLINE*/ fm.mask = fm.mask | 4/*_CFM_UNDERLINE*/; if(fm.underlineType===null)fm.underlineType=1/*_CFU_UNDERLINE*/ } if(fm.italic!==null){ if(fm.italic) fm.effects = (fm.effects:0)|2/*_CFE_ITALIC*/ fm.mask = fm.mask |2/*_CFM_ITALIC*/; } if(fm.strikeout!==null){ if(fm.strikeout) fm.effects = (fm.effects:0)|8/*_CFE_STRIKEOUT*/ fm.mask = fm.mask |8/*_CFM_STRIKEOUT*/; } if(fm.disabled!==null){ if(fm.disabled) fm.effects = (fm.effects:0)|0x2000/*_CFE_DISABLED*/ fm.mask = fm.mask |0x2000/*_CFM_DISABLED*/; } if(fm.link!==null){ if(fm.link) fm.effects = (fm.effects:0)|0x20/*_CFE_LINK*/; fm.mask = fm.mask |0x20/*_CFM_LINK*/; } if(fm.hidden!==null){ if(fm.hidden) fm.effects = (fm.effects:0)| 0x100/*_CFE_HIDDEN*/ fm.mask = fm.mask |0x100/*_CFM_HIDDEN*/; } if(fm.protected!==null){ if(fm.protected) fm.effects = (fm.effects:0)|0x10/*_CFE_PROTECTED*/ fm.mask = fm.mask |0x10/*_CFM_PROTECTED*/; } with fm{ if( textColor ) mask = mask | 0x40000000/*_CFM_COLOR*/ if( backColor ) mask = mask | 0x4000000/*_CFM_BACKCOLOR*/ if( yHeight ) mask = mask | 0x80000000/*_CFM_SIZE*/ if( yOffset ) mask = mask | 0x10000000/*_CFM_OFFSET*/ if( charSet ) mask = mask | 0x8000000/*_CFM_CHARSET*/ if( faceName ) mask = mask | 0x20000000/*_CFM_FACE*/ if( weight ) mask = mask | 0x400000/*_CFM_WEIGHT*/ if( spacing ) mask = mask | 0x200000/*_CFM_SPACING*/ if( lcid ) mask = mask | 0x2000000/*_CFM_LCID*/ if( style ) mask = mask | 0x80000/*_CFM_STYLE*/ if( kerning ) mask = mask | 0x100000/*_CFM_KERNING*/ if( revAuthor ) mask = mask | 0x8000/*_CFM_REVAUTHOR*/ } if(fm.yHeight) fm.yHeight = fm.yHeight / owner.dpiScale(1); return !!::SendMessageByStruct( owner[["hwnd"]], 0x444/*_EM_SETCHARFORMAT*/, wParam || 0/*_SCF_DEFAULT*/, fm ); }; getCharformat = function(mask){ var f = CHARFORMAT2(); if(mask) f.mask = mask; ::SendMessageByStruct(owner[["hwnd"]],0x43A/*_EM_GETCHARFORMAT*/,0/*_SCF_DEFAULT*/,f) if(f.yHeight) f.yHeight = f.yHeight * owner.dpiScale(1); return f; }; getFont = function(noScale){ var lf = owner.getCharformat().getFont().stripPoint(,owner.hwnd); if(lf){ lf.color = owner.color if(noScale) lf.noScale = true; else { var yScale = owner[["dpiScaleY"]]; if(yScale && yScale>1) f[["h"]] = f[["h"]] / yScale; }; return lf; } }; setSelCharformat = function(fm){ if(!fm) fm = owner.getCharformat(); return owner.setCharformat(fm,1/*_SCF_SELECTION*/); }; getSelCharformat = function(mask){ var f = CHARFORMAT2(); if(mask) f.mask = mask; ::SendMessageByStruct(owner[["hwnd"]],0x43A/*_EM_GETCHARFORMAT*/,1/*_SCF_SELECTION*/,f) return f; }; setLink = function(href,cf2){ var b,e = owner.getSel(); if(!e) return; cf2 = ..table.assign({link = true,underline = true,textColor = 0xFF0000},cf2) ::SendMessageInt(owner.hwnd, 0x45b/*EM_AUTOURLDETECT*/, 0, 0); owner.setSelCharformat(cf2); if(!owner.$predefHyperLinks){ owner.$predefHyperLinks = {}; if(!owner.onHyperlink){ owner.onHyperlink =function(message,href){ if( message = 0x202/*_WM_LBUTTONUP*/ ) { ..raw.execute(href); } } } } if(!href) return; owner.$predefHyperLinks["HYPERLINK_RANGE:"+(b-1)+"-"+e] = href || title; owner.$predefHyperLinks[owner.selText] = href; }; appendLink = function(title,href,cf2){ var b = owner.getLength(); var e = owner.appendText(title); owner.setSel(b+1,e); owner.setLink(href,cf2); owner.setSel(e+1); }; langOptions = { _get = function(){ return ::SendMessage(this.hwnd,0x479/*_EM_GETLANGOPTIONS*/); } _set = function( v ){ ::SendMessageInt(this.hwnd,0x478/*_EM_SETLANGOPTIONS*/,0,v); } } selText = { _get = function(){ var min,max = owner.getSel(); if(max && max>=min){ var len,str = ::SendMessageByStr( owner[["hwnd"]],0x43E/*_EM_GETSELTEXT*/,0,(max-min)*2+2); if( len ){ return str; } } } _set = function( str ){ return ::User32.SendMessage(owner[["hwnd"]],0xC2/*_EM_REPLACESEL*/,,str?tostring(str):"") } }; text = { _get = function(){ return ..win.getText( owner[["hwnd"]] ); } _set = function(v){ var zoom = owner.zoom; ..win.setText( owner[["hwnd"]],v !== null ? tostring(v) : "" ); var transParent = owner[["transparent"]]; if( transParent === null ){ transParent = !!..win.getStyleEx(owner.hwnd,0x20/*_WS_EX_TRANSPARENT*/); owner[["transparent"]] = transParent; } owner.zoom = zoom; if( transParent ) owner.redrawTransparent(); } }; zoom = { _get = function(){ var numerator ={int v}; var denominator ={int v}; ::User32.SendMessage(owner.hwnd,0x4E0/*_EM_GETZOOM*/,numerator,denominator) if(numerator.v && numerator.v){ var v = numerator.v / denominator.v; if(owner[["dpiScaleY"]]){ v = v / owner[["dpiScaleY"]]; } return v; } return 0; } _set = function(v){ var style = ..win.getStyle(owner.hwnd); var left = !(style & 3/*_ES_CENTER | _ES_RIGHT*/ ) if(v){ if(left && owner[["dpiScaleY"]]){ v = v * owner[["dpiScaleY"]]; } } else { if(left && owner[["dpiScaleY"]]!==1) v = owner[["dpiScaleY"]]; } if(v){ ::User32.SendMessage(owner.hwnd,0x4E1/*_EM_SETZOOM*/,v * 100,100) } else { ::User32.SendMessage(owner.hwnd,0x4E1/*_EM_SETZOOM*/,0,0) } } }; onDpiFontChange = function(f,scale){ var style = ..win.getStyle(owner.hwnd); if(style & 3/*_ES_CENTER | _ES_RIGHT*/ ){ //Richedit BUG:如果不是左对齐,只要设置缩放 —— 包含中文就会偏移错乱 return owner.setFont(f) } var zoom = 1; var numerator ={int v}; var denominator ={int v}; ::User32.SendMessage(owner.hwnd,0x4E0/*_EM_GETZOOM*/,numerator,denominator) if(numerator.v && numerator.v){ zoom = numerator.v / denominator.v; } var v = zoom * scale; v = v!=1?v:0; if(v){ ::User32.SendMessage(owner.hwnd,0x4E1/*_EM_SETZOOM*/,v * 100,100) } else { ::User32.SendMessage(owner.hwnd,0x4E1/*_EM_SETZOOM*/,0,0) } }; rangeText = function(min,max){ if(min>0)min--; var rg = TEXTRANGE(); rg.chrg.cpMin = min; rg.chrg.cpMax = max; rg.lpstrText = ..raw.buffer( (max-min)*2 ) ::User32.SendMessage(owner.hwnd, 0x44B/*_EM_GETTEXTRANGE*/, 0, rg); return rg.lpstrText; } modifyEvent = function( remove=0,add=0 ){ var e = ( ::SendMessage(owner.hwnd, 0x43B/*_EM_GETEVENTMASK*/ ) & ~remove ) | add ; ::SendMessageInt(owner.hwnd, 0x445/*_EM_SETEVENTMASK*/, 0, e ) return e; } limit = { _set = function( v ){ ::SendMessageInt(owner[["hwnd"]], 0x435/*_EM_EXLIMITTEXT*/, 0, v) } _get = function(){ return ::SendMessage(owner[["hwnd"]], 0xD5/*_EM_GETLIMITTEXT*/); } } wrap = { _set = function(v){ var hwnd = owner.hwnd;; if (v) { var hdc = ::GetDC(hwnd); ::SendMessage(hwnd, 0x448/*_EM_SETTARGETDEVICE*/, hdc); ::ReleaseDC(hwnd, hdc); } else ::SendMessageInt(hwnd, 0x448/*_EM_SETTARGETDEVICE*/, 0, 1); } }; link = { _set = function(v){ owner.modifyEvent( , 0x4000000/*_ENM_LINK */ ) ::SendMessageInt(owner.hwnd, 0x45b/*EM_AUTOURLDETECT*/, v?1:0, 0); if(v){ owner.text = owner.text; if(!owner.onHyperlink){ owner.onHyperlink =function(message,href){ if( message = 0x202/*_WM_LBUTTONUP*/ ) { ..raw.execute(href); } } } } } }; color = { _get = function(){ var f = owner.getCharformat( 0x40000000/*_CFM_COLOR*/ ); return f ? f.textColor; } _set = function(v){ owner.setCharformat( textColor = v; mask = 0x40000000/*_CFM_COLOR*/ ) } }; bgcolor = { _set = function(v){ ::SendMessageInt(owner.hwnd,0x443/*_EM_SETBKGNDCOLOR*/,0, v); } }; onChange = { _get = function(){ return null; } _set = function(v){ if(v) owner.modifyEvent(,1/*_ENM_CHANGE*/) owner[["onChange"]] = v; } }; onSelChange = { _get = function(){ return owner[["$onSelChange"]]; } _set = function(v){ if(v) { owner.modifyEvent(,0x80000/*_ENM_SELCHANGE*/) owner.prenotify = { [0x0702/*_EN_SELCHANGE*/] = function(id,code,ptr){ var sc = ..raw.convert(ptr, { struct nmhdr = ::NMHDR(); struct chrg = {int min,int max} WORD typ; } ); var min,max = sc.chrg.min,sc.chrg.max if( min >=0 ) { if( min != max ) min = min +1; }; owner.$onSelChange(min,max,sc.typ); } } } else{ owner.modifyEvent(0x80000/*_ENM_SELCHANGE*/) } owner[["$onSelChange"]] = v; } }; canRedo = function(){ return !!::SendMessage(owner.hwnd,0x455/*_EM_CANREDO*/) }; canPaste = function(){ return !!::SendMessage(owner.hwnd,0x432/*_EM_CANPASTE*/) }; onHyperlink = { _get = function(){ return owner[["onHyperlink"]]; } _set = function(v){ owner[["onHyperlink"]] = v; if(v){ owner.modifyEvent( , 0x4000000/*_ENM_LINK */ ); } owner.prenotify = { [0x070B/*_EN_LINK*/] = function(id,code,ptr){ if(!owner.onHyperlink) return; var link = ..raw.convert(ptr, EN_CHARRANG() ); var href; if(owner.$predefHyperLinks){ var tag = "HYPERLINK_RANGE:"+link.chrg.cpMin+"-"+link.chrg.cpMax; href = owner.$predefHyperLinks[tag]; } if(!href) href = tostring(owner.rangeText( link.chrg.cpMin+1,link.chrg.cpMax)); return owner.onHyperlink(link.msg,href,link ) } } } }; onClipFormat = { _get = function(){ return owner[["onClipFormat"]]; } _set = function(v){ owner[["onClipFormat"]] = v; if(v){ owner.modifyEvent( , 0x80/*_ENM_CLIPFORMAT*/ ); } owner.prenotify = { [0x712/*_EN_CLIPFORMAT*/] = function(id,code,ptr){ var cf = ..raw.convert(ptr, {struct hdr = ::NMHDR();int format}); return owner.onClipFormat(cf.format) } } } }; oleCallback = function(v){ if(!..table.get("com.interface.IRichEditOleCallback")){ error("请先导入 com.interface.IRichEditOleCallback 接口库!",2); } var oleCb = ..com.interface(v,"IRichEditOleCallback"); if( owner.sendMessage(0x446/*_EM_SETOLECALLBACK*/,,oleCb) ){ return oleCb; } }; onProtected = { _get = function(){ return owner[["onProtected"]]; } _set = function(v){ owner[["onProtected"]] = v; if(v) owner.modifyEvent(,0x200000/*_ENM_PROTECTED*/); owner.prenotify = { [0x704/*_EN_PROTECTED*/] = function(id,code,ptr){ if(!owner.onProtected) return; var enProtected = ..raw.convert(ptr,EN_CHARRANG()); return owner.onProtected(enProtected.chrg.cpMin+1,enProtected.chrg.cpMax,enProtected.msg,enProtected ) } } } }; rtf = { _get = function(){ return owner.streamOut(2/*_SF_RTF*/); } _set = function(v){ if(..string.find(v,":")) owner.streamIn(v,0xFDE90022/*(_CP_UTF8 << 16) |_SF_USECODEPAGE |_SF_RTF*/); else owner.streamIn(v); } }; load = function(path){ if(!type.isString(path)) error("文件路径必须是字符串",2); format = ..string.endsWith(path,".rtf",true) ? 2/*_SF_RTF*/ : 1/*_SF_TEXT*/; var r,e = owner.streamIn(format,path); owner.modified = false; return r,e; }; save = function(path){ if(!type.isString(path)) error("文件路径必须是字符串",2); format = ..string.endsWith(path,".rtf",true) ? 2/*_SF_RTF*/: 1/*_SF_TEXT*/; var r,e = owner.streamOut(format,path); owner.modified = false; return r,e; }; streamOut = function(format,callback){ if( format === null ) format = 2/*_SF_RTF*/; var file,err; var ret = true; if(type(callback) != "function"){ if(type.isString(callback)){ file,err = ..io.file(callback,"w+b"); if(!file) return null,err; callback = function(format,buf,len){ if(len){ var ok,bytes = file.writeBuffer(buf,len) if(ok) return 0,bytes; else return 0x1D/*_ERROR_WRITE_FAULT*/; } } } else { ret = ""; callback = function(format,buf,len){ if(len){ ret = ret ++ ..raw.tostring(buf,1,len); return 0,len; } } } } callback = ..raw.tostdcall(callback,"INT(INT cookie, pointer buf,INT count, INT & read)"); var edstream = { INT cookie = format;INT err; pointer pfnCallback = callback } ; ::SendMessageByStruct(owner.hwnd,0x44A/*_EM_STREAMOUT*/,format,edstream); if( !edstream.err ) { if(file) file.close(); return ret; } return null,edstream.err; }; streamIn = function(format,callback){ var dataIn; if(type.isString(format)){ dataIn = format; format = type(callback) === "number" ? callback : 2/*_SF_RTF*/; callback = null; } elseif( format === null ) format = 2/*_SF_RTF*/; if(type.isString(callback)){ var file = ..io.file(callback,"rb"); if(!file){ if(..io.localpath(callback)){ dataIn = ..string.load(callback); callback = null; } else { return null,"Opening file failed"; } } else { callback = function(format,buf,len){ var size = file.readBuffer(buf,len):0; if(!size){ file.close() } return 0,size; } } } if(dataIn){ if(callback) error(2,"参数 @2 应数 null"); var offset = 0; callback = function(format,buf,len){ var size = #dataIn - offset; if(size>len){ size = len; } if(!size){return 0,0 } ..raw.copy(buf,..raw.toPointer(dataIn,offset),size); offset = offset+size; return 0,size; }; } callback = ..raw.tostdcall(callback,"INT(INT cookie, pointer buf,INT count, INT & read)"); var edstream = { INT cookie = format;INT err; pointer pfnCallback = callback } ; ::SendMessageByStruct(owner.hwnd,0x449/*_EM_STREAMIN*/,format,edstream); if( !edstream.err )return true; return null,edstream.err; }; createTextDocument = function(){ var out = {pointer ptr} if( ::SendMessageByStruct(owner.hwnd,0x43C/*_EM_GETOLEINTERFACE*/,0,out) ){ var textDocument = ..com.QueryObject(out.ptr); ..com.Release(out.ptr); return textDocument; } }; getTextObjectModel = lambda()createTextDocument(); //@Deprecated replaceAll = function(str,rep,flags){ owner.setSel(0); while(owner.replaceText(str,rep,flags) ){}; }; replaceText = function(str,rep,flags,b,e){ var min,max = owner.getSel(); if( max && max >= min){ owner.selText = rep; } var min,max = owner.findText(str,flags,b,e); if( min > 0 && max >= min ){ return min,max; }; }; findText = function(str,flags,b,e){ if(flags===null) flags = 0x1/*_FR_DOWN*/; var asel = (b===null) && (e===null); var re,min,max = ::SendMessageByInt(owner[["hwnd"]],0xB0/*_EM_GETSEL*/,0,0); var len = owner.getLength(); if( flags & 0x1/*_FR_DOWN*/){ if(b===null) { if( min === len ) b = 0; else b = max; } if(e===null) e = -1; if( e < b && ( e >= 0)) b,e = e,b; } else { if(b===null) { if( max === 0 ) b = len; else b = min; } if(e===null) e = 0; if( b < e && ( b >= 0) ) b,e = e,b; } var ft = { struct chrg = {int min=b;int max=e}; ustring text = str; struct chrgText = {int min;int max}; } ::User32.SendMessage(owner[["hwnd"]],0x47C/*_EM_FINDTEXTEXW*/,flags,ft); var min,max = ft.chrgText.min,ft.chrgText.max; if( (min >=0) && (max >= 0 ) ) { if( min == max ) return min; min = min +1; if(asel) owner.setSel(min,max); } else { if(asel) { if( flags & 0x1/*_FR_DOWN*/)owner.setSel(-1); else owner.setSel(0); } } return min,max; }; setFont = function(f,own = true ){ var h = f[["h"]]; if(f[["point"]]){ f = ::LOGFONT(f) f.stripPoint(,owner.hwnd); h = f[["h"]]; //richedit 自己会缩放,要避免重复缩放 f[["h"]] = h / ..gdi.getDpiScale(,owner.hwnd); } elseif(h){ if(f[["noScale"]]){ var yScale = owner[["dpiScaleY"]]; //richedit 自己会缩放,要避免重复缩放 if(yScale && yScale>1){ f[["h"]] = h / yScale; } } } var hfont = ..gdi.setFont( owner[["hwnd"]],f ); if(f[["color"]]!==null) owner.color = f.color; if(h) f[["h"]] = h; if(hfont){ if( owner[["transparent"]] && ..win.getParent(owner.hwnd) ) owner.redrawTransparent(); if( own ){ if(owner[["(ownFontHandle)"]]){ ::DeleteObject(owner[["(ownFontHandle)"]]); } owner[["(ownFontHandle)"]] = hfont; if( f[["handle"]] ){ f[["handle"]] = null; } } return true; } }; charFromPoint = function(x,y){ var r = ::User32.SendMessage(owner.hwnd,0x427/*_EM_CHARFROMPOS*/,,::POINT(x,y)); return r+1; }; pointFromChar = function(idx){ if(idx===null) idx = owner.caretPos; var pt = ::POINT(x,y); var r = ::User32.SendMessage(owner.hwnd,0x426/*_EM_POSFROMCHAR*/,pt,idx-1); return pt.x,pt.y; }; }) /**details(必读文档) ## 设置文本样式 ### 使用 COM 接口实现的 TOM 文本对象设置样式 以下是一个简单示例: textDoc = winform.richedit.createTextDocument() textDoc.Selection.Text = "这是红色加粗的文本" textDoc.Selection.Font.ForeColor = gdi.RGB(255,0,0) // 红色 textDoc.Selection.Font.Bold = textDoc.tomTrue // 加粗 [TOM 文档](https://learn.microsoft.com/zh-cn/windows/win32/controls/text-object-model?redirectedfrom=MSDN) ### 使用 CHARFORMAT2 结构体设置样式 以下函数会用到 CHARFORMAT2(这里指的是 win.ui.ctrl.CHARFORMAT2 类) 结构体。 - appendText 支持任意个文本参数与任意个表参数, 表参数转换为 CHARFORMAT2 并修改当前插入样式,默认对后续参数生效。 参数为空表 `{}` 恢复默认样式,函数退出时也会恢复默认样式。 [示例](doc://example/Windows/edit/CHARFORMAT2.html); - setSelCharformat 修改当前选区或插入点样式,传入指定 CHARFORMAT2 部分字段的表即可。 - getSelCharformat 获取当前选区或插入点样式,返回 CHARFORMAT2 - setCharformat 修改样式 ,传入指定 CHARFORMAT2 部分字段的表即可。 - getCharformat 获取样式,返回 CHARFORMAT2 - setLink 设置超链接样式 - appendLink 追加超链接 设置样式的函数可直接指定普通表参数(不需要指定全部 CHARFORMAT2 字段), aardio 会自动将这些表转换为完整的 CHARFORMAT2 结构体。 并且会自动设置 mask 字段的掩码。 在 aardio 会自动设置值,所以这个最复杂的部分可以不用管了。 所以只要字段名写对就可以了。 CHARFORMAT2 有以下字段 - backColor: 背景颜色,GDI 颜色格式( 0xBBGGRR )。 - textColor: 字体颜色,GDI 颜色格式( 0xBBGGRR )。 - faceName: 字体名 - bold = 是否粗体,仅用于写入样式。\n设置了这个值会在应用样式前自动修改 weight 属性。 - underline = 是否粗体,仅用于写入样式。 - italic = 是否斜体,仅用于写入样式。 - strikeout = 是否显示删除线,仅用于写入样式。 - disabled = 偏移一个像素,仅用于写入样式。 - protected = 保护文本,修改时触发 onProtected 事件。\n仅用于写入样式,仅用于设置全部文本,不适用选区。 - weight: 字体重量,一般不要改,改 bold 即可 - spacing: 字体间距 - effects: 效果 - yHeight: 字体大小,以 twips 为单位 - point: 字体大小,以 pt 为单位。设置样式时指定 point 会在应用样式前自动修改 yHeight 的值。 - yOffset: 偏移 - charSet: 字符集 - pitchAndFamily: 字体族 - lcid: 区域ID - underlineType: 下划线类型 - mask 掩码,这个一般不用管 - getFont() 返回LOGFONT对象 注意修改字体大小请在窗体显示并且 DPI 初始化缩放完成以后再设置,不然会被重复放大。 如果要在窗体显示前设置,请在设置字体大小前主动调用 winform.enableDpiScaling("init"); ## 参考文档: https://learn.microsoft.com/en-us/windows/win32/controls/about-rich-edit-controls https://github.com/MicrosoftDocs/win32/blob/docs/desktop-src/Controls/about-rich-edit-controls.md https://learn.microsoft.com/zh-cn/windows/win32/api/richedit/ns-richedit-charformatw https://learn.microsoft.com/zh-cn/windows/win32/api/richedit/ns-richedit-charformat2w API 常量在 aardio 里需要在前面加一个下划线,然后利用 IDE 的自动完成可自动转换为实际的常量值。 end details**/ /***intellisense() win.ui.ctrl.richedit = 多功能文本框控件支持库 win.ui.ctrl.richedit() = 多功能文本框控件\n!richedit. ?win.ui.ctrl.richedit = !richedit. !richedit.zoom = 缩放比例,必须大于 1/32 并小于 32\nRichedit 存在一个 BUG:如果不是左对齐,只要设置缩放 —— 包含中文就会偏移错乱,\n所以右对齐或居中对齐后,自动 DPI 缩放将会更改为设置文本框默认字体,\n这会导致局部字体样式被重置为默认字体 !richedit.color = 取或修改字体颜色、数值 !richedit.bgcolor= 获取或修改背景颜色、数值 !richedit.cls = 设计时类名 !richedit.className = 运行时类名 !richedit.modifyEvent(.(移除通知,添加通知) = 启用或禁用通知消息,返回EVENTMASK值 !richedit.lineCount = 获取行数 !richedit.setSel(.(起始位置,结束位置) = 设置选区,选区包含起始与结束位置。\n如果结束位置小于开始位置则自动交换参数位置。\n\n位置参数以字符为单位,1 为首字符。\n计算选区时 richedit 控件将回车换行( CRLF '\r\\n' )计为一个字符单位\n,而 edit 控件控件将 CRLF 计为两个字符单位。 !richedit.setSel(.(当前位置) = 无选区,\n移动光标到指定位置的字符后面 !richedit.getSel() = 获取选区起始位置,结束位置。\n选区包含起始与结束位置的字符,首字符位置为 1 。\n开始位置在指定的字符前面,结束位置表示指定的字符后面。\n只有一个返回值时表示无选区,并表示输入光标在返回的字符位置后面。\n返回单个 0 表示输入光标在最前面,并且无选区。 !richedit.selText = 获取或写入选区文本\n可写入 UTF-8 或 UTF-16 字符串。\n指定 null 值设为空字符串,其他类型转为字符串写入。\n\n如果要读写选区 RTF 格式内容,请使用 streamIn,streamOut 方法,\n并指定 `2/*_SF_RTF*/|0x8000/*_SFF_SELECTION*/` 选项。 !richedit.selLine = 获取或设置当前行,\n光标移动到该行开始处,并且滚动到该行,\n设为-1跳转到最后一行 !richedit.modified = 用户手动输入会修改此属性。\n调用 selText,appendText,appendLink 等模拟用户输入文本的函数会变更此属性为 true 。\n调用 load,save 会重置此属性为 false 。\n\n修改 text 属性、以及调用 log,print 等函数不会自动变更此属性。\nonChange 事件可使用此属性区分是否用户修改导致的变更。 !richedit.getLength() = 获取文本长度\n注意是按字符计数,而不是按字节计数 !richedit.rangeText(.(起始位置,结束位置) = 返回指定位置文本\n1表示首字符 !richedit.validateText( = 校验输入文本。\n全部文本完全符合要求返回 true,否则返回 false。\n\n可在 onChange 事件内调用此函数实时校验输入。 !richedit.validateText(.(模式串,是否设置光标) = 用字符串参数 @1 指定的模式串校验控件的文本属性。\n将文本设为符合匹配的文本,如果找不到匹配文本设为空字符串。\n\n如果修改了文本且指定了参数 @2 为 true,\则将焦点切换到该控件,然后将输入光标设置到文本尾部。\n注意仅 edit 控件可直接显示气泡提示,\nrichedit 无此功能可使用其他控件显示错误信息。 !richedit.validateText(.(校验输入函数,是否设置光标) = 调用参数 @1 指定的函数校验控件的文本属性,\n该函数必须自参数中接收当前文本并返回合法文本或 null。\n然后将文本设为符合合法的文本,如果校验函数返回 null 设为空字符串。\n\n如果修改了文本且指定了参数 @2 为 true,\则将焦点切换到该控件,然后将输入光标设置到文本尾部。\n注意仅 edit 控件可直接显示气泡提示,\nrichedit 无此功能可使用其他控件显示错误信息。 !richedit.validateText(.(限制金额示例,是否设置光标) = @.validateText("<\d+\.\d\d>|<\d+\.\d>|<\d+\.>|<\d+>",true) !richedit.text = 获取或写入编辑控件文本属性。\n可写入 UTF8 或 UTF16 字符串,写入 null 值转换为空字符串(""),\n其他类型值自动调用 tostring 转为字符串后写入。\n\nedit 或 richedit 控件的文本属性(text)都以回车换行(CRLF '\r\\n')表示换行。\nedit 控件不显示输入文本中缺少回车的单个换行符('\\n'),但 richedit 将其转换为回车换行('\r\\n')。\n注意双引号或反引号包围的原样字符串会将源码中的回车换行解析为'\\n',\n而块注释赋值为原样字符串时会将源码中的单换行解析为'\r\\n'。\n\n在计算选区时 edit 将 CRLF 计为两个字符单位,richedit 将 CRLF 计为一个字符单位。 !richedit.rtf = 获取或写入 RTF 格式文本。\n读写此属性会自动转换为调用 streamIn,streamOut 函数。 !richedit.disabledText = 当指定文本时,禁用此控件并显示指定文本。\n指定为 null 时,启用此控件并恢复控件之前的正常文本。 !richedit.log( __,'\r\n' ) = 追加字符串到文本框,可输入多个参数\n如果超出limit属性设定的字符数限制则移除头部多余的字符\n为提升性能,limit不可过大 !richedit.dump(__/*变量*/) = 显示变量的值,支持多参数\n注意仅显示普通table,string,number等类型的值,不显示函数等 !richedit.hwnd = 控件句柄 !richedit.id = 控件ID !richedit.readonly = 是否只读\n只读时禁止编辑 !richedit._parentForm = 返回创建控件的容器窗口(win.form对象),所有窗口控件都拥有此只读属性。\n对于使用窗体设计器创建的窗口,也就是返回设计时窗体容器。\n\n即使子窗口移除子窗口样式、更改父子关系,或以 orphanWindow显示,\n控件的 _parentForm 始终都不会改变\n!winform. !richedit.getParent() = 返回父窗口\n!static. !richedit.setParent(__/*控件对象*/) = 改变父窗口 !richedit.hide = 当前控件窗口是否隐藏。\n仅检查当前窗口的可见性样式(窗口 是否移除了 _WS_VISIBLE 样式)。\n不考虑父窗口是否可见,不考虑是否被其他窗口遮挡。\n如果需要同时判断父窗口的可见性,应改用 win.isVisible 函数。\n\n以 `.` 前导的 `.hide()` 作为成员函数被调用时,\n作用等价于将 hide 属性设为 null 。 !richedit.disabled = 控件时否可见 !richedit.value = 控件文本,text 属性的别名。 !richedit.left = 左侧坐标 !richedit.right = 右侧坐标 !richedit.top = 顶部坐标 !richedit.bottom = 底部坐标 !richedit.width = 宽度 !richedit.height = 高度 !richedit.limit = 字符数限制 !richedit.scrollCaret() = 滚动到输入光标处,可选用一个数值参数指定新的输入光标位置。 !richedit.scrollToLine(.(指定行) = 将光标移动到指定行,并滚动到该行。 !richedit.deselect() = 取消选定 !richedit.setSelCharformat( = 设置选区样式。\n用法细节请参考 richedit 库函数文档。\n如果当前没有选区则修改插入点样式,新的样式仅在插入位置修改前有效。\n\n可调用控件的 setFocus,setSel 函数修改选区或光标插入位置。 !richedit.setSelCharformat(.(charformat2) = 参数 @charformat2 使用 win.ui.ctrl.CHARFORMAT2 结构体设置文本样式,\n传入仅指定部分字段的表将自动转换为 CHARFORMAT2 结构体。\例如修改字体颜色可传入 {textColor=0x00ff00} !richedit.setSelCharformat(.(null) = 将选区或光标插入点样式设为默认样式。 !richedit.setCharformat( = 修改样式,\n如果修改字体大小请在窗体显示 DPI 缩放以后设置。\n用法细节请参考 richedit 库函数文档。 !richedit.setCharformat(.(charformat2,wParam) = 参数 @charformat2 使用 win.ui.ctrl.CHARFORMAT2 结构体设置文本样式,\n可仅指定部分字段\nwParam可省略,可选值参考EM_SETCHARFORMAT消息。\例如修改字体颜色可传入 {textColor=0x00ff00} !richedit.getSelCharformat() = !charformat2. !richedit.getCharformat() = !charformat2. !richedit.getCharformat(.(mask) = 返回 win.ui.ctrl.CHARFORMAT2 结构体。\n参数 @mask 指定获取字段掩码,省略获取全部值 !richedit.getSelCharformat(mask) = 返回选区 win.ui.ctrl.CHARFORMAT2 结构体。\n参数 @mask 指定获取字段掩码,省略获取全部值 !richedit.print(.(->->->) = 将多个参数转换为字符串,\n并使用制表符分隔各参数追加到文本尾部并追加换行。\n\n对于 table 对象,aardio 会将其序列化为文本然后输出,\n如果当前已经导入了 JSON 库,则将 table 对象自动转换为 JSON 后输出,\n可以用于调试代码显示变量的值 !richedit.printf(.(->->->) = 将多个参数调用string.format格式化后追加到文本尾部\n并追加换行 !richedit.appendText( = 追加文本并移动光标插入点到文本尾部。\n用法细节请参考 richedit 库函数文档。 !richedit.appendText(.(追加文本-->->) = 追加文本并移动光标插入点到文本尾部。\n可指定零个、或多个参数,参数可以是字符串、buffer、数值。\n如果参数是表对象,则将该表作为 setSelCharformat 的参数修改插入点样式。\n如果修改字体大小请在窗体显示 DPI 缩放以后设置。\n如果传入了样式参数,在函数调用结束总是会恢复为默认样式。\n传入其他类型参数会抛出异常。\n\n函数返回插入文本以后控件的文本总长度。 !richedit.appendLink( = 在文本尾部追加超链接。\n如果未定义 onHyperlink 事件,\n则添加默认 onHyperlink 调用 raw.execute 函数打开链接。\n\n此函数会自动禁用自动识别超链接功能,但不会取消之前自动识别的超链接。 !richedit.appendLink(.(title,href,charFormat2) = 在文本尾部追加超链接。\n参数 @title 指定显示文本。\n参数 @href 指定超链接或文件路径。\n参数 @charFormat2 可选指定包含 win.ui.ctrl.CHARFORMAT2 部分字段的表\n\n默认会通过插入位置获取超链接,\n如果超链接位置改变而获取失败,则通过 title 指定显示名称获取链接。 !richedit.link = 控件是否自动识别超链接。\n启用此属性会重新设置控件文本内容并丢失选区样式。\n如果未定义 onHyperlink 事件,\n则添加默认 onHyperlink 调用 raw.execute 函数打开链接。\n\n禁用此属性不会重置文本内容,之前识别的超链接不会失效。 !richedit.setLink( = 设置当前选区为超链接。\n如果未定义 onHyperlink 事件,\n则添加默认 onHyperlink 调用 raw.execute 函数打开链接。\n\n此函数会自动禁用自动识别超链接功能,但不会取消之前自动识别的超链接。 !richedit.setLink(.(href,charFormat2) = 设置当前选区为超链接。\n参数 @href 指定超链接或文件路径。\n参数 @charFormat2 可选指定包含 win.ui.ctrl.CHARFORMAT2 部分字段的表\n\n默认会通过插入位置获取超链接,\n如果超链接位置改变而获取失败,则通过 title 指定显示名称获取链接 !richedit.lineScroll(.(滚动到指定行) = 滚动条移动到指定行\n如果不指定参数则滚动到最后一行 !richedit.lineToChar(__/*指定行号*/) = 获取指定行首字符位置\n省略参数表示当前行,小于0表示自尾部倒数到指定行,-1表示最后一行 !richedit.lineFromChar(__/*指定位置*/) = 返回指定位置行数 !richedit.lineFromChar() = 不指定参数则返回当前行 !richedit.lineText(__/*指定行号*/) = 获取指定行文本\n省略参数表示当前行,小于0表示自尾部倒数到指定行,-1表示最后一行 !richedit.lineText() = 不指定行号参数,则获取当前行文本 !richedit.lineLength(__/*指定行号*/) = 返回指定行字符数\n省略参数表示当前行,小于0表示自尾部倒数到指定行,-1表示最后一行 !richedit.lineSel(.(行号,替换文本) = 选择指定的行的全部文本,\n行号为-1时表示选取最后一行,\n可选使用参数@2指定一个字符串用于替换该行文本 !richedit.redraw() = 刷新 !richedit.show(true__) = 显示控件 !richedit.passwordChar = 单行或多行文本框可用此属性指定隐藏密码的占位字符。\n在创建 richedit 控件时未启用密码样式此属性无效。\n例如指定为`*`则所有输入字符显示星号,指定为 null 正常显示文本 !richedit.getRect() = 控件区块位置(::RECT结构体) !richedit.getRect(true) = 控件屏幕区块位置(::RECT结构体) !richedit.setRect(rc) = 设置控件区块位置(::RECT结构体) !richedit.setRect(rc,true) = 设置控件屏幕区块位置(::RECT结构体) !richedit.getClientRect() = 控件客户区块位置(::RECT结构体)\n!rect. !richedit.setClientRect(.(RECT区块) = 参数为指定文本客户区的RECT结构体\n文本框必须在设计时指定为多行\n在文本框改变大小必须重新设置 !richedit.inflateClientRect(.(dx,dy) = 正数增大,负数缩小文本客户区\n文本框必须在设计时指定为多行\n在文本框改变大小必须重新设置 !richedit.clientWidth = 控件窗口客户区宽度,只读属性。 !richedit.clientHeight = 控件窗口客户区宽度,只读属性。 !richedit.setPadding(.(上,右,下,左) = 设置文本边距\n文本框必须在设计时指定为多行\n在文本框改变大小后仍然可以保持此边距 !richedit.padding = 文本边距\n应通过setPadding函数设置该值 !richedit.getFont() = 返回控件 LOGFONT 字体,\n返回对象的 h 值会按控件的 DPI 缩放设置自动还原为缩放前大小。\n!logfont. !richedit.getFont(true) = 返回控件 LOGFONT 字体,\n返回对象的 h 值为字体实际大小,不会按控件 DPI 设置还原。\n返回字体会设置 noScale 属性为 true,\n使用控件的 setFont 函数或赋值 font 属性时,\nnoScale 属性为 true 的字体同样不会进行自动 DPI 缩放\n!logfont. !richedit.setFont(__/*指定字体*/) = 指定 LOGFONT 字体对象,或逻辑字体句柄\n如果不指定 point 值并指定 h 值,字体会按控件的 DPI 缩放设置自动缩放。 !richedit.setFont(混入字体属性) = @.setFont(h=-12;name="Tahoma"); !richedit.font = 控件字体(LOGFONT 结构体),\n注意获取该属性总是返回新的 LOGFONT 对象,\n修改返回字体并不会更新控件字体,\n除非重新赋值。\n\n建议尽量优先使用 getFont 或 setFont 函数,\n以增强代码可读性\n\n字体会根据控件设置自动处理 DPI 缩放,不需要事先缩放字体大小 !richedit.theme = 外观主题,例如\nwinform.button.theme = "Explorer"\nwinform.button.theme = false !richedit.modifyStyle(.(remove,add,swpFlags) = 修改窗口样式,所有参数都是可选参数,\n@remove 用数值指定要移除的样式,可使用 _WS_ 前缀的常量\n@add 用数值指定要添加的样式,可使用 _WS_ 前缀的常量\n@swpFlags 可选用数值指定调整窗口选项,可使用 _SWP_ 前缀的常量\n如果指定了 @swpFlag ,则使用该参数调用::SetWindowPos\n细节请参考 win.modifyStyle 函数源码 !richedit.modifyStyleEx(.(remove,add,swpFlags) = 修改窗口扩展样式,所有参数都是可选参数,\n@remove 用数值指定要移除的样式,可使用 _WS_EX_ 前缀的常量\n@add 用数值指定要添加的样式,可使用 _WS_EX_ 前缀的常量\n@swpFlags 可选用数值指定调整窗口选项,可使用 _SWP_ 前缀的常量\n如果指定了 @swpFlag ,则使用该参数调用::SetWindowPos\n细节请参考 win.modifyStyle 函数源码 !richedit.capture = 是否捕获全局鼠标消息 !richedit.close() = 关闭控件窗 !richedit.invalidate(__/*可选使用::RECT()对象指定客户区*/) = 使窗口绘图区无效 !richedit.invalidate(__/*可选使用::RECT()对象指定客户区*/,0) = 使窗口绘图区无效\n不刷新背景 !richedit.update() = 重绘invalidate函数指定的区块 !richedit.wrap = 是否启用自动换行,仅 richedit 支持 !richedit.onHyperlink = @.onHyperlink =function(message,href){\n\n if( message = 0x202/*_WM_LBUTTONUP*/ ) {\n raw.execute(href);\n }\n} !richedit.setFocus() = 设置焦点 !richedit.tabNext(.(移动焦点,是否反向) = 获取下一个支持tab控制焦点的控件\n参数@1为true会自动移动焦点到该控件\n参数@2为true则获取上一个控件,否则获取下一个控件 !richedit.tabNext() = !static. !richedit.disableInputMethod() = 在此控件中关闭输入法, 仅支持英文输入 !richedit.center(.(目标窗口) = 在目标窗口区域内居中,并调整以保证显示在父窗口或屏幕工作区内。\n\n参数 @1 可指定目标窗口或者目标窗口句柄,设为 0 表示桌面,\n省略目标窗口则默认取父窗口或所有者窗口。 !richedit.setPos(.(x坐标,y坐标,宽,高,插入位置,参数) = 调整窗口位置或排序,所有参数可选\n同时指定x,y坐标则移动位置\n同时指定宽高则改变大小\n指定插入位置(句柄或_HWND前缀常量)则调整Z序 !richedit.getPos() = 返回相对坐标,宽,高\nx,y,cx,cy=win.getPos(hwnd) !richedit.vScroll() = 滚动到底部 !richedit.hScroll() = 滚动到右侧 !richedit.vScroll(_SB__) = 滚动竖向滚动条 !richedit.hScroll(_SB__) = 滚动横向滚动条 !richedit.selectAll() = 全选 !richedit.popMenu() = 弹出右键菜单 !richedit.enablePopMenu() = @.enablePopMenu(function(){\n return { \n { /*分隔线*/ }; \n { "自定义菜单项"; function(id){\n __/*enablePopMenu 用于启用文本框的右键菜单。\n可选指定要增加的菜单配置表(或返回菜单配置表的函数)作为参数。\n菜单配置表将作为参数传给 win.ui.popmenu 对象的 addTable 函数。\n格式请查看该函数说明*/\n }; !owner.canCopy() ? 1/*_MF_GRAYED*/ : 0}; \n }\n}) !richedit.redo() = 重做 !richedit.undo() = 撤消 !richedit.clear() = 清除选中文本 !richedit.copy() = 复制选区。\n如果要复制全部 RTF 文档,\n可用 win.clip.data("Rich Text Format") 复制 !richedit.cut() = 剪切选区 !richedit.paste() = 粘贴 !richedit.canRedo() = 能否重做 !richedit.canUndo() = 能否撤消 !richedit.canPaste() = 能否粘贴 !richedit.canCopy() = 能否复制 !richedit.langOptions = 设置输入和远东语言选项\n选项为数值,使用_IMF_前缀常量表示 !richedit.load(__) = 自参数 @1 指定的文件路径加载文本。\n如果文件后缀为 .rtf 则加载 RTF 格式文本。\n此函数内部调用 streamIn 函数。 !richedit.save(__) = 保存控件文本到参数 @1 指定的文件路径。\n如果文件后缀为 .rtf 则保存 RTF 格式文本。\n此函数内部调用 streamOut 函数。 !richedit.streamIn( = 读入 RTF 文档或普通文档 !richedit.streamIn(.(格式,RTF文件路径) = 读入 RTF 文档或普通文档,\n格式参数默认为 _SF_RTF 用于读入 RTF 文档,\n指定为 _SF_TEXT 则读入普通文本。\n如果要修改选区请添加 `0x8000/*_SFF_SELECTION*/` 选项。 !richedit.streamIn(.(格式,输入函数) = @.streamIn(,function(format,buf,len){ \n return 0,file.readBuffer(buf,len);__/*格式参数默认为 _SF_RTF,用于读入 RTF 文档,\n指定为 _SF_TEXT 则读入普通文本。\n成功读取文件回调函数应返回 0 与读取长度,\n否则请返回错误代码*/\n} ) !richedit.streamIn(.(RTF文本) = 加载字符串参数 @1 指定的 RTF 格式文本 !richedit.streamOut( = 输出 RTF 文档或普通文档 !richedit.streamOut(.(格式,输出函数) = @.streamOut(,function(format,buf,len){ \n var ok,bytes = file.writeBuffer(buf,len);__/*保存RTF文档*/ \n return 0,bytes;\n} ) !richedit.streamOut(.(格式,RTF文件路径) = 输出 RTF 文档或普通文档,\n格式参数默认为 `2/*_SF_RTF*/` 用于输出 RTF 文档。\n格式为 `2/*_SF_RTF*/` 则输出 ANSI 编码 RTF 文档。\n指定为 `1/*_SF_TEXT*/` 则输出普通文本。\n如果要获取选区请添加 `0x8000/*_SFF_SELECTION*/` 选项。 !richedit.streamOut(.(格式) = 直接返回 RTF 格式或普通文本。\n格式参数默认为 `2/*_SF_RTF*/` 用于输出 RTF 格式文档。\n指定为 `1/*_SF_TEXT*/` 则输出普通文本。 !richedit.saveScale(.(scaleX,scaleY,dpiScaleX,dpiScaleY) = 根据控件当前的运行时位置更新设计时大小\如果控件允许自动缩放,窗口缩放时依据设计时大小按比例缩放\n所有参数可省略,并且不建议写参数 !richedit.orphanWindow(.(transparent,hwndBuddy,borderless) = 创建悬浮窗口。\n悬浮窗口是模仿子窗口外观效果的独立窗口,父窗口可自动调整子窗口到设定位置。\n可选参数 @transparent 为 true 则转换为分层透明窗口。\n可选利用 @hwndBuddy 参数指定外部进程窗口句柄的并附加在内部控件上以实现相同的效果。\n伙伴窗口总是会保持在悬浮窗口前面,并保持相同的大小、位置。\n可重复调用此函数更换伙伴窗口,旧的伙伴窗口必须自行关闭。\n可选指定 @borderless 参数 为 true 以移除 @hwndBuddy 的窗口边框。 !richedit.createTextDocument() = 返回 COM 接口的[TOM 文本对象](https://learn.microsoft.com/zh-cn/windows/win32/controls/text-object-model?redirectedfrom=MSDN)\n!TextDocument. !richedit.threadCallable() = 开启此控件的跨线程调用功能 !richedit.lines(忽略空白) = @for line in ??.lines(true){ __/*遍历所有文本行,\n如果迭代器参数为true则清除每行首尾空格,并忽略空行*/ } !richedit.isDialogMessage = @.isDialogMessage = function(hParent,msg){__/*在控件范围内替代父窗口的 isDialogMessage,\n可用于在控件范围内屏蔽对话框快捷键。\n可用于禁用 tab 控制键的多行文本框支持按 tab 输入制表符*/} !richedit.translateAccelerator(msg) = @.translateAccelerator = function(msg){ var vk = msg.wParam; if( (vk == 0x9/*_VK_TAB*/ ) || (vk = 0xD/*_VK_RETURN*/) ){ if( msg.message == 0x100/*_WM_KEYDOWN*/) { owner.tabNext(true); return true;__/*在此事件中可拦截键盘消息并自定义快捷键\ntabNext函数切换到下一个支持tab控制键的控件\n如果这是一个快捷键返回true以取消该消息的默认行为*/ } } } !richedit.onOk() = @.onOk = function(ctrl,alt,shift){ __/*在控件内按下回车触发此事件,返回 true 阻止默认事件。\n回调参数分别表示对应按钮是否已同时按下。*/ return true; } !richedit.onCancel() = @.onCancel = function(){ __/*当前已按下ESC,返回true阻止默认事件*/ return true; } !richedit.onProtected = @.onProtected = function(b,e,message){ __/*修改保护样式文本触发此事件。\nb,e 为被修改的选区,message 为触发消息。\n返回 true 禁止修改*/ return true; } !richedit.onChange() = @.onChange = function(){ if(!owner.modified) return; //只有用户输入导致更改时 modified 才会是 true owner.validateText("<\d+\.\d\d>|<\d+\.\d>|<\d+\.>|<\d+>" ,"请输入金额,小数点后不能超过 2 位!"); __/*响应事件,文本内容已变更,\nrichedit 无论是否设为多行,修改 text 属性赋值文本会触发此事件。\n但可以通过 modified 属性判断是否由用户输入导致变更。\n*/ } !richedit.onFocusGot() = @.onFocusGot = function(){ __/*响应事件,文本框已获得输入焦点*/ } !richedit.onFocusLost() = @.onFocusLost = function(){ __/*响应事件,文本框已失去输入焦点*/ } !richedit.onClipFormat(format) = @.onClipFormat = function(format){ __/*特定的剪贴板格式发生粘贴。\nformat 为数值表示的剪贴板格式,可用 `_CF_` 前缀常量表示。\n例如 _CF_UNICODETEXT ,细节可参考 win.clip 命名空间的库。\n仅支持 Win8 之后的操作系统。*/ } !richedit.onSize = @.onSize = function( width,height,wParam ) { \n __/*父窗口改变大小后会自动触发此事件函数。\n注意 onSize 是 adjust 事件的别名,作用相同\n所有 win.form 创建的窗体和控件都支持 onSize(或 adjust)事件,\n重复赋值 onSize(或 adjust)会追加事件触发器而非替换原来的值。\n\nwidth 参数为窗口客户区宽度,height 参数为窗口客户区高度,\nwParam 参数来自 _WM_SIZE 消息的 wParam 参数,一般不用管。\n一般不建议添加一个 wndproc 仅仅是为了处理 _WM_SIZE 消息,\n定义 onSize 事件是更好的选择。\n \n};*/ !richedit.adjust = @.adjust = function( cx,cy,wParam ) { \n __/*父窗口改变大小后会自动触发此事件函数。\n注意 onSize 是 adjust 事件的别名,作用相同。\n所有 win.form 创建的窗体和控件都支持 adjust 事件,\n重复赋值 adjust 会追加事件触发器而非替换原来的值。\n\ncx 参数为窗口客户区宽度,cy 参数为窗口客户区高度,\nwParam 参数来自 _WM_SIZE 消息的 wParam 参数,一般不用管。\n一般不建议添加一个 wndproc 仅仅是为了处理 _WM_SIZE 消息,\n定义 adjust 事件是更好的选择。\n\n如果主动调用此函数,则检查并执行所有之前添加的 adjust 事件函数。\n省略调用参数时 cx,cy 参数默认设为窗口大小*/ \n}; !richedit.preadjust = @.preadjust = function( cx,cy,wParam ) { \n __/*窗口缩放后重绘前、触发 adjust 事件之前触发此事件。\n所有 win.form 创建的窗体和控件都支持此事件,\n与 adjust 事件不同,对 preadjust 重复赋值则覆盖而不是追加事件。 \n\ncx 参数为窗口宽度,cy 参数为窗口高度,\nwParam 为 _WM_SIZE 消息参数。*/ \n}; !richedit.resize(.(宽度,高度) = 如果指定了参数则调整窗口大小,\n无论是否实际调整窗口大小,发送 _WM_SIZE 消息给窗口 !richedit.addCtrl(controlsPropertiesTable) = @.addCtrl(\n cls="spin";marginRight=2;marginTop=1;marginBottom=4;width=16;\n oncommand = function(id,event,pos){ \n if( pos && event == 0x4/*_SB_THUMBPOSITION*/ ){\n winform.edit.text = string.format("%.2f",pos / 100 )\n }\n }\n)__/*在 richedit 控件窗口内添加子窗口*/ !richedit.replaceAll( = 全部替换查找到的文本 !richedit.replaceAll(.(查找字符串,替换字符串,选项) = 全部替换查找到的文本\n选项可使用win.dlg.findReplace对象的flags属性 !richedit.replaceText( = 替换替换文本 !richedit.replaceText(.(查找字符串,替换字符串,选项,开始位置,结束位置) = 替换当前选区并查找下一个,\n选项可使用 win.dlg.findReplace 对象的 flags 属性,\n不指定查找范围时,aardio 根据选区自动确定下次替换的范围\n替换完全部文本会重新重新设置查找范围为全部文本,\n此函数返回2个数值,表示下一个找到的字符串起始、结束位置 !richedit.findText( = 查找文本 !richedit.findText(.(查找字符串,选项,开始位置,结束位置) = 查找\n选项可使用 win.dlg.findReplace 对象的 flags 属性,\n不指定查找范围时,aardio 根据选区自动确定下次替换的范围\n替换完全部文本会重新重新设置查找范围为全部文本\n\n此函数返回2个数值,表示查找到的字符串起始、结束位置\n如果参数中未指定查找位置,会自动选中查找到的字符串 !richedit.sendMessage(.(msg,wParam,lParam) = 发送窗口消息\n此函数用法请参考 ::User32.SendMessage !richedit.postMessage(.(msg,wParam,lParam) = 投递窗口消息到消息队列中\n此函数用法请参考 ::User32.PostMessage !richedit.publish("__/*发布消息名*/",) = 在窗口所在界面线程发布消息,\n运行界面线程所有所有调用subscribe函数订阅此消息的函数,\n可添加任意个触发参数 !richedit.onDpiFontChange = @.onDpiFontChange = function(f){\n return owner.setFont(f);__/*自定义DPI字体缩放*/\n}; !richedit.getRoot() = 获取顶层父窗口,这个函数会查找 orphanWindow 的父窗口 !richedit._defWindowProc(hwnd,message,wParam,lParam) = 用于在 wndproc 回调中提前调用默认消息回调函数,\n所有窗口和控件定义了 wndproc 回调以后会自动创建这个函数,\n调用此函数以后,wndproc 必须指定非 null 返回值,\n以避免再次重复调用默认回调函数 !richedit.translateCommand() = 允许转发转发子窗口的命令(_WM_COMMAND)与通知(_WM_NOTIFY)消息,\n避免子窗口 oncommand,onnotify 等回调失效。\n同时会处理子窗口的 _WM_CTLCOLORSTATIC 等消息,\n以避免部分外观属性失效 !richedit.valid = 窗口是否有效,\n窗口未关闭返回 true ,\n窗口已关闭或正在关闭返回 false !richedit.caretPos = 输入光标所在文本插入点位置,以字符为单位。\n有选区则返回选区最后一个字符的位置,\n无选区则返回插入点前面的字符位置。\n获取文本选区请改用 getSel 函数。 !richedit.leftText(__) = 获取参数 @1 指定位置之前的文本,\n以字符为单位,负数自右侧倒计数。\n不指定参数获取输和光标所在插入点之前的文本。 !richedit.rightText(__) = 获取参数 @1 指定位置之后的文本,\n以字符为单位,负数自左侧倒计数。\n不指定参数获取输和光标所在插入点之后的文本。 !richedit.oleCallback(implTable) = @import com.interface.IRichEditOleCallback;\n\n??.oleCallback(\n \n __/*自定义 OLE 回调事件,细节请参考 IRichEditOleCallback 接口文档。\n使用此函数必须先导入 com.interface.IRichEditOleCallback 接口。*/\n GetDragDropEffect = function(drag,grfKeyState,effect){\n \n /*所有 COM 实现的原生接口,第一个返回值为 0 表示执行成功。\n 返回值 2 指定拖放效果(effect),不指定则鼠标指针显示禁止拖放图标。*/\n return 0,1/*_DROPEFFECT_COPY*/\n }\n\n QueryAcceptData = function(ptrDataObj,format,reco,really,hMetaPict){\n \n /*really 表示放开鼠标,接收拖放数据*/\n if(really){\n \n /*用法请参考 com.interface.IDataObject 库参考文档*/\n var dataObj = com.interface.IDataObject(ptrDataObj);\n var txt = dataObj.getText();\n \n /*返回 0 继续默认处理,文本放入控件,返回 _S_FALSE 阻止默认处理。*/\n return 1/*_S_FALSE*/\n }\n \n return 0; \n }\n) !richedit.charFromPoint(.(x,y) = 返回 x,y 指定的控件客户区坐标所在的字符索引,\n第一个字符索引为 1 。 !richedit.pointFromChar(__) = 返回参数 @1 指定索引的字符所在的客户区坐标。\n第一个字符索引为 1,不指定参数则默认取当前输入光标所在字符位置。\n函数返回两个值,分别为坐标 x,y 。 !richedit.autoComplete(__) = 启用自动完成功能。\n参数 @1 可用数值指定选项,IDE 中可用 _SHACF_ 前缀列出可用的自动完成常量。\n全部可用选项请参考 API 函数 ::Shlwapi.SHAutoComplete 的文档。\n\n如果不指定参数则默认指定为 `0x21/*_SHACF_FILESYS_DIRS | _SHACF_FILESYSTEM*/`\n以启用文件与目录路径的自动完成功能。\n\n使用 com.autoComplete 库可以自行指定自动完成列表的数据。 !richedit.addCtrl() = !static. !TextDocument.tomTrue = 真值 !TextDocument.tomFalse = 假值 !TextDocument.tomToggle = 切换原来的值 !TextDocument.tomUndefined = 未定义值 !TextDocument.Name = 文件名 !TextDocument.Saved = 是否保存 !TextDocument.DefaultTabStop = 制表符宽度 !TextDocument.New() = 新建文件 !TextDocument.Open(.(filename,flags,codepage) = 打开文档 !TextDocument.Save(.(filename,flags,codepage) = 保存文档 !TextDocument.BeginEditCollection() = 开始编辑 !TextDocument.EndEditCollection() = 结束编辑 !TextDocument.Undo(.(number) = 撤消 !TextDocument.Redo(.(number) = 重做 !TextDocument.Range(.(first,lim) = 返回选区 !TextDocument.RangeFromPoint(.(x,y) = 指定坐标返回选区 !TextDocument.Selection = 选区对象 !TextDocument.Selection.Font = 字体\n!TomTextFont. !TomTextFont.Name = 字体名 !TomTextFont.Bold = 是否粗体,\n可使用tomTrue,tomFalse等值 !TomTextFont.Size = 字体大小 !TomTextFont.ForeColor = 字体颜色 win.ui.ctrl.CHARFORMAT2() = 用于创建 richedit 支持的 CHARFORMAT2 结构体。\n因为 richedit 提供的函数可将样式表参数自动转换为此结构,\n所以一般不需要手动创建此结构体。\n\n注意 aardio 中的 CHARFORMAT2 结构体字段命名与 MSDN 有微小差别。\n!charformat2. !charformat2.getFont() = 返回LOGFONT对象 !charformat2.mask = 掩码,这个一般不用管 !charformat2.backColor = 背景颜色,GDI 颜色格式( 0xBBGGRR )。 !charformat2.textColor = 字体颜色,GDI 颜色格式( 0xBBGGRR )。 !charformat2.faceName = 字体名 !charformat2.weight = 字体重量 !charformat2.bold = 是否粗体,仅用于写入样式。\n设置了这个值会在应用样式前自动修改 weight 属性。 !charformat2.underline = 是否粗体,仅用于写入样式。 !charformat2.italic = 是否斜体,仅用于写入样式。 !charformat2.strikeout = 是否显示删除线,仅用于写入样式。 !charformat2.disabled = 偏移一个像素,仅用于写入样式。 !charformat2.protected = 保护文本,修改时触发 onProtected 事件。\n仅用于整个控件,不适用于部分选区。\n此属性仅用于写入样式。 !charformat2.spacing = 字体间距 !charformat2.effects = 效果 !charformat2.yHeight = 字体大小,以 twips 为单位。\n修改字体大小请在窗体显示并且 DPI 初始化缩放完成以后再设置,不然会被重复放大。 !charformat2.point = 字体大小,以 pt 为单位。仅用于写入样式。\n在设置样式时指定 point 会在应用样式前自动修改 yHeight 的值。\n修改字体大小请在窗体显示并且 DPI 初始化缩放完成以后再设置,不然会被重复放大。 !charformat2.yOffset = 偏移 !charformat2.charSet = 字符集 !charformat2.pitchAndFamily = 字体族 !charformat2.lcid = 区域ID !charformat2.underlineType = 下划线类型 !charformat2.getFont() = 返回 LOGFONT 对象\n!logfont. _ENM_NONE=@0x0/*_ENM_NONE*/ _ENM_CHANGE=@0x1/*_ENM_CHANGE*/ _ENM_UPDATE=@0x2/*_ENM_UPDATE*/ _ENM_SCROLL=@0x4/*_ENM_SCROLL*/ _ENM_SCROLLEVENTS=@0x8/*_ENM_SCROLLEVENTS*/ _ENM_DRAGDROPDONE=@0x10/*_ENM_DRAGDROPDONE*/ _ENM_PARAGRAPHEXPANDED=@0x20/*_ENM_PARAGRAPHEXPANDED*/ _ENM_PAGECHANGE=@0x40/*_ENM_PAGECHANGE*/ _ENM_KEYEVENTS=@0x10000/*_ENM_KEYEVENTS*/ _ENM_MOUSEEVENTS=@0x20000/*_ENM_MOUSEEVENTS*/ _ENM_REQUESTRESIZE=@0x40000/*_ENM_REQUESTRESIZE*/ _ENM_SELCHANGE=@0x80000/*_ENM_SELCHANGE*/ _ENM_DROPFILES=@0x100000/*_ENM_DROPFILES*/ _ENM_PROTECTED=@0x200000/*_ENM_PROTECTED*/ _ENM_CORRECTTEXT=@0x400000/*_ENM_CORRECTTEXT*/ _ENM_IMECHANGE=@0x800000/*_ENM_IMECHANGE*/ _ENM_LANGCHANGE=@0x1000000/*_ENM_LANGCHANGE*/ _ENM_OBJECTPOSITIONS=@0x2000000/*_ENM_OBJECTPOSITIONS*/ _ENM_LINK=@0x4000000/*_ENM_LINK*/ _ENM_LOWFIRTF=@0x8000000/*_ENM_LOWFIRTF*/ _IMF_AUTOKEYBOARD=@1/*_IMF_AUTOKEYBOARD*/ _IMF_AUTOFONT=@2/*_IMF_AUTOFONT*/ _IMF_IMECANCELCOMPLETE=@4/*_IMF_IMECANCELCOMPLETE*/ _IMF_IMEALWAYSSENDNOTIFY=@8/*_IMF_IMEALWAYSSENDNOTIFY*/ _IMF_AUTOFONTSIZEADJUST=@0x10/*_IMF_AUTOFONTSIZEADJUST*/ _IMF_UIFONTS=@0x20/*_IMF_UIFONTS*/ _IMF_DUALFONT=@0x80/*_IMF_DUALFONT*/ _IMF_SMODE_PLAURALCLAUSE=@1/*_IMF_SMODE_PLAURALCLAUSE*/ _IMF_SMODE_NONE=@2/*_IMF_SMODE_NONE*/ _IMF_FORCENONE=@1/*_IMF_FORCENONE*/ _IMF_FORCEENABLE=@2/*_IMF_FORCEENABLE*/ _IMF_FORCEDISABLE=@4/*_IMF_FORCEDISABLE*/ _IMF_CLOSESTATUSWINDOW=@8/*_IMF_CLOSESTATUSWINDOW*/ _IMF_VERTICAL=@0x20/*_IMF_VERTICAL*/ _IMF_FORCEACTIVE=@0x40/*_IMF_FORCEACTIVE*/ _IMF_FORCEINACTIVE=@0x80/*_IMF_FORCEINACTIVE*/ _IMF_FORCEREMEMBER=@0x100/*_IMF_FORCEREMEMBER*/ _IMF_MULTIPLEEDIT=@0x400/*_IMF_MULTIPLEEDIT*/ _SF_TEXT=@1/*_SF_TEXT*/ _SF_RTF=@2/*_SF_RTF*/ _SF_UNICODE=@0x10/*_SF_UNICODE*/ _SF_UTEXT=@0x11/*_SF_UTEXT*/ _SFF_PWI=@0x800/*_SFF_PWI*/ _SFF_SELECTION=0x8000/*_SFF_SELECTION*/ _SF_PWI=@0x10802/*_SF_PWI*/ _SCF_DEFAULT=@0/*_SCF_DEFAULT*/ _SCF_SELECTION=@1/*_SCF_SELECTION*/ _SCF_WORD=@2/*_SCF_WORD*/ _SCF_ALL=@4/*_SCF_ALL*/ _SCF_USEUIRULES=@8/*_SCF_USEUIRULES*/ _SCF_ASSOCIATEFONT=@0x10/*_SCF_ASSOCIATEFONT*/ _SCF_NOKBUPDATE=@0x20/*_SCF_NOKBUPDATE*/ _SCF_ASSOCIATEFONT2=@0x40/*_SCF_ASSOCIATEFONT2*/ _CFM_BOLD=@1/*_CFM_BOLD*/ _CFM_ITALIC=@2/*_CFM_ITALIC*/ _CFM_UNDERLINE=@4/*_CFM_UNDERLINE*/ _CFM_STRIKEOUT=@8/*_CFM_STRIKEOUT*/ _CFM_PROTECTED=@0x10/*_CFM_PROTECTED*/ _CFM_LINK=@0x20/*_CFM_LINK*/ _CFM_SMALLCAPS=@0x40/*_CFM_SMALLCAPS*/ _CFM_ALLCAPS=@0x80/*_CFM_ALLCAPS*/ _CFM_HIDDEN=@0x100/*_CFM_HIDDEN*/ _CFM_OUTLINE=@0x200/*_CFM_OUTLINE*/ _CFM_SHADOW=@0x400/*_CFM_SHADOW*/ _CFM_EMBOSS=@0x800/*_CFM_EMBOSS*/ _CFM_IMPRINT=@0x1000/*_CFM_IMPRINT*/ _CFM_DISABLED=@0x2000/*_CFM_DISABLED*/ _CFM_REVISED=@0x4000/*_CFM_REVISED*/ _CFM_REVAUTHOR=@0x8000/*_CFM_REVAUTHOR*/ _CFM_SUBSCRIPT=@0x30000/*_CFM_SUBSCRIPT*/ _CFM_SUPERSCRIPT=@0x30000/*_CFM_SUPERSCRIPT*/ _CFM_ANIMATION=@0x40000/*_CFM_ANIMATION*/ _CFM_STYLE=@0x80000/*_CFM_STYLE*/ _CFM_KERNING=@0x100000/*_CFM_KERNING*/ _CFM_SPACING=@0x200000/*_CFM_SPACING*/ _CFM_WEIGHT=@0x400000/*_CFM_WEIGHT*/ _CFM_UNDERLINETYPE=@0x800000/*_CFM_UNDERLINETYPE*/ _CFM_LCID=@0x2000000/*_CFM_LCID*/ _CFM_BACKCOLOR=@0x4000000/*_CFM_BACKCOLOR*/ _CFM_CHARSET=@0x8000000/*_CFM_CHARSET*/ _CFM_OFFSET=@0x10000000/*_CFM_OFFSET*/ _CFM_FACE=@0x20000000/*_CFM_FACE*/ _CFM_COLOR=@0x40000000/*_CFM_COLOR*/ _CFM_SIZE=@0x80000000/*_CFM_SIZE*/ _CFE_PROTECTED=@0x10/*_CFE_PROTECTED*/ _CFM_EFFECTS=@0x4000003F/*_CFM_EFFECTS*/ _CFM_EFFECTS2=@0x44037FFF/*_CFM_EFFECTS2*/ _CFM_ALL=@-134217665/*_CFM_ALL*/ _CFM_ALL2=@-16777217/*_CFM_ALL2*/ _CFE_BOLD=@1/*_CFE_BOLD*/ _CFE_ITALIC=@2/*_CFE_ITALIC*/ _CFE_UNDERLINE=@4/*_CFE_UNDERLINE*/ _CFE_STRIKEOUT=@8/*_CFE_STRIKEOUT*/ _CFE_LINK=@0x20/*_CFE_LINK*/ _CFE_SUBSCRIPT=@0x10000/*_CFE_SUBSCRIPT*/ _CFE_SUPERSCRIPT=@0x20000/*_CFE_SUPERSCRIPT*/ _CFE_AUTOCOLOR=@0x40000000/*_CFE_AUTOCOLOR*/ _CFE_SMALLCAPS=@0x40/*_CFE_SMALLCAPS*/ _CFE_ALLCAPS=@0x80/*_CFE_ALLCAPS*/ _CFE_HIDDEN=@0x100/*_CFE_HIDDEN*/ _CFE_OUTLINE=@0x200/*_CFE_OUTLINE*/ _CFE_SHADOW=@0x400/*_CFE_SHADOW*/ _CFE_EMBOSS=@0x800/*_CFE_EMBOSS*/ _CFE_IMPRINT=@0x1000/*_CFE_IMPRINT*/ _CFE_DISABLED=@0x2000/*_CFE_DISABLED*/ _CFE_REVISED=@0x4000/*_CFE_REVISED*/ _CFE_AUTOBACKCOLOR=@0x4000000/*_CFE_AUTOBACKCOLOR*/ _CFU_UNDERLINENONE=@0/*_CFU_UNDERLINENONE*/ _CFU_UNDERLINE=@1/*_CFU_UNDERLINE*/ _CFU_UNDERLINEWORD=@2/*_CFU_UNDERLINEWORD*/ _CFU_UNDERLINEDOUBLE=@3/*_CFU_UNDERLINEDOUBLE*/ _CFU_UNDERLINEDOTTED=@4/*_CFU_UNDERLINEDOTTED*/ _CFU_UNDERLINEDASH=@5/*_CFU_UNDERLINEDASH*/ _CFU_UNDERLINEDASHDOT=@6/*_CFU_UNDERLINEDASHDOT*/ _CFU_UNDERLINEDASHDOTDOT=@7/*_CFU_UNDERLINEDASHDOTDOT*/ _CFU_UNDERLINEWAVE=@8/*_CFU_UNDERLINEWAVE*/ _CFU_UNDERLINETHICK=@9/*_CFU_UNDERLINETHICK*/ _CFU_UNDERLINEHAIRLINE=@0xA/*_CFU_UNDERLINEHAIRLINE*/ _CFU_UNDERLINEDOUBLEWAVE=@0xB/*_CFU_UNDERLINEDOUBLEWAVE*/ _CFU_UNDERLINEHEAVYWAVE=@0xC/*_CFU_UNDERLINEHEAVYWAVE*/ _CFU_UNDERLINELONGDASH=@0xD/*_CFU_UNDERLINELONGDASH*/ _CFU_UNDERLINETHICKDASH=@0xE/*_CFU_UNDERLINETHICKDASH*/ _CFU_UNDERLINETHICKDASHDOT=@0xF/*_CFU_UNDERLINETHICKDASHDOT*/ _CFU_UNDERLINETHICKDASHDOTDOT=@0x10/*_CFU_UNDERLINETHICKDASHDOTDOT*/ _CFU_UNDERLINETHICKDOTTED=@0x11/*_CFU_UNDERLINETHICKDOTTED*/ _CFU_UNDERLINETHICKLONGDASH=@0x12/*_CFU_UNDERLINETHICKLONGDASH*/ _CFU_INVERT=@0xFE/*_CFU_INVERT*/ _CFU_CF1UNDERLINE=@0xFF/*_CFU_CF1UNDERLINE*/ end intellisense***/