diff --git a/Makefile.linux b/Makefile.linux --- a/Makefile.linux +++ b/Makefile.linux @@ -1,3250 +1,3251 @@ #!/usr/bin/make # # This makefile generated by LgiIde # http://www.memecode.com/lgi.php # .SILENT : CC = gcc CPP = g++ Target = lgi ifndef Build Build = Debug endif BuildDir = $(Build) Flags = -fPIC -w -fno-inline -fpermissive Defs = -DLINUX -D_REENTRANT -D_FILE_OFFSET_BITS=64 -DLGI_LIBRARY ifeq ($(Build),Debug) Defs += -D_DEBUG Flags += -g Tag = d else Flags += -s -Os endif # Libraries Libs = \ -static-libgcc \ `pkg-config --libs gtk+-2.0` # Includes Inc = \ -I./include/common \ -I./include/linux/Gtk \ `pkg-config --cflags gtk+-2.0` \ -Iinclude/common \ -Iinclude/linux \ -Iinclude/linux/Gtk # Dependencies Depends = GDateTime.o \ GDragAndDrop.o \ GFile.o \ GExeCheck.o \ GFileCommon.o \ GApp.o \ GGeneral.o \ GPrinter.o \ GView.o \ GWindow.o \ LgiWidget.o \ GViewCommon.o \ GLibrary.o \ GMem.o \ GProperties.o \ GContainers.o \ GMenuGtk.o \ GProcess.o \ LgiRes.o \ Res.o \ Gel.o \ GPath.o \ GMemStream.o \ GStream.o \ GThread.o \ GMutex.o \ GThreadCommon.o \ GThreadEvent.o \ GVariant.o \ GGuiUtils.o \ GObject.o \ GToolTip.o \ GTrayIcon.o \ Lgi.o \ LgiMsg.o \ GFileSelect.o \ GAlert.o \ GFindReplace.o \ GFontSelect.o \ GInput.o \ Gdc2.o \ GColour.o \ GColourReduce.o \ GdcCommon.o \ GdcTools.o \ GDisplayString.o \ GFont.o \ GFont_A.o \ GFont_W.o \ GFontCodePages.o \ GFontSystem.o \ 15Bit.o \ 16Bit.o \ 24Bit.o \ 32Bit.o \ 8Bit.o \ Alpha.o \ GFilter.o \ GRect.o \ GMemDC.o \ GPrintDC.o \ GScreenDC.o \ GSurface.o \ GClipBoard.o \ GDataDlg.o \ GPassword.o \ GMru.o \ Base64.o \ GOptionsFile.o \ LgiRand.o \ GString.o \ GToken.o \ GUtf8.o \ GXmlTree.o \ GEdit.o \ GDocView.o \ GProgress.o \ GScrollBar.o \ GTextView3.o \ GBitmap.o \ GButton.o \ GCheckBox.o \ GCombo.o \ GList.o \ GPanel.o \ GPopup.o \ GProgressDlg.o \ GRadioGroup.o \ GStatusBar.o \ GTabView.o \ GTextLabel.o \ GToolBar.o \ GTree.o \ GCss.o \ GCssTools.o \ GLayout.o \ GWidgets.o \ GBox.o \ GTableLayout.o \ GSplitter.o \ INet.o \ INetTools.o \ md5.o # Target TargetFile = lib$(Target)$(Tag).so $(TargetFile) : outputfolder $(Depends) @echo Linking $(TargetFile) [$(Build)]... $(CPP)$s -shared \ \ -o $(BuildDir)/$(TargetFile) \ $(addprefix $(BuildDir)/,$(Depends)) \ $(Libs) @echo Done. # Create the output folder outputfolder : -mkdir -p $(BuildDir) 2> /dev/null # Clean out targets clean : rm -f $(BuildDir)/*.o $(BuildDir)/$(TargetFile) @echo Cleaned $(BuildDir) GDateTime.o : ./src/common/General/GDateTime.cpp ./include/common/Lgi.h \ ./include/common/GDateTime.h \ ./include/common/GToken.h \ ./include/common/GDocView.h @echo $( #include #include #include "Lgi.h" #include "GToken.h" #include "GUtf8.h" struct UnicodeMappings { int Unicode; char Ascii; } MissingMaps[] = { {0x2019, '\''}, {0x201C, '\"'}, {0x201D, '\"'}, {0, 0} }; typedef uint32 iso2022jp_block[16]; iso2022jp_block *iso2022jp_map[128]; iso2022jp_block iso2022jp_blocks[] = { {0,0,0x10000000,0,0,0x53118c,0x800000,0x800000,0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0xfffe0000,0xfffe03fb,0x3fb,0}, {0xffff0002,0xffffffff,0x2ffff,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0x33510000,0x80d0063,0,0,0,0,0,0,0x8,0x800,0,0,0xf0000,0,0x140000,0}, {0x6404098d,0x20301f81,0x40000,0xcc3,0xcc,0x20,0,0,0x40000,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0,0x3999900f,0x99999939,0x804,0,0,0x300c0003,0xc8c0,0x8000}, {0x60,0,0x5,0xa400,0,0,0,0,0,0,0,0,0,0,0,0}, {0x103fffef,0,0xfffffffe,0xffffffff,0x780fffff,0xfffffffe,0xffffffff,0x787fffff,0,0,0,0,0,0,0,0}, {0x43f36f8b,0x9b462442,0xe3e0e82c,0x400a0004,0xdb365f65,0x4497977,0xe3f0ecd7,0x8c56038,0x3403e602,0x35518000,0x7eabe0c8,0x98698200,0x2942a948,0x8060e803,0xad93441c,0x4568c03a}, {0x8656aa60,0x2403f7a,0x14618388,0x21741020,0x7022021,0x40bc3000,0x4462a624,0xa2060a8,0x85740217,0x9c840402,0x14157bfb,0x11e27f24,0x2efb665,0x20ff1f75,0x38403a70,0x676326c3}, {0x20924dd9,0xfc946b0,0x4850bc98,0xa03f8638,0x88162388,0x52323e09,0xe3a422aa,0xc72c00dd,0x26e1a166,0x8f0a840b,0x559e27eb,0x89bbc241,0x85400014,0x8496361,0x8ad07f0c,0x5cfff3e}, {0xa803ff1a,0x7b407a41,0x80024745,0x38eb0500,0x5d851,0x710c9934,0x1000397,0x24046366,0x5180d0,0x430ac000,0x30c89071,0x58000008,0xf7000e99,0x415f80,0x941000b0,0x62800018}, {0x9d00240,0x1568200,0x8015004,0x5101d10,0x1084c1,0x10504025,0x4d8a410f,0xa60d4009,0x914cab19,0x98121c0,0x3c485,0x80000652,0x80b04,0x9041d,0x905c4849,0x16900009}, {0x22200c65,0x24338412,0x47960c03,0x42250a04,0x90880028,0x4f084900,0xd3aa14a2,0x3e87d830,0x1f618604,0x41867ea4,0x5b3c390,0x211857a5,0x2a48241e,0x4a041128,0x161b0a40,0x88400d60}, {0x9502020a,0x10608221,0x4000243,0x80001444,0xc040000,0x70000000,0xc11a06,0xc00024a,0x401a00,0x40451404,0xbdb30029,0x52b0a78,0xbfa0bba9,0x8379407c,0xe81d12fc,0xc5694bf6}, {0x44aeff6,0xff022115,0x402bed63,0x242d033,0x131000,0x59ca1b02,0x20000a0,0x2c41a703,0x8ff24880,0x204,0x10055800,0x489200,0x20011894,0x34805004,0x684c3200,0x68be49ea}, {0x2e42184c,0x21c9a820,0x80b050b9,0xff7c001e,0x14e0849a,0x1e028c1,0xac49870e,0xdddb130f,0x89fbbe1a,0x51a2a2e0,0x32ca5502,0x928b3e46,0x438f1dbf,0x32186703,0x33c03028,0xa9230811}, {0x3a65c000,0x4028fe3,0x86252c4e,0xa1bf3d,0x8cd43a1a,0x317c06c9,0x950a00e0,0xedb018b,0x8c20e34b,0xf0101182,0xa7287d94,0x40fbc9ac,0x6534484,0x44445a90,0x13fc8,0xf5d40048}, {0xec577701,0x891dc442,0x49286b83,0xd2424109,0x59fe061d,0x3a221800,0x3b9fb7e4,0xc0eaf003,0x82021386,0xe4008980,0x10a1b200,0xcc44b80,0x8944d309,0x48341faf,0xc458259,0x450420a}, {0x10c8a040,0x44503140,0x1004004,0x5408280,0x442c0108,0x1a056a30,0x51420a6,0x645690cf,0x31000021,0xcbf09c18,0x63e2a120,0x1b5104c,0x9a83538c,0x3281b8b2,0xa84987a,0xc0233e7}, {0x9018d4cc,0x9070a1a1,0xe0048a1e,0x451c3d4,0x21c2439a,0x53104844,0x36400292,0xf3bd0241,0xe8f0ab09,0xa5d27dc0,0xd24bc242,0xd0afa43f,0x34a11aa0,0x3d88247,0x651bc452,0xc83ad294}, {0x40c8001c,0x33140e06,0xb21b614f,0xc0d00088,0xa898a02a,0x166ba1c5,0x85b42e50,0x604c08b,0x1e04f933,0xa251056e,0x76380400,0x73b8ec07,0x18324406,0xc8164081,0x63097c8a,0xaa042980}, {0xca9c1c24,0x27604e0e,0x83000990,0x81040046,0x10816011,0x908540d,0xcc0a000e,0xc000500,0xa0440430,0x6784008b,0x8a195288,0x8b18865e,0x41602e59,0x9cbe8c10,0x891c6861,0x89800}, {0x89a8100,0x41900018,0xe4a14007,0x640d0505,0xe4d310e,0xff0a4806,0x2aa81632,0xb852e,0xca841800,0x696c0e20,0x16000032,0x3905658,0x1a285120,0x11248000,0x432618e1,0xeaa5d52}, {0xae280fa0,0x4500fa7b,0x89406408,0xc044c880,0xb1419005,0x24c48424,0x603a1a34,0xc1949000,0x3a8246,0xc106180d,0x99100022,0x1511e050,0x824057,0x20a041a,0x8930004f,0x444ad813}, {0xed228a02,0x400510c0,0x1021000,0x31018808,0x2044600,0x708f000,0xa2008900,0x22020000,0x16100200,0x10400042,0x2605200,0x200052f4,0x82308510,0x42021100,0x80b54308,0x9a2070e1}, {0x8012040,0xfc653500,0xab0419c1,0x62140286,0x440087,0x2449085,0xa85405c,0x33803207,0xb8c00400,0xc0d0ce20,0x80c030,0xd250508,0x400a90,0x80c0200,0x40006505,0x41026421}, {0x268,0x847c0024,0xde200002,0x40498619,0x40000808,0x20010084,0x10108400,0x1c742cd,0xd52a7038,0x1d8f1968,0x3e12be50,0x81d92ef5,0x2412cec4,0x732e0828,0x4b3424ac,0xd41d020c}, {0x80002a02,0x8110097,0x114411c4,0x7d451786,0x64949d9,0x87914000,0xd8c4254c,0x491444ba,0xc8001b92,0x15800271,0xc000081,0xc200096a,0x40024800,0xba493021,0x1c802080,0x1008e2ac}, {0x341004,0x841400e1,0x20000020,0x10149800,0x4aa70c2,0x54208688,0x4130c62,0x20109180,0x2064082,0x54001c40,0xe4e90383,0x84802125,0x2000e433,0xe60944c0,0x81260a03,0x80112da}, {0x97906901,0xf8864001,0x81e24d,0xa6510a0e,0x81ec011a,0x8441c600,0xb62cadb8,0x8741a46f,0x4b028d54,0x2681161,0x2057bb60,0x43350a0,0xb7b4a8c0,0x1122402,0x20009ad3,0xc82271}, {0x809e2081,0xe1800c8a,0x8151b009,0x40281031,0x89a52a0e,0x620e69b6,0xd1444425,0x4d548085,0x1fb12c75,0x862dd807,0x4841d87c,0x226e414e,0x9e088200,0xed37f80c,0x75268c80,0x8149313}, {0xc8040e32,0x6ea6484e,0x66702c4a,0xba0126c0,0x185dd30c,0,0,0,0,0x5400000,0x81337020,0x3a54f81,0x641055ec,0x2344c318,0x341462,0x1a090a43}, {0x13a5187b,0xa8480102,0xc5440440,0xe2dd8106,0x2d481af0,0x416b626,0x6e405058,0x31128032,0xc0007e4,0x420a8208,0x803b4840,0x87134860,0x3428850d,0xe5290319,0x870a2345,0x5c1825a9}, {0xd9c577a6,0x3e85e00,0xa7000081,0x41c6cd54,0xa2042800,0x2b0ab860,0xda9e0020,0xe1a08ea,0x11c0427c,0x3768908,0x1058621,0x18a80000,0xc44846a0,0x20220d05,0x91485422,0x28978a01}, {0x87898,0x31221605,0x8804240,0x6a2fa4e,0x92110814,0x9b042002,0x6432e52,0x90105000,0x85ba0041,0x20203042,0x5a04f0b,0x40802708,0x1a930591,0x600df50,0x3021a202,0x4e800630}, {0x4c80cc4,0x8001a004,0xd4316000,0xa020880,0x281c00,0x418e18,0xca106ad0,0x4b00f210,0x1506274d,0x88900220,0x82a85a00,0x81504549,0x80002004,0x2c088804,0x508d1,0x4ac48001}, {0x62e020,0xa42008e,0x6a8c3055,0xe0a5090e,0x42c42906,0x80b34814,0xb330803e,0x731c0102,0x600d1494,0x9400c20,0xc040301a,0xc094a451,0x5c88dca,0xa40c96c2,0x34040001,0x11000c8}, {0xa9c9550d,0x1c5a2428,0x48370142,0x100f7a4d,0x452a32b4,0x9205317b,0x5c44b894,0x458a68d7,0x2ed15097,0x42081943,0x9d40d202,0x20979840,0x64d5409,0,0,0}, {0,0x84800000,0x4215542,0x17001c06,0x61107624,0xb9ddff87,0x5c0a659f,0x3c00245d,0x59adb0,0,0,0x9b28d0,0x2000422,0x44080108,0xac409804,0x90288d0a}, {0xe0018700,0x310400,0x82211794,0x10540019,0x21a2cb2,0x40039c02,0x88043d60,0x7900080c,0xba3c1628,0xcb088640,0x90807274,0x1e,0xd8000000,0x9c87e188,0x4124034,0x2791ae64}, {0xe6fbe86b,0x5366408f,0x537feea6,0xb5e4e32b,0x2869f,0x1228548,0x8004402,0x20a02116,0x2040004,0x52000,0x1547e00,0x1ac162c,0x10852a84,0x5308c14,0xb943fbc3,0x906000ca}, {0x40326000,0x80901200,0x4c810b30,0x40020054,0x1d6a0029,0x2802000,0x48000,0x150c2610,0x7018040,0xc24d94d,0x18502810,0x50205001,0x4d01000,0x2017080,0x21c30108,0x132}, {0x7190088,0x5600802,0x4c0e0012,0xf0a10405,0x2,0,0,0,0,0,0,0x800000,0x35a8e8d,0x5a0421bd,0x11703488,0x26}, {0x10000000,0x8804c502,0xf801b815,0x25ed147c,0x1bb0ed60,0x1bd70589,0x1a627af3,0xac50d0c,0x524ae5d1,0x63050490,0x52440354,0x16122b57,0x1101a872,0x182949,0x10080948,0x886c6000}, {0x58f916e,0x39903012,0x4930f840,0x1b8880,0,0x428500,0x98000058,0x7014ea04,0x611d1628,0x60005113,0xa71a24,0,0x3c00000,0x10187120,0xa9270172,0x89066004}, {0x20cc022,0x40810900,0x8ca0202d,0xe34,0,0x11012100,0xc11a8011,0x892ec4c,0x85000040,0x1806c7ac,0x512e03e,0x108000,0x80ce4008,0x2106d01,0x8568641,0x27011e}, {0x83d3750,0x4e05e032,0x48401c0,0x1400081,0,0,0,0x591aa0,0x882443c8,0xc8001d48,0x72030152,0x4049013,0x4008280,0xd148a10,0x2088056,0x2704a040}, {0x4c000000,0,0,0xa3200000,0xa0ae1902,0xdf002660,0x7b15f010,0x3ad08121,0x284180,0x48001003,0x8014cc00,0xc414cf,0x30202000,0x1,0,0}, {0,0,0,0,0,0,0,0,0xffffdf7a,0xefffffff,0x3fffffff,0,0,0,0,0x2} }; class LgiIso2022Jp { public: LgiIso2022Jp() { int n, o = 0, i = 0; for (n=0; n<3; n++) iso2022jp_map[o++] = &iso2022jp_blocks[i++]; o += 13; for (n=0; n<4; n++) iso2022jp_map[o++] = &iso2022jp_blocks[i++];; o += 4; iso2022jp_map[o++] = &iso2022jp_blocks[i++];; o += 14; for (n=0; n<41; n++) iso2022jp_map[o++] = &iso2022jp_blocks[i++];; o += 47; iso2022jp_map[o++] = &iso2022jp_blocks[i++];; LgiAssert(o == 128); } bool CanEncode(char16 *s, int l) { if (s) { if (l < 0) l = StrlenW(s); for (int i=0; i> 9]; if (!*block) { return false; } u &= 0x1ff; if ( ( *block[u >> 5] & (1 << (u & 0x1f)) ) == 0 ) { return false; } } return true; } return false; } } Iso2022Jp; ///////////////////////////////////////////////////////////////////////////////////// bool LgiIsUtf8(const char *s, int len) { #define LenCheck(Need) \ if (len >= 0 && (len - (s - Start)) < Need) \ goto Utf8Error; #define TrailCheck() \ if (!IsUtf8_Trail(*s)) \ goto Utf8Error; \ s++; if (!s || *s == 0) return true; const char *Start = s; while (*s && (len < 0 || ((s - Start) < len) ) ) { if (IsUtf8_1Byte(*s)) { s++; } else if (IsUtf8_2Byte(*s)) { s++; LenCheck(1); TrailCheck(); } else if (IsUtf8_3Byte(*s)) { s++; LenCheck(2); TrailCheck(); TrailCheck(); } else if (IsUtf8_4Byte(*s)) { s++; LenCheck(3); TrailCheck(); TrailCheck(); TrailCheck(); } else goto Utf8Error; } return true; Utf8Error: LgiTrace("%s:%i - Invalid utf...\n", _FL); return false; } ///////////////////////////////////////////////////////////////////////////////////// short _gdc_usascii_mapping[128] = { // 0x80 - 0x8f 0xc7, 0xfc, 0xe9, 0xe2, 0xe4, 0xe0, 0xe5, 0xe7, 0xea, 0xeb, 0xe8, 0xef, 0xee, 0xec, 0xc4, 0xc5, // 0x90 - 0x9f 0xc9, 0xe6, 0xc6, 0xf4, 0xf6, 0xf2, 0xfb, 0xf9, 0xff, 0xd6, 0xdc, 0xa2, 0xa3, 0xa5, 0x20a7, 0x192, // 0xa0 - 0xaf 0xe1, 0xed, 0xf3, 0xfa, 0xf1, 0xd1, 0xb2, 0xb0, 0xbf, 0x2310, 0xac, 0xbd, 0xbc, 0xa1, 0xab, 0xbb, // 0xb0 - 0xbf 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255d, 0x255c, 0x255b, 0x2510, // 0xc0 - 0xcf 0x2514, 0x2534, 0x252c, 0x251c, 0x2500, 0x253c, 0x255e, 0x255f, 0x255a, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256c, 0x2567, // 0xd0 - 0xdf 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256b, 0x256a, 0x2518, 0x250c, 0x2588, 0x2584, 0x258c, 0x2590, 0x2580, // 0xe0 - 0xef 0x3b1, 0x3b2, 0x393, 0x3a0, 0x3a3, 0x3c3, 0x3bc, 0x3c4, 0x3a6, 0x398, 0x3a9, 0x3b4, 0x221e, 0xd8, 0x3b6, 0x2229, // 0xf0 - 0xff 0x2261, 0xb1, 0x2265, 0x2264, 0x2320, 0x2321, 0xf7, 0x2248, 0xb0, 0x2022, 0x2219, 0x221a, 0x207f, 178, 0x25a0, 0x25a1 }; // This mapping just NUL's out the characters between 0x80 and 0x9f which aren't defined // in the ISO spec. The rest of the characters map to themselves. short _gdc_ISO_8859_identity_mapping[128] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff }; short _gdc_ISO_8859_2_mapping[128] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xa0, 0x104, 0x2d8, 0x141, 0xa4, 0x13d, 0x15a, 0xa7, 0xa8, 0x160, 0x15e, 0x164, 0x179, 0xad, 0x17d, 0x17b, 0xb0, 0x105, 0x2db, 0x142, 0xb4, 0x13e, 0x15b, 0x2c7, 0xb8, 0x161, 0x15f, 0x165, 0x17a, 0x2dd, 0x17e, 0x17c, 0x154, 0xc1, 0xc2, 0x102, 0xc4, 0x139, 0x106, 0xc7, 0x10c, 0xc9, 0x118, 0xcb, 0x11a, 0xcd, 0xce, 0x10e, 0x110, 0x143, 0x147, 0xd3, 0xd4, 0x150, 0xd6, 0xd7, 0x158, 0x16e, 0xda, 0x170, 0xdc, 0xdd, 0x162, 0xdf, 0x155, 0xe1, 0xe2, 0x103, 0xe4, 0x13a, 0x107, 0xe7, 0x10d, 0xe9, 0x119, 0xeb, 0x11b, 0xed, 0xee, 0x10f, 0x111, 0x144, 0x148, 0xf3, 0xf4, 0x151, 0xf6, 0xf7, 0x159, 0x16f, 0xfa, 0x171, 0xfc, 0xfd, 0x163, 0x2d9 }; short _gdc_ISO_8859_3_mapping[128] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xa0, 0x126, 0x2d8, 0xa3, 0xa4, 0, 0x124, 0xa7, 0xa8, 0x130, 0x15e, 0x11e, 0x134, 0xad, 0, 0x17b, 0xb0, 0x127, 0xb2, 0xb3, 0xb4, 0xb5, 0x125, 0xb7, 0xb8, 0x131, 0x15f, 0x11f, 0x135, 0xbd, 0, 0x17c, 0xc0, 0xc1, 0xc2, 0, 0xc4, 0x10a, 0x108, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0, 0xd1, 0xd2, 0xd3, 0xd4, 0x120, 0xd6, 0xd7, 0x11c, 0xd9, 0xda, 0xdb, 0xdc, 0x16c, 0x15c, 0xdf, 0xe0, 0xe1, 0xe2, 0, 0xe4, 0x10b, 0x109, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0, 0xf1, 0xf2, 0xf3, 0xf4, 0x121, 0xf6, 0xf7, 0x11d, 0xf9, 0xfa, 0xfb, 0xfc, 0x16d, 0x15d, 0x2d9 }; short _gdc_ISO_8859_4_mapping[128] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x0A0, 0x104, 0x138, 0x156, 0x0A4, 0x128, 0x13B, 0x0A7, 0x0A8, 0x160, 0x112, 0x122, 0x166, 0x0AD, 0x17D, 0x0AF, 0x0B0, 0x105, 0x2DB, 0x157, 0x0B4, 0x129, 0x13C, 0x2C7, 0x0B8, 0x161, 0x113, 0x123, 0x167, 0x14A, 0x17E, 0x14B, 0x100, 0x0C1, 0x0C2, 0x0C3, 0x0C4, 0x0C5, 0x0C6, 0x12E, 0x10C, 0x0C9, 0x118, 0x0CB, 0x116, 0x0CD, 0x0CE, 0x12A, 0x110, 0x145, 0x14C, 0x136, 0x0D4, 0x0D5, 0x0D6, 0x0D7, 0x0D8, 0x172, 0x0DA, 0x0DB, 0x0DC, 0x168, 0x16A, 0x0DF, 0x101, 0x0E1, 0x0E2, 0x0E3, 0x0E4, 0x0E5, 0x0E6, 0x12F, 0x10D, 0x0E9, 0x119, 0x0EB, 0x117, 0x0ED, 0x0EE, 0x12B, 0x111, 0x146, 0x14D, 0x137, 0x0F4, 0x0F5, 0x0F6, 0x0F7, 0x0F8, 0x173, 0x0FA, 0x0FB, 0x0FC, 0x169, 0x16B, 0x2D9 }; short _gdc_ISO_8859_5_mapping[128] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x0A0, 0x401, 0x402, 0x403, 0x404, 0x405, 0x406, 0x407, 0x408, 0x409, 0x40A, 0x40B, 0x40C, 0x0AD, 0x40E, 0x40F, 0x410, 0x411, 0x412, 0x413, 0x414, 0x415, 0x416, 0x417, 0x418, 0x419, 0x41A, 0x41B, 0x41C, 0x41D, 0x41E, 0x41F, 0x420, 0x421, 0x422, 0x423, 0x424, 0x425, 0x426, 0x427, 0x428, 0x429, 0x42A, 0x42B, 0x42C, 0x42D, 0x42E, 0x42F, 0x430, 0x431, 0x432, 0x433, 0x434, 0x435, 0x436, 0x437, 0x438, 0x439, 0x43A, 0x43B, 0x43C, 0x43D, 0x43E, 0x43F, 0x440, 0x441, 0x442, 0x443, 0x444, 0x445, 0x446, 0x447, 0x448, 0x449, 0x44A, 0x44B, 0x44C, 0x44D, 0x44E, 0x44F, 0x2116, 0x451, 0x452, 0x453, 0x454, 0x455, 0x456, 0x457, 0x458, 0x459, 0x45A, 0x45B, 0x45C, 0x0A7, 0x45E, 0x45F }; short _gdc_ISO_8859_6_mapping[128] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xA0, 0, 0, 0, 0xA4, 0, 0, 0, 0, 0, 0, 0, 0x60C, 0xAD, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x61B, 0, 0, 0, 0x61F, 0, 0x621, 0x622, 0x623, 0x624, 0x625, 0x626, 0x627, 0x628, 0x629, 0x62A, 0x62B, 0x62C, 0x62D, 0x62E, 0x62F, 0x630, 0x631, 0x632, 0x633, 0x634, 0x635, 0x636, 0x637, 0x638, 0x639, 0x63A, 0, 0, 0, 0, 0, 0x640, 0x641, 0x642, 0x643, 0x644, 0x645, 0x646, 0x647, 0x648, 0x649, 0x64A, 0x64B, 0x64C, 0x64D, 0x64E, 0x64F, 0x650, 0x651, 0x652, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; short _gdc_ISO_8859_7_mapping[128] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x0A0, 0x2BD, 0x2BC, 0x0A3, 0, 0, 0x0A6, 0x0A7, 0x0A8, 0x0A9, 0, 0x0AB, 0x0AC, 0x0AD, 0, 0x015, 0x0B0, 0x0B1, 0x0B2, 0x0B3, 0x384, 0x385, 0x386, 0x0B7, 0x388, 0x389, 0x38A, 0x0BB, 0x38C, 0x0BD, 0x38E, 0x38F, 0x390, 0x391, 0x392, 0x393, 0x394, 0x395, 0x396, 0x397, 0x398, 0x399, 0x39A, 0x39B, 0x39C, 0x39D, 0x39E, 0x39F, 0x3A0, 0x3A1, 0, 0x3A3, 0x3A4, 0x3A5, 0x3A6, 0x3A7, 0x3A8, 0x3A9, 0x3AA, 0x3AB, 0x3AC, 0x3AD, 0x3AE, 0x3AF, 0x3B0, 0x3B1, 0x3B2, 0x3B3, 0x3B4, 0x3B5, 0x3B6, 0x3B7, 0x3B8, 0x3B9, 0x3BA, 0x3BB, 0x3BC, 0x3BD, 0x3BE, 0x3BF, 0x3C0, 0x3C1, 0x3C2, 0x3C3, 0x3C4, 0x3C5, 0x3C6, 0x3C7, 0x3C8, 0x3C9, 0x3CA, 0x3CB, 0x3CC, 0x3CD, 0x3CE, 0 }; short _gdc_ISO_8859_8_mapping[128] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x0A0, 0, 0x0A2, 0x0A3, 0x0A4, 0x0A5, 0x0A6, 0x0A7, 0x0A8, 0x0A9, 0x0D7, 0x0AB, 0x0AC, 0x0AD, 0x0AE, 0x203E, 0x0B0, 0x0B1, 0x0B2, 0x0B3, 0x0B4, 0x0B5, 0x0B6, 0x0B7, 0x0B8, 0x0B9, 0x0F7, 0x0BB, 0x0BC, 0x0BD, 0x0BE, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x2017, 0x5D0, 0x5D1, 0x5D2, 0x5D3, 0x5D4, 0x5D5, 0x5D6, 0x5D7, 0x5D8, 0x5D9, 0x5DA, 0x5DB, 0x5DC, 0x5DD, 0x5DE, 0x5DF, 0x5E0, 0x5E1, 0x5E2, 0x5E3, 0x5E4, 0x5E5, 0x5E6, 0x5E7, 0x5E8, 0x5E9, 0x5EA, 0, 0, 0, 0, 0 }; short _gdc_ISO_8859_9_mapping[128] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, 0x11E, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0x130, 0x15E, 0xDF, 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, 0x11F, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0x131, 0x15F, 0xFF }; short _gdc_ISO_8859_13_mapping[128] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xA0, 0x201D, 0xA2, 0xA3, 0xA4, 0x201E, 0xA6, 0xA7, 0xD8, 0xA9, 0x156, 0xAB, 0xAC, 0xAD, 0xAE, 0xC6, 0xB0, 0xB1, 0xB2, 0xB3, 0x201C, 0xB5, 0xB6, 0xB7, 0xF8, 0xB9, 0x157, 0xBB, 0xBC, 0xBD, 0xBE, 0xE6, 0x104, 0x12E, 0x100, 0x106, 0xC4, 0xC5, 0x118, 0x112, 0x10C, 0xC9, 0x179, 0x116, 0x122, 0x136, 0x12A, 0x13B, 0x160, 0x143, 0x145, 0xD3, 0x14C, 0xD5, 0xD6, 0xD7, 0x172, 0x141, 0x15A, 0x16A, 0xDC, 0x17B, 0x17D, 0xDF, 0x105, 0x12F, 0x101, 0x107, 0xE4, 0xE5, 0x119, 0x113, 0x10D, 0xE9, 0x17A, 0x117, 0x123, 0x137, 0x12B, 0x13C, 0x161, 0x144, 0x146, 0xF3, 0x14D, 0xF5, 0xF6, 0xF7, 0x173, 0x142, 0x15B, 0x16B, 0xFC, 0x17C, 0x17E, 0x2019 }; short _gdc_ISO_8859_15_mapping[128] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xA0, 0xA1, 0xA2, 0xA3, 0x20AC, 0xA5, 0x160, 0xA7, 0x161, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xB0, 0xB1, 0xB2, 0xB3, 0x17D, 0xB5, 0xB6, 0xB7, 0x17E, 0xB9, 0xBA, 0xBB, 0x152, 0x153, 0x178, 0xBF, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF, 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF }; short _gdc_win_874_mapping[128] = { 0x20AC, 0, 0, 0, 0, 0x2026, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0, 0, 0, 0, 0, 0, 0, 0, 0xA0, 0xE01, 0xE02, 0xE03, 0xE04, 0xE05, 0xE06, 0xE07, 0xE08, 0xE09, 0xE0A, 0xE0B, 0xE0C, 0xE0D, 0xE0E, 0xE0F, 0xE10, 0xE11, 0xE12, 0xE13, 0xE14, 0xE15, 0xE16, 0xE17, 0xE18, 0xE19, 0xE1A, 0xE1B, 0xE1C, 0xE1D, 0xE1E, 0xE1F, 0xE20, 0xE21, 0xE22, 0xE23, 0xE24, 0xE25, 0xE26, 0xE27, 0xE28, 0xE29, 0xE2A, 0xE2B, 0xE2C, 0xE2D, 0xE2E, 0xE2F, 0xE30, 0xE31, 0xE32, 0xE33, 0xE34, 0xE35, 0xE36, 0xE37, 0xE38, 0xE39, 0xE3A, 0, 0, 0, 0, 0xE3F, 0xE40, 0xE41, 0xE42, 0xE43, 0xE44, 0xE45, 0xE46, 0xE47, 0xE48, 0xE49, 0xE4A, 0xE4B, 0xE4C, 0xE4D, 0xE4E, 0xE4F, 0xE50, 0xE51, 0xE52, 0xE53, 0xE54, 0xE55, 0xE56, 0xE57, 0xE58, 0xE59, 0xE5A, 0xE5B, 0, 0, 0, 0, }; short _gdc_win_1250_mapping[128] = { 0x20AC, 0, 0x201A, 0, 0x201E, 0x2026, 0x2020, 0x2021, 0, 0x2030, 0x0160, 0x2039, 0x015A, 0x0164, 0x017D, 0x0179, 0, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0, 0x2122, 0x0161, 0x203A, 0x015B, 0x0165, 0x017E, 0x017A, 0xA0, 0x02C7, 0x02D8, 0x0141, 0xA4, 0x0104, 0xA6, 0xA7, 0xA8, 0xA9, 0x015E, 0xAB, 0xAC, 0xAD, 0xAE, 0x017B, 0xB0, 0xB1, 0x02DB, 0x0142, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0x0105, 0x015F, 0xBB, 0x013D, 0x02DD, 0x013E, 0x017C, 0x0154, 0xC1, 0xC2, 0x0102, 0xC4, 0x0139, 0x0106, 0xC7, 0x010C, 0xC9, 0x0118, 0xCB, 0x011A, 0xCD, 0xCE, 0x010E, 0x0110, 0x0143, 0x0147, 0xD3, 0xD4, 0x0150, 0xD6, 0xD7, 0x0158, 0x016E, 0xDA, 0x0170, 0xDC, 0xDD, 0x0162, 0xDF, 0x0155, 0xE1, 0xE2, 0x0103, 0xE4, 0x013A, 0x0107, 0xE7, 0x010D, 0xE9, 0x0119, 0xEB, 0x011B, 0xED, 0xEE, 0x010F, 0x0111, 0x0144, 0x0148, 0xF3, 0xF4, 0x0151, 0xF6, 0xF7, 0x0159, 0x016F, 0xFA, 0x0171, 0xFC, 0xFD, 0x0163, 0x02D9 }; short _gdc_win_1251_mapping[128] = { 0x402, 0x403, 0x201A, 0x453, 0x201E, 0x2026, 0x2020, 0x2021, 0x20AC, 0x2030, 0x409, 0x2039, 0x40A, 0x40C, 0x40B, 0x40F, 0x452, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0, 0x2122, 0x459, 0x203A, 0x45A, 0x45C, 0x45B, 0x45F, 0x0A0, 0x40E, 0x45E, 0x408, 0x0A4, 0x490, 0x0A6, 0x0A7, 0x401, 0x0A9, 0x404, 0x0AB, 0x0AC, 0x0AD, 0x0AE, 0x407, 0x0B0, 0x0B1, 0x406, 0x456, 0x491, 0x0B5, 0x0B6, 0x0B7, 0x451, 0x2116, 0x454, 0x0BB, 0x458, 0x405, 0x455, 0x457, 0x410, 0x411, 0x412, 0x413, 0x414, 0x415, 0x416, 0x417, 0x418, 0x419, 0x41A, 0x41B, 0x41C, 0x41D, 0x41E, 0x41F, 0x420, 0x421, 0x422, 0x423, 0x424, 0x425, 0x426, 0x427, 0x428, 0x429, 0x42A, 0x42B, 0x42C, 0x42D, 0x42E, 0x42F, 0x430, 0x431, 0x432, 0x433, 0x434, 0x435, 0x436, 0x437, 0x438, 0x439, 0x43A, 0x43B, 0x43C, 0x43D, 0x43E, 0x43F, 0x440, 0x441, 0x442, 0x443, 0x444, 0x445, 0x446, 0x447, 0x448, 0x449, 0x44A, 0x44B, 0x44C, 0x44D, 0x44E, 0x44F }; short _gdc_win_1252_mapping[128] = { 0x20AC, 0, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, 0x02C6, 0x2030, 0x0160, 0x2039, 0x0152, 0, 0x017D, 0, 0, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x02DC, 0x2122, 0x0161, 0x203A, 0x0153, 0, 0x017E, 0x0178, 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF, 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF }; short _gdc_win_1253_mapping[128] = { 0x20AC, 0, 0x201A, 0x192, 0x201E, 0x2026, 0x2020, 0x2021, 0, 0x2030, 0, 0x2039, 0, 0, 0, 0, 0, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0, 0x2122, 0, 0x203A, 0, 0, 0, 0, 0xA0, 0x385, 0x386, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0, 0xAB, 0xAC, 0xAD, 0xAE, 0x2015, 0xB0, 0xB1, 0xB2, 0xB3, 0x384, 0xB5, 0xB6, 0xB7, 0x388, 0x389, 0x38A, 0xBB, 0x38C, 0xBD, 0x38E, 0x38F, 0x390, 0x391, 0x392, 0x393, 0x394, 0x395, 0x396, 0x397, 0x398, 0x399, 0x39A, 0x39B, 0x39C, 0x39D, 0x39E, 0x39F, 0x3A0, 0x3A1, 0, 0x3A3, 0x3A4, 0x3A5, 0x3A6, 0x3A7, 0x3A8, 0x3A9, 0x3AA, 0x3AB, 0x3AC, 0x3AD, 0x3AE, 0x3AF, 0x3B0, 0x3B1, 0x3B2, 0x3B3, 0x3B4, 0x3B5, 0x3B6, 0x3B7, 0x3B8, 0x3B9, 0x3BA, 0x3BB, 0x3BC, 0x3BD, 0x3BE, 0x3BF, 0x3C0, 0x3C1, 0x3C2, 0x3C3, 0x3C4, 0x3C5, 0x3C6, 0x3C7, 0x3C8, 0x3C9, 0x3CA, 0x3CB, 0x3CC, 0x3CD, 0x3CE, 0 }; short _gdc_win_1254_mapping[128] = { 0x20AC, 0, 0x201A, 0x192, 0x201E, 0x2026, 0x2020, 0x2021, 0x2C6, 0x2030, 0x160, 0x2039, 0x152, 0, 0, 0, 0, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x2DC, 0x2122, 0x161, 0x203A, 0x153, 0, 0, 0x178, 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, 0x11E, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0x130, 0x15E, 0xDF, 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, 0x11F, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0x131, 0x15F, 0xFF, }; short _gdc_win_1255_mapping[128] = { 0x20AC, 0, 0x201A, 0x192, 0x201E, 0x2026, 0x2020, 0x2021, 0x2C6, 0x2030, 0, 0x2039, 0, 0, 0, 0, 0, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x2DC, 0x2122, 0, 0x203A, 0, 0, 0, 0, 0xA0, 0xA1, 0xA2, 0xA3, 0x20AA, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xD7, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xF7, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF, 0x5B0, 0x5B1, 0x5B2, 0x5B3, 0x5B4, 0x5B5, 0x5B6, 0x5B7, 0x5B8, 0x5B9, 0, 0x5BB, 0x5BC, 0x5BD, 0x5BE, 0x5BF, 0x5C0, 0x5C1, 0x5C2, 0x5C3, 0x5F0, 0x5F1, 0x5F2, 0x5F3, 0x5F4, 0, 0, 0, 0, 0, 0, 0, 0x5D0, 0x5D1, 0x5D2, 0x5D3, 0x5D4, 0x5D5, 0x5D6, 0x5D7, 0x5D8, 0x5D9, 0x5DA, 0x5DB, 0x5DC, 0x5DD, 0x5DE, 0x5DF, 0x5E0, 0x5E1, 0x5E2, 0x5E3, 0x5E4, 0x5E5, 0x5E6, 0x5E7, 0x5E8, 0x5E9, 0x5EA, 0, 0, 0x200E, 0x200F, 0, }; short _gdc_win_1256_mapping[128] = { 0x20AC, 0x67E, 0x201A, 0x192, 0x201E, 0x2026, 0x2020, 0x2021, 0x2C6, 0x2030, 0x679, 0x2039, 0x152, 0x686, 0x698, 0x688, 0x6AF, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x6A9, 0x2122, 0x691, 0x203A, 0x153, 0x200C, 0x200D, 0x6BA, 0xA0, 0x60C, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0x6BE, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0x61B, 0xBB, 0xBC, 0xBD, 0xBE, 0x61F, 0x6C1, 0x621, 0x622, 0x623, 0x624, 0x625, 0x626, 0x627, 0x628, 0x629, 0x62A, 0x62B, 0x62C, 0x62D, 0x62E, 0x62F, 0x630, 0x631, 0x632, 0x633, 0x634, 0x635, 0x636, 0xD7, 0x637, 0x638, 0x639, 0x63A, 0x640, 0x641, 0x642, 0x643, 0xE0, 0x644, 0xE2, 0x645, 0x646, 0x647, 0x648, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0x649, 0x64A, 0xEE, 0xEF, 0x64B, 0x64C, 0x64D, 0x64E, 0xF4, 0x64F, 0x650, 0xF7, 0x651, 0xF9, 0x652, 0xFB, 0xFC, 0x200E, 0x200F, 0x6D2, }; short _gdc_win_1257_mapping[128] = { 0x20AC, 0, 0x201A, 0, 0x201E, 0x2026, 0x2020, 0x2021, 0, 0x2030, 0, 0x2039, 0, 0xA8, 0x2C7, 0xB8, 0, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0, 0x2122, 0, 0x203A, 0, 0xAF, 0x2DB, 0, 0xA0, 0, 0xA2, 0xA3, 0xA4, 0, 0xA6, 0xA7, 0xD8, 0xA9, 0x156, 0xAB, 0xAC, 0xAD, 0xAE, 0xC6, 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xF8, 0xB9, 0x157, 0xBB, 0xBC, 0xBD, 0xBE, 0xE6, 0x104, 0x12E, 0x100, 0x106, 0xC4, 0xC5, 0x118, 0x112, 0x10C, 0xC9, 0x179, 0x116, 0x122, 0x136, 0x12A, 0x13B, 0x160, 0x143, 0x145, 0xD3, 0x14C, 0xD5, 0xD6, 0xD7, 0x172, 0x141, 0x15A, 0x16A, 0xDC, 0x17B, 0x17D, 0xDF, 0x105, 0x12F, 0x101, 0x107, 0xE4, 0xE5, 0x119, 0x113, 0x10D, 0xE9, 0x17A, 0x117, 0x123, 0x137, 0x12B, 0x13C, 0x161, 0x144, 0x146, 0xF3, 0x14D, 0xF5, 0xF6, 0xF7, 0x173, 0x142, 0x15B, 0x16B, 0xFC, 0x17C, 0x17E, 0x2D9, }; short _gdc_win_1258_mapping[128] = { 0x20AC, 0, 0x201A, 0x192, 0x201E, 0x2026, 0x2020, 0x2021, 0x2C6, 0x2030, 0, 0x2039, 0x152, 0, 0, 0, 0, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x2DC, 0x2122, 0, 0x203A, 0x153, 0, 0, 0x178, 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF, 0xC0, 0xC1, 0xC2, 0x102, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0x300, 0xCD, 0xCE, 0xCF, 0x110, 0xD1, 0x309, 0xD3, 0xD4, 0x1A0, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0x1AF, 0x303, 0xDF, 0xE0, 0xE1, 0xE2, 0x103, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0x301, 0xED, 0xEE, 0xEF, 0x111, 0xF1, 0x323, 0xF3, 0xF4, 0x1A1, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0x1B0, 0x20AB, 0xFF, }; short _gdc_koi8r_mapping[128] = { 0x2500, 0x2502, 0x250C, 0x2510, 0x2514, 0x2518, 0x251C, 0x2524, 0x252C, 0x2534, 0x253C, 0x2580, 0x2584, 0x2588, 0x258C, 0x2590, 0x2591, 0x2592, 0x2593, 0x2320, 0x25A0, 0x2219, 0x221A, 0x2248, 0x2264, 0x2265, 0xA0, 0x2321, 0xB0, 0xB2, 0xB7, 0xF7, 0x2550, 0x2551, 0x2552, 0x451, 0x2553, 0x2554, 0x2555, 0x2556, 0x2557, 0x2558, 0x2559, 0x255A, 0x255B, 0x255C, 0x255D, 0x255E, 0x255F, 0x2560, 0x2561, 0x401, 0x2562, 0x2563, 0x2564, 0x2565, 0x2566, 0x2567, 0x2568, 0x2569, 0x256A, 0x256B, 0x256C, 0xA9, 0x44E, 0x430, 0x431, 0x446, 0x434, 0x435, 0x444, 0x433, 0x445, 0x438, 0x439, 0x43A, 0x43B, 0x43C, 0x43D, 0x43E, 0x43F, 0x44F, 0x440, 0x441, 0x442, 0x443, 0x436, 0x432, 0x44C, 0x44B, 0x437, 0x448, 0x44D, 0x449, 0x447, 0x44A, 0x42E, 0x410, 0x411, 0x426, 0x414, 0x415, 0x424, 0x413, 0x425, 0x418, 0x419, 0x41A, 0x41B, 0x41C, 0x41D, 0x41E, 0x41F, 0x42F, 0x420, 0x421, 0x422, 0x423, 0x416, 0x412, 0x42C, 0x42B, 0x417, 0x428, 0x42D, 0x429, 0x427, 0x42A }; short _gdc_koi8u_mapping[128] = { 0x2500, 0x2502, 0x250C, 0x2510, 0x2514, 0x2518, 0x251C, 0x2524, 0x252C, 0x2534, 0x253C, 0x2580, 0x2584, 0x2588, 0x258C, 0x2590, 0x2591, 0x2592, 0x2593, 0x2320, 0x25A0, 0x2019, 0x221A, 0x2248, 0x2264, 0x2265, 0xA0, 0x2321, 0xB0, 0xB2, 0xB7, 0xF7, 0x2550, 0x2551, 0x2552, 0x451, 0x454, 0x2554, 0x456, 0x457, 0x2557, 0x2558, 0x2559, 0x255A, 0x255B, 0x491, 0x255D, 0x255E, 0x255F, 0x2560, 0x2561, 0x401, 0x404, 0x2563, 0x406, 0x407, 0x2566, 0x2567, 0x2568, 0x2569, 0x256A, 0x490, 0x256C, 0xA9, 0x44E, 0x430, 0x431, 0x446, 0x434, 0x435, 0x444, 0x433, 0x445, 0x438, 0x439, 0x43A, 0x43B, 0x43C, 0x43D, 0x43E, 0x43F, 0x44F, 0x440, 0x441, 0x442, 0x443, 0x436, 0x432, 0x44C, 0x44B, 0x437, 0x448, 0x44D, 0x449, 0x447, 0x44A, 0x42E, 0x410, 0x411, 0x426, 0x414, 0x415, 0x424, 0x413, 0x425, 0x418, 0x419, 0x41A, 0x41B, 0x41C, 0x41D, 0x41E, 0x41F, 0x42F, 0x420, 0x421, 0x422, 0x423, 0x416, 0x412, 0x42C, 0x42B, 0x417, 0x428, 0x42D, 0x429, 0x427, 0x42A }; short _gdc_koi8ru_mapping[128] = { 0x2500, 0x2502, 0x250C, 0x2510, 0x2514, 0x2518, 0x251C, 0x2524, 0x252C, 0x2534, 0x253C, 0x2580, 0x2584, 0x2588, 0x258C, 0x2590, 0x2591, 0x2592, 0x2593, 0x201C, 0x25A0, 0x2219, 0x201D, 0x2014, 0x2116, 0x2122, 0xA0, 0xBB, 0xAE, 0xAB, 0xB7, 0xA4, 0x2550, 0x2551, 0x2552, 0x451, 0x454, 0x2554, 0x456, 0x457, 0x2557, 0x2558, 0x2559, 0x255A, 0x255B, 0x491, 0x45E, 0x255E, 0x255F, 0x2560, 0x2561, 0x401, 0x404, 0x2563, 0x406, 0x407, 0x2566, 0x2567, 0x2568, 0x2569, 0x256A, 0x490, 0x40E, 0xA9, 0x44E, 0x430, 0x431, 0x446, 0x434, 0x435, 0x444, 0x433, 0x445, 0x438, 0x439, 0x43A, 0x43B, 0x43C, 0x43D, 0x43E, 0x43F, 0x44F, 0x440, 0x441, 0x442, 0x443, 0x436, 0x432, 0x44C, 0x44B, 0x437, 0x448, 0x44D, 0x449, 0x447, 0x44A, 0x42E, 0x410, 0x411, 0x426, 0x414, 0x415, 0x424, 0x413, 0x425, 0x418, 0x419, 0x41A, 0x41B, 0x41C, 0x41D, 0x41E, 0x41F, 0x42F, 0x420, 0x421, 0x422, 0x423, 0x416, 0x412, 0x42C, 0x42B, 0x417, 0x428, 0x42D, 0x429, 0x427, 0x42A, }; #if defined WIN32 #define WinDef(d) d // Map the windows codepages to their real names. #define WINDOWS_1250 EASTEUROPE_CHARSET #define WINDOWS_1252 ANSI_CHARSET #define WINDOWS_1251 RUSSIAN_CHARSET #define WINDOWS_1253 GREEK_CHARSET #define WINDOWS_1254 TURKISH_CHARSET #define WINDOWS_1255 HEBREW_CHARSET #define WINDOWS_1256 ARABIC_CHARSET #define WINDOWS_1257 BALTIC_CHARSET #define WINDOWS_932 SHIFTJIS_CHARSET #define WINDOWS_936 GB2313_CHARSET #define WINDOWS_949 HANGEUL_CHARSET #define WINDOWS_950 CHINESEBIG5_CHARSET #else #define WinDef(d) 0 #endif GCharset::GCharset(const char *cp, const char *des, short *map, const char *alt) { Charset = cp; Description = des; UnicodeMap = map; IconvName = 0; AlternateNames = alt; Type = CpNone; if (cp) { if (stricmp(cp, "utf-8") == 0) { Type = CpUtf8; } else if (stricmp(cp, "utf-16") == 0 || stricmp(cp, "utf-32") == 0) { Type = CpWide; } else if (stricmp(cp, "ucs-2") == 0) { Type = CpWide; IconvName = "UCS-2-INTERNAL"; } else if (UnicodeMap) { Type = CpMapped; } #ifdef WIN32 else if (strnicmp(cp, "windows-", 8) == 0) { Type = CpWindowsDb; } #endif else { Type = CpIconv; } } } bool GCharset::IsUnicode() { return (Type == CpUtf8) || (Type == CpWide); } const char *GCharset::GetIconvName() { return IconvName ? IconvName : Charset; } bool GCharset::IsAvailable() { if (Type != CpIconv) return true; #ifndef LGI_STATIC GFontSystem *FontSys = GFontSystem::Inst(); if (FontSys) return FontSys->HasIconv(true); #endif // LGI_STATIC return false; } static GCharset LgiCharsets[] = { // Good 'ol ascii GCharset("us-ascii", "ASCII", _gdc_usascii_mapping, "ascii-us,iso-ir-6,ANSI_X3.4-1986,ISO_646.irv,ASCII,ISO646-US,us,IBM367,cp367,csASCII"), // Unicode (up here so they get found first) GCharset("utf-8", "Utf-8"), GCharset("utf-16", "Utf-16"), GCharset("utf-32", "Utf-32"), GCharset("ucs-2", "Ucs-2"), // ISO (get prefered over windows charsets by being first in this list) GCharset("iso-8859-1", "ISO 8859-1 (West Europe)", _gdc_ISO_8859_identity_mapping, "iso-ir-100,ISO_8859-1,latin1,l1,IBM819,CP819,csISOLatin1,iso8859-1"), GCharset("iso-8859-2", "ISO 8859-2 (East Europe)", _gdc_ISO_8859_2_mapping, "iso-ir-101,ISO_8859-2,latin2,l2,csISOLatin2"), GCharset("iso-8859-3", "ISO 8859-3 (Latin Script)", _gdc_ISO_8859_3_mapping, "iso-ir-109,ISO_8859-3,latin3,l3,csISOLatin3"), GCharset("iso-8859-4", "ISO 8859-4 (Baltic)", _gdc_ISO_8859_4_mapping, "iso-ir-110,ISO_8859-4,latin4,l4,csISOLatin4"), GCharset("iso-8859-5", "ISO 8859-5 (Russian)", _gdc_ISO_8859_5_mapping, "iso-ir-144,ISO_8859-5,cyrillic,csISOLatinCyrillic"), GCharset("iso-8859-6", "ISO 8859-6 (Arabic)", _gdc_ISO_8859_6_mapping, "iso-ir-127,ISO_8859-6,ECMA-114,ASMO-708,arabic,csISOLatinArabic"), GCharset("iso-8859-7", "ISO 8859-7 (Greek)", _gdc_ISO_8859_7_mapping, "iso-ir-126,ISO_8859-7,ELOT_928,ECMA-118,greek,greek8,csISOLatinGreek"), GCharset("iso-8859-8", "ISO 8859-8 (Hebrew)", _gdc_ISO_8859_8_mapping, "iso-ir-138,ISO_8859-8,hebrew,csISOLatinHebrew,iso-8859-8-i"), GCharset("iso-8859-9", "ISO 8859-9 (Turkish)", _gdc_ISO_8859_9_mapping, "iso-ir-148,ISO_8859-9,latin5,l5,csISOLatin5"), GCharset("iso-8859-13", "ISO 8859-13 (Baltik)", _gdc_ISO_8859_13_mapping, "ISO_8859-9"), GCharset("iso-8859-15", "ISO 8859-15 (Latic 9)", _gdc_ISO_8859_15_mapping, "ISO_8859-15"), // Windows GCharset("windows-874", "Windows 874 (Thai)", _gdc_win_874_mapping, "iso-8859-11,cp874"), GCharset("windows-932", "Windows 932 (Japanese)"), GCharset("windows-936", "Windows 936 (Chinese)"), GCharset("windows-949", "Windows 949 (Korean)"), GCharset("windows-950", "Windows 950 (Chinese)"), GCharset("windows-1250", "Windows 1250 (Latin 2)", _gdc_win_1250_mapping, "x-cp1250,cp1250"), GCharset("windows-1251", "Windows 1251 (Cyrillic)", _gdc_win_1251_mapping, "x-cp1251,cp1251"), GCharset("windows-1252", "Windows 1252 (Latin 1)", _gdc_win_1252_mapping, "x-cp1252,cp1252"), GCharset("windows-1253", "Windows 1253 (Greek)", _gdc_win_1253_mapping, "x-cp1253,cp1253"), GCharset("windows-1254", "Windows 1254 (Turkish)", _gdc_win_1254_mapping, "x-cp1254,cp1254"), GCharset("windows-1255", "Windows 1255 (Hebrew)", _gdc_win_1255_mapping, "x-cp1255,cp1255"), GCharset("windows-1256", "Windows 1256 (Arabic)", _gdc_win_1256_mapping, "x-cp1256,cp1256"), GCharset("windows-1257", "Windows 1257 (Baltic)", _gdc_win_1257_mapping, "x-cp1257,cp1257"), GCharset("windows-1258", "Windows 1258 (Veitnam)", _gdc_win_1258_mapping, "x-cp1258,cp1258"), // Russian GCharset("koi8-r", "KOI8-R", _gdc_koi8r_mapping, "csKOI8R"), GCharset("koi8-u", "KOI8-U", _gdc_koi8u_mapping, "csKOI8U"), GCharset("koi8-ru", "KOI8-RU", _gdc_koi8ru_mapping, "csKOI8RU"), GCharset("koi8-t", "KOI8-T (Tajik)"), // Codepages GCharset("cp850", "Cp850", 0, "IBM850,850,csPC850Multilingual"), GCharset("cp862", "Cp862", 0, "IBM862,862,csPC862LatinHebrew"), GCharset("cp866", "Cp866", 0, "IBM866,866,csIBM866"), GCharset("cp1133", "Cp1133 (Laotian)"), // Japanese GCharset("euc-jp", "EUC-JP", 0, "csEUCPkdFmtJapanese"), GCharset("shift_jis", "SHIFT_JIS", 0, "MS_Kanji,csShiftJIS"), GCharset("cp932", "cp932", 0, 0), GCharset("iso-2022-jp", "ISO-2022-JP", 0, "csISO2022JP"), GCharset("iso-2022-jp-1", "ISO-2022-JP-1"), GCharset("iso-2022-jp-2", "ISO-2022-JP-2", 0, "csISO2022JP2"), // Chinese GCharset("euc-cn", "EUC-CN (Chinese)"), GCharset("hz-gb-2312", "HZ (Chinese)", 0, "hz"), GCharset("gbk", "GBK (Chinese)", 0, "CP936,MS936,windows-936,x-gbk,gb2312,GB-2312,csGB2312,GB2312_CHARSET"), GCharset("gb18030", "GB18030 (Chinese)"), GCharset("euc-tw", "EUC-TW (Chinese)"), GCharset("big5", "BIG5 (Chinese)", 0, "csBig5"), GCharset("big5-hkscs", "BIG5-HKSCS (Chinese)"), // GCharset("gb2312", "GB-2312 (Chinese)", 0, "GB-2312,csGB2312"), GCharset("iso-2022-cn", "ISO-2022-CN (Chinese)"), GCharset("iso-2022-cn-eXT","ISO-2022-CN-EXT (Chinese)"), // Korean GCharset("euc-kr", "EUC-KR", 0, "csEUCKR"), GCharset("iso-2022-kr", "ISO-2022-KR", 0, "csISO2022KR"), GCharset("johab", "JOHAB"), GCharset("cp949", "CP949", 0, "ks_c_5601-1987,ks_c_5601"), // Armenian // GCharset("armscii-8", "ARMSCII-8 (Armenian)"), // Georgian GCharset("Georgian-Academy","Georgian-Academy"), GCharset("Georgian-PS", " Georgian-PS"), // Thai GCharset("tis-620", "TIS-620 (Thai)"), // Laotian GCharset("mulelao-1", "MuleLao-1"), // Vietnamese GCharset("viscii", "VISCII (Vietnamese)", 0, "csVISCII"), GCharset("tcvn", "TCVN (Vietnamese)"), // EOF marker GCharset() }; static GCharsetSystem CharsetSystem; GCharset *LgiGetCpInfo(const char *Cs) { return CharsetSystem.GetCsInfo(Cs); } ///////////////////////////////////////////////////////////////////////////// // Utf-16 conversion int LgiByteLen(const void *p, const char *cp) { if (p && cp) { - if (stricmp(cp, "utf-16") == 0) + if (stricmp(cp, LGI_WideCharset) == 0) { return StrlenW((char16*)p) * sizeof(char16); } else { return strlen((char*)p); } } return 0; } int LgiCpToAnsi(char *cp) { int Ansi = 0; if (cp && strnicmp(cp, "windows-", 8) == 0) { Ansi = atoi(cp+9); } return Ansi; } int LgiBufConvertCp(void *Out, const char *OutCp, int OutLen, const void *&In, const char *InCp, int &InLen) { int Status = 0; if (Out && OutCp && In && InCp) { GCharset *InInfo = LgiGetCpInfo(InCp); GCharset *OutInfo = LgiGetCpInfo(OutCp); if (InInfo && OutInfo) { char *In8 = (char*)In; uchar *Out8 = (uchar*)Out; if (InLen < 0) { InLen = LgiByteLen(In, InCp); } #ifdef WIN32 if (InInfo->Type == CpWindowsDb && OutInfo->Type == CpWide) { // mb -> unicode char Cp[32]; sprintf_s(Cp, sizeof(Cp), ".%s", InInfo->Charset + 8); setlocale(LC_ALL, Cp); void *Start = Out; while (OutLen >= sizeof(char16) && InLen > 0) { int s = mbtowc((char16*)Out, (char*)In, min(InLen, MB_CUR_MAX)); if (s > 0) { ((char*&)In) += s; InLen -= s; ((char16*&)Out)++; OutLen -= sizeof(char16); } else break; } return (NativeInt)Out-(NativeInt)Start; } else if (InInfo->Type == CpWide && OutInfo->Type == CpWindowsDb) { // unicode -> mb char Cp[32]; sprintf_s(Cp, sizeof(Cp), ".%s", OutInfo->Charset + 8); setlocale(LC_ALL, Cp); void *Start = Out; while (OutLen >= MB_CUR_MAX && InLen > sizeof(char16) ) { int s = wctomb((char*)Out, ((char16*)In)[0] ); if (s > 0) { ((char16*&)In)++; InLen -= sizeof(char16); ((char*&)Out) += s; OutLen -= s; } } return (NativeInt)Out-(NativeInt)Start; } else #endif if (InInfo->Type == CpIconv || OutInfo->Type == CpIconv) { #ifndef LGI_STATIC GFontSystem *Fs = GFontSystem::Inst(); if (Fs) return Fs->IconvConvert(OutInfo->GetIconvName(), (char*)Out, OutLen, InInfo->GetIconvName(), (const char*&)In, InLen); #else LgiAssert(!"No iconv in static build"); #endif } else { // Mapped or Utf conversion uint32 Utf32 = 0; while (OutLen > 0 && InLen > 0) { char *RewindIn = In8; int RewindInLen = InLen; // Convert input char to Utf-32 switch (InInfo->Type) { case CpMapped: { if (*In8) { uchar i = (uchar)*In8++; InLen--; if (i & 0x80) { Utf32 = InInfo->UnicodeMap[i - 0x80]; if (!Utf32) Utf32 = '?'; } else { Utf32 = i; } } else { Utf32 = 0; InLen = 0; } break; } case CpUtf8: { Utf32 = LgiUtf8To32((uint8 *&)In8, InLen); break; } case CpWide: { Utf32 = LgiUtf16To32((char16 *&)In8, InLen); if (Utf32 == 0xfeff || Utf32 == 0xfffe) continue; break; } default: LgiAssert(0); break; } if (!Utf32) { break; } // Convert Utf-32 into output format switch (OutInfo->Type) { case CpMapped: { if (Utf32 & ~0x7f) { int n; for (n=0; n<128; n++) { if (OutInfo->UnicodeMap[n] == Utf32) { *Out8++ = 0x80 + n; break; } } if (n >= 128) { for (n=0; MissingMaps[n].Unicode; n++) { if (MissingMaps[n].Unicode == Utf32) { *Out8++ = MissingMaps[n].Ascii; break; } } if (!MissingMaps[n].Unicode) { *Out8++ = '?'; } } } else { *Out8++ = Utf32; } OutLen--; break; } case CpUtf8: { uchar *PrevOut8 = Out8; if (!LgiUtf32To8(Utf32, (uint8*&) Out8, OutLen)) { // Not enough buffer to encode the character In8 = RewindIn; InLen = RewindInLen; OutLen = 0; } break; } case CpWide: { LgiUtf32To16(Utf32, (char16*&)Out8, OutLen); break; } default: { break; } } } In = (void*)In8; Status = (NativeInt)Out8-(NativeInt)Out; } } else { // printf("%s:%i - LgiBufConvertCp failed '%s' -> '%s'.\n", __FILE__, __LINE__, InCp, OutCp); } } return Status; } void *LgiNewConvertCp(const char *OutCp, const void *In, const char *InCp, int InLen) { GMemQueue b; if (OutCp && In && InCp) { GCharset *InInfo = LgiGetCpInfo(InCp); GCharset *OutInfo = LgiGetCpInfo(OutCp); if (InInfo && OutInfo) { if (InLen < 0) { InLen = LgiByteLen(In, InCp); } if (!stricmp(InCp, OutCp)) { if (InInfo->Type == CpWide) { return NewStrW((char16*)In, InLen); } else { return NewStr((char*)In, InLen); } } if (InInfo->Type == CpIconv || OutInfo->Type == CpIconv) { #ifndef LGI_STATIC GFontSystem *Fs = GFontSystem::Inst(); if (Fs) { if (!Fs->IconvConvert(OutInfo->GetIconvName(), &b, InInfo->GetIconvName(), (const char*&)In, InLen)) { InCp = "iso-8859-1"; goto BufConvert; } } #else LgiAssert(!"No inconv in static build"); #endif } else { BufConvert: char Buf[2 << 10]; while (InLen > 0) { int Bytes = LgiBufConvertCp(Buf, OutCp, sizeof(Buf), In, InCp, InLen); if (Bytes > 0) { b.Write((uchar*)Buf, Bytes); } else { break; } } } } } return b.GetSize() ? b.New(sizeof(char16)) : 0; } char16 *LgiNewUtf8To16(const char *In, int InLen) { if (In) { - return (char16*) LgiNewConvertCp("utf-16", In, "utf-8", InLen); + return (char16*) LgiNewConvertCp(LGI_WideCharset, In, "utf-8", InLen); } return 0; } char *LgiNewUtf16To8(const char16 *In, int InLen) { if (In) { - return (char*) LgiNewConvertCp("utf-8", In, "utf-16", InLen); + return (char*) LgiNewConvertCp("utf-8", In, LGI_WideCharset, InLen); } return 0; } int LgiCharLen(const void *Str, const char *Cp, int Bytes) { if (Str && Cp) { GCharset *InInfo = LgiGetCpInfo(Cp); if (InInfo) { switch (InInfo->Type) { default: case CpMapped: { return strlen((char*)Str); } case CpUtf8: { uchar *s = (uchar*)Str; int Len = 0; if (Bytes > 0) { uchar *e = s + Bytes; while (*s && s < e) { LgiNextUtf8((char*&)s); Len++; } } else { while (*s) { LgiNextUtf8((char*&)s); Len++; } } return Len; } case CpWide: { return StrlenW((char16*)Str); } } } } return 0; } bool LgiIsCpImplemented(char *Cp) { return LgiGetCpInfo(Cp) != 0; } const char *LgiAnsiToLgiCp(int AnsiCodePage) { if (AnsiCodePage < 0) { #ifdef WIN32 AnsiCodePage = GetACP(); #else return "utf-8"; #endif } #define WinCp(i) case i: return "windows-" #i; switch (AnsiCodePage) { WinCp(874) WinCp(932) WinCp(936) WinCp(949) WinCp(950) WinCp(1250) WinCp(1251) WinCp(1252) WinCp(1253) WinCp(1254) WinCp(1255) WinCp(1256) WinCp(1257) WinCp(1258) case 20127: return "us-ascii"; case 28591: return "iso-8859-1"; case 28592: return "iso-8859-2"; case 28593: return "iso-8859-3"; case 28594: return "iso-8859-4"; case 28595: return "iso-8859-5"; case 28596: return "iso-8859-6"; case 28597: return "iso-8859-7"; case 28598: return "iso-8859-8"; case 28599: return "iso-8859-9"; case 28600: return "ISO-8859-10"; case 28605: return "ISO-8859-15"; case 50220: case 50221: return "iso-2022-jp"; case 51932: return "euc-jp"; case 51949: return "euc-kr"; case 65001: return "utf-8"; } #undef WinCp return 0; } char *LgiSeekUtf8(const char *Ptr, int D, char *Start) { uchar *p = (uchar*)Ptr; if (p) { if (D >= 0) { for (int i=0; i(uchar*)Start; i++) { p--; while (p>(uchar*)Start && IsUtf8_Trail(*p)) p--; } } else { // You must pass a start point to move backwards in // the utf-8 string, otherwise you can run off the // beginning of the array. LgiAssert(0); } } return (char*)p; } bool LgiMatchCharset(short *Map, char16 *Utf, bool &Has8Bit) { if (Map && Utf) { // Test Charset because we have a map of all the chars in it... char16 *c; for (c = Utf; *c; c++) { if (*c > 0x7f) { // Check char Has8Bit = true; int i; for (i=0; i<128; i++) { if (Map[i] == *c) break; } if (i >= 128) { // Char not found return false; } } } if (Has8Bit) { if (!*c) { return true; } } } return false; } const char *LgiDetectCharset(const char *Utf8, int Len, List *Prefs) { const char *Status = "utf-8"; // The default.. - GAutoWString Utf((char16*)LgiNewConvertCp("utf-16", Utf8, "utf-8", Len)); + GAutoWString Utf((char16*)LgiNewConvertCp(LGI_WideCharset, Utf8, "utf-8", Len)); if (Utf) { if (Prefs) { for (char *p = Prefs->First(); p; p = Prefs->Next()) { GCharset *Cp = CharsetSystem.GetCsInfo(p); if (Cp && stricmp(Cp->Charset, "us-ascii") != 0 && Cp->UnicodeMap) { bool Has8Bit = false; if (LgiMatchCharset(Cp->UnicodeMap, Utf, Has8Bit)) { return Cp->Charset; } if (!Has8Bit) { return "us-ascii"; } } } } for (GCharset *Cp = LgiCharsets + 1; Cp->Charset; Cp++) { if (Cp->UnicodeMap) { bool Has8Bit = false; if (LgiMatchCharset(Cp->UnicodeMap, Utf, Has8Bit)) { return Cp->Charset; } if (!Has8Bit) { return "us-ascii"; } } } } return Status; } char *LgiToNativeCp(const char *In, int InLen) { const char *Cp = LgiAnsiToLgiCp(); #ifdef WIN32 GCharset *CpInfo = LgiGetCpInfo(Cp); if (!CpInfo || CpInfo->Type == CpWindowsDb) { if (In) { // Double byte charset conversion, don't rely on iconv // being around to do the conversion. setlocale(LC_ALL, ".ACP"); if (InLen < 0) { InLen = strlen(In); } char16 *Wide = LgiNewUtf8To16(In, InLen); if (Wide) { int Len = wcstombs(0, Wide, StrlenW(Wide)); char *Buf = Len > 0 ? new char[Len+1] : 0; if (Buf) { wcstombs(Buf, Wide, InLen); Buf[Len] = 0; } DeleteArray(Wide); return Buf; } } return 0; } #endif return (char*)LgiNewConvertCp(Cp, In, "utf-8", InLen); } char *LgiFromNativeCp(const char *In, int InLen) { const char *Cp = LgiAnsiToLgiCp(); #ifdef WIN32 GCharset *CpInfo = LgiGetCpInfo(Cp); if (!CpInfo || CpInfo->Type == CpWindowsDb) { if (In) { // Double byte charset conversion, don't rely on iconv // being around to do the conversion. setlocale(LC_ALL, ".ACP"); if (InLen < 0) { #ifdef __GNUC__ // FIXME InLen = strlen(In); #else InLen = _mbstrlen(In); #endif } else { // Work out how many chars 'InLen' bytes is int Bytes = InLen; const char *i = In; int Chars = 0; while (*i && Bytes > 0) { int n = mblen(i, MB_CUR_MAX); if (n > 0) { Chars++; Bytes -= n; i += n; } else break; } InLen = Chars; } int Len = mbstowcs(0, In, InLen); if (Len) { char16 *Buf = new char16[Len+1]; if (Buf) { mbstowcs(Buf, In, InLen); Buf[Len] = 0; char *Utf8 = LgiNewUtf16To8(Buf); DeleteArray(Buf); return Utf8; } } } return 0; } #endif return (char*)LgiNewConvertCp("utf-8", In, Cp, InLen); } /////////////////////////////////////////////////////////////////////////// struct GCharsetSystemPriv { GCharset *Utf8; GCharset *Utf16; GHashTable Charsets; GCharsetSystemPriv() : Charsets(512) { Utf8 = 0; Utf16 = 0; } }; GCharsetSystem::GCharsetSystem() { char l[256]; // Charset setup, store all the charset pointers // in a hash table for O(1) lookup. d = new GCharsetSystemPriv; d->Charsets.SetStringPool(true); LgiAssert(LgiCharsets->Charset); for (GCharset *Cs = LgiCharsets; Cs->Charset; Cs++) { strcpy(l, Cs->Charset); strlwr(l); if (!stricmp(l, "utf-8")) d->Utf8 = Cs; else if (!stricmp(l, "utf-16")) d->Utf16 = Cs; d->Charsets.Add(l, Cs); GToken a(Cs->AlternateNames, ","); for (int n=0; nCharsets.Add(l, Cs); } } } GCharsetSystem::~GCharsetSystem() { DeleteObj(d); } GCharset *GCharsetSystem::GetCsInfo(const char *Cp) { if (Cp && d) { // Lookup the charset in the hash table char l[256]; strcpy_s(l, sizeof(l), Cp); strlwr(l); if (!stricmp(l, "utf-8")) return d->Utf8; else if (!stricmp(l, "utf-16")) return d->Utf16; GCharset *Cs = (GCharset*) d->Charsets.Find(l); if (Cs) { return Cs; } else { // printf("%s:%i - No charset '%s' in font sub system.\n", __FILE__, __LINE__, l); // printf("Charsets=%i\n", Charsets->GetUsed()); } } return 0; } GCharset *LgiGetCsInfo(const char *Cs) { return CharsetSystem.GetCsInfo(Cs); } GCharset *GCharsetSystem::GetCsList() { return LgiCharsets; } GCharset *LgiGetCsList() { return LgiCharsets; } diff --git a/src/common/Lgi/GViewCommon.cpp b/src/common/Lgi/GViewCommon.cpp --- a/src/common/Lgi/GViewCommon.cpp +++ b/src/common/Lgi/GViewCommon.cpp @@ -1,1994 +1,1994 @@ /// \file /// \author Matthew Allen #ifdef LINUX #include #endif #include "Lgi.h" #include "GViewPriv.h" #include "GDragAndDrop.h" #include "GTableLayout.h" #include "GButton.h" #include "GCss.h" #define DEBUG_CAPTURE 0 #if WIN32NATIVE #define GViewFlags d->WndStyle #else #define GViewFlags WndFlags #endif ////////////////////////////////////////////////////////////////////////////////////// // Helper GMouse &lgi_adjust_click(GMouse &Info, GViewI *Wnd, bool Debug) { static GMouse Temp; Temp = Info; if (Wnd) { GdcPt2 Offset(0, 0); Temp.Target = Wnd; if (Wnd->WindowVirtualOffset(&Offset)) { GRect c = Wnd->GetClient(false); Temp.x -= Offset.x + c.x1; Temp.y -= Offset.y + c.y1; // LgiTrace("_lgi_adjust_click_for_window -= (%i+%i),(%i+%i)\n", Offset.x , c.x1, Offset.y , c.y1); } } else LgiAssert(!"No handle?"); return Temp; } ////////////////////////////////////////////////////////////////////////////////////// // Iterator GViewIter::GViewIter(GView *view) : i(view->Children.Start()) { v = view; } GViewI *GViewIter::First() { i = v->Children.Start(); return *i; } GViewI *GViewIter::Last() { i = v->Children.End(); return *i; } GViewI *GViewIter::Next() { i++; return *i; } GViewI *GViewIter::Prev() { i--; return *i; } int GViewIter::Length() { return v->Children.Length(); } int GViewIter::IndexOf(GViewI *view) { i = v->Children.Start(); return i.IndexOf(view); } GViewI *GViewIter::operator [](int Idx) { return v->Children[Idx]; } ////////////////////////////////////////////////////////////////////////////////////// // GView class methods GViewI *GView::_Capturing = 0; GViewI *GView::_Over = 0; GView::GView(OsView view) { #ifdef _DEBUG _Debug = false; #endif d = new GViewPrivate; _View = view; _Window = 0; _Lock = 0; _InLock = 0; _BorderSize = 0; _IsToolBar = false; Script = 0; Pos.ZOff(-1, -1); WndFlags = GWF_VISIBLE; } GView::~GView() { _Delete(); DeleteObj(d); } #ifdef _DEBUG void GView::Debug() { _Debug = true; } #endif GViewIterator *GView::IterateViews() { return new GViewIter(this); } bool GView::AddView(GViewI *v, int Where) { LgiAssert(!Children.HasItem(v)); bool Add = Children.Insert(v, Where); if (Add) { GView *gv = v->GetGView(); if (gv && gv->_Window != _Window) { LgiAssert(!_InLock); gv->_Window = _Window; } v->SetParent(this); } return Add; } bool GView::DelView(GViewI *v) { return Children.Delete(v); } bool GView::HasView(GViewI *v) { return Children.HasItem(v); } OsWindow GView::WindowHandle() { GWindow *w = GetWindow(); return (w) ? w->WindowHandle() : 0; } GWindow *GView::GetWindow() { if (!_Window) { for (GView *w = d->GetParent(); w; w = w->d ? w->d->GetParent() : NULL) { if (w->_Window) { LgiAssert(!_InLock); _Window = w->_Window; break; } } } return dynamic_cast(_Window); } bool GView::Lock(const char *file, int line, int TimeOut) { if (!_Window) GetWindow(); _InLock++; // LgiTrace("%s::%p Lock._InLock=%i %s:%i\n", GetClass(), this, _InLock, file, line); if (_Window && _Window->_Lock) { if (TimeOut < 0) { return _Window->_Lock->Lock(file, line); } else { return _Window->_Lock->LockWithTimeout(TimeOut, file, line); } } return true; } void GView::Unlock() { if (_Window && _Window->_Lock) { _Window->_Lock->Unlock(); } _InLock--; // LgiTrace("%s::%p Unlock._InLock=%i\n", GetClass(), this, _InLock); } void GView::OnMouseClick(GMouse &m) { if (Script && Script->OnScriptEvent(this)) { Script->OnMouseClick(m); } } void GView::OnMouseEnter(GMouse &m) { if (Script && Script->OnScriptEvent(this)) { Script->OnMouseEnter(m); } } void GView::OnMouseExit(GMouse &m) { if (Script && Script->OnScriptEvent(this)) { Script->OnMouseExit(m); } } void GView::OnMouseMove(GMouse &m) { if (Script && Script->OnScriptEvent(this)) { Script->OnMouseMove(m); } } bool GView::OnMouseWheel(double Lines) { if (Script && Script->OnScriptEvent(this)) return Script->OnMouseWheel(Lines); return false; } bool GView::OnKey(GKey &k) { if (Script && Script->OnScriptEvent(this)) { Script->OnKey(k); } return false; } void GView::OnAttach() { List::I it = Children.Start(); for (GViewI *v = *it; v; v = *++it) { if (!v->GetParent()) v->SetParent(this); } } void GView::OnCreate() { if (Script && Script->OnScriptEvent(this)) { Script->OnCreate(); } } void GView::OnDestroy() { if (Script && Script->OnScriptEvent(this)) { Script->OnDestroy(); } } void GView::OnFocus(bool f) { if (Script && Script->OnScriptEvent(this)) { Script->OnFocus(f); } } void GView::OnPulse() { if (Script && Script->OnScriptEvent(this)) { Script->OnPulse(); } } void GView::OnPosChange() { if (Script && Script->OnScriptEvent(this)) { Script->OnPosChange(); } } bool GView::OnRequestClose(bool OsShuttingDown) { if (Script && Script->OnScriptEvent(this)) { Script->OnRequestClose(OsShuttingDown); } return true; } int GView::OnHitTest(int x, int y) { if (Script && Script->OnScriptEvent(this)) { Script->OnHitTest(x, y); } return -1; } void GView::OnChildrenChanged(GViewI *Wnd, bool Attaching) { if (Script && Script->OnScriptEvent(this)) { Script->OnChildrenChanged(Wnd, Attaching); } } void GView::OnPaint(GSurface *pDC) { if (Script && Script->OnScriptEvent(this)) { Script->OnPaint(pDC); } } int GView::OnNotify(GViewI *Ctrl, int Flags) { if (Script && Script->OnScriptEvent(this)) { Script->OnNotify(Ctrl, Flags); } else if (Ctrl && d->Parent) { // default behaviour is just to pass the // notification up to the parent return d->Parent->OnNotify(Ctrl, Flags); } return 0; } int GView::OnCommand(int Cmd, int Event, OsView Wnd) { if (Script && Script->OnScriptEvent(this)) { Script->OnCommand(Cmd, Event, Wnd); } return 0; } void GView::OnNcPaint(GSurface *pDC, GRect &r) { int Border = Sunken() || Raised() ? _BorderSize : 0; if ( #if 0 // WIN32NATIVE !_View && #endif Border == 2) { #if WIN32NATIVE if (d->IsThemed) DrawThemeBorder(pDC, r); else #endif LgiWideBorder(pDC, r, Sunken() ? SUNKEN : RAISED); } else if (Border == 1) { LgiThinBorder(pDC, r, Sunken() ? SUNKEN : RAISED); } } void GView::_Paint(GSurface *pDC, int Ox, int Oy) { // Create temp DC if needed... GAutoPtr Local; if (!pDC) { Local.Reset(new GScreenDC(this)); pDC = Local; } if (!pDC) { printf("%s:%i - No context to draw in.\n", _FL); return; } #if 0 // This is useful for coverage checking pDC->Colour(Rgb24(255, 0, 255), 24); pDC->Rectangle(); #endif bool HasClient = false; GRect r(0, 0, Pos.X()-1, Pos.Y()-1); #if WIN32NATIVE if (!_View) #endif { // Non-Client drawing GRect Client = r; OnNcPaint(pDC, Client); HasClient = GetParent() && (Client != r); if (HasClient) { Client.Offset(Ox, Oy); pDC->SetClient(&Client); } } r.Offset(Ox, Oy); // Paint this view's contents OnPaint(pDC); #if PAINT_VIRTUAL_CHILDREN // Paint any virtual children List::I it = Children.Start(); // just in case the child access the child list while (it.Each()) { GViewI *i = *it; GView *w = i->GetGView(); if (w && !w->Handle() && w->Visible()) { GRect p = w->GetPos(); #ifdef __GTK_H__ p.Offset(_BorderSize, _BorderSize); #endif p.Offset(Ox, Oy); pDC->SetClient(&p); w->_Paint(pDC, p.x1, p.y1); pDC->SetClient(0); } } #endif if (HasClient) pDC->SetClient(0); } GViewI *GView::GetParent() { return d->Parent; } void GView::SetParent(GViewI *p) { d->Parent = p ? p->GetGView() : NULL; d->ParentI = p; } void GView::SendNotify(int Data) { GViewI *n = d->Notify ? d->Notify : d->Parent; if (n) { if (!_View || InThread()) { n->OnNotify(this, Data); } else { // We are not in the same thread as the target window. So we post a message // across to the view. GViewI *Ptr = this; if (GetId() <= 0) { // We are going to generate a control Id to help the receiver of the // M_CHANGE message find out view later on. The reason for doing this // instead of sending a pointer to the object, is that the object // _could_ be deleted between the message being sent and being received. // Which would result in an invalid memory access on that object. GViewI *p = GetWindow(); if (!p) { // No window? Find the top most parent we can... p = this; while (p->GetParent()) p = p->GetParent(); } if (p) { // Give the control a valid ID for (int i=10; i<1000; i++) { if (!p->FindControl(i)) { printf("Giving the ctrl '%s' the id '%i' for SendNotify\n", GetClass(), i); SetId(i); break; } } } else { // Ok this is really bad... go random (better than nothing) SetId(5000 + LgiRand(2000)); } } #if 0 // def BEOS // This is for debugging only static int ChangeId = 1; int Cid = ChangeId++; // printf("**** M_CHANGE sending **** Ctrl=%i %s, Data=%i, Cid=%i\n", Ptr->GetId(), Ptr->Name(), Data, Cid); BMessage Msg(M_CHANGE); Msg.AddInt32("a", GetId()); Msg.AddInt32("b", Data); Msg.AddInt32("cid", Cid); BMessenger m(n->Handle()); m.SendMessage(&Msg) == B_OK; #else LgiAssert(GetId() > 0); // We must have a valid ctrl ID at this point, otherwise // the receiver will never be able to find our object. n->PostEvent(M_CHANGE, GetId(), Data); #endif } } } GViewI *GView::GetNotify() { return d->Notify; } void GView::SetNotify(GViewI *p) { d->Notify = p; } #define ADJ_LEFT 1 #define ADJ_RIGHT 2 #define ADJ_UP 3 #define ADJ_DOWN 4 int IsAdjacent(GRect &a, GRect &b) { if ( (a.x1 == b.x2 + 1) && !(a.y1 > b.y2 || a.y2 < b.y1)) { return ADJ_LEFT; } if ( (a.x2 == b.x1 - 1) && !(a.y1 > b.y2 || a.y2 < b.y1)) { return ADJ_RIGHT; } if ( (a.y1 == b.y2 + 1) && !(a.x1 > b.x2 || a.x2 < b.x1)) { return ADJ_UP; } if ( (a.y2 == b.y1 - 1) && !(a.x1 > b.x2 || a.x2 < b.x1)) { return ADJ_DOWN; } return 0; } GRect JoinAdjacent(GRect &a, GRect &b, int Adj) { GRect t; switch (Adj) { case ADJ_LEFT: case ADJ_RIGHT: { t.y1 = max(a.y1, b.y1); t.y2 = min(a.y2, b.y2); t.x1 = min(a.x1, b.x1); t.x2 = max(a.x2, b.x2); break; } case ADJ_UP: case ADJ_DOWN: { t.y1 = min(a.y1, b.y1); t.y2 = max(a.y2, b.y2); t.x1 = max(a.x1, b.x1); t.x2 = min(a.x2, b.x2); break; } } return t; } GRect *GView::FindLargest(GRegion &r) { int Pixels = 0; GRect *Best = 0; static GRect Final; // do initial run through the list to find largest single region for (GRect *i = r.First(); i; i = r.Next()) { int Pix = i->X() * i->Y(); if (Pix > Pixels) { Pixels = Pix; Best = i; } } if (Best) { Final = *Best; Pixels = Final.X() * Final.Y(); int LastPixels = Pixels; GRect LastRgn = Final; int ThisPixels = Pixels; GRect ThisRgn = Final; GRegion TempList; for (GRect *i = r.First(); i; i = r.Next()) { TempList.Union(i); } TempList.Subtract(Best); do { LastPixels = ThisPixels; LastRgn = ThisRgn; // search for adjoining rectangles that maybe we can add for (GRect *i = TempList.First(); i; i = TempList.Next()) { int Adj = IsAdjacent(ThisRgn, *i); if (Adj) { GRect t = JoinAdjacent(ThisRgn, *i, Adj); int Pix = t.X() * t.Y(); if (Pix > ThisPixels) { ThisPixels = Pix; ThisRgn = t; TempList.Subtract(i); } } } } while (LastPixels < ThisPixels); Final = ThisRgn; } else return 0; return &Final; } GRect *GView::FindSmallestFit(GRegion &r, int Sx, int Sy) { int X = 1000000; int Y = 1000000; GRect *Best = 0; for (GRect *i = r.First(); i; i = r.Next()) { if ((i->X() >= Sx && i->Y() >= Sy) && (i->X() < X || i->Y() < Y)) { X = i->X(); Y = i->Y(); Best = i; } } return Best; } GRect *GView::FindLargestEdge(GRegion &r, int Edge) { GRect *Best = 0; for (GRect *i = r.First(); i; i = r.Next()) { if (!Best) { Best = i; } if ( ((Edge & GV_EDGE_TOP) && (i->y1 < Best->y1)) || ((Edge & GV_EDGE_RIGHT) && (i->x2 > Best->x2)) || ((Edge & GV_EDGE_BOTTOM) && (i->y2 > Best->y2)) || ((Edge & GV_EDGE_LEFT) && (i->x1 < Best->x1)) ) { Best = i; } if ( ( ((Edge & GV_EDGE_TOP) && (i->y1 == Best->y1)) || ((Edge & GV_EDGE_BOTTOM) && (i->y2 == Best->y2)) ) && ( i->X() > Best->X() ) ) { Best = i; } if ( ( ((Edge & GV_EDGE_RIGHT) && (i->x2 == Best->x2)) || ((Edge & GV_EDGE_LEFT) && (i->x1 == Best->x1)) ) && ( i->Y() > Best->Y() ) ) { Best = i; } } return Best; } GViewI *GView::FindReal(GdcPt2 *Offset) { if (Offset) { Offset->x = 0; Offset->y = 0; } GViewI *p = d->Parent; while (p && !p->Handle()) { if (Offset) { Offset->x += Pos.x1; Offset->y += Pos.y1; } p = p->GetParent(); } if (p && p->Handle()) { return p; } return NULL; } bool GView::HandleCapture(GView *Wnd, bool c) { bool Status = false; if (_View) { if (c) { GView *Cap = dynamic_cast(_Capturing); if (Cap) { Cap->HandleCapture(NULL, false); } #if defined __GTK_H__ ThreadCheck(); gtk_grab_add(_View); #elif WIN32NATIVE d->hPrevCapture = SetCapture(_View); #elif defined MAC #endif Status = true; _Capturing = Wnd; #if DEBUG_CAPTURE LgiTrace("%s::HandleCapture(%i) %s/%p\n", GetClass(), c, Wnd->GetClass(), Wnd); #endif } else { if (_Capturing) { #if defined __GTK_H__ ThreadCheck(); gtk_grab_remove(_View); #elif defined WIN32 ReleaseCapture(); #elif defined MAC #endif #if DEBUG_CAPTURE LgiTrace("%s::HandleCapture(%i) %s/%p\n", GetClass(), c, Wnd?Wnd->GetClass():0, Wnd); #endif _Capturing = 0; } } } else { if (d->GetParent()) { Status = d->GetParent()->HandleCapture(Wnd, c); } } return Status; } bool GView::IsCapturing() { return _Capturing == this; } bool GView::Capture(bool c) { return HandleCapture(this, c); } bool GView::Enabled() { #if WIN32NATIVE if (_View) return IsWindowEnabled(_View); #else #endif return !TestFlag(GViewFlags, GWF_DISABLED); } void GView::Enabled(bool i) { if (!i) SetFlag(GViewFlags, GWF_DISABLED); else ClearFlag(GViewFlags, GWF_DISABLED); if (_View) { #if WIN32NATIVE EnableWindow(_View, i); #elif defined MAC && !defined COCOA if (i) { OSStatus e = EnableControl(_View); if (e) printf("%s:%i - Error enabling control (%i)\n", _FL, (int)e); } else { OSStatus e = DisableControl(_View); if (e) printf("%s:%i - Error disabling control (%i)\n", _FL, (int)e); } #endif } Invalidate(); } bool GView::Visible() { #if WIN32NATIVE if (_View) /* This takes into account all the parent windows as well... Which is kinda not what I want. I want this to reflect just this window. return IsWindowVisible(_View); */ return (GetWindowLong(_View, GWL_STYLE) & WS_VISIBLE) != 0; #endif return TestFlag(GViewFlags, GWF_VISIBLE); } void GView::Visible(bool v) { if (v) SetFlag(GViewFlags, GWF_VISIBLE); else ClearFlag(GViewFlags, GWF_VISIBLE); if (_View) { #if WIN32NATIVE ShowWindow(_View, (v) ? SW_SHOWNORMAL : SW_HIDE); #elif defined(BEOS) if (v) _View->Show(); else _View->Hide(); // printf("\t\t%s::Vis(%i)\n", GetClass(), v); #elif defined __GTK_H__ ThreadCheck(); if (v) Gtk::gtk_widget_show(_View); else Gtk::gtk_widget_hide(_View); #elif defined MAC && !defined COCOA OSErr e = SetControlVisibility(_View, v, true); if (e) printf("%s:%i - SetControlVisibility(%p,%i,1) failed with %i (class=%s)\n", _FL, _View, v, e, GetClass()); #endif } else { Invalidate(); // printf("\t\t%s::Vis(%i) virtual\n", GetClass(), v); } } bool GView::Focus() { bool Has = false; GWindow *w = GetWindow(); #if defined(__GTK_H__) || defined(BEOS) if (w) { bool Active = w->IsActive(); if (Active) { Has = w->GetFocus() == static_cast(this); /* printf("%s::Focus() active: %p == %p = %i\n", GetClass(), w->GetFocus(), static_cast(this), Has); */ } else { // printf("%s::Focus() not active\n", GetClass()); } } #elif defined(WIN32NATIVE) if (_View) { HWND hFocus = GetFocus(); Has = hFocus == _View; } #elif defined(MAC) if (w) { ControlRef Cur; OSErr e = GetKeyboardFocus(w->WindowHandle(), &Cur); if (e) LgiTrace("%s:%i - GetKeyboardFocus failed with %i\n", _FL, e); else Has = (Cur == _View); } #endif if (Has) SetFlag(WndFlags, GWF_FOCUS); else ClearFlag(WndFlags, GWF_FOCUS); return Has; } void GView::Focus(bool i) { if (i) SetFlag(WndFlags, GWF_FOCUS); else ClearFlag(WndFlags, GWF_FOCUS); GWindow *Wnd = GetWindow(); if (Wnd) Wnd->SetFocus(this, i ? GWindow::GainFocus : GWindow::LoseFocus); if (_View) { #if WIN32NATIVE if (i) { HWND hCur = GetFocus(); if (hCur != _View) { if (In_SetWindowPos) { assert(0); LgiTrace("%s:%i - SetFocus %p (%-30s)\n", _FL, Handle(), Name()); } SetFocus(_View); } } else { if (In_SetWindowPos) { assert(0); LgiTrace("%s:%i - SetFocus(%p)\n", _FL, GetDesktopWindow()); } SetFocus(GetDesktopWindow()); } #elif defined __GTK_H__ #elif defined MAC #if !defined COCOA GViewI *Wnd = GetWindow(); if (Wnd && i) { OSErr e = SetKeyboardFocus(Wnd->WindowHandle(), _View, 1); if (e) printf("%s:%i - error setting keyboard focus (%i) to %s\n", _FL, e, GetClass()); } else printf("%s:%i - no window?\n", _FL); #endif #endif } } GDragDropTarget *&GView::DropTargetPtr() { return d->DropTarget; } bool GView::DropTarget() { return d->DropTarget != 0; } bool GView::DropTarget(bool t) { bool Status = false; if (t) SetFlag(GViewFlags, GWF_DROP_TARGET); else ClearFlag(GViewFlags, GWF_DROP_TARGET); #if WIN32NATIVE if (_View) { if (t) { if (!d->DropTarget) { DragAcceptFiles(_View, t); } else { Status = RegisterDragDrop(_View, (IDropTarget*) d->DropTarget) == S_OK; } } else { if (_View && d->DropTarget) { Status = RevokeDragDrop(_View) == S_OK; } } } else LgiAssert(!"No window handle"); #elif defined MAC GWindow *Wnd = dynamic_cast(GetWindow()); if (Wnd) { Wnd->SetDragHandlers(t); if (!d->DropTarget) d->DropTarget = t ? Wnd : 0; } #endif return Status; } bool GView::Sunken() { #if WIN32NATIVE return TestFlag(d->WndExStyle, WS_EX_CLIENTEDGE); #else return TestFlag(GViewFlags, GWF_SUNKEN); #endif } void GView::Sunken(bool i) { #if WIN32NATIVE if (i) SetFlag(d->WndExStyle, WS_EX_CLIENTEDGE); else ClearFlag(d->WndExStyle, WS_EX_CLIENTEDGE); if (_View) SetWindowLong(_View, GWL_EXSTYLE, d->WndExStyle); #else if (i) SetFlag(GViewFlags, GWF_SUNKEN); else ClearFlag(GViewFlags, GWF_SUNKEN); #endif if (!_BorderSize && i) { _BorderSize = 2; } } bool GView::Flat() { #if WIN32NATIVE return !TestFlag(d->WndExStyle, WS_EX_CLIENTEDGE) && !TestFlag(d->WndExStyle, WS_EX_WINDOWEDGE); #else return !TestFlag(GViewFlags, GWF_SUNKEN) && !TestFlag(GViewFlags, GWF_RAISED); #endif } void GView::Flat(bool i) { #if WIN32NATIVE ClearFlag(d->WndExStyle, (WS_EX_CLIENTEDGE|WS_EX_WINDOWEDGE)); #else ClearFlag(GViewFlags, (GWF_RAISED|GWF_SUNKEN)); #endif } bool GView::Raised() { #if WIN32NATIVE return TestFlag(d->WndExStyle, WS_EX_WINDOWEDGE); #else return TestFlag(GViewFlags, GWF_RAISED); #endif } void GView::Raised(bool i) { #if WIN32NATIVE if (i) SetFlag(d->WndExStyle, WS_EX_WINDOWEDGE); else ClearFlag(d->WndExStyle, WS_EX_WINDOWEDGE); #else if (i) SetFlag(GViewFlags, GWF_RAISED); else ClearFlag(GViewFlags, GWF_RAISED); #endif if (!_BorderSize && i) { _BorderSize = 2; } } int GView::GetId() { return d->CtrlId; } void GView::SetId(int i) { d->CtrlId = i; #if WIN32NATIVE if (_View) SetWindowLong(_View, GWL_ID, d->CtrlId); #elif defined __GTK_H__ #elif defined MAC #endif } bool GView::GetTabStop() { #if WIN32NATIVE return TestFlag(d->WndStyle, WS_TABSTOP); #else return d->TabStop; #endif } void GView::SetTabStop(bool b) { #if WIN32NATIVE if (b) SetFlag(d->WndStyle, WS_TABSTOP); else ClearFlag(d->WndStyle, WS_TABSTOP); #else d->TabStop = b; #endif #ifdef __GTK_H__ if (_View) { #if GtkVer(2, 18) ThreadCheck(); gtk_widget_set_can_focus(_View, b); #else LgiTrace("Error: no api to set tab stop.\n"); #endif } #endif } int64 GView::GetCtrlValue(int Id) { GViewI *w = FindControl(Id); return (w) ? w->Value() : 0; } void GView::SetCtrlValue(int Id, int64 i) { GViewI *w = FindControl(Id); if (w) w->Value(i); } char *GView::GetCtrlName(int Id) { GViewI *w = FindControl(Id); return (w) ? w->Name() : 0; } void GView::SetCtrlName(int Id, const char *s) { GViewI *w = FindControl(Id); if (w) w->Name(s); } bool GView::GetCtrlEnabled(int Id) { GViewI *w = FindControl(Id); return (w) ? w->Enabled() : 0; } void GView::SetCtrlEnabled(int Id, bool Enabled) { GViewI *w = FindControl(Id); if (w) w->Enabled(Enabled); } bool GView::GetCtrlVisible(int Id) { GViewI *w = FindControl(Id); return (w) ? w->Visible() : 0; } void GView::SetCtrlVisible(int Id, bool v) { GViewI *w = FindControl(Id); if (w) { w->Visible(v); } } bool GView::AttachChildren() { List::I it = Children.Start(); while (it.Each()) { GViewI *c = *it; if (!c->IsAttached()) { if (!c->Attach(this)) { LgiTrace("%s:%i - failed to attach %s\n", _FL, c->GetClass()); return false; } } } return true; } GFont *GView::GetFont() { return d->Font ? d->Font : SysFont; } void GView::SetFont(GFont *Font, bool OwnIt) { bool Change = d->Font != Font; if (Change) { if (d->FontOwn) { DeleteObj(d->Font); } if (!Font) { Font = SysFont; OwnIt = false; } d->FontOwn = OwnIt; GFont *Old = d->Font; d->Font = Font; #if WIN32NATIVE if (_View) SendMessage(_View, WM_SETFONT, (WPARAM) (Font ? Font->Handle() : 0), 0); #endif for (GViewI *p = GetParent(); p; p = p->GetParent()) { GTableLayout *Tl = dynamic_cast(p); if (Tl) { Tl->InvalidateLayout(); break; } } Invalidate(); } } bool GView::IsOver(GMouse &m) { return (m.x >= 0) && (m.y >= 0) && (m.x < Pos.X()) && (m.y < Pos.Y()); } bool GView::WindowVirtualOffset(GdcPt2 *Offset) { bool Status = false; if (Offset) { Offset->x = 0; Offset->y = 0; for (GViewI *Wnd = this; Wnd; Wnd = Wnd->GetParent()) { if (!Wnd->Handle()) { GRect r = Wnd->GetPos(); GViewI *Par = Wnd->GetParent(); if (Par) { GRect c = Par->GetClient(false); Offset->x += r.x1 + c.x1; Offset->y += r.y1 + c.y1; } else { Offset->x += r.x1; Offset->y += r.y1; } Status = true; } else break; } } return Status; } int Debug_Depth = 0; GViewI *GView::WindowFromPoint(int x, int y, bool Debug) { char Tabs[64]; if (Debug) { memset(Tabs, 9, Debug_Depth); Tabs[Debug_Depth] = 0; } // We iterate over the child in reverse order because if they overlap the // end of the list is on "top". So they should get the click or whatever // before the the lower windows. List::I it = Children.End(); for (GViewI *c = *it; c; c = *--it) { GRect CPos = c->GetPos(); if (CPos.Overlap(x, y) && c->Visible()) { GRect CClient; CClient = c->GetClient(false); int Ox = CPos.x1 + CClient.x1; int Oy = CPos.y1 + CClient.y1; if (Debug) { printf("%s%s Pos=%s Client=%s m(%i,%i)->(%i,%i)\n", Tabs, c->GetClass(), CPos.GetStr(), CClient.GetStr(), x, y, x - Ox, y - Oy); } Debug_Depth++; GViewI *Child = c->WindowFromPoint(x - Ox, y - Oy, Debug); Debug_Depth--; if (Child) { return Child; } } else if (Debug) { printf("%sMISSED %s Pos=%s m(%i,%i)\n", Tabs, c->GetClass(), CPos.GetStr(), x, y); } } if (x >= 0 && y >= 0 && x < Pos.X() && y < Pos.Y()) { return this; } return NULL; } bool GView::InThread() { #if WIN32NATIVE HWND Hnd = _View; for (GViewI *p = GetParent(); p && !Hnd; p = p->GetParent()) { Hnd = p->Handle(); } DWORD CurThread = GetCurrentThreadId(); DWORD ViewThread = GetWindowThreadProcessId(Hnd, NULL); return CurThread == ViewThread; #else OsThreadId Me = LgiGetCurrentThread(); return LgiApp->GetGuiThread() == Me; #endif } bool GView::PostEvent(int Cmd, GMessage::Param a, GMessage::Param b) { if (_View) { #if WIN32NATIVE return PostMessage(_View, Cmd, a, b); #else bool Ret = LgiPostEvent(_View, Cmd, a, b); return Ret; #endif } else if (InThread()) { GMessage e(Cmd, a, b); OnEvent(&e); return true; } else { LgiTrace("%s:%i - No view to post event to.\n", _FL); } return false; } bool GView::Invalidate(GRegion *r, bool Repaint, bool NonClient) { if (r) { for (int i=0; iLength(); i++) { bool Last = i == r->Length()-1; Invalidate((*r)[i], Last ? Repaint : false, NonClient); } return true; } return false; } GButton *FindDefault(GViewI *w) { GButton *But = 0; GViewIterator *i = w->IterateViews(); if (i) { for (GViewI *c = i->First(); c; c = i->Next()) { But = dynamic_cast(c); if (But && But->Default()) { break; } But = FindDefault(c); if (But) { break; } } DeleteObj(i); } return But; } bool GView::Name(const char *n) { GBase::Name(n); if (_View) { #if WIN32NATIVE if (IsWin9x) { char *Temp = LgiToNativeCp(n); SetWindowText(_View, Temp?Temp:""); DeleteArray(Temp); } else { char16 *Temp = GBase::NameW(); SetWindowTextW(_View, Temp ? Temp : L""); } #endif } Invalidate(); return true; } char *GView::Name() { #if WIN32NATIVE if (_View) { if (IsWin9x) { int Length = GetWindowTextLength(_View); if (Length > 0) { char *Buf = new char[Length+1]; if (Buf) { Buf[0] = 0; int Chars = GetWindowText(_View, Buf, Length+1); Buf[Chars] = 0; char *Temp = (char*)LgiNewConvertCp("utf-8", Buf, LgiAnsiToLgiCp()); if (Temp) { GBase::Name(Temp); DeleteArray(Temp); } } DeleteArray(Buf); } else { GBase::Name(0); } } else { GView::NameW(); } } #endif return GBase::Name(); } bool GView::NameW(const char16 *n) { GBase::NameW(n); #if WIN32NATIVE if (_View && n) { char16 *Txt = GBase::NameW(); if (IsWin9x) { - char *Temp = (char*)LgiNewConvertCp(LgiAnsiToLgiCp(), n, "utf-16"); + char *Temp = (char*)LgiNewConvertCp(LgiAnsiToLgiCp(), n, LGI_WideCharset); if (Temp) { SetWindowText(_View, Temp); DeleteArray(Temp); } } else { SetWindowTextW(_View, Txt); } } #endif Invalidate(); return true; } char16 *GView::NameW() { #if WIN32NATIVE if (_View) { if (IsWin9x) { GView::Name(); } else { int Length = GetWindowTextLengthW(_View); if (Length > 0) { char16 *Buf = new char16[Length+1]; if (Buf) { Buf[0] = 0; int Chars = GetWindowTextW(_View, Buf, Length+1); Buf[Chars] = 0; GBase::NameW(Buf); } DeleteArray(Buf); } else { GBase::NameW(0); } } } #endif return GBase::NameW(); } GViewI *GView::FindControl(int Id) { LgiAssert(Id != -1); if (GetId() == Id) { return this; } List::I List = Children.Start(); while (List.Each()) { GViewI *c = *List; GViewI *Ctrl = c->FindControl(Id); if (Ctrl) { return Ctrl; } } return 0; } GdcPt2 GView::GetMinimumSize() { return d->MinimumSize; } void GView::SetMinimumSize(GdcPt2 Size) { d->MinimumSize = Size; bool Change = false; GRect p = Pos; if (X() < d->MinimumSize.x) { p.x2 = p.x1 + d->MinimumSize.x - 1; Change = true; } if (Y() < d->MinimumSize.y) { p.y2 = p.y1 + d->MinimumSize.y - 1; Change = true; } if (Change) { SetPos(p); } } bool GView::SetColour(GColour &c, bool Fore) { GCss *css = GetCss(true); if (!css) return false; if (Fore) { if (c.IsValid()) css->Color(GCss::ColorDef(c.c32())); else css->DeleteProp(GCss::PropColor); } else { if (c.IsValid()) css->BackgroundColor(GCss::ColorDef(c.c32())); else css->DeleteProp(GCss::PropBackgroundColor); } return true; } bool GView::SetCssStyle(const char *CssStyle) { if (!d->Css && !d->Css.Reset(new GCss)) return false; const char *Defs = CssStyle; return d->Css->Parse(Defs, GCss::ParseRelaxed); } GCss *GView::GetCss(bool Create) { if (Create && !d->Css) d->Css.Reset(new GCss); return d->Css; } void GView::MoveOnScreen() { GRect p = GetPos(); GArray Displays; GRect Screen(0, 0, -1, -1); if ( #if WIN32NATIVE !IsZoomed(Handle()) && !IsIconic(Handle()) && #endif LgiGetDisplays(Displays)) { int Best = -1; int Pixels = 0; int Close = 0x7fffffff; for (int i=0; ir); if (o.Valid()) { int Pix = o.X()*o.Y(); if (Best < 0 || Pix > Pixels) { Best = i; Pixels = Pix; } } else if (Pixels == 0) { int n = Displays[i]->r.Near(p); if (n < Close) { Best = i; Close = n; } } } if (Best >= 0) Screen = Displays[Best]->r; } if (!Screen.Valid()) Screen.Set(0, 0, GdcD->X()-1, GdcD->Y()-1); if (p.x2 >= Screen.x2) p.Offset(Screen.x2 - p.x2, 0); if (p.y2 >= Screen.y2) p.Offset(0, Screen.y2 - p.y2); if (p.x1 < Screen.x1) p.Offset(Screen.x1 - p.x1, 0); if (p.y1 < Screen.y1) p.Offset(0, Screen.y1 - p.y1); SetPos(p, true); Displays.DeleteObjects(); } void GView::MoveToCenter() { GRect Screen(0, 0, GdcD->X()-1, GdcD->Y()-1); GRect p = GetPos(); p.Offset(-p.x1, -p.y1); p.Offset((Screen.X() - p.X()) / 2, (Screen.Y() - p.Y()) / 2); SetPos(p, true); } void GView::MoveToMouse() { GMouse m; if (GetMouse(m, true)) { GRect p = GetPos(); p.Offset(-p.x1, -p.y1); p.Offset(m.x-(p.X()/2), m.y-(p.Y()/2)); SetPos(p, true); MoveOnScreen(); } } GdcPt2 &GView::GetWindowBorderSize() { static GdcPt2 s; ZeroObj(s); #if WIN32NATIVE if (_View) { RECT Wnd, Client; GetWindowRect(Handle(), &Wnd); GetClientRect(Handle(), &Client); s.x = (Wnd.right-Wnd.left) - (Client.right-Client.left); s.y = (Wnd.bottom-Wnd.top) - (Client.bottom-Client.top); } #elif defined __GTK_H__ #elif defined MAC s.x = 0; s.y = 22; #endif return s; } #ifdef _DEBUG void GView::_Dump(int Depth) { char Sp[65]; memset(Sp, ' ', Depth << 2); Sp[Depth<<2] = 0; char s[256]; sprintf_s(s, sizeof(s), "%s%p::%s %s (_View=%p)\n", Sp, this, GetClass(), GetPos().GetStr(), _View); LgiTrace(s); List::I i = Children.Start(); for (GViewI *c = *i; c; c = *++i) { GView *v = c->GetGView(); if (v) v->_Dump(Depth+1); } } #endif //////////////////////////////////////////////////////////////////////////////////////////////////// #if defined(MAC) || defined(LINUX) || defined(BEOS) static char FactoryFile[MAX_PATH]; #elif defined(_WINDOWS) static HANDLE FactoryEvent; #else #error "Not impl" #endif static GArray *AllFactories = NULL; GViewFactory::GViewFactory() { #if defined(MAC) || defined(LINUX) || defined(BEOS) // This is a terrible way of doing it... but I don't have a better solution ATM. :( LgiGetTempPath(FactoryFile, sizeof(FactoryFile)); int len = strlen(FactoryFile); sprintf_s(FactoryFile+len, sizeof(FactoryFile)-len, "/LgiFactoryFile.%i", getpid()); if (!FileExists(FactoryFile)) { GFile file; file.Open(FactoryFile, O_WRITE); AllFactories = new GArray; } #elif defined(_WINDOWS) char Name[256]; sprintf_s(Name, sizeof(Name), "LgiFactoryEvent.%i", GetCurrentProcessId()); HANDLE h = CreateEvent(NULL, false, false, Name); DWORD err = GetLastError(); if (err != ERROR_ALREADY_EXISTS) { FactoryEvent = h; AllFactories = new GArray; } else { LgiAssert(AllFactories); } #else #error "Not impl" AllFactories = 0; #endif if (AllFactories) { AllFactories->Add(this); } } GViewFactory::~GViewFactory() { if (AllFactories) { AllFactories->Delete(this); if (AllFactories->Length() == 0) { DeleteObj(AllFactories); #if defined(MAC) || defined(LINUX) || defined(BEOS) unlink(FactoryFile); #elif defined(WIN32NATIVE) CloseHandle(FactoryEvent); #endif } } } GView *GViewFactory::Create(const char *Class, GRect *Pos, const char *Text) { if (ValidStr(Class) && AllFactories) { for (int i=0; iLength(); i++) { GView *v = (*AllFactories)[i]->NewView(Class, Pos, Text); if (v) { return v; } } } return 0; } diff --git a/src/common/Text/GTextView3.cpp b/src/common/Text/GTextView3.cpp --- a/src/common/Text/GTextView3.cpp +++ b/src/common/Text/GTextView3.cpp @@ -1,4672 +1,4672 @@ #include #include #include #include "Lgi.h" #include "GTextView3.h" #include "GInput.h" #include "GScrollBar.h" #ifdef WIN32 #include #endif #include "GClipBoard.h" #include "GDisplayString.h" #define DefaultCharset "utf-8" // historically LgiAnsiToLgiCp() #define SubtractPtr(a, b) ((a) - (b)) #define GDCF_UTF8 -1 #define LUIS_DEBUG 0 #define POUR_DEBUG 0 #define PROFILE_POUR 0 /* #ifdef BEOS #define DOUBLE_BUFFER_PAINT #endif */ #define ALLOC_BLOCK 64 #define IDC_VS 1000 #ifndef IDM_OPEN #define IDM_OPEN 1 #endif #ifndef IDM_NEW #define IDM_NEW 2 #endif #ifndef IDM_COPY #define IDM_COPY 3 #endif #ifndef IDM_CUT #define IDM_CUT 4 #endif #ifndef IDM_PASTE #define IDM_PASTE 5 #endif #define IDM_COPY_URL 6 #define IDM_AUTO_INDENT 7 #define IDM_UTF8 8 #define IDM_PASTE_NO_CONVERT 9 #ifndef IDM_UNDO #define IDM_UNDO 10 #endif #ifndef IDM_REDO #define IDM_REDO 11 #endif #define IDM_FIXED 12 #define IDM_SHOW_WHITE 13 #define IDM_HARD_TABS 14 #define IDM_INDENT_SIZE 15 #define IDM_TAB_SIZE 16 #define IDM_DUMP 17 #define IDM_RTL 18 #define PAINT_BORDER Back #define PAINT_AFTER_LINE Back #define CODEPAGE_BASE 100 #define CONVERT_CODEPAGE_BASE 200 #if !defined(WIN32) && !defined(toupper) #define toupper(c) (((c)>='a'&&(c)<='z') ? (c)-'a'+'A' : (c)) #endif static char SelectWordDelim[] = " \t\n.,()[]<>=?/\\{}\"\';:+=-|!@#$%^&*"; ////////////////////////////////////////////////////////////////////// class GDocFindReplaceParams3 : public GDocFindReplaceParams { public: // Find/Replace History char16 *LastFind; char16 *LastReplace; bool MatchCase; bool MatchWord; bool SelectionOnly; GDocFindReplaceParams3() { LastFind = 0; LastReplace = 0; MatchCase = false; MatchWord = false; SelectionOnly = false; } ~GDocFindReplaceParams3() { DeleteArray(LastFind); DeleteArray(LastReplace); } }; class GTextView3Private { public: GRect Margin; int PourX; bool LayoutDirty; int DirtyStart, DirtyLen; bool SimpleDelete; GColour UrlColour; bool CenterCursor; int WordSelectMode; // Find/Replace Params bool OwnFindReplaceParams; GDocFindReplaceParams3 *FindReplaceParams; // Map buffer int MapLen; char16 *MapBuf; GTextView3Private() { SimpleDelete = false; WordSelectMode = -1; PourX = -1; DirtyStart = DirtyLen = 0; UrlColour.Rgb(0, 0, 255); uint32 c24 = 0; if (_lgi_read_colour_config("colour.LC_URL", &c24)) UrlColour.c24(c24); CenterCursor = false; LayoutDirty = true; Margin.x1 = Margin.y1 = Margin.x2 = 2; Margin.y2 = 0; MapBuf = 0; MapLen = 0; OwnFindReplaceParams = true; FindReplaceParams = new GDocFindReplaceParams3; } ~GTextView3Private() { if (OwnFindReplaceParams) { DeleteObj(FindReplaceParams); } DeleteArray(MapBuf); } void SetDirty(int Start, int Len = 0) { LayoutDirty = true; DirtyStart = Start; DirtyLen = Len; } }; ////////////////////////////////////////////////////////////////////// enum UndoType { UndoDelete, UndoInsert, UndoChange }; class GTextView3Undo : public GUndoEvent { GTextView3 *View; UndoType Type; int At; char16 *Text; public: GTextView3Undo( GTextView3 *view, char16 *t, int len, int at, UndoType type) { View = view; Type = type; At = at; Text = NewStrW(t, len); } ~GTextView3Undo() { DeleteArray(Text); } void OnChange() { int Len = StrlenW(Text); if (View->Text) { char16 *t = View->Text + At; for (int i=0; id->SetDirty(At, Len); } // GUndoEvent void ApplyChange() { View->UndoOn = false; switch (Type) { case UndoInsert: { int Len = StrlenW(Text); View->Insert(At, Text, Len); View->Cursor = At + Len; break; } case UndoDelete: { View->Delete(At, StrlenW(Text)); View->Cursor = At; break; } case UndoChange: { OnChange(); break; } } View->UndoOn = true; View->Invalidate(); } void RemoveChange() { View->UndoOn = false; switch (Type) { case UndoInsert: { View->Delete(At, StrlenW(Text)); break; } case UndoDelete: { View->Insert(At, Text, StrlenW(Text)); break; } case UndoChange: { OnChange(); break; } } View->Cursor = At; View->UndoOn = true; View->Invalidate(); } }; void GTextView3::GStyle::RefreshLayout(int Start, int Len) { View->PourText(Start, Len); View->PourStyle(Start, Len); } ////////////////////////////////////////////////////////////////////// GTextView3::GTextView3( int Id, int x, int y, int cx, int cy, GFontType *FontType) : ResObject(Res_Custom) { // init vars d = new GTextView3Private; LineY = 1; MaxX = 0; Blink = true; TextCache = 0; UndoOn = true; Font = 0; FixedWidthFont = false; FixedFont = 0; ShowWhiteSpace = false; ObscurePassword = false; TabSize = TAB_SIZE; IndentSize = TAB_SIZE; HardTabs = true; CanScrollX = false; // setup window SetId(Id); // default options Dirty = false; #if WIN32NATIVE CrLf = true; SetDlgCode(DLGC_WANTALLKEYS); #else CrLf = false; #endif Underline = 0; BackColour = LC_WORKSPACE; #ifdef _DEBUG // debug times _PourTime = 0; _StyleTime = 0; _PaintTime = 0; #endif // Data Alloc = ALLOC_BLOCK; Text = new char16[Alloc]; if (Text) *Text = 0; Cursor = 0; Size = 0; // Display SelStart = SelEnd = -1; DocOffset = 0; ScrollX = 0; if (FontType) { Font = FontType->Create(); } else { GFontType Type; if (Type.GetSystemFont("Fixed")) { Font = Type.Create(); } else printf("%s:%i - failed to create font.\n", __FILE__, __LINE__); } if (Font) { // Font->PointSize(Font->PointSize() + 2); SetTabStop(true); Underline = new GFont; if (Underline) { *Underline = *Font; Underline->Underline(true); if (d->UrlColour.IsValid()) Underline->Fore(d->UrlColour.c24()); Underline->Create(); } OnFontChange(); } CursorPos.ZOff(1, LineY-1); CursorPos.Offset(d->Margin.x1, d->Margin.y1); GRect r; r.ZOff(cx-1, cy-1); r.Offset(x, y); SetPos(r); } GTextView3::~GTextView3() { Line.DeleteObjects(); Style.DeleteObjects(); DeleteArray(TextCache); DeleteArray(Text); if (Font != SysFont) DeleteObj(Font); DeleteObj(FixedFont); DeleteObj(Underline); DeleteObj(d); } char16 *GTextView3::MapText(char16 *Str, int Len, bool RtlTrailingSpace) { if (ObscurePassword || ShowWhiteSpace || RtlTrailingSpace) { if (Len > d->MapLen) { DeleteArray(d->MapBuf); d->MapBuf = new char16[Len + RtlTrailingSpace]; d->MapLen = Len; } if (d->MapBuf) { int n = 0; if (RtlTrailingSpace) { d->MapBuf[n++] = ' '; for (int i=0; iMapBuf[n++] = Str[i]; } } else if (ObscurePassword) { for (int i=0; iMapBuf[n++] = '*'; } } else if (ShowWhiteSpace) { for (int i=0; iMapBuf[n++] = 0xb7; } else if (Str[i] == '\t') { d->MapBuf[n++] = 0x2192; } else { d->MapBuf[n++] = Str[i]; } } } return d->MapBuf; } } return Str; } void GTextView3::SetFixedWidthFont(bool i) { if (FixedWidthFont ^ i) { if (i) { GFontType Type; if (Type.GetSystemFont("Fixed")) { GFont *f = FixedFont; FixedFont = Font; Font = f; if (!Font) { Font = Type.Create(); if (Font) { Font->PointSize(FixedFont->PointSize()); } } GDocView::SetFixedWidthFont(i); } } else if (FixedFont) { GFont *f = FixedFont; FixedFont = Font; Font = f; GDocView::SetFixedWidthFont(i); } OnFontChange(); Invalidate(); } } void GTextView3::SetReadOnly(bool i) { GDocView::SetReadOnly(i); #if WIN32NATIVE SetDlgCode(i ? DLGC_WANTARROWS : DLGC_WANTALLKEYS); #endif } void GTextView3::SetTabSize(uint8 i) { TabSize = limit(i, 2, 32); OnFontChange(); OnPosChange(); Invalidate(); } void GTextView3::SetWrapType(uint8 i) { GDocView::SetWrapType(i); CanScrollX = i != TEXTED_WRAP_REFLOW; OnPosChange(); Invalidate(); } GFont *GTextView3::GetFont() { return Font; } void GTextView3::SetFont(GFont *f, bool OwnIt) { if (!f) return; if (OwnIt) { DeleteObj(Font); Font = f; } else if (!Font) { Font = new GFont(*f); } else { *Font = *f; } if (Font) { if (!Underline) Underline = new GFont; *Underline = *Font; Underline->Underline(true); if (d->UrlColour.IsValid()) Underline->Fore(d->UrlColour.c24()); } OnFontChange(); } void GTextView3::OnFontChange() { if (Font) { // get line height // int OldLineY = LineY; if (!Font->Handle()) Font->Create(); LineY = Font->GetHeight(); if (LineY < 1) LineY = 1; // get tab size char Spaces[32]; memset(Spaces, 'A', TabSize); Spaces[TabSize] = 0; GDisplayString ds(Font, Spaces); Font->TabSize(ds.X()); // repour doc d->SetDirty(0, Size); // validate blue underline font if (Underline) { *Underline = *Font; Underline->Underline(true); Underline->Create(); } #if WIN32NATIVE // Set the IME font. HIMC hIMC = ImmGetContext(Handle()); if (hIMC) { COMPOSITIONFORM Cf; Cf.dwStyle = CFS_POINT; Cf.ptCurrentPos.x = CursorPos.x1; Cf.ptCurrentPos.y = CursorPos.y1; LOGFONT FontInfo; GetObject(Font->Handle(), sizeof(FontInfo), &FontInfo); ImmSetCompositionFont(hIMC, &FontInfo); ImmReleaseContext(Handle(), hIMC); } #endif } } void GTextView3::PourText(int Start, int Length /* == 0 means it's a delete */) { #if PROFILE_POUR int StartTime = LgiCurrentTime(); #endif GRect Client = GetClient(); int Mx = Client.X() - d->Margin.x1; // int y = 0; MaxX = 0; int Cx = 0, Cy = 0; bool SimplePour = d->SimpleDelete; // One line change int CurrentLine = -1; if (d->SimpleDelete || Start) { d->SimpleDelete = false; if (!SimplePour && WrapType == TEXTED_WRAP_NONE && Length > 0 && Length < 100) { SimplePour = true; for (int i=0; i= 0); if (!Current) { SimplePour = false; } else { Start = Current->Start; Cy = Current->r.y1; if (SimplePour) { #if POUR_DEBUG printf("SimplePour Start=%i Length=%i CurrentLine=%i\n", Start, Length, CurrentLine); #endif Line.Delete(Current); DeleteObj(Current); } else { #if POUR_DEBUG printf("PartialPour Start=%i Length=%i\n", Start, Length); #endif bool Done = false; for (GTextLine *l=Line.Last(); l && !Done; l=Line.Last()) { Done = l == Current; Line.Delete(l); DeleteObj(l); } } } } else { // Whole doc is dirty #if POUR_DEBUG printf("WholePour Start=%i Length=%i\n", Start, Length); #endif Start = 0; Line.DeleteObjects(); } if (Text && Font && Mx > 0) { // break array, break out of loop when we hit these chars #define ExitLoop(c) ( (c) == 0 || \ (c) == '\n' || \ (c) == ' ' || \ (c) == '\t' \ ) // extra breaking oportunities #define ExtraBreak(c) ( ( (c) >= 0x3040 && (c) <= 0x30FF ) || \ ( (c) >= 0x3300 && (c) <= 0x9FAF ) \ ) // tracking vars int e; int LastX = 0; int LastChar = Start; int WrapCol = GetWrapAtCol(); GDisplayString Sp(Font, " ", 1); int WidthOfSpace = Sp.X(); if (WidthOfSpace < 1) { printf("%s:%i - WidthOfSpace test failed.\n", _FL); return; } // alright... lets pour! for (int i=Start; iStart = LastChar; l->r.x1 = d->Margin.x1; l->Len = e - LastChar; l->r.x2 = l->r.x1 + Cx; LastChar = ++e; l->r.y1 = Cy; l->r.y2 = l->r.y1 + LineY - 1; Line.Insert(l, SimplePour ? CurrentLine : -1); MaxX = max(MaxX, l->r.X()); LastX = Cx = 0; Cy += LineY; } else break; if (SimplePour) { for (l=Line[++CurrentLine]; l; l=Line.Next()) { l->Start += Length; } break; } } else { e = i; int Width = 0; // Find break point if (WrapCol) { // Wrap at column // Find the end of line while (true) { if (e >= Size || Text[e] == '\n' || (e-i) >= WrapCol) { break; } e++; } // Seek back some characters if we are mid word int OldE = e; if (e < Size && Text[e] != '\n') { while (e > i) { if (ExitLoop(Text[e]) || ExtraBreak(Text[e])) { break; } e--; } } if (e == i) { // No line break at all, so seek forward instead for (e=OldE; e < Size && Text[e] != '\n'; e++) { if (ExitLoop(Text[e]) || ExtraBreak(Text[e])) break; } } // Calc the width GDisplayString ds(Font, Text + i, e - i); Width = ds.X(); } else { // Wrap to edge of screen int PrevExitChar = -1; int PrevX = -1; while (true) { if (e >= Size || ExitLoop(Text[e]) || ExtraBreak(Text[e])) { GDisplayString ds(Font, Text + i, e - i); if (ds.X() + Cx > Mx) { if (PrevExitChar > 0) { e = PrevExitChar; Width = PrevX; } else { Width = ds.X(); } break; } else if (e >= Size || Text[e] == '\n') { Width = ds.X(); break; } PrevExitChar = e; PrevX = ds.X(); } e++; } } // Create layout line GTextLine *l = new GTextLine; if (l) { l->Start = i; l->Len = e - i; l->r.x1 = d->Margin.x1; l->r.x2 = l->r.x1 + Width - 1; l->r.y1 = Cy; l->r.y2 = l->r.y1 + LineY - 1; Line.Insert(l); MaxX = max(MaxX, l->r.X()); Cy += LineY; if (e < Size) e++; } } } } GTextLine *Last = Line.Length() ? Line.Last() : 0; if (!Last || Last->Start + Last->Len < Size) { GTextLine *l = new GTextLine; if (l) { l->Start = Size; l->Len = 0; l->r.x1 = l->r.x2 = d->Margin.x1; l->r.y1 = Cy; l->r.y2 = l->r.y1 + LineY - 1; Line.Insert(l); MaxX = max(MaxX, l->r.X()); Cy += LineY; } } GRect c = GetClient(); bool ScrollYNeeded = c.Y() < (Line.Length() * LineY); SetScrollBars(false, ScrollYNeeded); d->LayoutDirty = false; UpdateScrollBars(); #if PROFILE_POUR int _PourTime = LgiCurrentTime() - StartTime; printf("TextPour: %i ms, %i lines\n", _PourTime, Line.Length()); #endif #ifdef _DEBUG if (GetWindow()) { static char s[256]; sprintf_s(s, sizeof(s), "Pour: %.2f sec", (double)_PourTime / 1000); GetWindow()->PostEvent(M_TEXTVIEW_DEBUG_TEXT, (GMessage::Param)s); } #endif // printf("PourTime=%ims\n", _PourTime); #if POUR_DEBUG printf("Lines=%i\n", Line.Length()); int Index = 0; for (GTextLine *l=Line.First(); l; l=Line.Next(), Index++) { printf("\t[%i] %i,%i (%s)\n", Index, l->Start, l->Len, l->r.Describe()); } #endif } bool GTextView3::InsertStyle(GAutoPtr s) { if (!s) return false; LgiAssert(s->Start >= 0); LgiAssert(s->Len > 0); int Last = 0; int n = 0; for (GStyle *i=Style.First(); i; i=Style.Next(), n++) { if (s->Overlap(i)) { if (s->Owner > i->Owner) { // Fail the insert return false; } else { // Replace mode... Style.Delete(i); Style.Insert(s.Release(), n); return true; } } if (s->Start >= Last && s->Start < i->Start) { Style.Insert(s.Release(), n); return true; } Last = i->Start; } Style.Insert(s.Release()); return true; } GTextView3::GStyle *GTextView3::GetNextStyle(int Where) { GStyle *s = (Where >= 0) ? Style.First() : Style.Next(); while (s) { // determin whether style is relevent.. // styles in the selected region are ignored int Min = min(SelStart, SelEnd); int Max = max(SelStart, SelEnd); if (SelStart >= 0 && s->Start >= Min && s->Start+s->Len < Max) { // style is completely inside selection: ignore s = Style.Next(); } else if (Where >= 0 && s->Start+s->Len < Where) { s = Style.Next(); } else { return s; } } return 0; } class GUrl : public GTextView3::GStyle { public: bool Email; GUrl(int own) : GStyle(own) { Email = false; } bool OnMouseClick(GMouse *m) { if (View) { if ( (m && m->Left() && m->Double()) || (!m) ) { char *Utf8 = LgiNewUtf16To8(View->NameW() + Start, Len * sizeof(char16)); if (Utf8) { View->OnUrl(Utf8); DeleteArray(Utf8); } return true; } } return false; } bool OnMenu(GSubMenu *m) { if (m) { if (Email) { m->AppendItem(LgiLoadString(L_TEXTCTRL_EMAIL_TO, "New Email to..."), IDM_NEW, true); } else { m->AppendItem(LgiLoadString(L_TEXTCTRL_OPENURL, "Open URL"), IDM_OPEN, true); } m->AppendItem(LgiLoadString(L_TEXTCTRL_COPYLINK, "Copy link location"), IDM_COPY_URL, true); return true; } return false; } void OnMenuClick(int i) { switch (i) { case IDM_NEW: case IDM_OPEN: { OnMouseClick(0); break; } case IDM_COPY_URL: { char *Url = LgiNewUtf16To8(View->NameW() + Start, Len * sizeof(char16)); if (Url) { GClipBoard Clip(View); Clip.Text(Url); DeleteArray(Url); } break; } } } TCHAR *GetCursor() { #ifdef WIN32 GArray Ver; int Os = LgiGetOs(&Ver); if ((Os == LGI_OS_WIN32 || Os == LGI_OS_WIN64) && Ver[0] >= 5) { return MAKEINTRESOURCE(32649); // hand } else { return IDC_ARROW; } #endif return 0; } }; GTextView3::GStyle *GTextView3::HitStyle(int i) { for (GStyle *s=Style.First(); s; s=Style.Next()) { if (i >= s->Start && i < s->Start+s->Len) { return s; } } return 0; } void GTextView3::PourStyle(int Start, int EditSize) { #ifdef _DEBUG int64 StartTime = LgiCurrentTime(); #endif if (!Text || Size < 1) return; int Length = max(EditSize, 0); // Expand re-style are to word boundaries before and after the area of change while (Start > 0 && UrlChar(Text[Start-1])) { // Move the start back Start--; Length++; } while (Start + Length < Size && UrlChar(Text[Start+Length])) { // Move the end back Length++; } // Delete all the styles that we own inside the changed area for (GStyle *s = Style.First(); s; ) { if (s->Owner == 0) { if (EditSize > 0) { if (s->Start > Start) { s->Start += EditSize; } if (s->Overlap(Start, abs(EditSize))) { Style.Delete(); DeleteObj(s); s = Style.Current(); continue; } } else { if (s->Overlap(Start, -EditSize)) { Style.Delete(); DeleteObj(s); s = Style.Current(); continue; } if (s->Start > Start) { s->Start += EditSize; } } } s = Style.Next(); } if (UrlDetect) { #if 1 GArray Links; if (LgiDetectLinks(Links, Text + Start, Length)) { for (uint32 i=0; i a(Url = new GUrl(0)); if (Url) { Url->View = this; Url->Start = (int) (Inf.Start + Start); Url->Len = Inf.Len; Url->Email = Inf.Email; Url->Font = Underline; Url->c = d->UrlColour; InsertStyle(a); } } } #else char16 Http[] = {'h', 't', 't', 'p', ':', '/', '/', 0 }; char16 Https[] = {'h', 't', 't', 'p', 's', ':', '/', '/', 0}; for (int i=0; i s && ! ( IsAlpha(e[-1]) || IsDigit(e[-1]) || e[-1] == '/' ) ) e--; GUrl *Url = new GUrl(0); if (Url) { Url->Email = false; Url->View = this; Url->Start = SubtractPtr(s, Text); Url->Len = SubtractPtr(e, s); Url->Font = Underline; Url->c = d->UrlColour; InsertStyle(Url); } i = SubtractPtr(e, Text); } break; } case '@': { // find start char16 *s = Text + (max(i, 1) - 1); for ( ; s > Text && EmailChar(*s); s--) ; if (s < Text + i) { if (!EmailChar(*s)) s++; bool FoundDot = false; char16 *Start = Text + i + 1; char16 *e = Start; for ( ; (SubtractPtr(e, Text) < Size) && EmailChar(*e); e++) { if (*e == '.') FoundDot = true; } while (e > Start && e[-1] == '.') e--; if (FoundDot) { GUrl *Url = new GUrl(0); if (Url) { Url->Email = true; Url->View = this; Url->Start = SubtractPtr(s, Text); Url->Len = SubtractPtr(e, s); Url->Font = Underline; Url->c = d->UrlColour; InsertStyle(Url); } i = SubtractPtr(e, Text); } } break; } } } #endif } #ifdef _DEBUG _StyleTime = LgiCurrentTime() - StartTime; #endif } bool GTextView3::Insert(int At, char16 *Data, int Len) { if (!ReadOnly && Len > 0) { // limit input to valid data At = min(Size, At); // make sure we have enough memory int NewAlloc = Size + Len + 1; NewAlloc += ALLOC_BLOCK - (NewAlloc % ALLOC_BLOCK); if (NewAlloc != Alloc) { char16 *NewText = new char16[NewAlloc]; if (NewText) { if (Text) { // copy any existing data across memcpy(NewText, Text, (Size + 1) * sizeof(char16)); } DeleteArray(Text); Text = NewText; Alloc = NewAlloc; } else { // memory allocation error return false; } } if (Text) { // insert the data // move the section after the insert memmove(Text+(At+Len), Text+At, (Size-At) * sizeof(char16)); if (Data) { // copy new data in if (UndoOn) { UndoQue += new GTextView3Undo(this, Data, Len, At, UndoInsert); } memcpy(Text+At, Data, Len * sizeof(char16)); Size += Len; } else { return false; } Text[Size] = 0; Dirty = true; PourText(At, Len); PourStyle(At, Len); SendNotify(GTVN_DOC_CHANGED); return true; } } return false; } bool GTextView3::Delete(int At, int Len) { bool Status = false; if (!ReadOnly) { // limit input At = max(At, 0); At = min(At, Size); Len = min(Size-At, Len); if (Len > 0) { bool HasNewLine = false; for (int i=0; iSimpleDelete = !HasNewLine; } else { int Index; GTextLine *Cur = GetLine(At, &Index); if (Cur) { GTextLine *Prev = Line[Index-1]; PrevLineStart = Prev ? Prev->Start : -1; GTextLine *Next = Line[Index+1]; NextLineStart = Next ? Next->Start : -1; } } // do delete if (UndoOn) { UndoQue += new GTextView3Undo(this, Text+At, Len, At, UndoDelete); } memmove(Text+At, Text+(At+Len), (Size-At-Len) * sizeof(char16)); Size -= Len; Text[Size] = 0; Dirty = true; Status = true; PourText(At, -Len); PourStyle(At, -Len); if (Cursor >= At && Cursor <= At + Len) { SetCursor(At, false, HasNewLine); } // Handle repainting in flowed mode, when the line starts change if (WrapType == TEXTED_WRAP_REFLOW) { int Index; GTextLine *Cur = GetLine(At, &Index); if (Cur) { GTextLine *Repaint = 0; GTextLine *Prev = Line[Index-1]; if (Prev && PrevLineStart != Prev->Start) { // Paint previous line down Repaint = Prev; } else { GTextLine *Next = Line[Index+1]; if (Next && NextLineStart != Next->Start) { // Paint next line down Repaint = Next; } } if (Repaint) { GRect r = Repaint->r; r.x2 = GetClient().x2; r.y2 = GetClient().y2; Invalidate(&r); } } } SendNotify(GTVN_DOC_CHANGED); Status = true; } } return Status; } void GTextView3::DeleteSelection(char16 **Cut) { if (SelStart >= 0) { int Min = min(SelStart, SelEnd); int Max = max(SelStart, SelEnd); if (Cut) { *Cut = NewStrW(Text + Min, Max - Min); } Delete(Min, Max - Min); SetCursor(Min, false, true); } } GTextView3::GTextLine *GTextView3::GetLine(int Offset, int *Index) { int i = 0; for (GTextLine *l=Line.First(); l; l=Line.Next(), i++) { if (Offset >= l->Start && Offset <= l->Start+l->Len) { if (Index) { *Index = i; } return l; } } return 0; } int64 GTextView3::Value() { char *n = Name(); #ifdef _MSC_VER return (n) ? _atoi64(n) : 0; #else return (n) ? atoll(n) : 0; #endif } void GTextView3::Value(int64 i) { char Str[32]; sprintf_s(Str, sizeof(Str), LGI_PrintfInt64, i); Name(Str); } char *GTextView3::Name() { UndoQue.Empty(); DeleteArray(TextCache); TextCache = LgiNewUtf16To8(Text); return TextCache; } bool GTextView3::Name(const char *s) { UndoQue.Empty(); DeleteArray(TextCache); DeleteArray(Text); LgiAssert(LgiIsUtf8(s)); Text = LgiNewUtf8To16(s); if (!Text) { Text = new char16[1]; if (Text) *Text = 0; } Size = Text ? StrlenW(Text) : 0; Alloc = Size + 1; Cursor = min(Cursor, Size); if (Text) { // Remove '\r's char16 *o = Text; for (char16 *i=Text; *i; i++) { if (*i != '\r') { *o++ = *i; } else Size--; } *o++ = 0; } // update everything else PourText(0, Size); PourStyle(0, Size); UpdateScrollBars(); Invalidate(); return true; } char16 *GTextView3::NameW() { return Text; } bool GTextView3::NameW(const char16 *s) { DeleteArray(Text); Size = s ? StrlenW(s) : 0; Alloc = Size + 1; Text = new char16[Alloc]; Cursor = min(Cursor, Size); if (Text) { memcpy(Text, s, Size * sizeof(char16)); // remove LF's int In = 0, Out = 0; CrLf = false; for (; In= 0) && (SelStart != SelEnd); } void GTextView3::SelectAll() { SelStart = 0; SelEnd = Size; Invalidate(); } void GTextView3::UnSelectAll() { bool Update = HasSelection(); SelStart = -1; SelEnd = -1; if (Update) { Invalidate(); } } int GTextView3::GetLines() { return Line.Length(); } void GTextView3::GetTextExtent(int &x, int &y) { PourText(0, Size); x = MaxX + d->Margin.x1; y = Line.Length() * LineY; } void GTextView3::PositionAt(int &x, int &y, int Index) { int FromIndex = 0; GTextLine *From = GetLine(Index < 0 ? Cursor : Index, &FromIndex); if (From) { x = Cursor - From->Start; y = FromIndex; } } int GTextView3::GetCursor(bool Cur) { if (Cur) { return Cursor; } return 0; } int GTextView3::IndexAt(int x, int y) { GTextLine *l = Line.ItemAt(y); if (l) { return l->Start + min(x, l->Len); } return 0; } void GTextView3::SetCursor(int i, bool Select, bool ForceFullUpdate) { // int _Start = LgiCurrentTime(); Blink = true; // Bound the new cursor position to the document if (i < 0) i = 0; if (i > Size) i = Size; // Store the old selection and cursor int s = SelStart, e = SelEnd, c = Cursor; // If there is going to be a selected area if (Select && i != SelStart) { // Then set the start if (SelStart < 0) { // We are starting a new selection SelStart = Cursor; } // And end SelEnd = i; } else { // Clear the selection SelStart = SelEnd = -1; } int FromIndex = 0; GTextLine *From = GetLine(Cursor, &FromIndex); Cursor = i; // check the cursor is on the screen int ToIndex = 0; GTextLine *To = GetLine(Cursor, &ToIndex); if (VScroll && To) { GRect Client = GetClient(); int DisplayLines = Client.Y() / LineY; if (ToIndex < VScroll->Value()) { // Above the visible region... if (d->CenterCursor) { int i = ToIndex - (DisplayLines >> 1); VScroll->Value(max(0, i)); } else { VScroll->Value(ToIndex); } ForceFullUpdate = true; } if (ToIndex >= VScroll->Value() + DisplayLines) { int YOff = d->CenterCursor ? DisplayLines >> 1 : DisplayLines; int v = min(ToIndex - YOff + 1, Line.Length() - DisplayLines); if (v != VScroll->Value()) { // Below the visible region VScroll->Value(v); ForceFullUpdate = true; } } } // check whether we need to update the screen if (ForceFullUpdate || !To || !From) { // need full update Invalidate(); } else if ( SelStart != s || SelEnd != e) { // Update just the selection bounds GRect Client = GetClient(); int Start, End; if (SelStart >= 0 && s >= 0) { // Selection has changed, union the before and after regions Start = min(Cursor, c); End = max(Cursor, c); } else if (SelStart >= 0) { // Selection created... Start = min(SelStart, SelEnd); End = max(SelStart, SelEnd); } else if (s >= 0) { // Selection removed... Start = min(s, e); End = max(s, e); } GTextLine *SLine = GetLine(Start); GTextLine *ELine = GetLine(End); GRect u; if (SLine && ELine) { if (SLine->r.Valid()) { u = SLine->r; u.Offset(0, d->Margin.y1-ScrollYPixel()); } else u.Set(0, 0, Client.X()-1, 1); // Start of visible page GRect b(0, Client.Y()-1, Client.X()-1, Client.Y()-1); if (ELine->r.Valid()) { b = ELine->r; b.Offset(0, d->Margin.y1-ScrollYPixel()); } else { b.Set(0, Client.Y()-1, Client.X()-1, Client.Y()-1); } u.Union(&b); u.x1 = 0; u.x2 = X(); } else { printf("%s,%i - Couldn't get SLine and ELine (%i, %i)\n", _FL, Start, End); u = Client; } Invalidate(&u); } else if (Cursor != c) { // just the cursor has moved // update the line the cursor moved to GRect r = To->r; r.Offset(-ScrollX, d->Margin.y1-DocOffset); r.x2 = X(); Invalidate(&r); if (To != From) { // update the line the cursor came from, // if it's a different line from the "to" r = From->r; r.Offset(-ScrollX, d->Margin.y1-DocOffset); r.x2 = X(); Invalidate(&r); } } if (c != Cursor) { // Send off notify SendNotify(GTVN_CURSOR_CHANGED); } //int _Time = LgiCurrentTime() - _Start; //printf("Setcursor=%ims\n", _Time); } void GTextView3::SetBorder(int b) { } bool GTextView3::Cut() { bool Status = false; char16 *Txt16 = 0; DeleteSelection(&Txt16); if (Txt16) { #ifdef WIN32 Txt16 = ConvertToCrLf(Txt16); #endif - char *Txt8 = (char*)LgiNewConvertCp(LgiAnsiToLgiCp(), Txt16, "utf-16"); + char *Txt8 = (char*)LgiNewConvertCp(LgiAnsiToLgiCp(), Txt16, LGI_WideCharset); GClipBoard Clip(this); Clip.Text(Txt8); Status = Clip.TextW(Txt16, false); DeleteArray(Txt8); DeleteArray(Txt16); } return Status; } bool GTextView3::Copy() { bool Status = true; if (SelStart >= 0) { int Min = min(SelStart, SelEnd); int Max = max(SelStart, SelEnd); char16 *Txt16 = NewStrW(Text+Min, Max-Min); #ifdef WIN32 Txt16 = ConvertToCrLf(Txt16); #endif - char *Txt8 = (char*)LgiNewConvertCp(LgiAnsiToLgiCp(), Txt16, "utf-16"); + char *Txt8 = (char*)LgiNewConvertCp(LgiAnsiToLgiCp(), Txt16, LGI_WideCharset); GClipBoard Clip(this); Clip.Text(Txt8); Clip.TextW(Txt16, false); DeleteArray(Txt8); DeleteArray(Txt16); } else LgiTrace("%s:%i - No selection.\n", _FL); return Status; } bool GTextView3::Paste() { GClipBoard Clip(this); GAutoWString Mem; char16 *t = Clip.TextW(); if (!t) // ala Win9x { char *s = Clip.Text(); if (s) { Mem.Reset(LgiNewUtf8To16(s)); t = Mem; } } if (!t) return false; if (SelStart >= 0) { DeleteSelection(); } // remove '\r's char16 *s = t, *d = t; for (; *s; s++) { if (*s != '\r') { *d++ = *s; } } *d++ = 0; // insert text int Len = StrlenW(t); Insert(Cursor, t, Len); SetCursor(Cursor+Len, false, true); // Multiline return true; } bool GTextView3::ClearDirty(bool Ask, char *FileName) { if (Dirty) { int Answer = (Ask) ? LgiMsg(this, LgiLoadString(L_TEXTCTRL_ASK_SAVE, "Do you want to save your changes to this document?"), LgiLoadString(L_TEXTCTRL_SAVE, "Save"), MB_YESNOCANCEL) : IDYES; if (Answer == IDYES) { GFileSelect Select; Select.Parent(this); if (!FileName && Select.Save()) { FileName = Select.Name(); } Save(FileName); } else if (Answer == IDCANCEL) { return false; } } return true; } bool GTextView3::Open(const char *Name, const char *CharSet) { bool Status = false; GFile f; if (f.Open(Name, O_READ|O_SHARE)) { DeleteArray(Text); size_t Bytes = (size_t)f.GetSize(); SetCursor(0, false); char *c8 = new char[Bytes + 4]; if (c8) { if (f.Read(c8, Bytes) == Bytes) { char *DataStart = c8; c8[Bytes] = 0; c8[Bytes+1] = 0; c8[Bytes+2] = 0; c8[Bytes+3] = 0; if ((uchar)c8[0] == 0xff && (uchar)c8[1] == 0xfe) { // utf-16 if (!CharSet) { CharSet = "utf-16"; DataStart += 2; } } // Convert to unicode first.... if (Bytes == 0) { Text = new char16[1]; if (Text) Text[0] = 0; } else { Text = (char16*)LgiNewConvertCp(LGI_WideCharset, DataStart, CharSet ? CharSet : DefaultCharset); } if (Text) { // Remove LF's char16 *In = Text, *Out = Text; CrLf = false; Size = 0; while (*In) { if (*In >= ' ' || *In == '\t' || *In == '\n') { *Out++ = *In; Size++; } else if (*In == '\r') { CrLf = true; } In++; } Size = (int) (Out - Text); *Out = 0; Alloc = Size + 1; Dirty = false; if (Text && Text[0] == 0xfeff) // unicode byte order mark { memmove(Text, Text+1, Size * sizeof(*Text)); Size--; } PourText(0, Size); PourStyle(0, Size); UpdateScrollBars(true); Status = true; } } DeleteArray(c8); } else { Alloc = Size = 0; } Invalidate(); } return Status; } bool GTextView3::Save(const char *Name, const char *CharSet) { GFile f; if (f.Open(Name, O_WRITE)) { f.SetSize(0); if (Text) { bool Status = false; - char *c8 = (char*)LgiNewConvertCp(CharSet ? CharSet : DefaultCharset, Text, "utf-16", Size * sizeof(char16)); + char *c8 = (char*)LgiNewConvertCp(CharSet ? CharSet : DefaultCharset, Text, LGI_WideCharset, Size * sizeof(char16)); if (c8) { int Len = (int)strlen(c8); if (CrLf) { Status = true; int BufLen = 1 << 20; GAutoPtr Buf(new char[BufLen]); char *b = Buf; char *e = Buf + BufLen; char *c = c8; while (*c) { if (b > e - 10) { int Bytes = b - Buf; if (f.Write(Buf, Bytes) != Bytes) { Status = false; break; } b = Buf; } if (*c == '\n') { *b++ = '\r'; *b++ = '\n'; } else { *b++ = *c; } c++; } int Bytes = b - Buf; if (f.Write(Buf, Bytes) != Bytes) Status = false; } else { Status = f.Write(c8, Len) == Len; } DeleteArray(c8); } Dirty = false; return Status; } } return false; } void GTextView3::UpdateScrollBars(bool Reset) { if (VScroll) { GRect Before = GetClient(); int DisplayLines = Y() / LineY; int Lines = GetLines(); VScroll->SetLimits(0, Lines); if (VScroll) { VScroll->SetPage(DisplayLines); int Max = Lines - DisplayLines + 1; bool Inval = false; if (VScroll->Value() > Max) { VScroll->Value(Max); Inval = true; } if (Reset) { VScroll->Value(0); SelStart = SelEnd = -1; } GRect After = GetClient(); if (Before != After && GetWrapType()) { d->SetDirty(0, Size); Inval = true; } if (Inval) { Invalidate(); } } } } bool GTextView3::DoCase(bool Upper) { if (Text) { int Min = min(SelStart, SelEnd); int Max = max(SelStart, SelEnd); if (Min < Max) { UndoQue += new GTextView3Undo(this, Text + Min, Max-Min, Min, UndoChange); for (int i=Min; i= 'a' && Text[i] <= 'z') { Text[i] = Text[i] - 'a' + 'A'; } } else { if (Text[i] >= 'A' && Text[i] <= 'Z') { Text[i] = Text[i] - 'A' + 'a'; } } } Dirty = true; d->SetDirty(Min, 0); Invalidate(); SendNotify(GTVN_DOC_CHANGED); } } return true; } void GTextView3::GotoLine(int i) { GTextLine *l = Line.ItemAt(i - 1); if (l) { d->CenterCursor = true; SetCursor(l->Start, false); d->CenterCursor = false; } } bool GTextView3::DoGoto() { GInput Dlg(this, "", LgiLoadString(L_TEXTCTRL_GOTO_LINE, "Goto line:"), "Text"); if (Dlg.DoModal() == IDOK && Dlg.Str) { GotoLine(atoi(Dlg.Str)); } return true; } GDocFindReplaceParams *GTextView3::CreateFindReplaceParams() { return new GDocFindReplaceParams3; } void GTextView3::SetFindReplaceParams(GDocFindReplaceParams *Params) { if (Params) { if (d->OwnFindReplaceParams) { DeleteObj(d->FindReplaceParams); } d->OwnFindReplaceParams = false; d->FindReplaceParams = (GDocFindReplaceParams3*) Params; } } bool GTextView3::DoFindNext() { if (d->FindReplaceParams->LastFind) { return OnFind( d->FindReplaceParams->LastFind, d->FindReplaceParams->MatchWord, d->FindReplaceParams->MatchCase, d->FindReplaceParams->SelectionOnly); } return false; } bool Text_FindCallback(GFindReplaceCommon *Dlg, bool Replace, void *User) { GTextView3 *v = (GTextView3*) User; Dlg->MatchWord = v->d->FindReplaceParams->MatchWord; Dlg->MatchCase = v->d->FindReplaceParams->MatchCase; DeleteArray(v->d->FindReplaceParams->LastFind); v->d->FindReplaceParams->MatchWord = Dlg->MatchWord; v->d->FindReplaceParams->MatchCase = Dlg->MatchCase; v->d->FindReplaceParams->LastFind = LgiNewUtf8To16(Dlg->Find); if (v->HasSelection() && v->SelEnd < v->SelStart) { v->Cursor = v->SelStart; } v->OnFind( v->d->FindReplaceParams->LastFind, v->d->FindReplaceParams->MatchWord, v->d->FindReplaceParams->MatchCase, v->d->FindReplaceParams->SelectionOnly); return true; } bool GTextView3::DoFind() { char *u = 0; if (HasSelection()) { int Min = min(SelStart, SelEnd); int Max = max(SelStart, SelEnd); u = LgiNewUtf16To8(Text + Min, (Max - Min) * sizeof(char16)); } else { u = LgiNewUtf16To8(d->FindReplaceParams->LastFind); } GFindDlg Dlg(this, u, Text_FindCallback, this); Dlg.DoModal(); DeleteArray(u); Focus(true); return false; } bool GTextView3::DoReplace() { char *LastFind8 = HasSelection() ? GetSelection() : LgiNewUtf16To8(d->FindReplaceParams->LastFind); char *LastReplace8 = LgiNewUtf16To8(d->FindReplaceParams->LastReplace); GReplaceDlg Dlg(this, LastFind8, LastReplace8); Dlg.MatchWord = d->FindReplaceParams->MatchWord; Dlg.MatchCase = d->FindReplaceParams->MatchCase; Dlg.SelectionOnly = HasSelection(); int Action = Dlg.DoModal(); DeleteArray(LastFind8); DeleteArray(LastReplace8); if (Action != IDCANCEL) { DeleteArray(d->FindReplaceParams->LastFind); d->FindReplaceParams->LastFind = LgiNewUtf8To16(Dlg.Find); DeleteArray(d->FindReplaceParams->LastReplace); d->FindReplaceParams->LastReplace = LgiNewUtf8To16(Dlg.Replace); d->FindReplaceParams->MatchWord = Dlg.MatchWord; d->FindReplaceParams->MatchCase = Dlg.MatchCase; d->FindReplaceParams->SelectionOnly = Dlg.SelectionOnly; /* printf("DoReplace '%S'->'%S' %i,%i,%i\n", d->FindReplaceParams->LastFind, d->FindReplaceParams->LastReplace, d->FindReplaceParams->MatchWord, d->FindReplaceParams->MatchCase, d->FindReplaceParams->SelectionOnly); */ } switch (Action) { case IDC_FR_FIND: { OnFind( d->FindReplaceParams->LastFind, d->FindReplaceParams->MatchWord, d->FindReplaceParams->MatchCase, d->FindReplaceParams->SelectionOnly); break; } case IDOK: case IDC_FR_REPLACE: { OnReplace( d->FindReplaceParams->LastFind, d->FindReplaceParams->LastReplace, Action == IDOK, d->FindReplaceParams->MatchWord, d->FindReplaceParams->MatchCase, d->FindReplaceParams->SelectionOnly); break; } } return false; } void GTextView3::SelectWord(int From) { for (SelStart = From; SelStart > 0; SelStart--) { if (strchr(SelectWordDelim, Text[SelStart])) { SelStart++; break; } } for (SelEnd = From; SelEnd < Size; SelEnd++) { if (strchr(SelectWordDelim, Text[SelEnd])) { break; } } Invalidate(); } int GTextView3::MatchText(char16 *Find, bool MatchWord, bool MatchCase, bool SelectionOnly) { if (ValidStrW(Find)) { int FindLen = StrlenW(Find); // Setup range to search int Begin, End; if (SelectionOnly && HasSelection()) { Begin = min(SelStart, SelEnd); End = max(SelStart, SelEnd); } else { Begin = 0; End = Size; } // Look through text... int i; bool Wrap = false; if (Cursor > End - FindLen) { Wrap = true; i = Begin; } else { i = Cursor; } if (i < Begin) i = Begin; if (i > End) i = End; if (MatchCase) { for (; i<=End-FindLen; i++) { if (Text[i] == Find[0]) { char16 *Possible = Text + i;; if (StrncmpW(Possible, Find, FindLen) == 0) { if (MatchWord) { // Check boundaries if (Possible > Text) // Check off the start { if (!IsWordBoundry(Possible[-1])) { continue; } } if (i + FindLen < Size) // Check off the end { if (!IsWordBoundry(Possible[FindLen])) { continue; } } } return SubtractPtr(Possible, Text); break; } } if (!Wrap && (i + 1 > End - FindLen)) { Wrap = true; i = Begin; End = Cursor; } } } else { // printf("i=%i s=%i e=%i c=%i flen=%i sz=%i\n", i, Begin, End, Cursor, FindLen, Size); for (; i<=End-FindLen; i++) { if (toupper(Text[i]) == toupper(Find[0])) { char16 *Possible = Text + i; if (StrnicmpW(Possible, Find, FindLen) == 0) { if (MatchWord) { // Check boundaries if (Possible > Text) // Check off the start { if (!IsWordBoundry(Possible[-1])) { continue; } } if (i + FindLen < Size) // Check off the end { if (!IsWordBoundry(Possible[FindLen])) { continue; } } } return SubtractPtr(Possible, Text); break; } } if (!Wrap && (i + 1 > End - FindLen)) { Wrap = true; i = Begin; End = Cursor; } } } } return -1; } bool GTextView3::OnFind(char16 *Find, bool MatchWord, bool MatchCase, bool SelectionOnly) { int Loc = MatchText(Find, MatchWord, MatchCase, SelectionOnly); if (Loc >= 0) { SetCursor(Loc, false); SetCursor(Loc + StrlenW(Find), true); return true; } return false; } bool GTextView3::OnReplace(char16 *Find, char16 *Replace, bool All, bool MatchWord, bool MatchCase, bool SelectionOnly) { if (ValidStrW(Find)) { // int Max = -1; int FindLen = StrlenW(Find); int ReplaceLen = StrlenW(Replace); int OldCursor = Cursor; int First = -1; while (true) { int Loc = MatchText(Find, MatchWord, MatchCase, SelectionOnly); if (First < 0) { First = Loc; } else if (Loc == First) { break; } if (Loc >= 0) { int OldSelStart = SelStart; int OldSelEnd = SelEnd; Delete(Loc, FindLen); Insert(Loc, Replace, ReplaceLen); SelStart = OldSelStart; SelEnd = OldSelEnd - FindLen + ReplaceLen; Cursor = Loc + ReplaceLen; } if (!All) { return Loc >= 0; } if (Loc < 0) break; } // SetCursor(OldCursor, false); } return false; } int GTextView3::SeekLine(int i, GTextViewSeek Where) { switch (Where) { case PrevLine: { for (; i > 0 && Text[i] != '\n'; i--); if (i > 0) i--; for (; i > 0 && Text[i] != '\n'; i--); if (i > 0) i++; break; } case NextLine: { for (; i < Size && Text[i] != '\n'; i++); i++; break; } case StartLine: { for (; i > 0 && Text[i] != '\n'; i--); if (i > 0) i++; break; } case EndLine: { for (; i < Size && Text[i] != '\n'; i++); break; } default: { LgiAssert(false); break; } } return i; } bool GTextView3::OnMultiLineTab(bool In) { bool Status = false; int Min = min(SelStart, SelEnd); int Max = max(SelStart, SelEnd); Min = SeekLine(Min, StartLine); GMemQueue p; int Ls = 0, i; for (i=Min; i=0; i--) { if (In) { // <- int n = Indexes[i], Space = 0; for (; Space int Len = Indexes[i]; for (; Text[Len] != '\n' && Len Indexes[i]) { if (HardTabs) { char16 Tab[] = {'\t', 0}; Insert(Indexes[i], Tab, 1); Max++; } else { char16 *Sp = new char16[IndentSize]; if (Sp) { for (int n=0; nSetDirty(Min, Max-Min); Invalidate(); Status = true; return Status; } void GTextView3::OnSetHidden(int Hidden) { } void GTextView3::OnPosChange() { static bool Processing = false; /* RECT rc; GetClientRect(Handle(), &rc); LgiTrace("GTextView3::OnPosChange par=%s iswin=%i isvis=%i rc=%i,%i,%i,%i par=%p\n", GetParent() ? GetParent()->Name() : "(none)", IsWindow(Handle()), IsWindowVisible(Handle()), rc.left, rc.top, rc.right, rc.bottom, ::GetParent(Handle()) ); if (Handle()) { GScreenDC scr(Handle()); scr.Colour(GColour(255, 0, 0)); scr.Line(0,0,X()-1, Y()-1); } */ if (!Processing) { Processing = true; GLayout::OnPosChange(); GRect c = GetClient(); bool ScrollYNeeded = c.Y() < (Line.Length() * LineY); SetScrollBars(false, ScrollYNeeded); UpdateScrollBars(); if (GetWrapType() && d->PourX != X()) { d->PourX = X(); d->SetDirty(0, Size); } Processing = false; } } int GTextView3::WillAccept(List &Formats, GdcPt2 Pt, int KeyState) { for (char *s = Formats.First(); s; ) { if (!_stricmp(s, "text/uri-list") || !_stricmp(s, "text/html") || !_stricmp(s, "UniformResourceLocatorW")) { s = Formats.Next(); } else { // LgiTrace("Ignoring format '%s'\n", s); Formats.Delete(s); DeleteArray(s); s = Formats.Current(); } } return Formats.Length() ? DROPEFFECT_COPY : DROPEFFECT_NONE; } int GTextView3::OnDrop(char *Format, GVariant *Data, GdcPt2 Pt, int KeyState) { if (!_stricmp(Format, "text/uri-list") || !_stricmp(Format, "text/html") || !_stricmp(Format, "UniformResourceLocatorW")) { if (Data->IsBinary()) { char16 *e = (char16*) ((char*)Data->Value.Binary.Data + Data->Value.Binary.Length); char16 *s = (char16*)Data->Value.Binary.Data; int len = 0; while (s < e && s[len]) { len++; } Insert(Cursor, s, len); Invalidate(); return DROPEFFECT_COPY; } } return DROPEFFECT_NONE; } void GTextView3::OnCreate() { SetWindow(this); DropTarget(true); if (Focus()) SetPulse(1500); } void GTextView3::OnEscape(GKey &K) { } bool GTextView3::OnMouseWheel(double l) { if (VScroll) { int NewPos = (int)VScroll->Value() + (int) l; NewPos = limit(NewPos, 0, GetLines()); VScroll->Value(NewPos); Invalidate(); } return true; } void GTextView3::OnFocus(bool f) { Invalidate(); SetPulse(f ? 500 : -1); } int GTextView3::HitText(int x, int y) { if (Text) { bool Down = y >= 0; int Y = (VScroll) ? (int)VScroll->Value() : 0; GTextLine *l = Line.ItemAt(Y); y += (l) ? l->r.y1 : 0; while (l) { if (l->r.Overlap(x, y)) { // Over a line int At = x - l->r.x1; int Char = 0; GDisplayString Ds(Font, MapText(Text + l->Start, l->Len), l->Len, 0); Ds.SetTabOrigin(d->Margin.x1); Char = Ds.CharAt(At); return l->Start + Char; } else if (y >= l->r.y1 && y <= l->r.y2) { // Click horizontally before of after line if (x < l->r.x1) { return l->Start; } else if (x > l->r.x2) { return l->Start + l->Len; } } l = (Down) ? Line.Next() : Line.Prev(); } // outside text area if (Down) { l = Line.Last(); if (l) { if (y > l->r.y2) { // end of document return Size; } } } } return 0; } void GTextView3::Undo() { UndoQue.Undo(); } void GTextView3::Redo() { UndoQue.Redo(); } void GTextView3::DoContextMenu(GMouse &m) { GSubMenu RClick; GAutoString ClipText; { GClipBoard Clip(this); ClipText.Reset(NewStr(Clip.Text())); } #if LUIS_DEBUG RClick.AppendItem("Dump Layout", IDM_DUMP, true); RClick.AppendSeparator(); #endif GStyle *s = HitStyle(HitText(m.x, m.y)); if (s) { if (s->OnMenu(&RClick)) { RClick.AppendSeparator(); } } RClick.AppendItem(LgiLoadString(L_TEXTCTRL_CUT, "Cut"), IDM_CUT, HasSelection()); RClick.AppendItem(LgiLoadString(L_TEXTCTRL_COPY, "Copy"), IDM_COPY, HasSelection()); RClick.AppendItem(LgiLoadString(L_TEXTCTRL_PASTE, "Paste"), IDM_PASTE, ClipText != 0); RClick.AppendSeparator(); RClick.AppendItem(LgiLoadString(L_TEXTCTRL_UNDO, "Undo"), IDM_UNDO, UndoQue.CanUndo()); RClick.AppendItem(LgiLoadString(L_TEXTCTRL_REDO, "Redo"), IDM_REDO, UndoQue.CanRedo()); RClick.AppendSeparator(); GMenuItem *i = RClick.AppendItem(LgiLoadString(L_TEXTCTRL_FIXED, "Fixed Width Font"), IDM_FIXED, true); if (i) i->Checked(GetFixedWidthFont()); i = RClick.AppendItem(LgiLoadString(L_TEXTCTRL_AUTO_INDENT, "Auto Indent"), IDM_AUTO_INDENT, true); if (i) i->Checked(AutoIndent); i = RClick.AppendItem(LgiLoadString(L_TEXTCTRL_SHOW_WHITESPACE, "Show Whitespace"), IDM_SHOW_WHITE, true); if (i) i->Checked(ShowWhiteSpace); i = RClick.AppendItem(LgiLoadString(L_TEXTCTRL_HARD_TABS, "Hard Tabs"), IDM_HARD_TABS, true); if (i) i->Checked(HardTabs); RClick.AppendItem(LgiLoadString(L_TEXTCTRL_INDENT_SIZE, "Indent Size"), IDM_INDENT_SIZE, true); RClick.AppendItem(LgiLoadString(L_TEXTCTRL_TAB_SIZE, "Tab Size"), IDM_TAB_SIZE, true); if (Environment) Environment->AppendItems(&RClick); int Id = 0; m.ToScreen(); switch (Id = RClick.Float(this, m.x, m.y)) { #if LUIS_DEBUG case IDM_DUMP: { int n=0; for (GTextLine *l=Line.First(); l; l=Line.Next(), n++) { LgiTrace("[%i] %i,%i (%s)\n", n, l->Start, l->Len, l->r.Describe()); char *s = LgiNewUtf16To8(Text + l->Start, l->Len * sizeof(char16)); if (s) { LgiTrace("%s\n", s); DeleteArray(s); } } break; } #endif case IDM_FIXED: { SetFixedWidthFont(!GetFixedWidthFont()); SendNotify(GTVN_FIXED_WIDTH_CHANGED); break; } case IDM_CUT: { Cut(); break; } case IDM_COPY: { Copy(); break; } case IDM_PASTE: { Paste(); break; } case IDM_UNDO: { Undo(); break; } case IDM_REDO: { Redo(); break; } case IDM_AUTO_INDENT: { AutoIndent = !AutoIndent; break; } case IDM_SHOW_WHITE: { ShowWhiteSpace = !ShowWhiteSpace; Invalidate(); break; } case IDM_HARD_TABS: { HardTabs = !HardTabs; break; } case IDM_INDENT_SIZE: { char s[32]; sprintf_s(s, sizeof(s), "%i", IndentSize); GInput i(this, s, "Indent Size:", "Text"); if (i.DoModal()) { IndentSize = atoi(i.Str); } break; } case IDM_TAB_SIZE: { char s[32]; sprintf_s(s, sizeof(s), "%i", TabSize); GInput i(this, s, "Tab Size:", "Text"); if (i.DoModal()) { SetTabSize(atoi(i.Str)); } break; } default: { if (s) { s->OnMenuClick(Id); } if (Environment) { Environment->OnMenu(this, Id, 0); } break; } } } void GTextView3::OnMouseClick(GMouse &m) { bool Processed = false; m.x += ScrollX; if (m.Down()) { if (!m.IsContextMenu()) { Focus(true); int Hit = HitText(m.x, m.y); if (Hit >= 0) { SetCursor(Hit, m.Shift()); GStyle *s = HitStyle(Hit); if (s) { Processed = s->OnMouseClick(&m); } } if (!Processed && m.Double()) { d->WordSelectMode = Cursor; SelectWord(Cursor); } else { d->WordSelectMode = -1; } } else { DoContextMenu(m); return; } } if (!Processed) { Capture(m.Down()); } } int GTextView3::OnHitTest(int x, int y) { #ifdef WIN32 if (GetClient().Overlap(x, y)) { return HTCLIENT; } #endif return GView::OnHitTest(x, y); } void GTextView3::OnMouseMove(GMouse &m) { m.x += ScrollX; int Hit = HitText(m.x, m.y); if (IsCapturing()) { if (d->WordSelectMode < 0) { SetCursor(Hit, m.Left()); } else { int Min = Hit < d->WordSelectMode ? Hit : d->WordSelectMode; int Max = Hit > d->WordSelectMode ? Hit : d->WordSelectMode; for (SelStart = Min; SelStart > 0; SelStart--) { if (strchr(SelectWordDelim, Text[SelStart])) { SelStart++; break; } } for (SelEnd = Max; SelEnd < Size; SelEnd++) { if (strchr(SelectWordDelim, Text[SelEnd])) { break; } } Cursor = SelEnd; Invalidate(); } } #ifdef WIN32 GRect c = GetClient(); c.Offset(-c.x1, -c.y1); if (c.Overlap(m.x, m.y)) { GStyle *s = HitStyle(Hit); TCHAR *c = (s) ? s->GetCursor() : 0; if (!c) c = IDC_IBEAM; ::SetCursor(LoadCursor(0, MAKEINTRESOURCE(c))); } #endif } int GTextView3::GetColumn() { int x = 0; GTextLine *l = GetLine(Cursor); if (l) { for (int i=l->Start; i> 1); m.Target = this; DoContextMenu(m); } else if (k.IsChar) { switch (k.c16) { default: { // process single char input if ( !GetReadOnly() && ( (k.c16 >= ' ' || k.c16 == VK_TAB) && k.c16 != 127 ) ) { if (k.Down()) { // letter/number etc if (SelStart >= 0) { bool MultiLine = false; if (k.c16 == VK_TAB) { int Min = min(SelStart, SelEnd), Max = max(SelStart, SelEnd); for (int i=Min; iLen : 0; if (l && k.c16 == VK_TAB && (!HardTabs || IndentSize != TabSize)) { int x = GetColumn(); int Add = IndentSize - (x % IndentSize); if (HardTabs && ((x + Add) % TabSize) == 0) { int Rx = x; int Remove; for (Remove = Cursor; Text[Remove - 1] == ' ' && Rx % TabSize != 0; Remove--, Rx--); int Chars = Cursor - Remove; Delete(Remove, Chars); Insert(Remove, &k.c16, 1); Cursor = Remove + 1; Invalidate(); } else { char16 *Sp = new char16[Add]; if (Sp) { for (int n=0; nLen : 0; SetCursor(Cursor + Add, false, Len != NewLen - 1); } DeleteArray(Sp); } } } else { char16 In = k.GetChar(); if (In == '\t' && k.Shift() && Cursor > 0) { l = GetLine(Cursor); if (Cursor > l->Start) { if (Text[Cursor-1] == '\t') { Delete(Cursor - 1, 1); SetCursor(Cursor, false, false); } else if (Text[Cursor-1] == ' ') { int Start = Cursor - 1; while (Start >= l->Start && strchr(" \t", Text[Start-1])) Start--; int Depth = SpaceDepth(Text + Start, Text + Cursor); int NewDepth = Depth - (Depth % IndentSize); if (NewDepth == Depth && NewDepth > 0) NewDepth -= IndentSize; int Use = 0; while (SpaceDepth(Text + Start, Text + Start + Use + 1) < NewDepth) Use++; Delete(Start + Use, Cursor - Start - Use); SetCursor(Start + Use, false, false); } } } else if (In && Insert(Cursor, &In, 1)) { l = GetLine(Cursor); int NewLen = (l) ? l->Len : 0; SetCursor(Cursor + 1, false, Len != NewLen - 1); } } } return true; } break; } case VK_RETURN: { if (GetReadOnly()) break; if (k.Down()) { OnEnter(k); } return true; break; } case VK_BACKSPACE: { if (GetReadOnly()) break; if (k.Ctrl()) { // Ctrl+H int asd=0; } else if (k.Down()) { if (SelStart >= 0) { // delete selection DeleteSelection(); } else { char Del = Cursor > 0 ? Text[Cursor-1] : 0; if (Del == ' ' && (!HardTabs || IndentSize != TabSize)) { // Delete soft tab int x = GetColumn(); int Max = x % IndentSize; if (Max == 0) Max = IndentSize; int i; for (i=Cursor-1; i>=0; i--) { if (Max-- <= 0 || Text[i] != ' ') { i++; break; } } if (i < 0) i = 0; if (i < Cursor - 1) { int Del = Cursor - i; Delete(i, Del); // SetCursor(i, false, false); // Invalidate(); break; } } else if (Del == '\t' && HardTabs && IndentSize != TabSize) { int x = GetColumn(); Delete(--Cursor, 1); for (int c=GetColumn(); c 0) { Delete(Cursor - 1, 1); } } } return true; break; } } } else // not a char { switch (k.vkey) { case VK_TAB: return true; case VK_RETURN: { return !GetReadOnly(); } case VK_BACKSPACE: { if (!GetReadOnly()) { if (k.Alt()) { if (k.Down()) { if (k.Ctrl()) { Redo(); } else { Undo(); } } } else if (k.Ctrl()) { if (k.Down()) { int Start = Cursor; while (IsWhiteSpace(Text[Cursor-1]) && Cursor > 0) Cursor--; while (!IsWhiteSpace(Text[Cursor-1]) && Cursor > 0) Cursor--; Delete(Cursor, Start - Cursor); Invalidate(); } } return true; } break; } case VK_F3: { if (k.Down()) { DoFindNext(); } return true; break; } case VK_LEFT: { if (k.Down()) { if (SelStart >= 0 && !k.Shift()) { SetCursor(min(SelStart, SelEnd), false); } else if (Cursor > 0) { int n = Cursor; #ifdef MAC if (k.System()) { goto Jump_StartOfLine; } else #endif if (k.Ctrl()) { // word move/select bool StartWhiteSpace = IsWhiteSpace(Text[n]); bool LeftWhiteSpace = n > 0 && IsWhiteSpace(Text[n-1]); if (!StartWhiteSpace || Text[n] == '\n') { n--; } // Skip ws for (; n > 0 && strchr(" \t", Text[n]); n--) ; if (Text[n] == '\n') { n--; } else if (!StartWhiteSpace || !LeftWhiteSpace) { if (IsDelimiter(Text[n])) { for (; n > 0 && IsDelimiter(Text[n]); n--); } else { for (; n > 0; n--) { //IsWordBoundry(Text[n]) if (IsWhiteSpace(Text[n]) || IsDelimiter(Text[n])) { break; } } } } if (n > 0) n++; } else { // single char n--; } SetCursor(n, k.Shift()); } } return true; break; } case VK_RIGHT: { if (k.Down()) { if (SelStart >= 0 && !k.Shift()) { SetCursor(max(SelStart, SelEnd), false); } else if (Cursor < Size) { int n = Cursor; #ifdef MAC if (k.System()) { goto Jump_EndOfLine; } else #endif if (k.Ctrl()) { // word move/select if (IsWhiteSpace(Text[n])) { for (; nStart, Cursor-l->Start); int ScreenX = CurLine.X(); GDisplayString PrevLine(Font, Text + Prev->Start, Prev->Len); int CharX = PrevLine.CharAt(ScreenX); SetCursor(Prev->Start + min(CharX, Prev->Len), k.Shift()); } } } return true; break; } case VK_DOWN: { if (k.Down()) { #ifdef MAC if (k.Ctrl()) goto GTextView3_PageDown; #endif GTextLine *l = GetLine(Cursor); if (l) { GTextLine *Next = Line.Next(); if (Next) { GDisplayString CurLine(Font, Text + l->Start, Cursor-l->Start); int ScreenX = CurLine.X(); GDisplayString NextLine(Font, Text + Next->Start, Next->Len); int CharX = NextLine.CharAt(ScreenX); SetCursor(Next->Start + min(CharX, Next->Len), k.Shift()); } } } return true; break; } case VK_END: { if (k.Down()) { if (k.Ctrl()) { SetCursor(Size, k.Shift()); } else { #ifdef MAC Jump_EndOfLine: #endif GTextLine *l = GetLine(Cursor); if (l) { SetCursor(l->Start + l->Len, k.Shift()); } } } return true; break; } case VK_HOME: { if (k.Down()) { if (k.Ctrl()) { SetCursor(0, k.Shift()); } else { #ifdef MAC Jump_StartOfLine: #endif GTextLine *l = GetLine(Cursor); if (l) { char16 *Line = Text + l->Start; char16 *s; char16 SpTab[] = {' ', '\t', 0}; for (s = Line; (SubtractPtr(s,Line) < l->Len) && StrchrW(SpTab, *s); s++); int Whitespace = SubtractPtr(s, Line); if (l->Start + Whitespace == Cursor) { SetCursor(l->Start, k.Shift()); } else { SetCursor(l->Start + Whitespace, k.Shift()); } } } } return true; break; } case VK_PAGEUP: { #ifdef MAC GTextView3_PageUp: #endif if (k.Down()) { GTextLine *l = GetLine(Cursor); if (l) { int DisplayLines = Y() / LineY; int CurLine = Line.IndexOf(l); GTextLine *New = Line.ItemAt(max(CurLine - DisplayLines, 0)); if (New) { SetCursor(New->Start + min(Cursor - l->Start, New->Len), k.Shift()); } } } return true; break; } case VK_PAGEDOWN: { #ifdef MAC GTextView3_PageDown: #endif if (k.Down()) { GTextLine *l = GetLine(Cursor); if (l) { int DisplayLines = Y() / LineY; int CurLine = Line.IndexOf(l); GTextLine *New = Line.ItemAt(min(CurLine + DisplayLines, GetLines()-1)); if (New) { SetCursor(New->Start + min(Cursor - l->Start, New->Len), k.Shift()); } } } return true; break; } case VK_INSERT: { if (k.Down()) { if (k.Ctrl()) { Copy(); } else if (k.Shift()) { if (!GetReadOnly()) { Paste(); } } } return true; break; } case VK_DELETE: { if (!GetReadOnly()) { if (k.Down()) { if (SelStart >= 0) { if (k.Shift()) { Cut(); } else { DeleteSelection(); } } else if (Cursor < Size && Delete(Cursor, 1)) { Invalidate(); } } return true; } break; } default: { if (k.c16 == 17) break; if (k.Modifier() && !k.Alt()) { switch (k.GetChar()) { case 0xbd: // Ctrl+'-' { if (k.Down() && Font->PointSize() > 1) { Font->PointSize(Font->PointSize() - 1); OnFontChange(); Invalidate(); } break; } case 0xbb: // Ctrl+'+' { if (k.Down() && Font->PointSize() < 100) { Font->PointSize(Font->PointSize() + 1); OnFontChange(); Invalidate(); } break; } case 'a': case 'A': { if (k.Down()) { // select all SelStart = 0; SelEnd = Size; Invalidate(); } return true; break; } case 'y': case 'Y': { if (!GetReadOnly()) { if (k.Down()) { Redo(); } return true; } break; } case 'z': case 'Z': { if (!GetReadOnly()) { if (k.Down()) { if (k.Shift()) { Redo(); } else { Undo(); } } return true; } break; } case 'x': case 'X': { if (!GetReadOnly()) { if (k.Down()) { Cut(); } return true; } break; } case 'c': case 'C': { if (k.Down()) { Copy(); } return true; break; } case 'v': case 'V': { if (!k.Shift() && !GetReadOnly()) { if (k.Down()) { Paste(); } return true; } break; } case 'f': { if (k.Down()) { DoFind(); } return true; break; } case 'g': case 'G': { if (k.Down()) { DoGoto(); } return true; break; } case 'h': case 'H': { if (k.Down()) { DoReplace(); } return true; break; } case 'u': case 'U': { if (!GetReadOnly()) { if (k.Down()) { DoCase(k.Shift()); } return true; } break; } case VK_RETURN: { if (!GetReadOnly() && !k.Shift()) { if (k.Down()) { OnEnter(k); } return true; } break; } } } break; } } } return false; } void GTextView3::OnEnter(GKey &k) { // enter if (SelStart >= 0) { DeleteSelection(); } char16 InsertStr[256] = {'\n', 0}; GTextLine *CurLine = GetLine(Cursor); if (CurLine && AutoIndent) { int WsLen = 0; for (; WsLen < CurLine->Len && WsLen < (Cursor - CurLine->Start) && strchr(" \t", Text[CurLine->Start + WsLen]); WsLen++); if (WsLen > 0) { memcpy(InsertStr+1, Text+CurLine->Start, WsLen * sizeof(char16)); InsertStr[WsLen+1] = 0; } } if (Insert(Cursor, InsertStr, StrlenW(InsertStr))) { SetCursor(Cursor + StrlenW(InsertStr), false, true); } } int GTextView3::TextWidth(GFont *f, char16 *s, int Len, int x, int Origin) { int w = x; int Size = f->TabSize(); for (char16 *c = s; SubtractPtr(c, s) < Len; ) { if (*c == 9) { w = ((((w-Origin) + Size) / Size) * Size) + Origin; c++; } else { char16 *e; for (e = c; SubtractPtr(e, s) < Len && *e != 9; e++); GDisplayString ds(f, c, SubtractPtr(e, c)); w += ds.X(); c = e; } } return w - x; } int GTextView3::ScrollYLine() { return (VScroll) ? (int)VScroll->Value() : 0; } int GTextView3::ScrollYPixel() { return ScrollYLine() * LineY; } void GTextView3::OnPaint(GSurface *pDC) { // LgiTrace("GTextView3::OnPaint par=%s\n", GetParent() ? GetParent()->Name() : "(none)"); #if LGI_EXCEPTIONS try { #endif // int StartTime = LgiCurrentTime(); if (d->LayoutDirty) { PourText(d->DirtyStart, d->DirtyLen); } GRect r = GetClient(); r.x2 += ScrollX; int Ox, Oy; pDC->GetOrigin(Ox, Oy); pDC->SetOrigin(Ox+ScrollX, Oy); #if 0 // Coverage testing... pDC->Colour(Rgb24(255, 0, 255), 24); pDC->Rectangle(); #endif GSurface *pOut = pDC; bool DrawSel = false; bool HasFocus = Focus(); GColour SelectedText(HasFocus ? LC_FOCUS_SEL_FORE : LC_NON_FOCUS_SEL_FORE, 24); GColour SelectedBack(HasFocus ? LC_FOCUS_SEL_BACK : LC_NON_FOCUS_SEL_BACK, 24); GCss::ColorDef ForeDef, BkDef; if (GetCss()) { ForeDef = GetCss()->Color(); BkDef = GetCss()->BackgroundColor(); } GColour Fore(ForeDef.Type == GCss::ColorRgb ? Rgb32To24(ForeDef.Rgb32) : LC_TEXT, 24); GColour Back(!ReadOnly ? (BkDef.Type == GCss::ColorRgb ? Rgb32To24(BkDef.Rgb32) : LC_WORKSPACE) : BackColour, 24); GColour Whitespace = Fore.Mix(Back, 0.85f); // printf("GTextView colours, Focus=%i, SelTxt=%s, SelBk=%s\n", HasFocus, SelectedText.GetStr(), SelectedBack.GetStr()); if (!Enabled()) { Fore.Set(LC_LOW, 24); Back.Set(LC_MED, 24); } #ifdef DOUBLE_BUFFER_PAINT GMemDC *pMem = new GMemDC; pOut = pMem; #endif if (Text && Font #ifdef DOUBLE_BUFFER_PAINT && pMem && pMem->Create(r.X()-d->Margin.x1, LineY, GdcD->GetBits()) #endif ) { int SelMin = min(SelStart, SelEnd); int SelMax = max(SelStart, SelEnd); // font properties Font->Colour(Fore, Back); Font->WhitespaceColour(Whitespace); Font->Transparent(false); // draw margins pDC->Colour(PAINT_BORDER); // top margin pDC->Rectangle(0, 0, r.x2, d->Margin.y1-1); // left margin pDC->Rectangle(0, d->Margin.y1, d->Margin.x1-1, r.y2); // draw lines of text int k = ScrollYLine(); GTextLine *l=Line.ItemAt(k); int Dy = (l) ? -l->r.y1 : 0; int NextSelection = (SelStart != SelEnd) ? SelMin : -1; // offset where selection next changes if (l && SelStart >= 0 && SelStart < l->Start && SelEnd > l->Start) { // start of visible area is in selection // init to selection colour DrawSel = true; Font->Colour(SelectedText, SelectedBack); NextSelection = SelMax; } GStyle *NextStyle = GetNextStyle((l) ? l->Start : 0); DocOffset = (l) ? l->r.y1 : 0; // loop through all visible lines int y = d->Margin.y1; for (; l && l->r.y1+Dy < r.Y(); l=Line.Next()) { GRect Tr = l->r; #ifdef DOUBLE_BUFFER_PAINT Tr.Offset(-Tr.x1, -Tr.y1); #else Tr.Offset(0, y - Tr.y1); #endif // deal with selection change on beginning of line if (NextSelection == l->Start) { // selection change DrawSel = !DrawSel; NextSelection = (NextSelection == SelMin) ? SelMax : -1; } if (DrawSel) { Font->Colour(SelectedText, SelectedBack); } else { GColour c = l->c.IsValid() ? l->c : Fore; Font->Colour(c, Back); } // draw text int Done = 0; // how many chars on this line have we // processed so far int TextX = 0; // pixels we have moved so far // loop through all sections of similar text on a line while (Done < l->Len) { // decide how big this block is int RtlTrailingSpace = 0; int Cur = l->Start + Done; int Block = l->Len - Done; // check for style change if (NextStyle) { // start if (l->Overlap(NextStyle->Start) && NextStyle->Start > Cur && NextStyle->Start - Cur < Block) { Block = NextStyle->Start - Cur; } // end int StyleEnd = NextStyle->Start + NextStyle->Len; if (l->Overlap(StyleEnd) && StyleEnd > Cur && StyleEnd - Cur < Block) { Block = StyleEnd - Cur; } } // check for next selection change // this may truncate the style if (NextSelection > Cur && NextSelection - Cur < Block) { Block = NextSelection - Cur; } LgiAssert(Block != 0); // sanity check int TabOri = Tr.x1 - d->Margin.x1; if (NextStyle && // There is a style (Cur < SelMin || Cur >= SelMax) && // && we're not drawing a selection block Cur >= NextStyle->Start && // && we're inside the styled area Cur < NextStyle->Start+NextStyle->Len) { if (NextStyle->Font) { // draw styled text if (NextStyle->c.IsValid()) NextStyle->Font->Colour(NextStyle->c, Back); NextStyle->Font->Transparent(false); LgiAssert(l->Start + Done >= 0); GDisplayString Ds( NextStyle->Font, MapText(Text + (l->Start + Done), Block, RtlTrailingSpace != 0), Block + RtlTrailingSpace); Ds.ShowVisibleTab(ShowWhiteSpace); Ds.SetTabOrigin(TabOri); TextX = Ds.X(); Ds.Draw(pOut, Tr.x1, Tr.y1, 0); if (NextStyle->Decor == GStyle::DecorSquiggle) { pOut->Colour(NextStyle->DecorColour.c24(), 24); for (int i=0; iSet(Tr.x1+i, Tr.y2-(i%2)); } GColour c = l->c.IsValid() ? l->c : Fore; NextStyle->Font->Colour(c, Back); } } else { // draw a block of normal text LgiAssert(l->Start + Done >= 0); GDisplayString Ds( Font, MapText(Text + (l->Start + Done), Block, RtlTrailingSpace != 0), Block + RtlTrailingSpace); Ds.ShowVisibleTab(ShowWhiteSpace); Ds.SetTabOrigin(TabOri); TextX = Ds.X(); Ds.Draw(pOut, Tr.x1, Tr.y1, 0); } if (NextStyle && Cur+Block >= NextStyle->Start+NextStyle->Len) { // end of this styled block NextStyle = GetNextStyle(); } if (NextSelection == Cur+Block) { // selection change DrawSel = !DrawSel; if (DrawSel) { Font->Colour(SelectedText, SelectedBack); } else { GColour c = l->c.IsValid() ? l->c : Fore; Font->Colour(c, Back); } NextSelection = (NextSelection == SelMin) ? SelMax : -1; } Tr.x1 += TextX; Done += Block + RtlTrailingSpace; } // end block loop // eol processing int EndOfLine = l->Start+l->Len; if (EndOfLine >= SelMin && EndOfLine < SelMax) { // draw the '\n' at the end of the line as selected GColour bk = Font->Back(); pOut->Colour(Font->Back()); pOut->Rectangle(Tr.x2, Tr.y1, Tr.x2+7, Tr.y2); Tr.x2 += 7; } else Tr.x2 = Tr.x1; // draw any space after text pOut->Colour(PAINT_AFTER_LINE); pOut->Rectangle(Tr.x2, Tr.y1, r.x2, Tr.y2); // cursor? if (HasFocus) { // draw the cursor if on this line if (Cursor >= l->Start && Cursor <= l->Start+l->Len) { CursorPos.ZOff(1, LineY-1); int At = Cursor-l->Start; GDisplayString Ds(Font, MapText(Text+l->Start, At), At); Ds.ShowVisibleTab(ShowWhiteSpace); int CursorX = Ds.X(); CursorPos.Offset(d->Margin.x1 + CursorX, Tr.y1); if (CanScrollX) { // Cursor on screen check GRect Scr = GetClient(); Scr.Offset(ScrollX, 0); GRect Cur = CursorPos; if (Cur.x2 > Scr.x2 - 5) // right edge check { ScrollX = ScrollX + Cur.x2 - Scr.x2 + 40; Invalidate(); } else if (Cur.x1 < Scr.x1 && ScrollX > 0) { ScrollX = max(0, Cur.x1 - 40); Invalidate(); } } if (Blink) { GRect c = CursorPos; #ifdef DOUBLE_BUFFER_PAINT c.Offset(-d->Margin.x1, -y); #endif pOut->Colour((!ReadOnly) ? Fore : GColour(192, 192, 192)); pOut->Rectangle(&c); } #if WIN32NATIVE HIMC hIMC = ImmGetContext(Handle()); if (hIMC) { COMPOSITIONFORM Cf; Cf.dwStyle = CFS_POINT; Cf.ptCurrentPos.x = CursorPos.x1; Cf.ptCurrentPos.y = CursorPos.y1; ImmSetCompositionWindow(hIMC, &Cf); ImmReleaseContext(Handle(), hIMC); } #endif } } #ifdef DOUBLE_BUFFER_PAINT // dump to screen pDC->Blt(d->Margin.x1, y, pOut); #endif y += LineY; } // end of line loop // draw any space under the lines if (y <= r.y2) { // printf("White %i, k=%i Lines=%i\n", r.y2 - y, k, Line.Length()); pDC->Colour(Back); pDC->Rectangle(d->Margin.x1, y, r.x2, r.y2); } #ifdef DOUBLE_BUFFER_PAINT DeleteObj(pMem); #endif } else { // default drawing: nothing pDC->Colour(Back); pDC->Rectangle(&r); } // _PaintTime = LgiCurrentTime() - StartTime; #ifdef PAINT_DEBUG if (GetNotify()) { char s[256]; sprintf_s(s, sizeof(s), "Pour:%i Style:%i Paint:%i ms", _PourTime, _StyleTime, _PaintTime); GMessage m = CreateMsg(DEBUG_TIMES_MSG, 0, (int)s); GetNotify()->OnEvent(&m); } #endif // printf("PaintTime: %ims\n", _PaintTime); #if LGI_EXCEPTIONS } catch (...) { LgiMsg(this, "GTextView3::OnPaint crashed.", "Lgi"); } #endif } GMessage::Result GTextView3::OnEvent(GMessage *Msg) { switch (MsgCode(Msg)) { case M_CUT: { Cut(); break; } case M_COPY: { Copy(); break; } case M_PASTE: { Paste(); break; } #if defined WIN32 case WM_GETTEXTLENGTH: { return Size; } case WM_GETTEXT: { int Chars = (int)MsgA(Msg); char *Out = (char*)MsgB(Msg); if (Out) { char *In = (char*)LgiNewConvertCp(LgiAnsiToLgiCp(), NameW(), LGI_WideCharset, Chars); if (In) { int Len = (int)strlen(In); memcpy(Out, In, Len); DeleteArray(In); return Len; } } return 0; } /* This is broken... the IME returns garbage in the buffer. :( case WM_IME_COMPOSITION: { if (Msg->b & GCS_RESULTSTR) { HIMC hIMC = ImmGetContext(Handle()); if (hIMC) { int Size = ImmGetCompositionString(hIMC, GCS_RESULTSTR, NULL, 0); char *Buf = new char[Size]; if (Buf) { ImmGetCompositionString(hIMC, GCS_RESULTSTR, Buf, Size); - char16 *Utf = (char16*)LgiNewConvertCp("utf-16", Buf, LgiAnsiToLgiCp(), Size); + char16 *Utf = (char16*)LgiNewConvertCp(LGI_WideCharset, Buf, LgiAnsiToLgiCp(), Size); if (Utf) { Insert(Cursor, Utf, StrlenW(Utf)); DeleteArray(Utf); } DeleteArray(Buf); } ImmReleaseContext(Handle(), hIMC); } return 0; } break; } */ #endif } return GLayout::OnEvent(Msg); } int GTextView3::OnNotify(GViewI *Ctrl, int Flags) { if (Ctrl->GetId() == IDC_VSCROLL && VScroll) { Invalidate(); } return 0; } void GTextView3::OnPulse() { if (!ReadOnly) { Blink = !Blink; GRect p = CursorPos; p.Offset(-ScrollX, 0); Invalidate(&p); } } void GTextView3::OnUrl(char *Url) { if (Environment) { Environment->OnNavigate(this, Url); } } GRect >extView3::GetMargin() { return d->Margin; } void GTextView3::SetMargin(GRect &m) { d->Margin.x1 = max(m.x1, 0); d->Margin.y1 = max(m.y1, 0); d->Margin.x2 = max(m.x2, 0); d->Margin.y2 = max(m.y2, 0); Invalidate(); } bool GTextView3::OnLayout(GViewLayoutInfo &Inf) { Inf.Width.Min = 32; Inf.Width.Max = -1; Inf.Height.Min = (Font ? Font->GetHeight() : 18) + 4; Inf.Height.Max = -1; return true; } /////////////////////////////////////////////////////////////////////////////// class GTextView3_Factory : public GViewFactory { GView *NewView(const char *Class, GRect *Pos, const char *Text) { if (_stricmp(Class, "GTextView3") == 0) { return new GTextView3(-1, 0, 0, 2000, 2000); } return 0; } } TextView3_Factory; diff --git a/src/common/Text/GTextView4.cpp b/src/common/Text/GTextView4.cpp --- a/src/common/Text/GTextView4.cpp +++ b/src/common/Text/GTextView4.cpp @@ -1,4583 +1,4583 @@ -#include -#include -#include - -#include "Lgi.h" -#include "GTextView3.h" -#include "GInput.h" -#include "GScrollBar.h" -#ifdef WIN32 -#include -#endif - -#define SubtractPtr(a, b) (a - b) - -#define GDCF_UTF8 -1 -#define LUIS_DEBUG 0 -#define POUR_DEBUG 0 - -#ifdef BEOS -#define DOUBLE_BUFFER_PAINT -#endif - -#define ALLOC_BLOCK 64 -#define IDC_VS 1000 - -#ifndef IDM_OPEN -#define IDM_OPEN 1 -#endif -#ifndef IDM_NEW -#define IDM_NEW 2 -#endif -#ifndef IDM_COPY -#define IDM_COPY 3 -#endif -#ifndef IDM_CUT -#define IDM_CUT 4 -#endif -#ifndef IDM_PASTE -#define IDM_PASTE 5 -#endif -#define IDM_COPY_URL 6 -#define IDM_AUTO_INDENT 7 -#define IDM_UTF8 8 -#define IDM_PASTE_NO_CONVERT 9 -#ifndef IDM_UNDO -#define IDM_UNDO 10 -#endif -#ifndef IDM_REDO -#define IDM_REDO 11 -#endif -#define IDM_FIXED 12 -#define IDM_SHOW_WHITE 13 -#define IDM_HARD_TABS 14 -#define IDM_INDENT_SIZE 15 -#define IDM_TAB_SIZE 16 -#define IDM_DUMP 17 -#define IDM_RTL 18 - -#if 0 -#define PAINT_BORDER Rgb24(255, 222, 255) -#define PAINT_AFTER_LINE Rgb24(222, 255, 255) -#else -#define PAINT_BORDER Back -#define PAINT_AFTER_LINE Back -#endif - -#define CODEPAGE_BASE 100 -#define CONVERT_CODEPAGE_BASE 200 - -#if !defined(WIN32) && !defined(toupper) -#define toupper(c) (((c)>='a'AND(c)<='z') ? (c)-'a'+'A' : (c)) -#endif - -static char SelectWordDelim[] = " \t\n.,()[]<>=?/\\{}\"\';:+=-|!@#$%^&*"; - -////////////////////////////////////////////////////////////////////// -class GDocFindReplaceParams3 : public GDocFindReplaceParams -{ -public: - // Find/Replace History - char16 *LastFind; - char16 *LastReplace; - bool MatchCase; - bool MatchWord; - bool SelectionOnly; - - GDocFindReplaceParams3() - { - LastFind = 0; - LastReplace = 0; - MatchCase = false; - MatchWord = false; - SelectionOnly = false; - } - - ~GDocFindReplaceParams3() - { - DeleteArray(LastFind); - DeleteArray(LastReplace); - } -}; - -class GTextView3Private -{ -public: - GRect Margin; - int PourX; - bool LayoutDirty; - int DirtyStart, DirtyLen; - bool SimpleDelete; - COLOUR UrlColour; - bool CenterCursor; - int WordSelectMode; - - // Find/Replace Params - bool OwnFindReplaceParams; - GDocFindReplaceParams3 *FindReplaceParams; - - // Map buffer - int MapLen; - char16 *MapBuf; - - GTextView3Private() - { - SimpleDelete = false; - WordSelectMode = -1; - PourX = -1; - DirtyStart = DirtyLen = 0; - UrlColour = Rgb24(0, 0, 255); - _lgi_read_colour_config("colour.LC_URL", (uint32*)&UrlColour); - CenterCursor = false; - - LayoutDirty = true; - Margin.x1 = Margin.y1 = Margin.x2 = 2; - Margin.y2 = 0; - - MapBuf = 0; - MapLen = 0; - - OwnFindReplaceParams = true; - FindReplaceParams = new GDocFindReplaceParams3; - } - - ~GTextView3Private() - { - if (OwnFindReplaceParams) - { - DeleteObj(FindReplaceParams); - } - - DeleteArray(MapBuf); - } - - void SetDirty(int Start, int Len = 0) - { - LayoutDirty = true; - DirtyStart = Start; - DirtyLen = Len; - } -}; - -////////////////////////////////////////////////////////////////////// -class GTextView3Undo : public GUndoEvent +#include +#include +#include + +#include "Lgi.h" +#include "GTextView3.h" +#include "GInput.h" +#include "GScrollBar.h" +#ifdef WIN32 +#include +#endif + +#define SubtractPtr(a, b) (a - b) + +#define GDCF_UTF8 -1 +#define LUIS_DEBUG 0 +#define POUR_DEBUG 0 + +#ifdef BEOS +#define DOUBLE_BUFFER_PAINT +#endif + +#define ALLOC_BLOCK 64 +#define IDC_VS 1000 + +#ifndef IDM_OPEN +#define IDM_OPEN 1 +#endif +#ifndef IDM_NEW +#define IDM_NEW 2 +#endif +#ifndef IDM_COPY +#define IDM_COPY 3 +#endif +#ifndef IDM_CUT +#define IDM_CUT 4 +#endif +#ifndef IDM_PASTE +#define IDM_PASTE 5 +#endif +#define IDM_COPY_URL 6 +#define IDM_AUTO_INDENT 7 +#define IDM_UTF8 8 +#define IDM_PASTE_NO_CONVERT 9 +#ifndef IDM_UNDO +#define IDM_UNDO 10 +#endif +#ifndef IDM_REDO +#define IDM_REDO 11 +#endif +#define IDM_FIXED 12 +#define IDM_SHOW_WHITE 13 +#define IDM_HARD_TABS 14 +#define IDM_INDENT_SIZE 15 +#define IDM_TAB_SIZE 16 +#define IDM_DUMP 17 +#define IDM_RTL 18 + +#if 0 +#define PAINT_BORDER Rgb24(255, 222, 255) +#define PAINT_AFTER_LINE Rgb24(222, 255, 255) +#else +#define PAINT_BORDER Back +#define PAINT_AFTER_LINE Back +#endif + +#define CODEPAGE_BASE 100 +#define CONVERT_CODEPAGE_BASE 200 + +#if !defined(WIN32) && !defined(toupper) +#define toupper(c) (((c)>='a'AND(c)<='z') ? (c)-'a'+'A' : (c)) +#endif + +static char SelectWordDelim[] = " \t\n.,()[]<>=?/\\{}\"\';:+=-|!@#$%^&*"; + +////////////////////////////////////////////////////////////////////// +class GDocFindReplaceParams3 : public GDocFindReplaceParams +{ +public: + // Find/Replace History + char16 *LastFind; + char16 *LastReplace; + bool MatchCase; + bool MatchWord; + bool SelectionOnly; + + GDocFindReplaceParams3() + { + LastFind = 0; + LastReplace = 0; + MatchCase = false; + MatchWord = false; + SelectionOnly = false; + } + + ~GDocFindReplaceParams3() + { + DeleteArray(LastFind); + DeleteArray(LastReplace); + } +}; + +class GTextView3Private +{ +public: + GRect Margin; + int PourX; + bool LayoutDirty; + int DirtyStart, DirtyLen; + bool SimpleDelete; + COLOUR UrlColour; + bool CenterCursor; + int WordSelectMode; + + // Find/Replace Params + bool OwnFindReplaceParams; + GDocFindReplaceParams3 *FindReplaceParams; + + // Map buffer + int MapLen; + char16 *MapBuf; + + GTextView3Private() + { + SimpleDelete = false; + WordSelectMode = -1; + PourX = -1; + DirtyStart = DirtyLen = 0; + UrlColour = Rgb24(0, 0, 255); + _lgi_read_colour_config("colour.LC_URL", (uint32*)&UrlColour); + CenterCursor = false; + + LayoutDirty = true; + Margin.x1 = Margin.y1 = Margin.x2 = 2; + Margin.y2 = 0; + + MapBuf = 0; + MapLen = 0; + + OwnFindReplaceParams = true; + FindReplaceParams = new GDocFindReplaceParams3; + } + + ~GTextView3Private() + { + if (OwnFindReplaceParams) + { + DeleteObj(FindReplaceParams); + } + + DeleteArray(MapBuf); + } + + void SetDirty(int Start, int Len = 0) + { + LayoutDirty = true; + DirtyStart = Start; + DirtyLen = Len; + } +}; + +////////////////////////////////////////////////////////////////////// +class GTextView3Undo : public GUndoEvent { public: struct Block { GAutoWString Text; int At; Block &operator =(Block &b) { Text.Reset(NewStrW(b.Text)); At = b.At; return *this; } }; enum UndoType { UndoDelete, UndoInsert, UndoChange }; protected: - GTextView3 *View; + GTextView3 *View; UndoType Type; - GArray Edits; - -public: + GArray Edits; + +public: GTextView3Undo( GTextView3 *view, char16 *t, int len, int at, UndoType type) { View = view; Type = type; Block &b = Edits.New(); b.Text.Reset(NewStrW(t, len)); b.At = at; } - GTextView3Undo( GTextView3 *view, - GArray &edits, - UndoType type) - { - View = view; - Type = type; + GTextView3Undo( GTextView3 *view, + GArray &edits, + UndoType type) + { + View = view; + Type = type; for (int i=0; iText) + + if (View->Text) { - char16 *a = blk.Text; - char16 *b = View->Text + blk.At; - for (Len=0; a[Len]; Len++) - { - char16 n = a[Len]; - a[Len] = b[Len]; - b[Len] = n; - } - } - - View->d->SetDirty(blk.At, Len); - } - - // GUndoEvent - void ApplyChange() - { - View->UndoOn = false; - + char16 *a = blk.Text; + char16 *b = View->Text + blk.At; + for (Len=0; a[Len]; Len++) + { + char16 n = a[Len]; + a[Len] = b[Len]; + b[Len] = n; + } + } + + View->d->SetDirty(blk.At, Len); + } + + // GUndoEvent + void ApplyChange() + { + View->UndoOn = false; + for (int i=0; iInsert(e.At, e.Text, Len); - View->Cursor = e.At + Len; - break; - } - case UndoDelete: - { - View->Delete(e.At, StrlenW(e.Text)); - View->Cursor = e.At; - break; - } - case UndoChange: - { - OnChange(e); - break; - } + switch (Type) + { + case UndoInsert: + { + int Len = StrlenW(e.Text); + View->Insert(e.At, e.Text, Len); + View->Cursor = e.At + Len; + break; + } + case UndoDelete: + { + View->Delete(e.At, StrlenW(e.Text)); + View->Cursor = e.At; + break; + } + case UndoChange: + { + OnChange(e); + break; + } } - } - - View->UndoOn = true; - View->Invalidate(); - } - - void RemoveChange() - { - View->UndoOn = false; - + } + + View->UndoOn = true; + View->Invalidate(); + } + + void RemoveChange() + { + View->UndoOn = false; + for (int i=0; iDelete(e.At, StrlenW(e.Text)); - break; - } - case UndoDelete: - { - View->Insert(e.At, e.Text, StrlenW(e.Text)); - break; - } - case UndoChange: - { - OnChange(e); - break; - } + switch (Type) + { + case UndoInsert: + { + View->Delete(e.At, StrlenW(e.Text)); + break; + } + case UndoDelete: + { + View->Insert(e.At, e.Text, StrlenW(e.Text)); + break; + } + case UndoChange: + { + OnChange(e); + break; + } } View->Cursor = e.At; - } - - View->UndoOn = true; - View->Invalidate(); - } -}; - -void GTextStyle::RefreshLayout(int Start, int Len) -{ - View->PourText(Start, Len); - View->PourStyle(Start, Len); -} - -////////////////////////////////////////////////////////////////////// -GTextView3::GTextView3( int Id, - int x, int y, int cx, int cy, - GFontType *FontType) - : ResObject(Res_Custom) -{ - // init vars - d = new GTextView3Private; - LineY = 1; - MaxX = 0; - Blink = true; - TextCache = 0; - UndoOn = true; - Font = 0; - FixedWidthFont = false; - FixedFont = 0; - ShowWhiteSpace = false; - ObscurePassword = false; - TabSize = TAB_SIZE; - IndentSize = TAB_SIZE; - HardTabs = true; - CanScrollX = false; - - // setup window - SetId(Id); - - // default options - Dirty = false; - #if WIN32NATIVE - CrLf = true; - SetDlgCode(DLGC_WANTALLKEYS); - #else - CrLf = false; - #endif - Underline = 0; - BackColour = LC_WORKSPACE; - - #ifdef _DEBUG - // debug times - _PourTime = 0; - _StyleTime = 0; - _PaintTime = 0; - #endif - - // Data - Alloc = ALLOC_BLOCK; - Text = new char16[Alloc]; - if (Text) *Text = 0; - Cursor = 0; - Size = 0; - - // Display - SelStart = SelEnd = -1; - DocOffset = 0; - ScrollX = 0; - - if (FontType) - { - Font = FontType->Create(); - } - else - { - GFontType Type; - if (Type.GetSystemFont("Fixed")) - { - Font = Type.Create(); - } - else printf("%s:%i - failed to create font.\n", __FILE__, __LINE__); - } - if (Font) - { - // Font->PointSize(Font->PointSize() + 2); - - #if defined BEOS - Handle()->SetViewColor(B_TRANSPARENT_COLOR); - #endif - SetTabStop(true); - - Underline = new GFont; - if (Underline) - { - *Underline = *Font; - Underline->Underline(true); - Underline->Fore(d->UrlColour); - Underline->Create(); - } - - OnFontChange(); - } - - CursorPos.ZOff(1, LineY-1); - CursorPos.Offset(d->Margin.x1, d->Margin.y1); - - GRect r; - r.ZOff(cx-1, cy-1); - r.Offset(x, y); - SetPos(r); -} - -GTextView3::~GTextView3() -{ - Line.DeleteObjects(); - Style.DeleteObjects(); - - DeleteArray(TextCache); - DeleteArray(Text); - - if (Font != SysFont) DeleteObj(Font); - DeleteObj(FixedFont); - DeleteObj(Underline); - DeleteObj(d); -} - -char16 *GTextView3::MapText(char16 *Str, int Len, bool RtlTrailingSpace) -{ - if (ObscurePassword OR ShowWhiteSpace OR RtlTrailingSpace) - { - if (Len > d->MapLen) - { - DeleteArray(d->MapBuf); - d->MapBuf = new char16[Len + RtlTrailingSpace]; - d->MapLen = Len; - } - - if (d->MapBuf) - { - int n = 0; - - if (RtlTrailingSpace) - { - d->MapBuf[n++] = ' '; - for (int i=0; iMapBuf[n++] = Str[i]; - } - } - else if (ObscurePassword) - { - for (int i=0; iMapBuf[n++] = '*'; - } - } - else if (ShowWhiteSpace) - { - for (int i=0; iMapBuf[n++] = 0xb7; - } - else if (Str[i] == '\t') - { - d->MapBuf[n++] = 0x2192; - } - else - { - d->MapBuf[n++] = Str[i]; - } - } - } - - return d->MapBuf; - } - } - - return Str; -} - -void GTextView3::SetFixedWidthFont(bool i) -{ - if (FixedWidthFont ^ i) - { - if (i) - { - GFontType Type; - if (Type.GetSystemFont("Fixed")) - { - GFont *f = FixedFont; - FixedFont = Font; - Font = f; - - if (!Font) - { - Font = Type.Create(); - if (Font) - { - Font->PointSize(FixedFont->PointSize()); - } - } - - GDocView::SetFixedWidthFont(i); - } - } - else if (FixedFont) - { - GFont *f = FixedFont; - FixedFont = Font; - Font = f; - GDocView::SetFixedWidthFont(i); - } - - OnFontChange(); - Invalidate(); - } -} - -void GTextView3::SetReadOnly(bool i) -{ - GDocView::SetReadOnly(i); - - #if WIN32NATIVE - SetDlgCode(i ? DLGC_WANTARROWS : DLGC_WANTALLKEYS); - #endif -} - -void GTextView3::SetTabSize(uint8 i) -{ - TabSize = limit(i, 2, 32); - OnFontChange(); - OnPosChange(); - Invalidate(); -} - -void GTextView3::SetWrapType(uint8 i) -{ - GDocView::SetWrapType(i); - CanScrollX = i != TEXTED_WRAP_REFLOW; - - OnPosChange(); - Invalidate(); -} - -GFont *GTextView3::GetFont() -{ - return Font; -} - -void GTextView3::SetFont(GFont *f, bool OwnIt) -{ - if (f) - { - if (OwnIt) - { - DeleteObj(Font); - Font = f; - } - else if (!Font) - { - Font = new GFont(*f); - } - else - { - *Font = *f; - } - - if (Font) - { - if (!Underline) - Underline = new GFont; - *Underline = *Font; - Underline->Underline(true); - Underline->Fore(d->UrlColour); - } - - OnFontChange(); - } -} - -void GTextView3::OnFontChange() -{ - if (Font) - { - // get line height - int OldLineY = LineY; - LineY = Font->GetHeight(); - if (LineY < 1) LineY = 1; - - // get tab size - char Spaces[32]; - memset(Spaces, 'A', TabSize); - Spaces[TabSize] = 0; - GDisplayString ds(Font, Spaces); - Font->TabSize(ds.X()); - - // repour doc - d->SetDirty(0, Size); - - // validate blue underline font - if (Underline) - { - *Underline = *Font; - Underline->Underline(true); - Underline->Create(); - } - - #if WIN32NATIVE // Set the IME font. - HIMC hIMC = ImmGetContext(Handle()); - if (hIMC) - { - COMPOSITIONFORM Cf; - Cf.dwStyle = CFS_POINT; - Cf.ptCurrentPos.x = CursorPos.x1; - Cf.ptCurrentPos.y = CursorPos.y1; - - LOGFONT FontInfo; - GetObject(Font->Handle(), sizeof(FontInfo), &FontInfo); - ImmSetCompositionFont(hIMC, &FontInfo); - - ImmReleaseContext(Handle(), hIMC); - } - #endif - - } -} - -void GTextView3::PourText(int Start, int Length /* < 0 means it's a delete */) -{ - GRect Client = GetClient(); - int Mx = Client.X() - d->Margin.x1; - int y = 0; - - MaxX = 0; - - int Cx = 0, Cy = 0; - bool SimplePour = d->SimpleDelete; // One line change - int CurrentLine = -1; - if (d->SimpleDelete OR Start) - { - d->SimpleDelete = false; - if (!SimplePour AND - WrapType == TEXTED_WRAP_NONE AND - Length > 0 AND - Length < 100) - { - SimplePour = true; - for (int i=0; i= 0); - - if (!Current) - { - SimplePour = false; - } - else - { - Start = Current->Start; - Cy = Current->r.y1; - - if (SimplePour) - { - #if POUR_DEBUG - printf("SimplePour Start=%i Length=%i CurrentLine=%i\n", Start, Length, CurrentLine); - #endif - Line.Delete(Current); - DeleteObj(Current); - } - else - { - #if POUR_DEBUG - printf("PartialPour Start=%i Length=%i\n", Start, Length); - #endif - bool Done = false; - for (GTextLine *l=Line.Last(); l AND !Done; l=Line.Last()) - { - Done = l == Current; - Line.Delete(l); - DeleteObj(l); - } - } - } - } - else - { - // Whole doc is dirty - #if POUR_DEBUG - printf("WholePour Start=%i Length=%i\n", Start, Length); - #endif - Start = 0; - Line.DeleteObjects(); - } - - if (Text AND Font AND Mx > 0) - { - // break array, break out of loop when we hit these chars - #define ExitLoop(c) ( (c) == 0 OR \ - (c) == '\n' OR \ - (c) == ' ' OR \ - (c) == '\t' \ - ) - - // extra breaking oportunities - #define ExtraBreak(c) ( ( (c) >= 0x3040 AND (c) <= 0x30FF ) OR \ - ( (c) >= 0x3300 AND (c) <= 0x9FAF ) \ - ) - - // tracking vars - int e; - int LastX = 0; - int LastChar = Start; - int WrapCol = GetWrapAtCol(); - - GDisplayString Sp(Font, " ", 1); - int WidthOfSpace = Sp.X(); - if (WidthOfSpace < 1) - { - printf("%s:%i - WidthOfSpace test failed.\n", __FILE__, __LINE__); - return; - } - - // alright... lets pour! - for (int i=Start; iStart = LastChar; - l->r.x1 = d->Margin.x1; - l->Len = e - LastChar; - l->r.x2 = l->r.x1 + Cx; - LastChar = ++e; - - l->r.y1 = Cy; - l->r.y2 = l->r.y1 + LineY - 1; - - Line.Insert(l, SimplePour ? CurrentLine : -1); - - MaxX = max(MaxX, l->r.X()); - LastX = Cx = 0; - Cy += LineY; - } - else break; - - if (SimplePour) - { - for (l=Line[++CurrentLine]; l; l=Line.Next()) - { - l->Start += Length; - } - break; - } - } - else - { - e = i; - int Width = 0; - - // Find break point - if (WrapCol) - { - // Wrap at column - - // Find the end of line - while (true) - { - if (e >= Size OR - Text[e] == '\n' OR - (e-i) >= WrapCol) - { - break; - } - - e++; - } - - // Seek back some characters if we are mid word - int OldE = e; - if (e < Size AND - Text[e] != '\n') - { - while (e > i) - { - if (ExitLoop(Text[e]) OR - ExtraBreak(Text[e])) - { - break; - } - - e--; - } - } - - if (e == i) - { - // No line break at all, so seek forward instead - for (e=OldE; e < Size AND Text[e] != '\n'; e++) - { - if (ExitLoop(Text[e]) OR - ExtraBreak(Text[e])) - break; - } - } - - // Calc the width - GDisplayString ds(Font, Text + i, e - i); - Width = ds.X(); - } - else - { - // Wrap to edge of screen - int PrevExitChar = -1; - int PrevX = -1; - - while (true) - { - if (e >= Size OR - ExitLoop(Text[e]) OR - ExtraBreak(Text[e])) - { - GDisplayString ds(Font, Text + i, e - i); - if (ds.X() + Cx > Mx) - { - if (PrevExitChar > 0) - { - e = PrevExitChar; - Width = PrevX; - } - else - { - Width = ds.X(); - } - break; - } - else if (e >= Size OR - Text[e] == '\n') - { - Width = ds.X(); - break; - } - - PrevExitChar = e; - PrevX = ds.X(); - } - - e++; - } - } - - // Create layout line - GTextLine *l = new GTextLine; - if (l) - { - l->Start = i; - l->Len = e - i; - l->r.x1 = d->Margin.x1; - l->r.x2 = l->r.x1 + Width - 1; - - l->r.y1 = Cy; - l->r.y2 = l->r.y1 + LineY - 1; - - Line.Insert(l); - - MaxX = max(MaxX, l->r.X()); - Cy += LineY; - - if (e < Size) - e++; - } - } - } - } - - GTextLine *Last = Line.Length() ? Line.Last() : 0; - if (!Last OR - Last->Start + Last->Len < Size) - { - GTextLine *l = new GTextLine; - if (l) - { - l->Start = Size; - l->Len = 0; - l->r.x1 = l->r.x2 = d->Margin.x1; - l->r.y1 = Cy; - l->r.y2 = l->r.y1 + LineY - 1; - - Line.Insert(l); - - MaxX = max(MaxX, l->r.X()); - Cy += LineY; - } - } - - GRect c = GetClient(); - bool ScrollYNeeded = c.Y() < (Line.Length() * LineY); - SetScrollBars(false, ScrollYNeeded); - d->LayoutDirty = false; - UpdateScrollBars(); - -//int _PourTime = LgiCurrentTime() - StartTime; - #ifdef _DEBUG - if (GetWindow()) - { - static char s[256]; - sprintf(s, "Pour: %.2f sec", (double)_PourTime / 1000); - GetWindow()->PostEvent(M_TEXTVIEW_DEBUG_TEXT, (GMessage::Param)s); - } - #endif -// printf("PourTime=%ims\n", _PourTime); - - #if POUR_DEBUG - printf("Lines=%i\n", Line.Length()); - int Index = 0; - for (GTextLine *l=Line.First(); l; l=Line.Next(), Index++) - { - printf("\t[%i] %i,%i (%s)\n", Index, l->Start, l->Len, l->r.Describe()); - } - #endif -} - -bool GTextView3::InsertStyle(GTextStyle *s) -{ - if (!s) - return false; + } + + View->UndoOn = true; + View->Invalidate(); + } +}; + +void GTextStyle::RefreshLayout(int Start, int Len) +{ + View->PourText(Start, Len); + View->PourStyle(Start, Len); +} + +////////////////////////////////////////////////////////////////////// +GTextView3::GTextView3( int Id, + int x, int y, int cx, int cy, + GFontType *FontType) + : ResObject(Res_Custom) +{ + // init vars + d = new GTextView3Private; + LineY = 1; + MaxX = 0; + Blink = true; + TextCache = 0; + UndoOn = true; + Font = 0; + FixedWidthFont = false; + FixedFont = 0; + ShowWhiteSpace = false; + ObscurePassword = false; + TabSize = TAB_SIZE; + IndentSize = TAB_SIZE; + HardTabs = true; + CanScrollX = false; + + // setup window + SetId(Id); + + // default options + Dirty = false; + #if WIN32NATIVE + CrLf = true; + SetDlgCode(DLGC_WANTALLKEYS); + #else + CrLf = false; + #endif + Underline = 0; + BackColour = LC_WORKSPACE; + + #ifdef _DEBUG + // debug times + _PourTime = 0; + _StyleTime = 0; + _PaintTime = 0; + #endif + + // Data + Alloc = ALLOC_BLOCK; + Text = new char16[Alloc]; + if (Text) *Text = 0; + Cursor = 0; + Size = 0; + + // Display + SelStart = SelEnd = -1; + DocOffset = 0; + ScrollX = 0; + + if (FontType) + { + Font = FontType->Create(); + } + else + { + GFontType Type; + if (Type.GetSystemFont("Fixed")) + { + Font = Type.Create(); + } + else printf("%s:%i - failed to create font.\n", __FILE__, __LINE__); + } + if (Font) + { + // Font->PointSize(Font->PointSize() + 2); + + #if defined BEOS + Handle()->SetViewColor(B_TRANSPARENT_COLOR); + #endif + SetTabStop(true); + + Underline = new GFont; + if (Underline) + { + *Underline = *Font; + Underline->Underline(true); + Underline->Fore(d->UrlColour); + Underline->Create(); + } + + OnFontChange(); + } + + CursorPos.ZOff(1, LineY-1); + CursorPos.Offset(d->Margin.x1, d->Margin.y1); + + GRect r; + r.ZOff(cx-1, cy-1); + r.Offset(x, y); + SetPos(r); +} + +GTextView3::~GTextView3() +{ + Line.DeleteObjects(); + Style.DeleteObjects(); + + DeleteArray(TextCache); + DeleteArray(Text); + + if (Font != SysFont) DeleteObj(Font); + DeleteObj(FixedFont); + DeleteObj(Underline); + DeleteObj(d); +} + +char16 *GTextView3::MapText(char16 *Str, int Len, bool RtlTrailingSpace) +{ + if (ObscurePassword OR ShowWhiteSpace OR RtlTrailingSpace) + { + if (Len > d->MapLen) + { + DeleteArray(d->MapBuf); + d->MapBuf = new char16[Len + RtlTrailingSpace]; + d->MapLen = Len; + } + + if (d->MapBuf) + { + int n = 0; + + if (RtlTrailingSpace) + { + d->MapBuf[n++] = ' '; + for (int i=0; iMapBuf[n++] = Str[i]; + } + } + else if (ObscurePassword) + { + for (int i=0; iMapBuf[n++] = '*'; + } + } + else if (ShowWhiteSpace) + { + for (int i=0; iMapBuf[n++] = 0xb7; + } + else if (Str[i] == '\t') + { + d->MapBuf[n++] = 0x2192; + } + else + { + d->MapBuf[n++] = Str[i]; + } + } + } + + return d->MapBuf; + } + } + + return Str; +} + +void GTextView3::SetFixedWidthFont(bool i) +{ + if (FixedWidthFont ^ i) + { + if (i) + { + GFontType Type; + if (Type.GetSystemFont("Fixed")) + { + GFont *f = FixedFont; + FixedFont = Font; + Font = f; + + if (!Font) + { + Font = Type.Create(); + if (Font) + { + Font->PointSize(FixedFont->PointSize()); + } + } + + GDocView::SetFixedWidthFont(i); + } + } + else if (FixedFont) + { + GFont *f = FixedFont; + FixedFont = Font; + Font = f; + GDocView::SetFixedWidthFont(i); + } + + OnFontChange(); + Invalidate(); + } +} + +void GTextView3::SetReadOnly(bool i) +{ + GDocView::SetReadOnly(i); + + #if WIN32NATIVE + SetDlgCode(i ? DLGC_WANTARROWS : DLGC_WANTALLKEYS); + #endif +} + +void GTextView3::SetTabSize(uint8 i) +{ + TabSize = limit(i, 2, 32); + OnFontChange(); + OnPosChange(); + Invalidate(); +} + +void GTextView3::SetWrapType(uint8 i) +{ + GDocView::SetWrapType(i); + CanScrollX = i != TEXTED_WRAP_REFLOW; + + OnPosChange(); + Invalidate(); +} + +GFont *GTextView3::GetFont() +{ + return Font; +} + +void GTextView3::SetFont(GFont *f, bool OwnIt) +{ + if (f) + { + if (OwnIt) + { + DeleteObj(Font); + Font = f; + } + else if (!Font) + { + Font = new GFont(*f); + } + else + { + *Font = *f; + } + + if (Font) + { + if (!Underline) + Underline = new GFont; + *Underline = *Font; + Underline->Underline(true); + Underline->Fore(d->UrlColour); + } + + OnFontChange(); + } +} + +void GTextView3::OnFontChange() +{ + if (Font) + { + // get line height + int OldLineY = LineY; + LineY = Font->GetHeight(); + if (LineY < 1) LineY = 1; + + // get tab size + char Spaces[32]; + memset(Spaces, 'A', TabSize); + Spaces[TabSize] = 0; + GDisplayString ds(Font, Spaces); + Font->TabSize(ds.X()); + + // repour doc + d->SetDirty(0, Size); + + // validate blue underline font + if (Underline) + { + *Underline = *Font; + Underline->Underline(true); + Underline->Create(); + } + + #if WIN32NATIVE // Set the IME font. + HIMC hIMC = ImmGetContext(Handle()); + if (hIMC) + { + COMPOSITIONFORM Cf; + Cf.dwStyle = CFS_POINT; + Cf.ptCurrentPos.x = CursorPos.x1; + Cf.ptCurrentPos.y = CursorPos.y1; + + LOGFONT FontInfo; + GetObject(Font->Handle(), sizeof(FontInfo), &FontInfo); + ImmSetCompositionFont(hIMC, &FontInfo); + + ImmReleaseContext(Handle(), hIMC); + } + #endif + + } +} + +void GTextView3::PourText(int Start, int Length /* < 0 means it's a delete */) +{ + GRect Client = GetClient(); + int Mx = Client.X() - d->Margin.x1; + int y = 0; + + MaxX = 0; + + int Cx = 0, Cy = 0; + bool SimplePour = d->SimpleDelete; // One line change + int CurrentLine = -1; + if (d->SimpleDelete OR Start) + { + d->SimpleDelete = false; + if (!SimplePour AND + WrapType == TEXTED_WRAP_NONE AND + Length > 0 AND + Length < 100) + { + SimplePour = true; + for (int i=0; i= 0); + + if (!Current) + { + SimplePour = false; + } + else + { + Start = Current->Start; + Cy = Current->r.y1; + + if (SimplePour) + { + #if POUR_DEBUG + printf("SimplePour Start=%i Length=%i CurrentLine=%i\n", Start, Length, CurrentLine); + #endif + Line.Delete(Current); + DeleteObj(Current); + } + else + { + #if POUR_DEBUG + printf("PartialPour Start=%i Length=%i\n", Start, Length); + #endif + bool Done = false; + for (GTextLine *l=Line.Last(); l AND !Done; l=Line.Last()) + { + Done = l == Current; + Line.Delete(l); + DeleteObj(l); + } + } + } + } + else + { + // Whole doc is dirty + #if POUR_DEBUG + printf("WholePour Start=%i Length=%i\n", Start, Length); + #endif + Start = 0; + Line.DeleteObjects(); + } + + if (Text AND Font AND Mx > 0) + { + // break array, break out of loop when we hit these chars + #define ExitLoop(c) ( (c) == 0 OR \ + (c) == '\n' OR \ + (c) == ' ' OR \ + (c) == '\t' \ + ) + + // extra breaking oportunities + #define ExtraBreak(c) ( ( (c) >= 0x3040 AND (c) <= 0x30FF ) OR \ + ( (c) >= 0x3300 AND (c) <= 0x9FAF ) \ + ) + + // tracking vars + int e; + int LastX = 0; + int LastChar = Start; + int WrapCol = GetWrapAtCol(); + + GDisplayString Sp(Font, " ", 1); + int WidthOfSpace = Sp.X(); + if (WidthOfSpace < 1) + { + printf("%s:%i - WidthOfSpace test failed.\n", __FILE__, __LINE__); + return; + } + + // alright... lets pour! + for (int i=Start; iStart = LastChar; + l->r.x1 = d->Margin.x1; + l->Len = e - LastChar; + l->r.x2 = l->r.x1 + Cx; + LastChar = ++e; + + l->r.y1 = Cy; + l->r.y2 = l->r.y1 + LineY - 1; + + Line.Insert(l, SimplePour ? CurrentLine : -1); + + MaxX = max(MaxX, l->r.X()); + LastX = Cx = 0; + Cy += LineY; + } + else break; + + if (SimplePour) + { + for (l=Line[++CurrentLine]; l; l=Line.Next()) + { + l->Start += Length; + } + break; + } + } + else + { + e = i; + int Width = 0; + + // Find break point + if (WrapCol) + { + // Wrap at column + + // Find the end of line + while (true) + { + if (e >= Size OR + Text[e] == '\n' OR + (e-i) >= WrapCol) + { + break; + } + + e++; + } + + // Seek back some characters if we are mid word + int OldE = e; + if (e < Size AND + Text[e] != '\n') + { + while (e > i) + { + if (ExitLoop(Text[e]) OR + ExtraBreak(Text[e])) + { + break; + } + + e--; + } + } + + if (e == i) + { + // No line break at all, so seek forward instead + for (e=OldE; e < Size AND Text[e] != '\n'; e++) + { + if (ExitLoop(Text[e]) OR + ExtraBreak(Text[e])) + break; + } + } + + // Calc the width + GDisplayString ds(Font, Text + i, e - i); + Width = ds.X(); + } + else + { + // Wrap to edge of screen + int PrevExitChar = -1; + int PrevX = -1; + + while (true) + { + if (e >= Size OR + ExitLoop(Text[e]) OR + ExtraBreak(Text[e])) + { + GDisplayString ds(Font, Text + i, e - i); + if (ds.X() + Cx > Mx) + { + if (PrevExitChar > 0) + { + e = PrevExitChar; + Width = PrevX; + } + else + { + Width = ds.X(); + } + break; + } + else if (e >= Size OR + Text[e] == '\n') + { + Width = ds.X(); + break; + } + + PrevExitChar = e; + PrevX = ds.X(); + } + + e++; + } + } + + // Create layout line + GTextLine *l = new GTextLine; + if (l) + { + l->Start = i; + l->Len = e - i; + l->r.x1 = d->Margin.x1; + l->r.x2 = l->r.x1 + Width - 1; + + l->r.y1 = Cy; + l->r.y2 = l->r.y1 + LineY - 1; + + Line.Insert(l); + + MaxX = max(MaxX, l->r.X()); + Cy += LineY; + + if (e < Size) + e++; + } + } + } + } + + GTextLine *Last = Line.Length() ? Line.Last() : 0; + if (!Last OR + Last->Start + Last->Len < Size) + { + GTextLine *l = new GTextLine; + if (l) + { + l->Start = Size; + l->Len = 0; + l->r.x1 = l->r.x2 = d->Margin.x1; + l->r.y1 = Cy; + l->r.y2 = l->r.y1 + LineY - 1; + + Line.Insert(l); + + MaxX = max(MaxX, l->r.X()); + Cy += LineY; + } + } + + GRect c = GetClient(); + bool ScrollYNeeded = c.Y() < (Line.Length() * LineY); + SetScrollBars(false, ScrollYNeeded); + d->LayoutDirty = false; + UpdateScrollBars(); + +//int _PourTime = LgiCurrentTime() - StartTime; + #ifdef _DEBUG + if (GetWindow()) + { + static char s[256]; + sprintf(s, "Pour: %.2f sec", (double)_PourTime / 1000); + GetWindow()->PostEvent(M_TEXTVIEW_DEBUG_TEXT, (GMessage::Param)s); + } + #endif +// printf("PourTime=%ims\n", _PourTime); + + #if POUR_DEBUG + printf("Lines=%i\n", Line.Length()); + int Index = 0; + for (GTextLine *l=Line.First(); l; l=Line.Next(), Index++) + { + printf("\t[%i] %i,%i (%s)\n", Index, l->Start, l->Len, l->r.Describe()); + } + #endif +} + +bool GTextView3::InsertStyle(GTextStyle *s) +{ + if (!s) + return false; LgiAssert(s->Start >= 0); - LgiAssert(s->Len > 0); - int Last = 0; - int n = 0; - - for (GTextStyle *i=Style.First(); i; i=Style.Next(), n++) - { - if (s->Overlap(i)) - { - if (s->Owner > i->Owner) - { - // Fail the insert - DeleteObj(s); - return false; - } - else - { - // Replace mode... - Style.Delete(i); - Style.Insert(s, n); - return true; - } - } - - if (s->Start >= Last AND s->Start < i->Start) - { - Style.Insert(s, n); - return true; - } - - Last = i->Start; - } - - Style.Insert(s); - return true; -} - -GTextStyle *GTextView3::GetNextStyle(int Where) -{ - GTextStyle *s = (Where >= 0) ? Style.First() : Style.Next(); - while (s) - { - // determin whether style is relevent.. - // styles in the selected region are ignored - int Min = min(SelStart, SelEnd); - int Max = max(SelStart, SelEnd); - if (SelStart >= 0 AND - s->Start >= Min AND - s->Start+s->Len < Max) - { - // style is completely inside selection: ignore - s = Style.Next(); - } - else if (Where >= 0 AND - s->Start+s->Len < Where) - { - s = Style.Next(); - } - else - { - return s; - } - } - - return 0; -} - -class GUrl : public GTextStyle -{ -public: - bool Email; - - GUrl(int own) : GTextStyle(own) - { - Email = false; - } - - bool OnMouseClick(GMouse *m) - { - if (View) - { - if ( (m AND m->Left() AND m->Double()) OR (!m) ) - { - char *Utf8 = LgiNewUtf16To8(View->NameW() + Start, Len * sizeof(char16)); - if (Utf8) - { - View->OnUrl(Utf8); - DeleteArray(Utf8); - } - - return true; - } - } - - return false; - } - - bool OnMenu(GSubMenu *m) - { - if (m) - { - if (Email) - { - m->AppendItem(LgiLoadString(L_TEXTCTRL_EMAIL_TO, "New Email to..."), IDM_NEW, true); - } - else - { - m->AppendItem(LgiLoadString(L_TEXTCTRL_OPENURL, "Open URL"), IDM_OPEN, true); - } - - m->AppendItem(LgiLoadString(L_TEXTCTRL_COPYLINK, "Copy link location"), IDM_COPY_URL, true); - - return true; - } - - return false; - } - - void OnMenuClick(int i) - { - switch (i) - { - case IDM_NEW: - case IDM_OPEN: - { - OnMouseClick(0); - break; - } - case IDM_COPY_URL: - { - char *Url = LgiNewUtf16To8(View->NameW() + Start, Len * sizeof(char16)); - if (Url) - { - GClipBoard Clip(View); - Clip.Text(Url); - DeleteArray(Url); - } - break; - } - } - } - - TCHAR *GetCursor() - { - #ifdef WIN32 - GArray Ver; - if (LgiGetOs(&Ver) == LGI_OS_WINNT AND - Ver[0] >= 5) - { - return MAKEINTRESOURCE(32649); // hand - } - else - { - return IDC_ARROW; - } - #endif - return 0; - } -}; - -GTextStyle *GTextView3::HitStyle(int i) -{ - for (GTextStyle *s=Style.First(); s; s=Style.Next()) - { - if (i >= s->Start AND i < s->Start+s->Len) - { - return s; - } - } - - return 0; -} - -void GTextView3::PourStyle(int Start, int Length) -{ - #ifdef _DEBUG - int StartTime = LgiCurrentTime(); - #endif - - for (GTextStyle *s = Style.First(); s; ) - { - if (s->Owner == 0) - { - Style.Delete(); - DeleteObj(s); - s = Style.Current(); - } - else s = Style.Next(); - } - - if (UrlDetect AND Text) - { - char16 Http[] = {'h', 't', 't', 'p', ':', '/', '/', 0 }; - char16 Https[] = {'h', 't', 't', 'p', 's', ':', '/', '/', 0}; - - for (int i=0; i s AND - ! - ( - isalpha(e[-1]) OR - isdigit(e[-1]) OR - e[-1] == '/' - ) - ) - e--; - - GUrl *Url = new GUrl(0); - if (Url) - { - Url->Email = false; - Url->View = this; - Url->Start = SubtractPtr(s, Text); - Url->Len = SubtractPtr(e, s); - Url->Font = Underline; - Url->c = d->UrlColour; - - InsertStyle(Url); - } - i = SubtractPtr(e, Text); - } - break; - } - case '@': - { - // find start - char16 *s = Text + (max(i, 1) - 1); - - for ( ; s > Text AND EmailChar(*s); s--) - ; - - if (s < Text + i) - { - if (!EmailChar(*s)) - s++; - - bool FoundDot = false; - char16 *Start = Text + i + 1; - char16 *e = Start; - for ( ; (SubtractPtr(e, Text) < Size) AND - EmailChar(*e); e++) - { - if (*e == '.') FoundDot = true; - } - while (e > Start AND e[-1] == '.') e--; - - if (FoundDot) - { - GUrl *Url = new GUrl(0); - if (Url) - { - Url->Email = true; - Url->View = this; - Url->Start = SubtractPtr(s, Text); - Url->Len = SubtractPtr(e, s); - Url->Font = Underline; - Url->c = d->UrlColour; - - InsertStyle(Url); - } - i = SubtractPtr(e, Text); - } - } - break; - } - } - } - } - - #ifdef _DEBUG - _StyleTime = LgiCurrentTime() - StartTime; - #endif -} - + LgiAssert(s->Len > 0); + int Last = 0; + int n = 0; + + for (GTextStyle *i=Style.First(); i; i=Style.Next(), n++) + { + if (s->Overlap(i)) + { + if (s->Owner > i->Owner) + { + // Fail the insert + DeleteObj(s); + return false; + } + else + { + // Replace mode... + Style.Delete(i); + Style.Insert(s, n); + return true; + } + } + + if (s->Start >= Last AND s->Start < i->Start) + { + Style.Insert(s, n); + return true; + } + + Last = i->Start; + } + + Style.Insert(s); + return true; +} + +GTextStyle *GTextView3::GetNextStyle(int Where) +{ + GTextStyle *s = (Where >= 0) ? Style.First() : Style.Next(); + while (s) + { + // determin whether style is relevent.. + // styles in the selected region are ignored + int Min = min(SelStart, SelEnd); + int Max = max(SelStart, SelEnd); + if (SelStart >= 0 AND + s->Start >= Min AND + s->Start+s->Len < Max) + { + // style is completely inside selection: ignore + s = Style.Next(); + } + else if (Where >= 0 AND + s->Start+s->Len < Where) + { + s = Style.Next(); + } + else + { + return s; + } + } + + return 0; +} + +class GUrl : public GTextStyle +{ +public: + bool Email; + + GUrl(int own) : GTextStyle(own) + { + Email = false; + } + + bool OnMouseClick(GMouse *m) + { + if (View) + { + if ( (m AND m->Left() AND m->Double()) OR (!m) ) + { + char *Utf8 = LgiNewUtf16To8(View->NameW() + Start, Len * sizeof(char16)); + if (Utf8) + { + View->OnUrl(Utf8); + DeleteArray(Utf8); + } + + return true; + } + } + + return false; + } + + bool OnMenu(GSubMenu *m) + { + if (m) + { + if (Email) + { + m->AppendItem(LgiLoadString(L_TEXTCTRL_EMAIL_TO, "New Email to..."), IDM_NEW, true); + } + else + { + m->AppendItem(LgiLoadString(L_TEXTCTRL_OPENURL, "Open URL"), IDM_OPEN, true); + } + + m->AppendItem(LgiLoadString(L_TEXTCTRL_COPYLINK, "Copy link location"), IDM_COPY_URL, true); + + return true; + } + + return false; + } + + void OnMenuClick(int i) + { + switch (i) + { + case IDM_NEW: + case IDM_OPEN: + { + OnMouseClick(0); + break; + } + case IDM_COPY_URL: + { + char *Url = LgiNewUtf16To8(View->NameW() + Start, Len * sizeof(char16)); + if (Url) + { + GClipBoard Clip(View); + Clip.Text(Url); + DeleteArray(Url); + } + break; + } + } + } + + TCHAR *GetCursor() + { + #ifdef WIN32 + GArray Ver; + if (LgiGetOs(&Ver) == LGI_OS_WINNT AND + Ver[0] >= 5) + { + return MAKEINTRESOURCE(32649); // hand + } + else + { + return IDC_ARROW; + } + #endif + return 0; + } +}; + +GTextStyle *GTextView3::HitStyle(int i) +{ + for (GTextStyle *s=Style.First(); s; s=Style.Next()) + { + if (i >= s->Start AND i < s->Start+s->Len) + { + return s; + } + } + + return 0; +} + +void GTextView3::PourStyle(int Start, int Length) +{ + #ifdef _DEBUG + int StartTime = LgiCurrentTime(); + #endif + + for (GTextStyle *s = Style.First(); s; ) + { + if (s->Owner == 0) + { + Style.Delete(); + DeleteObj(s); + s = Style.Current(); + } + else s = Style.Next(); + } + + if (UrlDetect AND Text) + { + char16 Http[] = {'h', 't', 't', 'p', ':', '/', '/', 0 }; + char16 Https[] = {'h', 't', 't', 'p', 's', ':', '/', '/', 0}; + + for (int i=0; i s AND + ! + ( + isalpha(e[-1]) OR + isdigit(e[-1]) OR + e[-1] == '/' + ) + ) + e--; + + GUrl *Url = new GUrl(0); + if (Url) + { + Url->Email = false; + Url->View = this; + Url->Start = SubtractPtr(s, Text); + Url->Len = SubtractPtr(e, s); + Url->Font = Underline; + Url->c = d->UrlColour; + + InsertStyle(Url); + } + i = SubtractPtr(e, Text); + } + break; + } + case '@': + { + // find start + char16 *s = Text + (max(i, 1) - 1); + + for ( ; s > Text AND EmailChar(*s); s--) + ; + + if (s < Text + i) + { + if (!EmailChar(*s)) + s++; + + bool FoundDot = false; + char16 *Start = Text + i + 1; + char16 *e = Start; + for ( ; (SubtractPtr(e, Text) < Size) AND + EmailChar(*e); e++) + { + if (*e == '.') FoundDot = true; + } + while (e > Start AND e[-1] == '.') e--; + + if (FoundDot) + { + GUrl *Url = new GUrl(0); + if (Url) + { + Url->Email = true; + Url->View = this; + Url->Start = SubtractPtr(s, Text); + Url->Len = SubtractPtr(e, s); + Url->Font = Underline; + Url->c = d->UrlColour; + + InsertStyle(Url); + } + i = SubtractPtr(e, Text); + } + } + break; + } + } + } + } + + #ifdef _DEBUG + _StyleTime = LgiCurrentTime() - StartTime; + #endif +} + bool GTextView3::Insert(int At, char16 *Data, int Len) { GArray Edits; EditInfo &i = Edits.New(); i.At = max(At, 0); i.At = min(At, Size); i.Len = Len; i.Txt = Data; return Insert(Edits); } static int EditCmp(GTextView3::EditInfo *a, GTextView3::EditInfo *b) { return a->At - b->At; } bool GTextView3::Insert(GArray &Edits) -{ +{ if (ReadOnly) return false; Edits.Sort(EditCmp); for (int Idx=Edits.Length()-1; Idx>=0; Idx--) { EditInfo &e = Edits[Idx]; - if (e.Len > 0) - { - // limit input to valid data - e.At = min(Size, e.At); - - // make sure we have enough memory - int NewAlloc = Size + e.Len + 1; - NewAlloc += ALLOC_BLOCK - (NewAlloc % ALLOC_BLOCK); - if (NewAlloc != Alloc) - { - char16 *NewText = new char16[NewAlloc]; - if (NewText) - { - if (Text) - { - // copy any existing data across - memcpy(NewText, Text, (Size + 1) * sizeof(char16)); - } - - DeleteArray(Text); - Text = NewText; - Alloc = NewAlloc; - } - else - { - // memory allocation error - return false; - } - } - - if (Text) - { - // insert the data - // move the section after the insert - memmove(Text+(e.At+e.Len), Text+e.At, (Size-e.At) * sizeof(char16)); - - if (e.Txt) - { - // copy new data in - if (UndoOn) - { - UndoQue += new GTextView3Undo(this, e.Txt, e.Len, e.At, GTextView3Undo::UndoInsert); - } - - memcpy(Text+e.At, e.Txt, e.Len * sizeof(char16)); - Size += e.Len; - } - else - { - return false; - } - - Text[Size] = 0; - Dirty = true; - PourText(e.At, e.Len); - PourStyle(e.At, e.Len); + if (e.Len > 0) + { + // limit input to valid data + e.At = min(Size, e.At); + + // make sure we have enough memory + int NewAlloc = Size + e.Len + 1; + NewAlloc += ALLOC_BLOCK - (NewAlloc % ALLOC_BLOCK); + if (NewAlloc != Alloc) + { + char16 *NewText = new char16[NewAlloc]; + if (NewText) + { + if (Text) + { + // copy any existing data across + memcpy(NewText, Text, (Size + 1) * sizeof(char16)); + } + + DeleteArray(Text); + Text = NewText; + Alloc = NewAlloc; + } + else + { + // memory allocation error + return false; + } } - } - } + + if (Text) + { + // insert the data + // move the section after the insert + memmove(Text+(e.At+e.Len), Text+e.At, (Size-e.At) * sizeof(char16)); + + if (e.Txt) + { + // copy new data in + if (UndoOn) + { + UndoQue += new GTextView3Undo(this, e.Txt, e.Len, e.At, GTextView3Undo::UndoInsert); + } + + memcpy(Text+e.At, e.Txt, e.Len * sizeof(char16)); + Size += e.Len; + } + else + { + return false; + } + + Text[Size] = 0; + Dirty = true; + PourText(e.At, e.Len); + PourStyle(e.At, e.Len); + } + } + } SendNotify(GTVN_DOC_CHANGED); - return true; -} - + return true; +} + bool GTextView3::Delete(int At, int Len) { if (Len < 1) { LgiAssert(0); return false; } if (At < 0 || At >= Size) { LgiAssert(0); return false; } GArray Edits; EditInfo &i = Edits.New(); i.At = At; i.Len = min(Size - At, Len); return Delete(Edits); } bool GTextView3::Delete(GArray &Edits) -{ - bool Status = false; - +{ + bool Status = false; + if (ReadOnly) - return false; + return false; bool HasNewLine = false; GArray Blocks; Edits.Sort(EditCmp); // Work out how complicated this edit is... does it cross lines boundaries? EditInfo *MinEdit = 0, *MaxEdit = 0; int n; for (n=0; !HasNewLine && nAt) MinEdit = &e; if (!MaxEdit || e.At + e.Len > MaxEdit->End()) MaxEdit = &e; - } - - int PrevLineStart = -1; - int NextLineStart = -1; - if (WrapType == TEXTED_WRAP_NONE) - { - d->SimpleDelete = !HasNewLine; - } - else if (MinEdit && MaxEdit) + } + + int PrevLineStart = -1; + int NextLineStart = -1; + if (WrapType == TEXTED_WRAP_NONE) { - // Work out the dimensions of the change - int MinIndex, MaxIndex; + d->SimpleDelete = !HasNewLine; + } + else if (MinEdit && MaxEdit) + { + // Work out the dimensions of the change + int MinIndex, MaxIndex; GTextLine *Min = GetLine(MinEdit->At, &MinIndex); if (Min) { GTextLine *Prev = Line[MinIndex-1]; PrevLineStart = Prev ? Prev->Start : -1; } GTextLine *Max = GetLine(MaxEdit->End(), &MaxIndex); if (Max) { - GTextLine *Next = Line[MaxIndex+1]; - NextLineStart = Next ? Next->Start : -1; + GTextLine *Next = Line[MaxIndex+1]; + NextLineStart = Next ? Next->Start : -1; } } else { LgiAssert(0); return false; - } - - // Store the undo information - if (UndoOn) - UndoQue += new GTextView3Undo(this, Blocks, GTextView3Undo::UndoDelete); + } + + // Store the undo information + if (UndoOn) + UndoQue += new GTextView3Undo(this, Blocks, GTextView3Undo::UndoDelete); // Apply the changes to the document... char16 *Done = Text + Edits[0].At; - int NewCursor = Cursor; + int NewCursor = Cursor; for (n=0; n= e.At && Cursor < e.End()) { NewCursor = Done - Text; } if (Next) { // Move section between this edit and the next int Between = Next->At - e.End(); memmove(Done, Text + Next->At - Between, Between * sizeof(char16)); e.At = Done - Text; // Reposition the edit's index to the new loc Done += Between; } else { // Move section between this edit and the end of the document int Remaining = Size - e.End(); memmove(Done, Text + Size - Remaining, Remaining * sizeof(char16)); e.At = Done - Text; // Reposition the edit's index to the new loc Done += Remaining; } // Reduce the size of the document // Adjust the layout and style... PourText(e.At, -e.Len); PourStyle(e.At, -e.Len); } Size = Done - Text; // Set the size of the document - *Done = 0; // Null terminate - - Dirty = true; - if (Cursor != NewCursor) - SetCursor(NewCursor, false, HasNewLine); - - // Handle repainting in flowed mode, when the line starts change - if (WrapType == TEXTED_WRAP_REFLOW) + *Done = 0; // Null terminate + + Dirty = true; + if (Cursor != NewCursor) + SetCursor(NewCursor, false, HasNewLine); + + // Handle repainting in flowed mode, when the line starts change + if (WrapType == TEXTED_WRAP_REFLOW) { - #if 0 - int Index; - GTextLine *Cur = GetLine(e.At, &Index); - if (Cur) - { - GTextLine *Repaint = 0; - GTextLine *Prev = Line[Index-1]; - if (Prev AND PrevLineStart != Prev->Start) - { - // Paint previous line down - Repaint = Prev; - } - else - { - GTextLine *Next = Line[Index+1]; - if (Next AND NextLineStart != Next->Start) - { - // Paint next line down - Repaint = Next; - } - } - - if (Repaint) - { - GRect r = Repaint->r; - r.x2 = GetClient().x2; - r.y2 = GetClient().y2; - Invalidate(&r); - } + #if 0 + int Index; + GTextLine *Cur = GetLine(e.At, &Index); + if (Cur) + { + GTextLine *Repaint = 0; + GTextLine *Prev = Line[Index-1]; + if (Prev AND PrevLineStart != Prev->Start) + { + // Paint previous line down + Repaint = Prev; + } + else + { + GTextLine *Next = Line[Index+1]; + if (Next AND NextLineStart != Next->Start) + { + // Paint next line down + Repaint = Next; + } + } + + if (Repaint) + { + GRect r = Repaint->r; + r.x2 = GetClient().x2; + r.y2 = GetClient().y2; + Invalidate(&r); + } } - #endif - } - + #endif + } + SendNotify(GTVN_DOC_CHANGED); return true; -} - -void GTextView3::DeleteSelection(char16 **Cut) -{ - if (SelStart >= 0) - { - int Min = min(SelStart, SelEnd); - int Max = max(SelStart, SelEnd); - - if (Cut) - { - *Cut = NewStrW(Text + Min, Max - Min); - } - - Delete(Min, Max - Min); - SetCursor(Min, false, true); - } -} - -GTextView3::GTextLine *GTextView3::GetLine(int Offset, int *Index) -{ - int i = 0; - for (GTextLine *l=Line.First(); l; l=Line.Next(), i++) - { - if (Offset >= l->Start AND Offset <= l->Start+l->Len) - { - if (Index) - { - *Index = i; - } - - return l; - } - } - - return 0; -} - -int64 GTextView3::Value() -{ - char *n = Name(); - #ifdef _MSC_VER - return (n) ? _atoi64(n) : 0; - #else - return (n) ? atoll(n) : 0; - #endif -} - -void GTextView3::Value(int64 i) -{ - char Str[32]; - sprintf(Str, LGI_PrintfInt64, i); - Name(Str); -} - -char *GTextView3::Name() -{ - UndoQue.Empty(); - DeleteArray(TextCache); - TextCache = LgiNewUtf16To8(Text); - - return TextCache; -} - -bool GTextView3::Name(char *s) -{ - UndoQue.Empty(); - DeleteArray(TextCache); - DeleteArray(Text); - - LgiAssert(LgiIsUtf8(s)); - Text = LgiNewUtf8To16(s); - if (!Text) - { - Text = new char16[1]; - if (Text) *Text = 0; - } - - Size = Text ? StrlenW(Text) : 0; - Alloc = Size + 1; - Cursor = min(Cursor, Size); - if (Text) - { - // Remove '\r's - char16 *o = Text; - for (char16 *i=Text; *i; i++) - { - if (*i != '\r') - { - *o++ = *i; - } - else Size--; - } - *o++ = 0; - } - - // update everything else - PourText(0, Size); - PourStyle(0, Size); - UpdateScrollBars(); - Invalidate(); - - return true; -} - -char16 *GTextView3::NameW() -{ - return Text; -} - -bool GTextView3::NameW(char16 *s) -{ - DeleteArray(Text); - Size = s ? StrlenW(s) : 0; - Alloc = Size + 1; - Text = new char16[Alloc]; - Cursor = min(Cursor, Size); - if (Text) - { - memcpy(Text, s, Size * sizeof(char16)); - - // remove LF's - int In = 0, Out = 0; - CrLf = false; - for (; In= 0) AND (SelStart != SelEnd); -} - -void GTextView3::SelectAll() -{ - SelStart = 0; - SelEnd = Size; - Invalidate(); -} - -void GTextView3::UnSelectAll() -{ - bool Update = HasSelection(); - - SelStart = -1; - SelEnd = -1; - - if (Update) - { - Invalidate(); - } -} - -int GTextView3::GetLines() -{ - return Line.Length(); -} - -void GTextView3::GetTextExtent(int &x, int &y) -{ - PourText(0, Size); - - x = MaxX + d->Margin.x1; - y = Line.Length() * LineY; -} - -void GTextView3::PositionAt(int &x, int &y, int Index) -{ - int FromIndex = 0; - GTextLine *From = GetLine(Index < 0 ? Cursor : Index, &FromIndex); - if (From) - { - x = Cursor - From->Start; - y = FromIndex; - } -} - -int GTextView3::GetCursor(bool Cur) -{ - if (Cur) - { - return Cursor; - } - - return 0; -} - -int GTextView3::IndexAt(int x, int y) -{ - GTextLine *l = Line.ItemAt(y); - if (l) - { - return l->Start + min(x, l->Len); - } - - return 0; -} - -void GTextView3::SetCursor(int i, bool Select, bool ForceFullUpdate) -{ - //int _Start = LgiCurrentTime(); - // LgiTrace("SetCursor(%i)\n", i); - - // Bound the new cursor position to the document - if (i < 0) i = 0; - if (i > Size) i = Size; - - // Store the old selection and cursor - int s = SelStart, e = SelEnd, c = Cursor; - - // If there is going to be a selected area - if (Select AND i != SelStart) - { - // Then set the start - if (SelStart < 0) - { - // We are starting a new selection - SelStart = Cursor; - } - - // And end - SelEnd = i; - } - else - { - // Clear the selection - SelStart = SelEnd = -1; - } - - int FromIndex = 0; - GTextLine *From = GetLine(Cursor, &FromIndex); - - Cursor = i; - - // check the cursor is on the screen - int ToIndex = 0; - GTextLine *To = GetLine(Cursor, &ToIndex); - if (VScroll AND To) - { - GRect Client = GetClient(); - int DisplayLines = Client.Y() / LineY; - - if (ToIndex < VScroll->Value()) - { - // Above the visible region... - if (d->CenterCursor) - { - int i = ToIndex - (DisplayLines >> 1); - VScroll->Value(max(0, i)); - } - else - { - VScroll->Value(ToIndex); - } - ForceFullUpdate = true; - } - - if (ToIndex >= VScroll->Value() + DisplayLines) - { - int YOff = d->CenterCursor ? DisplayLines >> 1 : DisplayLines; - - int v = min(ToIndex - YOff + 1, Line.Length() - DisplayLines); - if (v != VScroll->Value()) - { - // Below the visible region - VScroll->Value(v); - ForceFullUpdate = true; - } - } - } - - // check whether we need to update the screen - if (ForceFullUpdate OR - !To OR - !From) - { - // need full update - Invalidate(); - } - else if ( SelStart != s OR - SelEnd != e) - { - // Update just the selection bounds - GRect Client = GetClient(); - int Start, End; - if (SelStart >= 0 AND s >= 0) - { - // Selection has changed, union the before and after regions - Start = min(Cursor, c); - End = max(Cursor, c); - } - else if (SelStart >= 0) - { - // Selection created... - Start = min(SelStart, SelEnd); - End = max(SelStart, SelEnd); - } - else if (s >= 0) - { - // Selection removed... - Start = min(s, e); - End = max(s, e); - } - - GTextLine *SLine = GetLine(Start); - GTextLine *ELine = GetLine(End); - GRect u; - if (SLine AND ELine) - { - if (SLine->r.Valid()) - { - u = SLine->r; - u.Offset(0, d->Margin.y1-ScrollYPixel()); - } - else - u.Set(0, 0, Client.X()-1, 1); // Start of visible page - - GRect b(0, Client.Y()-1, Client.X()-1, Client.Y()-1); - if (ELine->r.Valid()) - { - b = ELine->r; - b.Offset(0, d->Margin.y1-ScrollYPixel()); - } - else - { - b.Set(0, Client.Y()-1, Client.X()-1, Client.Y()-1); - } - u.Union(&b); - - u.x1 = 0; - u.x2 = X(); - } - else - { - printf("%s,%i - Couldn't get SLine and ELine (%i, %i)\n", _FL, Start, End); - u = Client; - } - - Invalidate(&u); - } - else if (Cursor != c) - { - // just the cursor has moved - - // update the line the cursor moved to - GRect r = To->r; - r.Offset(-ScrollX, d->Margin.y1-DocOffset); - r.x2 = X(); - Invalidate(&r); - - if (To != From) - { - // update the line the cursor came from, - // if it's a different line from the "to" - r = From->r; - r.Offset(-ScrollX, d->Margin.y1-DocOffset); - r.x2 = X(); - Invalidate(&r); - } - } - - if (c != Cursor) - { - // Send off notify - SendNotify(GTVN_CURSOR_CHANGED); - } - -//int _Time = LgiCurrentTime() - _Start; -//printf("Setcursor=%ims\n", _Time); -} - -void GTextView3::SetBorder(int b) -{ -} - -bool GTextView3::Cut() -{ - bool Status = false; - char16 *Txt16 = 0; - - DeleteSelection(&Txt16); - if (Txt16) - { - #ifdef WIN32 - Txt16 = ConvertToCrLf(Txt16); - #endif - char *Txt8 = (char*)LgiNewConvertCp(LgiAnsiToLgiCp(), Txt16, "utf-16"); - - GClipBoard Clip(this); - - Clip.Text(Txt8); - Status = Clip.TextW(Txt16, false); - - DeleteArray(Txt8); - DeleteArray(Txt16); - } - - return Status; -} - -bool GTextView3::Copy() -{ - bool Status = true; - - if (SelStart >= 0) - { - int Min = min(SelStart, SelEnd); - int Max = max(SelStart, SelEnd); - - char16 *Txt16 = NewStrW(Text+Min, Max-Min); - #ifdef WIN32 - Txt16 = ConvertToCrLf(Txt16); - #endif - char *Txt8 = (char*)LgiNewConvertCp(LgiAnsiToLgiCp(), Txt16, "utf-16"); - - GClipBoard Clip(this); - - Clip.Text(Txt8); - Clip.TextW(Txt16, false); - - DeleteArray(Txt8); - DeleteArray(Txt16); - } - - return Status; -} - -bool GTextView3::Paste() -{ - GClipBoard Clip(this); - - char16 *t = Clip.TextW(); - - if (!t) // ala Win9x - { - char *s = Clip.Text(); - if (s) - { - t = LgiNewUtf8To16(s); - DeleteArray(s); - } - } - - if (t) - { - if (SelStart >= 0) - { - DeleteSelection(); - } - - // remove '\r's - bool Multiline = false; - char16 *s = t, *d = t; - for (; *s; s++) - { - if (*s == '\n') - { - Multiline = true; - } - - if (*s != '\r') - { - *d++ = *s; - } - } - *d++ = 0; - - // insert text - int Len = StrlenW(t); - Insert(Cursor, t, Len); - SetCursor(Cursor+Len, false, true); // Multiline - - // clean up - DeleteArray(t); - } - return false; -} - -bool GTextView3::ClearDirty(bool Ask, char *FileName) -{ - if (Dirty) - { - int Answer = (Ask) ? LgiMsg(this, - LgiLoadString(L_TEXTCTRL_ASK_SAVE, "Do you want to save your changes to this document?"), - LgiLoadString(L_TEXTCTRL_SAVE, "Save"), - MB_YESNOCANCEL) : IDYES; - if (Answer == IDYES) - { - GFileSelect Select; - Select.Parent(this); - if (!FileName AND - Select.Save()) - { - FileName = Select.Name(); - } - - Save(FileName); - } - else if (Answer == IDCANCEL) - { - return false; - } - } - - return true; -} - -bool GTextView3::Open(char *Name, char *CharSet) -{ - bool Status = false; - GFile f; - - if (f.Open(Name, O_READ|O_SHARE)) - { - DeleteArray(Text); - int Bytes = f.GetSize(); - SetCursor(0, false); - - char *c8 = new char[Bytes + 4]; - if (c8) - { - if (f.Read(c8, Bytes) == Bytes) - { - char *DataStart = c8; - - c8[Bytes] = 0; - c8[Bytes+1] = 0; - c8[Bytes+2] = 0; - c8[Bytes+3] = 0; - - if ((uchar)c8[0] == 0xff AND (uchar)c8[1] == 0xfe) - { - // utf-16 - if (!CharSet) - { - CharSet = "utf-16"; - DataStart += 2; - } - } - - // Convert to unicode first.... - if (Bytes == 0) - { - Text = new char16[1]; - if (Text) Text[0] = 0; - } - else - { - Text = (char16*)LgiNewConvertCp(LGI_WideCharset, DataStart, CharSet ? CharSet : LgiAnsiToLgiCp()); - } - if (Text) - { - // Remove LF's - char16 *In = Text, *Out = Text; - CrLf = false; - Size = 0; - while (*In) - { - if (*In >= ' ' OR - *In == '\t' OR - *In == '\n') - { - *Out++ = *In; - Size++; - } - else if (*In == '\r') - { - CrLf = true; - } - In++; - } - Size = Out - Text; - *Out = 0; - - Alloc = Size + 1; - Dirty = false; - - if (Text AND Text[0] == 0xfeff) // unicode byte order mark - { - memmove(Text, Text+1, Size * sizeof(*Text)); - Size--; - } - - PourText(0, Size); - PourStyle(0, Size); - UpdateScrollBars(true); - - Status = true; - } - } - - DeleteArray(c8); - } - else - { - Alloc = Size = 0; - } - - Invalidate(); - } - - return Status; -} - -bool GTextView3::Save(char *Name, char *CharSet) -{ - GFile f; - if (f.Open(Name, O_WRITE)) - { - f.SetSize(0); - if (Text) - { - bool Status = false; - - char *c8 = (char*)LgiNewConvertCp(CharSet ? CharSet : LgiAnsiToLgiCp(), Text, "utf-16", Size * sizeof(char16)); - if (c8) - { - int Len = strlen(c8); - if (CrLf) - { - int Start = 0; - Status = true; - for (int i=0; i<=Len AND Status; i++) - { - if (c8[i] == '\n' OR i >= Len) - { - Status = f.Write(c8 + Start, i - Start) == (i - Start); - if (i < Len) - { - Status = f.Write((char*)"\r\n", 2) == 2; - } - Start = i+1; - } - } - } - else - { - int Len = strlen(c8); - Status = f.Write(c8, Len) == Len; - } - - DeleteArray(c8); - } - - Dirty = false; - return Status; - } - } - return false; -} - -void GTextView3::UpdateScrollBars(bool Reset) -{ - if (VScroll) - { - GRect Before = GetClient(); - - int DisplayLines = Y() / LineY; - int Lines = GetLines(); - VScroll->SetLimits(0, Lines); - if (VScroll) - { - VScroll->SetPage(DisplayLines); - - int Max = Lines - DisplayLines + 1; - bool Inval = false; - if (VScroll->Value() > Max) - { - - VScroll->Value(Max); - Inval = true; - } - - if (Reset) - { - VScroll->Value(0); - SelStart = SelEnd = -1; - } - - GRect After = GetClient(); - - if (Before != After AND GetWrapType()) - { - d->SetDirty(0, Size); - Inval = true; - } - - if (Inval) - { - Invalidate(); - } - } - } -} - -bool GTextView3::DoCase(bool Upper) -{ - if (Text) - { - int Min = min(SelStart, SelEnd); - int Max = max(SelStart, SelEnd); - - if (Min < Max) - { - UndoQue += new GTextView3Undo(this, Text + Min, Max-Min, Min, GTextView3Undo::UndoChange); - - for (int i=Min; i= 'a' AND Text[i] <= 'z') - { - Text[i] = Text[i] - 'a' + 'A'; - } - } - else - { - if (Text[i] >= 'A' AND Text[i] <= 'Z') - { - Text[i] = Text[i] - 'A' + 'a'; - } - } - } - - Dirty = true; - d->SetDirty(Min, 0); - Invalidate(); - - printf("docase GTVN_DOC_CHANGED\n"); - SendNotify(GTVN_DOC_CHANGED); - } - } - - return true; -} - -void GTextView3::GotoLine(int i) -{ - GTextLine *l = Line.ItemAt(i); - if (l) - { - d->CenterCursor = true; - SetCursor(l->Start, false); - d->CenterCursor = false; - } -} - -bool GTextView3::DoGoto() -{ - GInput Dlg(this, "", LgiLoadString(L_TEXTCTRL_GOTO_LINE, "Goto line:"), "Text"); - if (Dlg.DoModal() == IDOK AND - Dlg.Str) - { - GotoLine(atoi(Dlg.Str) - 1); - } - - return true; -} - -GDocFindReplaceParams *GTextView3::CreateFindReplaceParams() -{ - return new GDocFindReplaceParams3; -} - -void GTextView3::SetFindReplaceParams(GDocFindReplaceParams *Params) -{ - if (Params) - { - if (d->OwnFindReplaceParams) - { - DeleteObj(d->FindReplaceParams); - } - - d->OwnFindReplaceParams = false; - d->FindReplaceParams = (GDocFindReplaceParams3*) Params; - } -} - -bool GTextView3::DoFindNext() -{ - if (d->FindReplaceParams->LastFind) - { - return OnFind( d->FindReplaceParams->LastFind, - d->FindReplaceParams->MatchWord, - d->FindReplaceParams->MatchCase, - d->FindReplaceParams->SelectionOnly); - } - - return false; -} - -bool Text_FindCallback(GFindReplaceCommon *Dlg, bool Replace, void *User) -{ - GTextView3 *v = (GTextView3*) User; - - Dlg->MatchWord = v->d->FindReplaceParams->MatchWord; - Dlg->MatchCase = v->d->FindReplaceParams->MatchCase; - - DeleteArray(v->d->FindReplaceParams->LastFind); - v->d->FindReplaceParams->MatchWord = Dlg->MatchWord; - v->d->FindReplaceParams->MatchCase = Dlg->MatchCase; - - v->d->FindReplaceParams->LastFind = LgiNewUtf8To16(Dlg->Find); - - if (v->HasSelection() AND - v->SelEnd < v->SelStart) - { - v->Cursor = v->SelStart; - } - - v->OnFind( v->d->FindReplaceParams->LastFind, - v->d->FindReplaceParams->MatchWord, - v->d->FindReplaceParams->MatchCase, - v->d->FindReplaceParams->SelectionOnly); - - return true; -} - -bool GTextView3::DoFind() -{ - char *u = 0; - if (HasSelection()) - { - int Min = min(SelStart, SelEnd); - int Max = max(SelStart, SelEnd); - - u = LgiNewUtf16To8(Text + Min, (Max - Min) * sizeof(char16)); - } - else - { - u = LgiNewUtf16To8(d->FindReplaceParams->LastFind); - } - - GFindDlg Dlg(this, u, Text_FindCallback, this); - Dlg.DoModal(); - DeleteArray(u); - - Focus(true); - - return false; -} - -bool GTextView3::DoReplace() -{ - char *LastFind8 = HasSelection() ? GetSelection() : LgiNewUtf16To8(d->FindReplaceParams->LastFind); - char *LastReplace8 = LgiNewUtf16To8(d->FindReplaceParams->LastReplace); - - GReplaceDlg Dlg(this, LastFind8, LastReplace8); - - Dlg.MatchWord = d->FindReplaceParams->MatchWord; - Dlg.MatchCase = d->FindReplaceParams->MatchCase; - Dlg.SelectionOnly = d->FindReplaceParams->SelectionOnly; - - int Action = Dlg.DoModal(); - - DeleteArray(LastFind8); - DeleteArray(LastReplace8); - - if (Action != IDCANCEL) - { - DeleteArray(d->FindReplaceParams->LastFind); - d->FindReplaceParams->LastFind = LgiNewUtf8To16(Dlg.Find); - - DeleteArray(d->FindReplaceParams->LastReplace); - d->FindReplaceParams->LastReplace = LgiNewUtf8To16(Dlg.Replace); - - d->FindReplaceParams->MatchWord = Dlg.MatchWord; - d->FindReplaceParams->MatchCase = Dlg.MatchCase; - d->FindReplaceParams->SelectionOnly = Dlg.SelectionOnly; - - printf("DoReplace '%S'->'%S' %i,%i,%i\n", d->FindReplaceParams->LastFind, d->FindReplaceParams->LastReplace, d->FindReplaceParams->MatchWord, d->FindReplaceParams->MatchCase, d->FindReplaceParams->SelectionOnly); - } - - switch (Action) - { - case IDC_FR_FIND: - { - OnFind( d->FindReplaceParams->LastFind, - d->FindReplaceParams->MatchWord, - d->FindReplaceParams->MatchCase, - d->FindReplaceParams->SelectionOnly); - break; - } - case IDOK: - case IDC_FR_REPLACE: - { - OnReplace( d->FindReplaceParams->LastFind, - d->FindReplaceParams->LastReplace, - Action == IDOK, - d->FindReplaceParams->MatchWord, - d->FindReplaceParams->MatchCase, - d->FindReplaceParams->SelectionOnly); - break; - } - } - - return false; -} - -void GTextView3::SelectWord(int From) -{ - for (SelStart = From; SelStart > 0; SelStart--) - { - if (strchr(SelectWordDelim, Text[SelStart])) - { - SelStart++; - break; - } - } - - for (SelEnd = From; SelEnd < Size; SelEnd++) - { - if (strchr(SelectWordDelim, Text[SelEnd])) - { - break; - } - } - - Invalidate(); -} - -int GTextView3::MatchText(char16 *Find, bool MatchWord, bool MatchCase, bool SelectionOnly) -{ - if (ValidStrW(Find)) - { - int FindLen = StrlenW(Find); - - // Setup range to search - int Begin, End; - if (SelectionOnly AND HasSelection()) - { - Begin = min(SelStart, SelEnd); - End = max(SelStart, SelEnd); - } - else - { - Begin = 0; - End = Size; - } - - // Look through text... - int i; - bool Wrap = false; - if (Cursor > End - FindLen) - { - Wrap = true; - i = Begin; - } - else - { - i = Cursor; - } - - if (i < Begin) i = Begin; - if (i > End) i = End; - - if (MatchCase) - { - for (; i<=End-FindLen; i++) - { - if (Text[i] == Find[0]) - { - char16 *Possible = Text + i;; - if (StrncmpW(Possible, Find, FindLen) == 0) - { - if (MatchWord) - { - // Check boundaries - - if (Possible > Text) // Check off the start - { - if (!IsWordBoundry(Possible[-1])) - { - continue; - } - } - if (i + FindLen < Size) // Check off the end - { - if (!IsWordBoundry(Possible[FindLen])) - { - continue; - } - } - } - - return SubtractPtr(Possible, Text); - break; - } - } - - if (!Wrap AND (i + 1 > End - FindLen)) - { - Wrap = true; - i = Begin; - End = Cursor; - } - } - } - else - { - // printf("i=%i s=%i e=%i c=%i flen=%i sz=%i\n", i, Begin, End, Cursor, FindLen, Size); - for (; i<=End-FindLen; i++) - { - if (toupper(Text[i]) == toupper(Find[0])) - { - char16 *Possible = Text + i; - if (StrnicmpW(Possible, Find, FindLen) == 0) - { - if (MatchWord) - { - // Check boundaries - - if (Possible > Text) // Check off the start - { - if (!IsWordBoundry(Possible[-1])) - { - continue; - } - } - if (i + FindLen < Size) // Check off the end - { - if (!IsWordBoundry(Possible[FindLen])) - { - continue; - } - } - } - - return SubtractPtr(Possible, Text); - break; - } - } - - if (!Wrap AND (i + 1 > End - FindLen)) - { - Wrap = true; - i = Begin; - End = Cursor; - } - } - } - } - - return -1; -} - -bool GTextView3::OnFind(char16 *Find, bool MatchWord, bool MatchCase, bool SelectionOnly) -{ - int Loc = MatchText(Find, MatchWord, MatchCase, SelectionOnly); - if (Loc >= 0) - { - SetCursor(Loc, false); - SetCursor(Loc + StrlenW(Find), true); - return true; - } - - return false; -} - -bool GTextView3::OnReplace(char16 *Find, char16 *Replace, bool All, bool MatchWord, bool MatchCase, bool SelectionOnly) -{ - if (ValidStrW(Find)) - { - int Max = -1; - int FindLen = StrlenW(Find); - int ReplaceLen = StrlenW(Replace); - int OldCursor = Cursor; - int First = -1; - - while (true) - { - int Loc = MatchText(Find, MatchWord, MatchCase, SelectionOnly); - if (First < 0) - { - First = Loc; - } - else if (Loc == First) - { - break; - } - - if (Loc >= 0) - { - Delete(Loc, FindLen); - Insert(Loc, Replace, ReplaceLen); - Cursor = Loc + ReplaceLen; - } - if (!All) - { - return Loc >= 0; - } - if (Loc < 0) break; - } - - SetCursor(OldCursor, false); - } - - return false; -} - -int GTextView3::SeekLine(int i, GTextViewSeek Where) -{ - switch (Where) - { - case PrevLine: - { - for (; i > 0 AND Text[i] != '\n'; i--); - if (i > 0) i--; - for (; i > 0 AND Text[i] != '\n'; i--); - if (i > 0) i++; - break; - } - case NextLine: - { - for (; i < Size AND Text[i] != '\n'; i++); - i++; - break; - } - case StartLine: - { - for (; i > 0 AND Text[i] != '\n'; i--); - if (i > 0) i++; - break; - } - case EndLine: - { - for (; i < Size AND Text[i] != '\n'; i++); - break; - } - default: - { - LgiAssert(false); - break; - } - } - - return i; -} - -bool GTextView3::OnMultiLineTab(bool In) +} + +void GTextView3::DeleteSelection(char16 **Cut) +{ + if (SelStart >= 0) + { + int Min = min(SelStart, SelEnd); + int Max = max(SelStart, SelEnd); + + if (Cut) + { + *Cut = NewStrW(Text + Min, Max - Min); + } + + Delete(Min, Max - Min); + SetCursor(Min, false, true); + } +} + +GTextView3::GTextLine *GTextView3::GetLine(int Offset, int *Index) +{ + int i = 0; + for (GTextLine *l=Line.First(); l; l=Line.Next(), i++) + { + if (Offset >= l->Start AND Offset <= l->Start+l->Len) + { + if (Index) + { + *Index = i; + } + + return l; + } + } + + return 0; +} + +int64 GTextView3::Value() +{ + char *n = Name(); + #ifdef _MSC_VER + return (n) ? _atoi64(n) : 0; + #else + return (n) ? atoll(n) : 0; + #endif +} + +void GTextView3::Value(int64 i) +{ + char Str[32]; + sprintf(Str, LGI_PrintfInt64, i); + Name(Str); +} + +char *GTextView3::Name() +{ + UndoQue.Empty(); + DeleteArray(TextCache); + TextCache = LgiNewUtf16To8(Text); + + return TextCache; +} + +bool GTextView3::Name(char *s) +{ + UndoQue.Empty(); + DeleteArray(TextCache); + DeleteArray(Text); + + LgiAssert(LgiIsUtf8(s)); + Text = LgiNewUtf8To16(s); + if (!Text) + { + Text = new char16[1]; + if (Text) *Text = 0; + } + + Size = Text ? StrlenW(Text) : 0; + Alloc = Size + 1; + Cursor = min(Cursor, Size); + if (Text) + { + // Remove '\r's + char16 *o = Text; + for (char16 *i=Text; *i; i++) + { + if (*i != '\r') + { + *o++ = *i; + } + else Size--; + } + *o++ = 0; + } + + // update everything else + PourText(0, Size); + PourStyle(0, Size); + UpdateScrollBars(); + Invalidate(); + + return true; +} + +char16 *GTextView3::NameW() +{ + return Text; +} + +bool GTextView3::NameW(char16 *s) +{ + DeleteArray(Text); + Size = s ? StrlenW(s) : 0; + Alloc = Size + 1; + Text = new char16[Alloc]; + Cursor = min(Cursor, Size); + if (Text) + { + memcpy(Text, s, Size * sizeof(char16)); + + // remove LF's + int In = 0, Out = 0; + CrLf = false; + for (; In= 0) AND (SelStart != SelEnd); +} + +void GTextView3::SelectAll() +{ + SelStart = 0; + SelEnd = Size; + Invalidate(); +} + +void GTextView3::UnSelectAll() +{ + bool Update = HasSelection(); + + SelStart = -1; + SelEnd = -1; + + if (Update) + { + Invalidate(); + } +} + +int GTextView3::GetLines() +{ + return Line.Length(); +} + +void GTextView3::GetTextExtent(int &x, int &y) +{ + PourText(0, Size); + + x = MaxX + d->Margin.x1; + y = Line.Length() * LineY; +} + +void GTextView3::PositionAt(int &x, int &y, int Index) +{ + int FromIndex = 0; + GTextLine *From = GetLine(Index < 0 ? Cursor : Index, &FromIndex); + if (From) + { + x = Cursor - From->Start; + y = FromIndex; + } +} + +int GTextView3::GetCursor(bool Cur) +{ + if (Cur) + { + return Cursor; + } + + return 0; +} + +int GTextView3::IndexAt(int x, int y) +{ + GTextLine *l = Line.ItemAt(y); + if (l) + { + return l->Start + min(x, l->Len); + } + + return 0; +} + +void GTextView3::SetCursor(int i, bool Select, bool ForceFullUpdate) +{ + //int _Start = LgiCurrentTime(); + // LgiTrace("SetCursor(%i)\n", i); + + // Bound the new cursor position to the document + if (i < 0) i = 0; + if (i > Size) i = Size; + + // Store the old selection and cursor + int s = SelStart, e = SelEnd, c = Cursor; + + // If there is going to be a selected area + if (Select AND i != SelStart) + { + // Then set the start + if (SelStart < 0) + { + // We are starting a new selection + SelStart = Cursor; + } + + // And end + SelEnd = i; + } + else + { + // Clear the selection + SelStart = SelEnd = -1; + } + + int FromIndex = 0; + GTextLine *From = GetLine(Cursor, &FromIndex); + + Cursor = i; + + // check the cursor is on the screen + int ToIndex = 0; + GTextLine *To = GetLine(Cursor, &ToIndex); + if (VScroll AND To) + { + GRect Client = GetClient(); + int DisplayLines = Client.Y() / LineY; + + if (ToIndex < VScroll->Value()) + { + // Above the visible region... + if (d->CenterCursor) + { + int i = ToIndex - (DisplayLines >> 1); + VScroll->Value(max(0, i)); + } + else + { + VScroll->Value(ToIndex); + } + ForceFullUpdate = true; + } + + if (ToIndex >= VScroll->Value() + DisplayLines) + { + int YOff = d->CenterCursor ? DisplayLines >> 1 : DisplayLines; + + int v = min(ToIndex - YOff + 1, Line.Length() - DisplayLines); + if (v != VScroll->Value()) + { + // Below the visible region + VScroll->Value(v); + ForceFullUpdate = true; + } + } + } + + // check whether we need to update the screen + if (ForceFullUpdate OR + !To OR + !From) + { + // need full update + Invalidate(); + } + else if ( SelStart != s OR + SelEnd != e) + { + // Update just the selection bounds + GRect Client = GetClient(); + int Start, End; + if (SelStart >= 0 AND s >= 0) + { + // Selection has changed, union the before and after regions + Start = min(Cursor, c); + End = max(Cursor, c); + } + else if (SelStart >= 0) + { + // Selection created... + Start = min(SelStart, SelEnd); + End = max(SelStart, SelEnd); + } + else if (s >= 0) + { + // Selection removed... + Start = min(s, e); + End = max(s, e); + } + + GTextLine *SLine = GetLine(Start); + GTextLine *ELine = GetLine(End); + GRect u; + if (SLine AND ELine) + { + if (SLine->r.Valid()) + { + u = SLine->r; + u.Offset(0, d->Margin.y1-ScrollYPixel()); + } + else + u.Set(0, 0, Client.X()-1, 1); // Start of visible page + + GRect b(0, Client.Y()-1, Client.X()-1, Client.Y()-1); + if (ELine->r.Valid()) + { + b = ELine->r; + b.Offset(0, d->Margin.y1-ScrollYPixel()); + } + else + { + b.Set(0, Client.Y()-1, Client.X()-1, Client.Y()-1); + } + u.Union(&b); + + u.x1 = 0; + u.x2 = X(); + } + else + { + printf("%s,%i - Couldn't get SLine and ELine (%i, %i)\n", _FL, Start, End); + u = Client; + } + + Invalidate(&u); + } + else if (Cursor != c) + { + // just the cursor has moved + + // update the line the cursor moved to + GRect r = To->r; + r.Offset(-ScrollX, d->Margin.y1-DocOffset); + r.x2 = X(); + Invalidate(&r); + + if (To != From) + { + // update the line the cursor came from, + // if it's a different line from the "to" + r = From->r; + r.Offset(-ScrollX, d->Margin.y1-DocOffset); + r.x2 = X(); + Invalidate(&r); + } + } + + if (c != Cursor) + { + // Send off notify + SendNotify(GTVN_CURSOR_CHANGED); + } + +//int _Time = LgiCurrentTime() - _Start; +//printf("Setcursor=%ims\n", _Time); +} + +void GTextView3::SetBorder(int b) +{ +} + +bool GTextView3::Cut() +{ + bool Status = false; + char16 *Txt16 = 0; + + DeleteSelection(&Txt16); + if (Txt16) + { + #ifdef WIN32 + Txt16 = ConvertToCrLf(Txt16); + #endif + char *Txt8 = (char*)LgiNewConvertCp(LgiAnsiToLgiCp(), Txt16, LGI_WideCharset); + + GClipBoard Clip(this); + + Clip.Text(Txt8); + Status = Clip.TextW(Txt16, false); + + DeleteArray(Txt8); + DeleteArray(Txt16); + } + + return Status; +} + +bool GTextView3::Copy() +{ + bool Status = true; + + if (SelStart >= 0) + { + int Min = min(SelStart, SelEnd); + int Max = max(SelStart, SelEnd); + + char16 *Txt16 = NewStrW(Text+Min, Max-Min); + #ifdef WIN32 + Txt16 = ConvertToCrLf(Txt16); + #endif + char *Txt8 = (char*)LgiNewConvertCp(LgiAnsiToLgiCp(), Txt16, LGI_WideCharset); + + GClipBoard Clip(this); + + Clip.Text(Txt8); + Clip.TextW(Txt16, false); + + DeleteArray(Txt8); + DeleteArray(Txt16); + } + + return Status; +} + +bool GTextView3::Paste() +{ + GClipBoard Clip(this); + + char16 *t = Clip.TextW(); + + if (!t) // ala Win9x + { + char *s = Clip.Text(); + if (s) + { + t = LgiNewUtf8To16(s); + DeleteArray(s); + } + } + + if (t) + { + if (SelStart >= 0) + { + DeleteSelection(); + } + + // remove '\r's + bool Multiline = false; + char16 *s = t, *d = t; + for (; *s; s++) + { + if (*s == '\n') + { + Multiline = true; + } + + if (*s != '\r') + { + *d++ = *s; + } + } + *d++ = 0; + + // insert text + int Len = StrlenW(t); + Insert(Cursor, t, Len); + SetCursor(Cursor+Len, false, true); // Multiline + + // clean up + DeleteArray(t); + } + return false; +} + +bool GTextView3::ClearDirty(bool Ask, char *FileName) +{ + if (Dirty) + { + int Answer = (Ask) ? LgiMsg(this, + LgiLoadString(L_TEXTCTRL_ASK_SAVE, "Do you want to save your changes to this document?"), + LgiLoadString(L_TEXTCTRL_SAVE, "Save"), + MB_YESNOCANCEL) : IDYES; + if (Answer == IDYES) + { + GFileSelect Select; + Select.Parent(this); + if (!FileName AND + Select.Save()) + { + FileName = Select.Name(); + } + + Save(FileName); + } + else if (Answer == IDCANCEL) + { + return false; + } + } + + return true; +} + +bool GTextView3::Open(char *Name, char *CharSet) +{ + bool Status = false; + GFile f; + + if (f.Open(Name, O_READ|O_SHARE)) + { + DeleteArray(Text); + int Bytes = f.GetSize(); + SetCursor(0, false); + + char *c8 = new char[Bytes + 4]; + if (c8) + { + if (f.Read(c8, Bytes) == Bytes) + { + char *DataStart = c8; + + c8[Bytes] = 0; + c8[Bytes+1] = 0; + c8[Bytes+2] = 0; + c8[Bytes+3] = 0; + + if ((uchar)c8[0] == 0xff AND (uchar)c8[1] == 0xfe) + { + // utf-16 + if (!CharSet) + { + CharSet = "utf-16"; + DataStart += 2; + } + } + + // Convert to unicode first.... + if (Bytes == 0) + { + Text = new char16[1]; + if (Text) Text[0] = 0; + } + else + { + Text = (char16*)LgiNewConvertCp(LGI_WideCharset, DataStart, CharSet ? CharSet : LgiAnsiToLgiCp()); + } + if (Text) + { + // Remove LF's + char16 *In = Text, *Out = Text; + CrLf = false; + Size = 0; + while (*In) + { + if (*In >= ' ' OR + *In == '\t' OR + *In == '\n') + { + *Out++ = *In; + Size++; + } + else if (*In == '\r') + { + CrLf = true; + } + In++; + } + Size = Out - Text; + *Out = 0; + + Alloc = Size + 1; + Dirty = false; + + if (Text AND Text[0] == 0xfeff) // unicode byte order mark + { + memmove(Text, Text+1, Size * sizeof(*Text)); + Size--; + } + + PourText(0, Size); + PourStyle(0, Size); + UpdateScrollBars(true); + + Status = true; + } + } + + DeleteArray(c8); + } + else + { + Alloc = Size = 0; + } + + Invalidate(); + } + + return Status; +} + +bool GTextView3::Save(char *Name, char *CharSet) +{ + GFile f; + if (f.Open(Name, O_WRITE)) + { + f.SetSize(0); + if (Text) + { + bool Status = false; + + char *c8 = (char*)LgiNewConvertCp(CharSet ? CharSet : LgiAnsiToLgiCp(), Text, LGI_WideCharset, Size * sizeof(char16)); + if (c8) + { + int Len = strlen(c8); + if (CrLf) + { + int Start = 0; + Status = true; + for (int i=0; i<=Len AND Status; i++) + { + if (c8[i] == '\n' OR i >= Len) + { + Status = f.Write(c8 + Start, i - Start) == (i - Start); + if (i < Len) + { + Status = f.Write((char*)"\r\n", 2) == 2; + } + Start = i+1; + } + } + } + else + { + int Len = strlen(c8); + Status = f.Write(c8, Len) == Len; + } + + DeleteArray(c8); + } + + Dirty = false; + return Status; + } + } + return false; +} + +void GTextView3::UpdateScrollBars(bool Reset) +{ + if (VScroll) + { + GRect Before = GetClient(); + + int DisplayLines = Y() / LineY; + int Lines = GetLines(); + VScroll->SetLimits(0, Lines); + if (VScroll) + { + VScroll->SetPage(DisplayLines); + + int Max = Lines - DisplayLines + 1; + bool Inval = false; + if (VScroll->Value() > Max) + { + + VScroll->Value(Max); + Inval = true; + } + + if (Reset) + { + VScroll->Value(0); + SelStart = SelEnd = -1; + } + + GRect After = GetClient(); + + if (Before != After AND GetWrapType()) + { + d->SetDirty(0, Size); + Inval = true; + } + + if (Inval) + { + Invalidate(); + } + } + } +} + +bool GTextView3::DoCase(bool Upper) +{ + if (Text) + { + int Min = min(SelStart, SelEnd); + int Max = max(SelStart, SelEnd); + + if (Min < Max) + { + UndoQue += new GTextView3Undo(this, Text + Min, Max-Min, Min, GTextView3Undo::UndoChange); + + for (int i=Min; i= 'a' AND Text[i] <= 'z') + { + Text[i] = Text[i] - 'a' + 'A'; + } + } + else + { + if (Text[i] >= 'A' AND Text[i] <= 'Z') + { + Text[i] = Text[i] - 'A' + 'a'; + } + } + } + + Dirty = true; + d->SetDirty(Min, 0); + Invalidate(); + + printf("docase GTVN_DOC_CHANGED\n"); + SendNotify(GTVN_DOC_CHANGED); + } + } + + return true; +} + +void GTextView3::GotoLine(int i) +{ + GTextLine *l = Line.ItemAt(i); + if (l) + { + d->CenterCursor = true; + SetCursor(l->Start, false); + d->CenterCursor = false; + } +} + +bool GTextView3::DoGoto() +{ + GInput Dlg(this, "", LgiLoadString(L_TEXTCTRL_GOTO_LINE, "Goto line:"), "Text"); + if (Dlg.DoModal() == IDOK AND + Dlg.Str) + { + GotoLine(atoi(Dlg.Str) - 1); + } + + return true; +} + +GDocFindReplaceParams *GTextView3::CreateFindReplaceParams() +{ + return new GDocFindReplaceParams3; +} + +void GTextView3::SetFindReplaceParams(GDocFindReplaceParams *Params) +{ + if (Params) + { + if (d->OwnFindReplaceParams) + { + DeleteObj(d->FindReplaceParams); + } + + d->OwnFindReplaceParams = false; + d->FindReplaceParams = (GDocFindReplaceParams3*) Params; + } +} + +bool GTextView3::DoFindNext() +{ + if (d->FindReplaceParams->LastFind) + { + return OnFind( d->FindReplaceParams->LastFind, + d->FindReplaceParams->MatchWord, + d->FindReplaceParams->MatchCase, + d->FindReplaceParams->SelectionOnly); + } + + return false; +} + +bool Text_FindCallback(GFindReplaceCommon *Dlg, bool Replace, void *User) +{ + GTextView3 *v = (GTextView3*) User; + + Dlg->MatchWord = v->d->FindReplaceParams->MatchWord; + Dlg->MatchCase = v->d->FindReplaceParams->MatchCase; + + DeleteArray(v->d->FindReplaceParams->LastFind); + v->d->FindReplaceParams->MatchWord = Dlg->MatchWord; + v->d->FindReplaceParams->MatchCase = Dlg->MatchCase; + + v->d->FindReplaceParams->LastFind = LgiNewUtf8To16(Dlg->Find); + + if (v->HasSelection() AND + v->SelEnd < v->SelStart) + { + v->Cursor = v->SelStart; + } + + v->OnFind( v->d->FindReplaceParams->LastFind, + v->d->FindReplaceParams->MatchWord, + v->d->FindReplaceParams->MatchCase, + v->d->FindReplaceParams->SelectionOnly); + + return true; +} + +bool GTextView3::DoFind() +{ + char *u = 0; + if (HasSelection()) + { + int Min = min(SelStart, SelEnd); + int Max = max(SelStart, SelEnd); + + u = LgiNewUtf16To8(Text + Min, (Max - Min) * sizeof(char16)); + } + else + { + u = LgiNewUtf16To8(d->FindReplaceParams->LastFind); + } + + GFindDlg Dlg(this, u, Text_FindCallback, this); + Dlg.DoModal(); + DeleteArray(u); + + Focus(true); + + return false; +} + +bool GTextView3::DoReplace() +{ + char *LastFind8 = HasSelection() ? GetSelection() : LgiNewUtf16To8(d->FindReplaceParams->LastFind); + char *LastReplace8 = LgiNewUtf16To8(d->FindReplaceParams->LastReplace); + + GReplaceDlg Dlg(this, LastFind8, LastReplace8); + + Dlg.MatchWord = d->FindReplaceParams->MatchWord; + Dlg.MatchCase = d->FindReplaceParams->MatchCase; + Dlg.SelectionOnly = d->FindReplaceParams->SelectionOnly; + + int Action = Dlg.DoModal(); + + DeleteArray(LastFind8); + DeleteArray(LastReplace8); + + if (Action != IDCANCEL) + { + DeleteArray(d->FindReplaceParams->LastFind); + d->FindReplaceParams->LastFind = LgiNewUtf8To16(Dlg.Find); + + DeleteArray(d->FindReplaceParams->LastReplace); + d->FindReplaceParams->LastReplace = LgiNewUtf8To16(Dlg.Replace); + + d->FindReplaceParams->MatchWord = Dlg.MatchWord; + d->FindReplaceParams->MatchCase = Dlg.MatchCase; + d->FindReplaceParams->SelectionOnly = Dlg.SelectionOnly; + + printf("DoReplace '%S'->'%S' %i,%i,%i\n", d->FindReplaceParams->LastFind, d->FindReplaceParams->LastReplace, d->FindReplaceParams->MatchWord, d->FindReplaceParams->MatchCase, d->FindReplaceParams->SelectionOnly); + } + + switch (Action) + { + case IDC_FR_FIND: + { + OnFind( d->FindReplaceParams->LastFind, + d->FindReplaceParams->MatchWord, + d->FindReplaceParams->MatchCase, + d->FindReplaceParams->SelectionOnly); + break; + } + case IDOK: + case IDC_FR_REPLACE: + { + OnReplace( d->FindReplaceParams->LastFind, + d->FindReplaceParams->LastReplace, + Action == IDOK, + d->FindReplaceParams->MatchWord, + d->FindReplaceParams->MatchCase, + d->FindReplaceParams->SelectionOnly); + break; + } + } + + return false; +} + +void GTextView3::SelectWord(int From) +{ + for (SelStart = From; SelStart > 0; SelStart--) + { + if (strchr(SelectWordDelim, Text[SelStart])) + { + SelStart++; + break; + } + } + + for (SelEnd = From; SelEnd < Size; SelEnd++) + { + if (strchr(SelectWordDelim, Text[SelEnd])) + { + break; + } + } + + Invalidate(); +} + +int GTextView3::MatchText(char16 *Find, bool MatchWord, bool MatchCase, bool SelectionOnly) +{ + if (ValidStrW(Find)) + { + int FindLen = StrlenW(Find); + + // Setup range to search + int Begin, End; + if (SelectionOnly AND HasSelection()) + { + Begin = min(SelStart, SelEnd); + End = max(SelStart, SelEnd); + } + else + { + Begin = 0; + End = Size; + } + + // Look through text... + int i; + bool Wrap = false; + if (Cursor > End - FindLen) + { + Wrap = true; + i = Begin; + } + else + { + i = Cursor; + } + + if (i < Begin) i = Begin; + if (i > End) i = End; + + if (MatchCase) + { + for (; i<=End-FindLen; i++) + { + if (Text[i] == Find[0]) + { + char16 *Possible = Text + i;; + if (StrncmpW(Possible, Find, FindLen) == 0) + { + if (MatchWord) + { + // Check boundaries + + if (Possible > Text) // Check off the start + { + if (!IsWordBoundry(Possible[-1])) + { + continue; + } + } + if (i + FindLen < Size) // Check off the end + { + if (!IsWordBoundry(Possible[FindLen])) + { + continue; + } + } + } + + return SubtractPtr(Possible, Text); + break; + } + } + + if (!Wrap AND (i + 1 > End - FindLen)) + { + Wrap = true; + i = Begin; + End = Cursor; + } + } + } + else + { + // printf("i=%i s=%i e=%i c=%i flen=%i sz=%i\n", i, Begin, End, Cursor, FindLen, Size); + for (; i<=End-FindLen; i++) + { + if (toupper(Text[i]) == toupper(Find[0])) + { + char16 *Possible = Text + i; + if (StrnicmpW(Possible, Find, FindLen) == 0) + { + if (MatchWord) + { + // Check boundaries + + if (Possible > Text) // Check off the start + { + if (!IsWordBoundry(Possible[-1])) + { + continue; + } + } + if (i + FindLen < Size) // Check off the end + { + if (!IsWordBoundry(Possible[FindLen])) + { + continue; + } + } + } + + return SubtractPtr(Possible, Text); + break; + } + } + + if (!Wrap AND (i + 1 > End - FindLen)) + { + Wrap = true; + i = Begin; + End = Cursor; + } + } + } + } + + return -1; +} + +bool GTextView3::OnFind(char16 *Find, bool MatchWord, bool MatchCase, bool SelectionOnly) +{ + int Loc = MatchText(Find, MatchWord, MatchCase, SelectionOnly); + if (Loc >= 0) + { + SetCursor(Loc, false); + SetCursor(Loc + StrlenW(Find), true); + return true; + } + + return false; +} + +bool GTextView3::OnReplace(char16 *Find, char16 *Replace, bool All, bool MatchWord, bool MatchCase, bool SelectionOnly) +{ + if (ValidStrW(Find)) + { + int Max = -1; + int FindLen = StrlenW(Find); + int ReplaceLen = StrlenW(Replace); + int OldCursor = Cursor; + int First = -1; + + while (true) + { + int Loc = MatchText(Find, MatchWord, MatchCase, SelectionOnly); + if (First < 0) + { + First = Loc; + } + else if (Loc == First) + { + break; + } + + if (Loc >= 0) + { + Delete(Loc, FindLen); + Insert(Loc, Replace, ReplaceLen); + Cursor = Loc + ReplaceLen; + } + if (!All) + { + return Loc >= 0; + } + if (Loc < 0) break; + } + + SetCursor(OldCursor, false); + } + + return false; +} + +int GTextView3::SeekLine(int i, GTextViewSeek Where) +{ + switch (Where) + { + case PrevLine: + { + for (; i > 0 AND Text[i] != '\n'; i--); + if (i > 0) i--; + for (; i > 0 AND Text[i] != '\n'; i--); + if (i > 0) i++; + break; + } + case NextLine: + { + for (; i < Size AND Text[i] != '\n'; i++); + i++; + break; + } + case StartLine: + { + for (; i > 0 AND Text[i] != '\n'; i--); + if (i > 0) i++; + break; + } + case EndLine: + { + for (; i < Size AND Text[i] != '\n'; i++); + break; + } + default: + { + LgiAssert(false); + break; + } + } + + return i; +} + +bool GTextView3::OnMultiLineTab(bool In) { uint64 Time0 = LgiCurrentTime(); - - int Min = min(SelStart, SelEnd); - int Max = max(SelStart, SelEnd); - - GArray Lines; - int i; - for (i=SeekLine(Min, StartLine); i Lines; + int i; + for (i=SeekLine(Min, StartLine); i Spaces; if (!HardTabs) { Spaces.Length(IndentSize); for (int n=0; n Edits; - for (i=Lines.Length()-1; i>=0; i--) - { - if (In) - { - // <- - int n = Lines[i], Space = 0; - for (; Space=0; i--) + { + if (In) + { + // <- + int n = Lines[i], Space = 0; + for (; Space - int Len = Lines[i]; - for (; Text[Len] != '\n' AND Len Lines[i]) - { - if (HardTabs) - { + e.Len = n-Lines[i]; + Max -= e.Len; + } + else + { + // -> + int Len = Lines[i]; + for (; Text[Len] != '\n' AND Len Lines[i]) + { + if (HardTabs) + { EditInfo &e = Edits.New(); e.Txt = Tab; e.At = Lines[i]; e.Len = 1; - Max++; - } - else - { + Max++; + } + else + { EditInfo &e = Edits.New(); e.Txt = &Spaces[0]; e.At = Lines[i]; - e.Len = IndentSize; - Max += IndentSize; - } - } - } + e.Len = IndentSize; + Max += IndentSize; + } + } + } } - bool Status = (In) ? Delete(Edits) : Insert(Edits); - - SelStart = Min; - SelEnd = Cursor = Max; - + bool Status = (In) ? Delete(Edits) : Insert(Edits); + + SelStart = Min; + SelEnd = Cursor = Max; + uint64 Time2 = LgiCurrentTime(); - d->SetDirty(Min, Max-Min); - Invalidate(); - Status = true; + d->SetDirty(Min, Max-Min); + Invalidate(); + Status = true; uint64 Time3 = LgiCurrentTime(); LgiTrace("MultiTab = %i,%i,%i\n", (int)(Time1-Time0), (int)(Time2-Time1), (int)(Time3-Time2) ); - - return Status; -} - -void GTextView3::OnSetHidden(int Hidden) -{ -} - -void GTextView3::OnPosChange() -{ - static bool Processing = false; - - if (!Processing) - { - Processing = true; - GLayout::OnPosChange(); - - GRect c = GetClient(); - bool ScrollYNeeded = c.Y() < (Line.Length() * LineY); - SetScrollBars(false, ScrollYNeeded); - UpdateScrollBars(); - - if (GetWrapType() AND d->PourX != X()) - { - d->PourX = X(); - d->SetDirty(0, Size); - } - Processing = false; - } -} - -void GTextView3::OnCreate() -{ -} - -void GTextView3::OnEscape(GKey &K) -{ -} - -void GTextView3::OnMouseWheel(double l) -{ - if (VScroll) - { - int NewPos = VScroll->Value() + (int) l; - NewPos = limit(NewPos, 0, GetLines()); - VScroll->Value(NewPos); - Invalidate(); - } -} - -void GTextView3::OnFocus(bool f) -{ - Invalidate(); - - SetPulse(f ? 500 : -1); -} - -int GTextView3::HitText(int x, int y) + + return Status; +} + +void GTextView3::OnSetHidden(int Hidden) +{ +} + +void GTextView3::OnPosChange() +{ + static bool Processing = false; + + if (!Processing) + { + Processing = true; + GLayout::OnPosChange(); + + GRect c = GetClient(); + bool ScrollYNeeded = c.Y() < (Line.Length() * LineY); + SetScrollBars(false, ScrollYNeeded); + UpdateScrollBars(); + + if (GetWrapType() AND d->PourX != X()) + { + d->PourX = X(); + d->SetDirty(0, Size); + } + Processing = false; + } +} + +void GTextView3::OnCreate() +{ +} + +void GTextView3::OnEscape(GKey &K) +{ +} + +void GTextView3::OnMouseWheel(double l) +{ + if (VScroll) + { + int NewPos = VScroll->Value() + (int) l; + NewPos = limit(NewPos, 0, GetLines()); + VScroll->Value(NewPos); + Invalidate(); + } +} + +void GTextView3::OnFocus(bool f) +{ + Invalidate(); + + SetPulse(f ? 500 : -1); +} + +int GTextView3::HitText(int x, int y) { if (Text) - { - bool Down = y >= 0; - int64 Y = (VScroll) ? VScroll->Value() : 0; - GTextLine *l = Line.ItemAt(Y); - y += (l) ? l->r.y1 : 0; - - while (l) - { - if (l->r.Overlap(x, y)) - { - // Over a line - int At = x - l->r.x1; - int Char = 0; - - GDisplayString Ds(Font, MapText(Text + l->Start, l->Len), l->Len, 0, d->Margin.x1); - Char = Ds.CharAt(At); - - return l->Start + Char; - } - else if (y >= l->r.y1 AND y <= l->r.y2) - { - // Click horizontally before of after line - if (x < l->r.x1) - { - return l->Start; - } - else if (x > l->r.x2) - { - return l->Start + l->Len; - } - } - - l = (Down) ? Line.Next() : Line.Prev(); - } - - // outside text area - if (Down) - { - l = Line.Last(); - if (l) - { - if (y > l->r.y2) - { - // end of document - return Size; - } - } + { + bool Down = y >= 0; + int64 Y = (VScroll) ? VScroll->Value() : 0; + GTextLine *l = Line.ItemAt(Y); + y += (l) ? l->r.y1 : 0; + + while (l) + { + if (l->r.Overlap(x, y)) + { + // Over a line + int At = x - l->r.x1; + int Char = 0; + + GDisplayString Ds(Font, MapText(Text + l->Start, l->Len), l->Len, 0, d->Margin.x1); + Char = Ds.CharAt(At); + + return l->Start + Char; + } + else if (y >= l->r.y1 AND y <= l->r.y2) + { + // Click horizontally before of after line + if (x < l->r.x1) + { + return l->Start; + } + else if (x > l->r.x2) + { + return l->Start + l->Len; + } + } + + l = (Down) ? Line.Next() : Line.Prev(); + } + + // outside text area + if (Down) + { + l = Line.Last(); + if (l) + { + if (y > l->r.y2) + { + // end of document + return Size; + } + } + } + } + + return 0; +} + +void GTextView3::Undo() +{ + UndoQue.Undo(); +} + +void GTextView3::Redo() +{ + UndoQue.Redo(); +} + +void GTextView3::OnMouseClick(GMouse &m) +{ + bool Processed = false; + + m.x += ScrollX; + + if (m.Down()) + { + if (!m.IsContextMenu()) + { + Focus(true); + + int Hit = HitText(m.x, m.y); + if (Hit >= 0) + { + SetCursor(Hit, m.Shift()); + + GTextStyle *s = HitStyle(Hit); + if (s) + { + Processed = s->OnMouseClick(&m); + } + } + + if (!Processed && m.Double()) + { + d->WordSelectMode = Cursor; + SelectWord(Cursor); + } + else + { + d->WordSelectMode = -1; + } + } +#ifdef XWIN + else if (m.Middle()) + { + uchar *Data = 0; + ulong Len = 0; + /* + Display *Dsp = Handle()->XDisplay(); + Atom Utf8 = XInternAtom(Dsp, "UTF-8", false); + Atom Utf8String = XInternAtom(Dsp, "UTF8_STRING", false); + + if (Handle()->XApp()->GetSelection + ( + Handle()->handle(), + XA_PRIMARY, + Utf8, + Data, + Len + ) OR + Handle()->XApp()->GetSelection + ( + Handle()->handle(), + XA_PRIMARY, + Utf8String, + Data, + Len + )) + { + char16 *s = LgiNewUtf8To16((char*)Data, Len); + XFree(Data); + if (s) + { + int Chars = StrlenW(s); + Insert(Cursor, s, Chars); + DeleteArray(s); + SetCursor(Cursor+Chars, false); + } + } + else + { + printf("%s:%i - GetSelection failed.\n", __FILE__, __LINE__); + } + */ + } +#endif + else + { + GSubMenu *RClick = new GSubMenu; + if (RClick) + { + GAutoString ClipText; + { + GClipBoard Clip(this); + ClipText.Reset(Clip.Text()); + } + + #if LUIS_DEBUG + RClick->AppendItem("Dump Layout", IDM_DUMP, true); + RClick->AppendSeparator(); + #endif + + GTextStyle *s = HitStyle(HitText(m.x, m.y)); + if (s) + { + if (s->OnMenu(RClick)) + { + RClick->AppendSeparator(); + } + } + + RClick->AppendItem(LgiLoadString(L_TEXTCTRL_CUT, "Cut"), IDM_CUT, HasSelection()); + RClick->AppendItem(LgiLoadString(L_TEXTCTRL_COPY, "Copy"), IDM_COPY, HasSelection()); + RClick->AppendItem(LgiLoadString(L_TEXTCTRL_PASTE, "Paste"), IDM_PASTE, ClipText != 0); + RClick->AppendSeparator(); + + RClick->AppendItem(LgiLoadString(L_TEXTCTRL_UNDO, "Undo"), IDM_UNDO, UndoQue.CanUndo()); + RClick->AppendItem(LgiLoadString(L_TEXTCTRL_REDO, "Redo"), IDM_REDO, UndoQue.CanRedo()); + RClick->AppendSeparator(); + + GMenuItem *i = RClick->AppendItem(LgiLoadString(L_TEXTCTRL_FIXED, "Fixed Width Font"), IDM_FIXED, true); + if (i) i->Checked(GetFixedWidthFont()); + + i = RClick->AppendItem(LgiLoadString(L_TEXTCTRL_AUTO_INDENT, "Auto Indent"), IDM_AUTO_INDENT, true); + if (i) i->Checked(AutoIndent); + + i = RClick->AppendItem(LgiLoadString(L_TEXTCTRL_SHOW_WHITESPACE, "Show Whitespace"), IDM_SHOW_WHITE, true); + if (i) i->Checked(ShowWhiteSpace); + + i = RClick->AppendItem(LgiLoadString(L_TEXTCTRL_HARD_TABS, "Hard Tabs"), IDM_HARD_TABS, true); + if (i) i->Checked(HardTabs); + + RClick->AppendItem(LgiLoadString(L_TEXTCTRL_INDENT_SIZE, "Indent Size"), IDM_INDENT_SIZE, true); + RClick->AppendItem(LgiLoadString(L_TEXTCTRL_TAB_SIZE, "Tab Size"), IDM_TAB_SIZE, true); + + if (Environment) + { + RClick->AppendSeparator(); + Environment->AppendItems(RClick); + } + + if (GetMouse(m, true)) + { + int Id = 0; + switch (Id = RClick->Float(this, m.x, m.y)) + { + #if LUIS_DEBUG + case IDM_DUMP: + { + int n=0; + for (GTextLine *l=Line.First(); l; l=Line.Next(), n++) + { + LgiTrace("[%i] %i,%i (%s)\n", n, l->Start, l->Len, l->r.Describe()); + + char *s = LgiNewUtf16To8(Text + l->Start, l->Len * sizeof(char16)); + if (s) + { + LgiTrace("%s\n", s); + DeleteArray(s); + } + } + break; + } + #endif + case IDM_FIXED: + { + SetFixedWidthFont(!GetFixedWidthFont()); + SendNotify(GTVN_FIXED_WIDTH_CHANGED); + break; + } + case IDM_CUT: + { + Cut(); + break; + } + case IDM_COPY: + { + Copy(); + break; + } + case IDM_PASTE: + { + Paste(); + break; + } + case IDM_UNDO: + { + Undo(); + break; + } + case IDM_REDO: + { + Redo(); + break; + } + case IDM_AUTO_INDENT: + { + AutoIndent = !AutoIndent; + break; + } + case IDM_SHOW_WHITE: + { + ShowWhiteSpace = !ShowWhiteSpace; + Invalidate(); + break; + } + case IDM_HARD_TABS: + { + HardTabs = !HardTabs; + break; + } + case IDM_INDENT_SIZE: + { + char s[32]; + sprintf(s, "%i", IndentSize); + GInput i(this, s, "Indent Size:", "Text"); + if (i.DoModal()) + { + IndentSize = atoi(i.Str); + } + break; + } + case IDM_TAB_SIZE: + { + char s[32]; + sprintf(s, "%i", TabSize); + GInput i(this, s, "Tab Size:", "Text"); + if (i.DoModal()) + { + SetTabSize(atoi(i.Str)); + } + break; + } + default: + { + if (s) + { + s->OnMenuClick(Id); + } + + if (Environment) + { + Environment->OnMenu(this, Id, 0); + } + break; + } + } + } + + DeleteObj(RClick); + } + + return; + } + } + + if (!Processed) + { + Capture(m.Down()); + } +} + +int GTextView3::OnHitTest(int x, int y) +{ + #ifdef WIN32 + if (GetClient().Overlap(x, y)) + { + return HTCLIENT; + } + #endif + return GView::OnHitTest(x, y); +} + +void GTextView3::OnMouseMove(GMouse &m) +{ + m.x += ScrollX; + + int Hit = HitText(m.x, m.y); + if (IsCapturing()) + { + if (d->WordSelectMode < 0) + { + SetCursor(Hit, m.Left()); + } + else + { + int Min = Hit < d->WordSelectMode ? Hit : d->WordSelectMode; + int Max = Hit > d->WordSelectMode ? Hit : d->WordSelectMode; + + for (SelStart = Min; SelStart > 0; SelStart--) + { + if (strchr(SelectWordDelim, Text[SelStart])) + { + SelStart++; + break; + } + } + + for (SelEnd = Max; SelEnd < Size; SelEnd++) + { + if (strchr(SelectWordDelim, Text[SelEnd])) + { + break; + } + } + + Cursor = SelEnd; + Invalidate(); + } + } + + #ifdef WIN32 + GRect c = GetClient(); + c.Offset(-c.x1, -c.y1); + if (c.Overlap(m.x, m.y)) + { + GTextStyle *s = HitStyle(Hit); + TCHAR *c = (s) ? s->GetCursor() : 0; + if (!c) c = IDC_IBEAM; + ::SetCursor(LoadCursor(0, MAKEINTRESOURCE(c))); + } + #endif +} + +int GTextView3::GetColumn() +{ + int x = 0; + GTextLine *l = GetLine(Cursor); + if (l) + { + for (int i=l->Start; i= ' ' || k.c16 == 9) + && + k.c16 != 127 + ) + ) + { + if (k.Down()) + { + // letter/number etc + if (SelStart >= 0) + { + bool MultiLine = false; + if (k.c16 == 9) + { + int Min = min(SelStart, SelEnd), Max = max(SelStart, SelEnd); + for (int i=Min; i<=Max; i++) + { + if (Text[i] == '\n') + { + MultiLine = true; + } + } + } + if (MultiLine) + { + if (OnMultiLineTab(k.Shift())) + { + return true; + } + } + else + { + DeleteSelection(); + } + } + + GTextLine *l = GetLine(Cursor); + int Len = (l) ? l->Len : 0; + + if (l && k.c16 == 9 && (!HardTabs || IndentSize != TabSize)) + { + int x = GetColumn(); + int Add = IndentSize - (x % IndentSize); + + if (HardTabs AND ((x + Add) % TabSize) == 0) + { + int Rx = x; + int Remove; + for (Remove = Cursor; Text[Remove - 1] == ' ' AND Rx % TabSize != 0; Remove--, Rx--); + int Chars = Cursor - Remove; + Delete(Remove, Chars); + Insert(Remove, &k.c16, 1); + Cursor = Remove + 1; + + Invalidate(); + } + else + { + char16 *Sp = new char16[Add]; + if (Sp) + { + for (int n=0; nLen : 0; + SetCursor(Cursor + Add, false, Len != NewLen - 1); + } + DeleteArray(Sp); + } + } + } + else + { + char16 In = k.GetChar(); + + if (In AND Insert(Cursor, &In, 1)) + { + l = GetLine(Cursor); + int NewLen = (l) ? l->Len : 0; + SetCursor(Cursor + 1, false, Len != NewLen - 1); + } + } + } + return true; + } + break; + } + case '\r': + { + if (GetReadOnly()) + break; + + if (k.Down()) + { + OnEnter(k); + } + return true; + break; + } + case '\b': + { + if (GetReadOnly()) + break; + + if (k.Down()) + { + if (SelStart >= 0) + { + // delete selection + DeleteSelection(); + } + else + { + char Del = Cursor > 0 ? Text[Cursor-1] : 0; + + if (Del == ' ' AND (!HardTabs OR IndentSize != TabSize)) + { + // Delete soft tab + int x = GetColumn(); + int Max = x % IndentSize; + if (Max == 0) Max = IndentSize; + int i; + for (i=Cursor-1; i>=0; i--) + { + if (Max-- <= 0 OR Text[i] != ' ') + { + i++; + break; + } + } + + if (i < 0) i = 0; + + if (i < Cursor - 1) + { + int Del = Cursor - i; + Delete(i, Del); + // SetCursor(i, false, false); + // Invalidate(); + break; + } + } + else if (Del == '\t' AND HardTabs AND IndentSize != TabSize) + { + int x = GetColumn(); + Delete(--Cursor, 1); + + for (int c=GetColumn(); c 0) + { + Delete(Cursor - 1, 1); + } + } + } + return true; + break; + } } - } - - return 0; -} - -void GTextView3::Undo() -{ - UndoQue.Undo(); -} - -void GTextView3::Redo() -{ - UndoQue.Redo(); -} - -void GTextView3::OnMouseClick(GMouse &m) -{ - bool Processed = false; - - m.x += ScrollX; - - if (m.Down()) - { - if (!m.IsContextMenu()) - { - Focus(true); - - int Hit = HitText(m.x, m.y); - if (Hit >= 0) - { - SetCursor(Hit, m.Shift()); - - GTextStyle *s = HitStyle(Hit); - if (s) - { - Processed = s->OnMouseClick(&m); - } - } - - if (!Processed && m.Double()) - { - d->WordSelectMode = Cursor; - SelectWord(Cursor); - } - else - { - d->WordSelectMode = -1; - } - } -#ifdef XWIN - else if (m.Middle()) - { - uchar *Data = 0; - ulong Len = 0; - /* - Display *Dsp = Handle()->XDisplay(); - Atom Utf8 = XInternAtom(Dsp, "UTF-8", false); - Atom Utf8String = XInternAtom(Dsp, "UTF8_STRING", false); - - if (Handle()->XApp()->GetSelection - ( - Handle()->handle(), - XA_PRIMARY, - Utf8, - Data, - Len - ) OR - Handle()->XApp()->GetSelection - ( - Handle()->handle(), - XA_PRIMARY, - Utf8String, - Data, - Len - )) - { - char16 *s = LgiNewUtf8To16((char*)Data, Len); - XFree(Data); - if (s) - { - int Chars = StrlenW(s); - Insert(Cursor, s, Chars); - DeleteArray(s); - SetCursor(Cursor+Chars, false); - } - } - else - { - printf("%s:%i - GetSelection failed.\n", __FILE__, __LINE__); - } - */ - } -#endif - else - { - GSubMenu *RClick = new GSubMenu; - if (RClick) - { - GAutoString ClipText; - { - GClipBoard Clip(this); - ClipText.Reset(Clip.Text()); - } - - #if LUIS_DEBUG - RClick->AppendItem("Dump Layout", IDM_DUMP, true); - RClick->AppendSeparator(); - #endif - - GTextStyle *s = HitStyle(HitText(m.x, m.y)); - if (s) - { - if (s->OnMenu(RClick)) - { - RClick->AppendSeparator(); - } - } - - RClick->AppendItem(LgiLoadString(L_TEXTCTRL_CUT, "Cut"), IDM_CUT, HasSelection()); - RClick->AppendItem(LgiLoadString(L_TEXTCTRL_COPY, "Copy"), IDM_COPY, HasSelection()); - RClick->AppendItem(LgiLoadString(L_TEXTCTRL_PASTE, "Paste"), IDM_PASTE, ClipText != 0); - RClick->AppendSeparator(); - - RClick->AppendItem(LgiLoadString(L_TEXTCTRL_UNDO, "Undo"), IDM_UNDO, UndoQue.CanUndo()); - RClick->AppendItem(LgiLoadString(L_TEXTCTRL_REDO, "Redo"), IDM_REDO, UndoQue.CanRedo()); - RClick->AppendSeparator(); - - GMenuItem *i = RClick->AppendItem(LgiLoadString(L_TEXTCTRL_FIXED, "Fixed Width Font"), IDM_FIXED, true); - if (i) i->Checked(GetFixedWidthFont()); - - i = RClick->AppendItem(LgiLoadString(L_TEXTCTRL_AUTO_INDENT, "Auto Indent"), IDM_AUTO_INDENT, true); - if (i) i->Checked(AutoIndent); - - i = RClick->AppendItem(LgiLoadString(L_TEXTCTRL_SHOW_WHITESPACE, "Show Whitespace"), IDM_SHOW_WHITE, true); - if (i) i->Checked(ShowWhiteSpace); - - i = RClick->AppendItem(LgiLoadString(L_TEXTCTRL_HARD_TABS, "Hard Tabs"), IDM_HARD_TABS, true); - if (i) i->Checked(HardTabs); - - RClick->AppendItem(LgiLoadString(L_TEXTCTRL_INDENT_SIZE, "Indent Size"), IDM_INDENT_SIZE, true); - RClick->AppendItem(LgiLoadString(L_TEXTCTRL_TAB_SIZE, "Tab Size"), IDM_TAB_SIZE, true); - - if (Environment) - { - RClick->AppendSeparator(); - Environment->AppendItems(RClick); - } - - if (GetMouse(m, true)) - { - int Id = 0; - switch (Id = RClick->Float(this, m.x, m.y)) - { - #if LUIS_DEBUG - case IDM_DUMP: - { - int n=0; - for (GTextLine *l=Line.First(); l; l=Line.Next(), n++) - { - LgiTrace("[%i] %i,%i (%s)\n", n, l->Start, l->Len, l->r.Describe()); - - char *s = LgiNewUtf16To8(Text + l->Start, l->Len * sizeof(char16)); - if (s) - { - LgiTrace("%s\n", s); - DeleteArray(s); - } - } - break; - } - #endif - case IDM_FIXED: - { - SetFixedWidthFont(!GetFixedWidthFont()); - SendNotify(GTVN_FIXED_WIDTH_CHANGED); - break; - } - case IDM_CUT: - { - Cut(); - break; - } - case IDM_COPY: - { - Copy(); - break; - } - case IDM_PASTE: - { - Paste(); - break; - } - case IDM_UNDO: - { - Undo(); - break; - } - case IDM_REDO: - { - Redo(); - break; - } - case IDM_AUTO_INDENT: - { - AutoIndent = !AutoIndent; - break; - } - case IDM_SHOW_WHITE: - { - ShowWhiteSpace = !ShowWhiteSpace; - Invalidate(); - break; - } - case IDM_HARD_TABS: - { - HardTabs = !HardTabs; - break; - } - case IDM_INDENT_SIZE: - { - char s[32]; - sprintf(s, "%i", IndentSize); - GInput i(this, s, "Indent Size:", "Text"); - if (i.DoModal()) - { - IndentSize = atoi(i.Str); - } - break; - } - case IDM_TAB_SIZE: - { - char s[32]; - sprintf(s, "%i", TabSize); - GInput i(this, s, "Tab Size:", "Text"); - if (i.DoModal()) - { - SetTabSize(atoi(i.Str)); - } - break; - } - default: - { - if (s) - { - s->OnMenuClick(Id); - } - - if (Environment) - { - Environment->OnMenu(this, Id, 0); - } - break; - } - } - } - - DeleteObj(RClick); - } - - return; - } - } - - if (!Processed) - { - Capture(m.Down()); - } -} - -int GTextView3::OnHitTest(int x, int y) -{ - #ifdef WIN32 - if (GetClient().Overlap(x, y)) - { - return HTCLIENT; - } - #endif - return GView::OnHitTest(x, y); -} - -void GTextView3::OnMouseMove(GMouse &m) -{ - m.x += ScrollX; - - int Hit = HitText(m.x, m.y); - if (IsCapturing()) - { - if (d->WordSelectMode < 0) - { - SetCursor(Hit, m.Left()); - } - else - { - int Min = Hit < d->WordSelectMode ? Hit : d->WordSelectMode; - int Max = Hit > d->WordSelectMode ? Hit : d->WordSelectMode; - - for (SelStart = Min; SelStart > 0; SelStart--) - { - if (strchr(SelectWordDelim, Text[SelStart])) - { - SelStart++; - break; - } - } - - for (SelEnd = Max; SelEnd < Size; SelEnd++) - { - if (strchr(SelectWordDelim, Text[SelEnd])) - { - break; - } - } - - Cursor = SelEnd; - Invalidate(); - } - } - - #ifdef WIN32 - GRect c = GetClient(); - c.Offset(-c.x1, -c.y1); - if (c.Overlap(m.x, m.y)) - { - GTextStyle *s = HitStyle(Hit); - TCHAR *c = (s) ? s->GetCursor() : 0; - if (!c) c = IDC_IBEAM; - ::SetCursor(LoadCursor(0, MAKEINTRESOURCE(c))); - } - #endif -} - -int GTextView3::GetColumn() -{ - int x = 0; - GTextLine *l = GetLine(Cursor); - if (l) - { - for (int i=l->Start; i= ' ' || k.c16 == 9) - && - k.c16 != 127 - ) - ) - { - if (k.Down()) - { - // letter/number etc - if (SelStart >= 0) - { - bool MultiLine = false; - if (k.c16 == 9) - { - int Min = min(SelStart, SelEnd), Max = max(SelStart, SelEnd); - for (int i=Min; i<=Max; i++) - { - if (Text[i] == '\n') - { - MultiLine = true; - } - } - } - if (MultiLine) - { - if (OnMultiLineTab(k.Shift())) - { - return true; - } - } - else - { - DeleteSelection(); - } - } - - GTextLine *l = GetLine(Cursor); - int Len = (l) ? l->Len : 0; - - if (l && k.c16 == 9 && (!HardTabs || IndentSize != TabSize)) - { - int x = GetColumn(); - int Add = IndentSize - (x % IndentSize); - - if (HardTabs AND ((x + Add) % TabSize) == 0) - { - int Rx = x; - int Remove; - for (Remove = Cursor; Text[Remove - 1] == ' ' AND Rx % TabSize != 0; Remove--, Rx--); - int Chars = Cursor - Remove; - Delete(Remove, Chars); - Insert(Remove, &k.c16, 1); - Cursor = Remove + 1; - - Invalidate(); - } - else - { - char16 *Sp = new char16[Add]; - if (Sp) - { - for (int n=0; nLen : 0; - SetCursor(Cursor + Add, false, Len != NewLen - 1); - } - DeleteArray(Sp); - } - } - } - else - { - char16 In = k.GetChar(); - - if (In AND Insert(Cursor, &In, 1)) - { - l = GetLine(Cursor); - int NewLen = (l) ? l->Len : 0; - SetCursor(Cursor + 1, false, Len != NewLen - 1); - } - } - } - return true; - } - break; - } - case '\r': - { - if (GetReadOnly()) - break; - - if (k.Down()) - { - OnEnter(k); - } - return true; - break; - } - case '\b': - { - if (GetReadOnly()) - break; - - if (k.Down()) - { - if (SelStart >= 0) - { - // delete selection - DeleteSelection(); - } - else - { - char Del = Cursor > 0 ? Text[Cursor-1] : 0; - - if (Del == ' ' AND (!HardTabs OR IndentSize != TabSize)) - { - // Delete soft tab - int x = GetColumn(); - int Max = x % IndentSize; - if (Max == 0) Max = IndentSize; - int i; - for (i=Cursor-1; i>=0; i--) - { - if (Max-- <= 0 OR Text[i] != ' ') - { - i++; - break; - } - } - - if (i < 0) i = 0; - - if (i < Cursor - 1) - { - int Del = Cursor - i; - Delete(i, Del); - // SetCursor(i, false, false); - // Invalidate(); - break; - } - } - else if (Del == '\t' AND HardTabs AND IndentSize != TabSize) - { - int x = GetColumn(); - Delete(--Cursor, 1); - - for (int c=GetColumn(); c 0) - { - Delete(Cursor - 1, 1); - } - } - } - return true; - break; - } - } - } - else // not a char - { - switch (k.vkey) - { - case VK_TAB: - return true; - case VK_RETURN: - { - return !GetReadOnly(); - } - case VK_BACKSPACE: - { - if (!GetReadOnly()) - { - if (k.Alt()) - { - if (k.Down()) - { - if (k.Ctrl()) - { - Redo(); - } - else - { - Undo(); - } - } - } - else if (k.Ctrl()) - { - if (k.Down()) - { - int Start = Cursor; - while (IsWhiteSpace(Text[Cursor-1]) AND Cursor > 0) - Cursor--; - - while (!IsWhiteSpace(Text[Cursor-1]) AND Cursor > 0) - Cursor--; - - Delete(Cursor, Start - Cursor); - Invalidate(); - } - } - - return true; - } - break; - } - case VK_F3: - { - if (k.Down()) - { - DoFindNext(); - } - return true; - break; - } - case VK_LEFT: - { - if (k.Down()) - { - if (SelStart >= 0 AND - !k.Shift()) - { - SetCursor(min(SelStart, SelEnd), false); - } - else if (Cursor > 0) - { - int n = Cursor; - - #ifdef MAC - if (k.System()) - { - goto Jump_StartOfLine; - } - else - #endif - if (k.Ctrl()) - { - // word move/select - bool StartWhiteSpace = IsWhiteSpace(Text[n]); - bool LeftWhiteSpace = n > 0 AND IsWhiteSpace(Text[n-1]); - - if (!StartWhiteSpace OR - Text[n] == '\n') - { - n--; - } - - // Skip ws - for (; n > 0 AND strchr(" \t", Text[n]); n--) - ; - - if (Text[n] == '\n') - { - n--; - } - else if (!StartWhiteSpace OR !LeftWhiteSpace) - { - if (IsDelimiter(Text[n])) - { - for (; n > 0 AND IsDelimiter(Text[n]); n--); - } - else - { - for (; n > 0; n--) - { - //IsWordBoundry(Text[n]) - if (IsWhiteSpace(Text[n]) OR - IsDelimiter(Text[n])) - { - break; - } - } - } - } - if (n > 0) n++; - } - else - { - // single char - n--; - } - - SetCursor(n, k.Shift()); - } - } - return true; - break; - } - case VK_RIGHT: - { - if (k.Down()) - { - if (SelStart >= 0 AND - !k.Shift()) - { - SetCursor(max(SelStart, SelEnd), false); - } - else if (Cursor < Size) - { - int n = Cursor; - - #ifdef MAC - if (k.System()) - { - goto Jump_EndOfLine; - } - else - #endif - if (k.Ctrl()) - { - // word move/select - if (IsWhiteSpace(Text[n])) - { - for (; nStart, Cursor-l->Start); - int ScreenX = ds.X(); - int CharX = ds.CharAt(ScreenX); - - SetCursor(Prev->Start + min(CharX, Prev->Len), k.Shift()); - } - } - } - return true; - break; - } - case VK_DOWN: - { - if (k.Down()) - { - #ifdef MAC - if (k.Ctrl()) - { - goto GTextView3_PageDown; - } - #endif - - int Old = Cursor; - GTextLine *l = GetLine(Cursor); - if (l) - { - GTextLine *Next = Line.Next(); - if (Next) - { - GDisplayString ds(Font, Text + l->Start, Cursor-l->Start); - int ScreenX = ds.X(); - int CharX = ds.CharAt(ScreenX); - - SetCursor(Next->Start + min(CharX, Next->Len), k.Shift()); - } - } - } - return true; - break; - } - case VK_END: - { - if (k.Down()) - { - if (k.Ctrl()) - { - SetCursor(Size, k.Shift()); - } - else - { - Jump_EndOfLine: - GTextLine *l = GetLine(Cursor); - if (l) - { - SetCursor(l->Start + l->Len, k.Shift()); - } - } - } - return true; - break; - } - case VK_HOME: - { - if (k.Down()) - { - if (k.Ctrl()) - { - SetCursor(0, k.Shift()); - } - else - { - Jump_StartOfLine: - GTextLine *l = GetLine(Cursor); - if (l) - { - char16 *Line = Text + l->Start; - char16 *s; - char16 SpTab[] = {' ', '\t', 0}; - for (s = Line; (SubtractPtr(s,Line) < l->Len) AND StrchrW(SpTab, *s); s++); - int Whitespace = SubtractPtr(s, Line); - - if (l->Start + Whitespace == Cursor) - { - SetCursor(l->Start, k.Shift()); - } - else - { - SetCursor(l->Start + Whitespace, k.Shift()); - } - } - } - } - return true; - break; - } - case VK_PAGEUP: - { - GTextView3_PageUp: - if (k.Down()) - { - GTextLine *l = GetLine(Cursor); - if (l) - { - int DisplayLines = Y() / LineY; - int CurLine = Line.IndexOf(l); - - GTextLine *New = Line.ItemAt(max(CurLine - DisplayLines, 0)); - if (New) - { - SetCursor(New->Start + min(Cursor - l->Start, New->Len), k.Shift()); - } - } - } - return true; - break; - } - case VK_PAGEDOWN: - { - GTextView3_PageDown: - if (k.Down()) - { - GTextLine *l = GetLine(Cursor); - if (l) - { - int DisplayLines = Y() / LineY; - int CurLine = Line.IndexOf(l); - - GTextLine *New = Line.ItemAt(min(CurLine + DisplayLines, GetLines()-1)); - if (New) - { - SetCursor(New->Start + min(Cursor - l->Start, New->Len), k.Shift()); - } - } - } - return true; - break; - } - case VK_INSERT: - { - if (k.Down()) - { - if (k.Ctrl()) - { - Copy(); - } - else if (k.Shift()) - { - if (!GetReadOnly()) - { - Paste(); - } - } - } - return true; - break; - } - case VK_DELETE: - { - if (!GetReadOnly()) - { - if (k.Down()) - { - if (SelStart >= 0) - { - if (k.Shift()) - { - Cut(); - } - else - { - DeleteSelection(); - } - } - else if (Cursor < Size AND - Delete(Cursor, 1)) - { - Invalidate(); - } - } - return true; - } - break; - } - default: - { - if (k.c16 == 17) break; - - if (k.Modifier() AND - !k.Alt()) - { - switch (k.GetChar()) - { - case 0xbd: // Ctrl+'-' - { - if (k.Down() AND - Font->PointSize() > 1) - { - Font->PointSize(Font->PointSize() - 1); - OnFontChange(); - Invalidate(); - } - break; - } - case 0xbb: // Ctrl+'+' - { - if (k.Down() AND - Font->PointSize() < 100) - { - Font->PointSize(Font->PointSize() + 1); - OnFontChange(); - Invalidate(); - } - break; - } - case 'a': - case 'A': - { - if (k.Down()) - { - // select all - SelStart = 0; - SelEnd = Size; - Invalidate(); - } - return true; - break; - } - case 'y': - case 'Y': - { - if (!GetReadOnly()) - { - if (k.Down()) - { - Redo(); - } - return true; - } - break; - } - case 'z': - case 'Z': - { - if (!GetReadOnly()) - { - if (k.Down()) - { - if (k.Shift()) - { - Redo(); - } - else - { - Undo(); - } - } - return true; - } - break; - } - case 'x': - case 'X': - { - if (!GetReadOnly()) - { - if (k.Down()) - { - Cut(); - } - return true; - } - break; - } - case 'c': - case 'C': - { - if (k.Down()) - { - Copy(); - } - return true; - break; - } - case 'v': - case 'V': - { - if (!k.Shift() AND - !GetReadOnly()) - { - if (k.Down()) - { - Paste(); - } - return true; - } - break; - } - case 'f': - { - if (k.Down()) - { - DoFind(); - } - return true; - break; - } - case 'g': - case 'G': - { - if (k.Down()) - { - DoGoto(); - } - return true; - break; - } - case 'h': - case 'H': - { - if (k.Down()) - { - DoReplace(); - } - return true; - break; - } - case 'u': - case 'U': - { - if (!GetReadOnly()) - { - if (k.Down()) - { - DoCase(k.Shift()); - } - return true; - } - break; - } - case VK_RETURN: - { - if (!GetReadOnly() AND !k.Shift()) - { - if (k.Down()) - { - OnEnter(k); - } - return true; - } - break; - } - } - } - break; - } - } - } - - return false; -} - -void GTextView3::OnEnter(GKey &k) -{ - // enter - if (SelStart >= 0) - { - DeleteSelection(); - } - - char16 InsertStr[256] = {'\n', 0}; - - GTextLine *CurLine = GetLine(Cursor); - if (CurLine AND AutoIndent) - { - int WsLen = 0; - for (; WsLen < CurLine->Len AND - WsLen < (Cursor - CurLine->Start) AND - strchr(" \t", Text[CurLine->Start + WsLen]); WsLen++); - if (WsLen > 0) - { - memcpy(InsertStr+1, Text+CurLine->Start, WsLen * sizeof(char16)); - InsertStr[WsLen+1] = 0; - } - } - - if (Insert(Cursor, InsertStr, StrlenW(InsertStr))) - { - SetCursor(Cursor + StrlenW(InsertStr), false, true); - } -} - -int GTextView3::TextWidth(GFont *f, char16 *s, int Len, int x, int Origin) -{ - int w = x; - int Size = f->TabSize(); - - for (char16 *c = s; SubtractPtr(c, s) < Len; ) - { - if (*c == 9) - { - w = ((((w-Origin) + Size) / Size) * Size) + Origin; - c++; - } - else - { - char16 *e; - for (e = c; SubtractPtr(e, s) < Len AND *e != 9; e++); - - GDisplayString ds(f, c, SubtractPtr(e, c)); - w += ds.X(); - c = e; - } - } - - return w - x; -} - -int GTextView3::ScrollYLine() -{ - return (VScroll) ? VScroll->Value() : 0; -} - -int GTextView3::ScrollYPixel() -{ - return ScrollYLine() * LineY; -} - -void GTextView3::OnPaint(GSurface *pDC) -{ - #if LGI_EXCEPTIONS - try - { - #endif - // int StartTime = LgiCurrentTime(); - - if (d->LayoutDirty) - { - PourText(d->DirtyStart, d->DirtyLen); - } - - GRect r = GetClient(); - r.x2 += ScrollX; - - int Ox, Oy; - pDC->GetOrigin(Ox, Oy); - pDC->SetOrigin(Ox+ScrollX, Oy); - - #if 0 - // Coverage testing... - pDC->Colour(Rgb24(255, 0, 255), 24); - pDC->Rectangle(); - #endif - - GSurface *pOut = pDC; - bool DrawSel = false; - COLOUR Fore = LC_TEXT; - COLOUR Selected = LC_SELECTION; - GViewFill *BackFill = GetBackgroundFill(); - COLOUR Back = (!ReadOnly) ? (BackFill ? Rgb32To24(BackFill->GetC32()) : LC_WORKSPACE) : BackColour; - - bool HasFocus = Focus(); - if (!HasFocus) - { - COLOUR Work = LC_WORKSPACE; - Selected = Rgb24( (R24(Work)+R24(Selected))/2, - (G24(Work)+G24(Selected))/2, - (B24(Work)+B24(Selected))/2); - } - - if (!Enabled()) - { - Fore = LC_LOW; - Back = LC_MED; - } - - #ifdef DOUBLE_BUFFER_PAINT - GMemDC *pMem = new GMemDC; - pOut = pMem; - #endif - if (Text AND - Font - #ifdef DOUBLE_BUFFER_PAINT - AND - pMem AND - pMem->Create(r.X()-d->Margin.x1, LineY, GdcD->GetBits()) - #endif - ) - { - int SelMin = min(SelStart, SelEnd); - int SelMax = max(SelStart, SelEnd); - - // font properties - Font->Colour(Fore, Back); - Font->Transparent(false); - - // draw margins - pDC->Colour(PAINT_BORDER, 24); - // top margin - pDC->Rectangle(0, 0, r.x2, d->Margin.y1-1); - // left margin - pDC->Rectangle(0, d->Margin.y1, d->Margin.x1-1, r.y2); - - // draw lines of text - int k = ScrollYLine(); - GTextLine *l=Line.ItemAt(k); - int Dy = (l) ? -l->r.y1 : 0; - int NextSelection = (SelStart != SelEnd) ? SelMin : -1; // offset where selection next changes - if (l AND - SelStart >= 0 AND - SelStart < l->Start AND - SelEnd > l->Start) - { - // start of visible area is in selection - // init to selection colour - DrawSel = true; - Font->Colour(LC_SEL_TEXT, Selected); - NextSelection = SelMax; - } - - GTextStyle *NextStyle = GetNextStyle((l) ? l->Start : 0); - - DocOffset = (l) ? l->r.y1 : 0; - - // loop through all visible lines - int y = d->Margin.y1; - for (; l AND l->r.y1+Dy < r.Y(); l=Line.Next()) - { - GRect Tr = l->r; - #ifdef DOUBLE_BUFFER_PAINT - Tr.Offset(-Tr.x1, -Tr.y1); - #else - Tr.Offset(0, y - Tr.y1); - #endif - - // deal with selection change on beginning of line - if (NextSelection == l->Start) - { - // selection change - DrawSel = !DrawSel; - NextSelection = (NextSelection == SelMin) ? SelMax : -1; - } - if (DrawSel) - { - Font->Colour(LC_SEL_TEXT, Selected); - } - else - { - Font->Colour((l->Col&0x80000000)?Fore:l->Col, Back); - } - - // draw text - int Done = 0; // how many chars on this line have we - // processed so far - int TextX = 0; // pixels we have moved so far - - // loop through all sections of similar text on a line - while (Done < l->Len) - { - // decide how big this block is - int RtlTrailingSpace = 0; - int Cur = l->Start + Done; - int Block = l->Len - Done; - - // check for style change - if (NextStyle) - { - // start - if (l->Overlap(NextStyle->Start) AND - NextStyle->Start > Cur AND - NextStyle->Start - Cur < Block) - { - Block = NextStyle->Start - Cur; - } - - // end - int StyleEnd = NextStyle->Start + NextStyle->Len; - if (l->Overlap(StyleEnd) AND - StyleEnd > Cur AND - StyleEnd - Cur < Block) - { - Block = StyleEnd - Cur; - } - } - - // check for next selection change - // this may truncate the style - if (NextSelection > Cur AND - NextSelection - Cur < Block) - { - Block = NextSelection - Cur; - } - - LgiAssert(Block != 0); // sanity check - - int TabOri = Tr.x1 - d->Margin.x1; - - if (NextStyle AND // There is a style - (Cur < SelMin OR Cur >= SelMax) AND // AND we're not drawing a selection block - Cur >= NextStyle->Start AND // AND we're inside the styled area - Cur < NextStyle->Start+NextStyle->Len) - { - if (NextStyle->Font) - { - // draw styled text - NextStyle->Font->Colour(NextStyle->c, Back); - NextStyle->Font->Transparent(false); - - LgiAssert(l->Start + Done >= 0); - - GDisplayString Ds( NextStyle->Font, - MapText(Text + (l->Start + Done), - Block, - RtlTrailingSpace), - Block + RtlTrailingSpace, - 0, - TabOri); - - TextX = Ds.X(); - Ds.Draw(pOut, Tr.x1, Tr.y1, 0); - - if (NextStyle->Style == GTextStyle::DecorSquiggle) - { - pOut->Colour(NextStyle->DecorColour, 24); - for (int i=0; iSet(Tr.x1+i, Tr.y2-(i%2)); - } - - NextStyle->Font->Colour(Fore, Back); - } - } - else - { - // draw a block of normal text - LgiAssert(l->Start + Done >= 0); - - GDisplayString Ds( Font, - MapText(Text + (l->Start + Done), - Block, - RtlTrailingSpace), - Block + RtlTrailingSpace, - 0, - TabOri); - TextX = Ds.X(); - - Ds.Draw(pOut, Tr.x1, Tr.y1, 0); - } - - if (NextStyle AND - Cur+Block >= NextStyle->Start+NextStyle->Len) - { - // end of this styled block - NextStyle = GetNextStyle(); - } - - if (NextSelection == Cur+Block) - { - // selection change - DrawSel = !DrawSel; - if (DrawSel) - { - Font->Colour(LC_SEL_TEXT, Selected); - } - else - { - Font->Colour((l->Col&0x80000000)?Fore:l->Col, Back); - } - NextSelection = (NextSelection == SelMin) ? SelMax : -1; - } - - Tr.x1 += TextX; - Done += Block + RtlTrailingSpace; - - } // end block loop - - // eol processing - int EndOfLine = l->Start+l->Len; - if (EndOfLine >= SelMin AND EndOfLine < SelMax) - { - // draw the '\n' at the end of the line as selected - pOut->Colour(Font->Back(), 24); - pOut->Rectangle(Tr.x2, Tr.y1, Tr.x2+7, Tr.y2); - Tr.x2 += 7; - } - else Tr.x2 = Tr.x1; - - // draw any space after text - pOut->Colour(PAINT_AFTER_LINE, 24); - pOut->Rectangle(Tr.x2, Tr.y1, r.x2, Tr.y2); - - // cursor? - if (Focus()) - { - // draw the cursor if on this line - if (Cursor >= l->Start AND Cursor <= l->Start+l->Len) - { - CursorPos.ZOff(1, LineY-1); - - int At = Cursor-l->Start; - - GDisplayString Ds(Font, MapText(Text+l->Start, At), At); - int CursorX = Ds.X(); - CursorPos.Offset(d->Margin.x1 + CursorX, Tr.y1); - - if (CanScrollX) - { - // Cursor on screen check - GRect Scr = GetClient(); - Scr.Offset(ScrollX, 0); - GRect Cur = CursorPos; - if (Cur.x2 > Scr.x2 - 5) // right edge check - { - ScrollX = ScrollX + Cur.x2 - Scr.x2 + 40; - Invalidate(); - } - else if (Cur.x1 < Scr.x1 AND ScrollX > 0) - { - ScrollX = max(0, Cur.x1 - 40); - Invalidate(); - } - } - - if (Blink) - { - GRect c = CursorPos; - #ifdef DOUBLE_BUFFER_PAINT - c.Offset(-d->Margin.x1, -y); - #endif - - pOut->Colour((!ReadOnly) ? Fore : Rgb24(192, 192, 192), 24); - pOut->Rectangle(&c); - } - - #if WIN32NATIVE - HIMC hIMC = ImmGetContext(Handle()); - if (hIMC) - { - COMPOSITIONFORM Cf; - Cf.dwStyle = CFS_POINT; - Cf.ptCurrentPos.x = CursorPos.x1; - Cf.ptCurrentPos.y = CursorPos.y1; - ImmSetCompositionWindow(hIMC, &Cf); - ImmReleaseContext(Handle(), hIMC); - } - #endif - } - } - - #ifdef DOUBLE_BUFFER_PAINT - // dump to screen - pDC->Blt(d->Margin.x1, y, pOut); - #endif - y += LineY; - - } // end of line loop - - // draw any space under the lines - if (y <= r.y2) - { - // printf("White %i, k=%i Lines=%i\n", r.y2 - y, k, Line.Length()); - - pDC->Colour(Back, 24); - pDC->Rectangle(d->Margin.x1, y, r.x2, r.y2); - } - - #ifdef DOUBLE_BUFFER_PAINT - DeleteObj(pMem); - #endif - } - else - { - // default drawing: nothing - pDC->Colour(Back, 24); - pDC->Rectangle(&r); - } - - // _PaintTime = LgiCurrentTime() - StartTime; - #ifdef PAINT_DEBUG - if (GetNotify()) - { - char s[256]; - sprintf(s, "Pour:%i Style:%i Paint:%i ms", _PourTime, _StyleTime, _PaintTime); - GMessage m = CreateMsg(DEBUG_TIMES_MSG, 0, (int)s); - GetNotify()->OnEvent(&m); - } - #endif - // printf("PaintTime: %ims\n", _PaintTime); - - #if LGI_EXCEPTIONS - } - catch (...) - { - LgiMsg(this, "GTextView3::OnPaint crashed.", "Lgi"); - } - #endif -} - -int GTextView3::OnEvent(GMessage *Msg) -{ - switch (MsgCode(Msg)) - { - case M_CUT: - { - Cut(); - break; - } - case M_COPY: - { - Copy(); - break; - } - case M_PASTE: - { - Paste(); - break; - } - #if defined WIN32 - case WM_GETTEXTLENGTH: - { - return Size; - } - case WM_GETTEXT: - { - int Chars = MsgA(Msg); - char *Out = (char*)MsgB(Msg); - if (Out) - { - char *In = (char*)LgiNewConvertCp(LgiAnsiToLgiCp(), NameW(), LGI_WideCharset, Chars); - if (In) - { - int Len = strlen(In); - memcpy(Out, In, Len); - DeleteArray(In); - return Len; - } - } - return 0; - } - - /* This is broken... the IME returns garbage in the buffer. :( - case WM_IME_COMPOSITION: - { - if (Msg->b & GCS_RESULTSTR) - { - HIMC hIMC = ImmGetContext(Handle()); - if (hIMC) - { - int Size = ImmGetCompositionString(hIMC, GCS_RESULTSTR, NULL, 0); - char *Buf = new char[Size]; - if (Buf) - { - ImmGetCompositionString(hIMC, GCS_RESULTSTR, Buf, Size); - - char16 *Utf = (char16*)LgiNewConvertCp("utf-16", Buf, LgiAnsiToLgiCp(), Size); - if (Utf) - { - Insert(Cursor, Utf, StrlenW(Utf)); - DeleteArray(Utf); - } - - DeleteArray(Buf); - } - - ImmReleaseContext(Handle(), hIMC); - } - return 0; - } - break; - } - */ - #endif - } - - return GLayout::OnEvent(Msg); -} - -int GTextView3::OnNotify(GViewI *Ctrl, int Flags) -{ - if (Ctrl->GetId() == IDC_VSCROLL AND VScroll) - { - Invalidate(); - } - - return 0; -} - -void GTextView3::OnPulse() -{ - if (!ReadOnly) - { - Blink = !Blink; - - GRect p = CursorPos; - p.Offset(-ScrollX, 0); - Invalidate(&p); - } -} - -void GTextView3::OnUrl(char *Url) -{ - if (Environment) - { - Environment->OnNavigate(Url); - } -} - -GRect >extView3::GetMargin() -{ - return d->Margin; -} - -void GTextView3::SetMargin(GRect &m) -{ - d->Margin.x1 = max(m.x1, 0); - d->Margin.y1 = max(m.y1, 0); - d->Margin.x2 = max(m.x2, 0); - d->Margin.y2 = max(m.y2, 0); - - Invalidate(); -} - -/////////////////////////////////////////////////////////////////////////////// -class GTextView3_Factory : public GViewFactory -{ - GView *NewView(char *Class, GRect *Pos, char *Text) - { - if (stricmp(Class, "GTextView3") == 0) - { - return new GTextView3(-1, 0, 0, 2000, 2000); - } - - return 0; - } - -} TextView3_Factory; + } + else // not a char + { + switch (k.vkey) + { + case VK_TAB: + return true; + case VK_RETURN: + { + return !GetReadOnly(); + } + case VK_BACKSPACE: + { + if (!GetReadOnly()) + { + if (k.Alt()) + { + if (k.Down()) + { + if (k.Ctrl()) + { + Redo(); + } + else + { + Undo(); + } + } + } + else if (k.Ctrl()) + { + if (k.Down()) + { + int Start = Cursor; + while (IsWhiteSpace(Text[Cursor-1]) AND Cursor > 0) + Cursor--; + + while (!IsWhiteSpace(Text[Cursor-1]) AND Cursor > 0) + Cursor--; + + Delete(Cursor, Start - Cursor); + Invalidate(); + } + } + + return true; + } + break; + } + case VK_F3: + { + if (k.Down()) + { + DoFindNext(); + } + return true; + break; + } + case VK_LEFT: + { + if (k.Down()) + { + if (SelStart >= 0 AND + !k.Shift()) + { + SetCursor(min(SelStart, SelEnd), false); + } + else if (Cursor > 0) + { + int n = Cursor; + + #ifdef MAC + if (k.System()) + { + goto Jump_StartOfLine; + } + else + #endif + if (k.Ctrl()) + { + // word move/select + bool StartWhiteSpace = IsWhiteSpace(Text[n]); + bool LeftWhiteSpace = n > 0 AND IsWhiteSpace(Text[n-1]); + + if (!StartWhiteSpace OR + Text[n] == '\n') + { + n--; + } + + // Skip ws + for (; n > 0 AND strchr(" \t", Text[n]); n--) + ; + + if (Text[n] == '\n') + { + n--; + } + else if (!StartWhiteSpace OR !LeftWhiteSpace) + { + if (IsDelimiter(Text[n])) + { + for (; n > 0 AND IsDelimiter(Text[n]); n--); + } + else + { + for (; n > 0; n--) + { + //IsWordBoundry(Text[n]) + if (IsWhiteSpace(Text[n]) OR + IsDelimiter(Text[n])) + { + break; + } + } + } + } + if (n > 0) n++; + } + else + { + // single char + n--; + } + + SetCursor(n, k.Shift()); + } + } + return true; + break; + } + case VK_RIGHT: + { + if (k.Down()) + { + if (SelStart >= 0 AND + !k.Shift()) + { + SetCursor(max(SelStart, SelEnd), false); + } + else if (Cursor < Size) + { + int n = Cursor; + + #ifdef MAC + if (k.System()) + { + goto Jump_EndOfLine; + } + else + #endif + if (k.Ctrl()) + { + // word move/select + if (IsWhiteSpace(Text[n])) + { + for (; nStart, Cursor-l->Start); + int ScreenX = ds.X(); + int CharX = ds.CharAt(ScreenX); + + SetCursor(Prev->Start + min(CharX, Prev->Len), k.Shift()); + } + } + } + return true; + break; + } + case VK_DOWN: + { + if (k.Down()) + { + #ifdef MAC + if (k.Ctrl()) + { + goto GTextView3_PageDown; + } + #endif + + int Old = Cursor; + GTextLine *l = GetLine(Cursor); + if (l) + { + GTextLine *Next = Line.Next(); + if (Next) + { + GDisplayString ds(Font, Text + l->Start, Cursor-l->Start); + int ScreenX = ds.X(); + int CharX = ds.CharAt(ScreenX); + + SetCursor(Next->Start + min(CharX, Next->Len), k.Shift()); + } + } + } + return true; + break; + } + case VK_END: + { + if (k.Down()) + { + if (k.Ctrl()) + { + SetCursor(Size, k.Shift()); + } + else + { + Jump_EndOfLine: + GTextLine *l = GetLine(Cursor); + if (l) + { + SetCursor(l->Start + l->Len, k.Shift()); + } + } + } + return true; + break; + } + case VK_HOME: + { + if (k.Down()) + { + if (k.Ctrl()) + { + SetCursor(0, k.Shift()); + } + else + { + Jump_StartOfLine: + GTextLine *l = GetLine(Cursor); + if (l) + { + char16 *Line = Text + l->Start; + char16 *s; + char16 SpTab[] = {' ', '\t', 0}; + for (s = Line; (SubtractPtr(s,Line) < l->Len) AND StrchrW(SpTab, *s); s++); + int Whitespace = SubtractPtr(s, Line); + + if (l->Start + Whitespace == Cursor) + { + SetCursor(l->Start, k.Shift()); + } + else + { + SetCursor(l->Start + Whitespace, k.Shift()); + } + } + } + } + return true; + break; + } + case VK_PAGEUP: + { + GTextView3_PageUp: + if (k.Down()) + { + GTextLine *l = GetLine(Cursor); + if (l) + { + int DisplayLines = Y() / LineY; + int CurLine = Line.IndexOf(l); + + GTextLine *New = Line.ItemAt(max(CurLine - DisplayLines, 0)); + if (New) + { + SetCursor(New->Start + min(Cursor - l->Start, New->Len), k.Shift()); + } + } + } + return true; + break; + } + case VK_PAGEDOWN: + { + GTextView3_PageDown: + if (k.Down()) + { + GTextLine *l = GetLine(Cursor); + if (l) + { + int DisplayLines = Y() / LineY; + int CurLine = Line.IndexOf(l); + + GTextLine *New = Line.ItemAt(min(CurLine + DisplayLines, GetLines()-1)); + if (New) + { + SetCursor(New->Start + min(Cursor - l->Start, New->Len), k.Shift()); + } + } + } + return true; + break; + } + case VK_INSERT: + { + if (k.Down()) + { + if (k.Ctrl()) + { + Copy(); + } + else if (k.Shift()) + { + if (!GetReadOnly()) + { + Paste(); + } + } + } + return true; + break; + } + case VK_DELETE: + { + if (!GetReadOnly()) + { + if (k.Down()) + { + if (SelStart >= 0) + { + if (k.Shift()) + { + Cut(); + } + else + { + DeleteSelection(); + } + } + else if (Cursor < Size AND + Delete(Cursor, 1)) + { + Invalidate(); + } + } + return true; + } + break; + } + default: + { + if (k.c16 == 17) break; + + if (k.Modifier() AND + !k.Alt()) + { + switch (k.GetChar()) + { + case 0xbd: // Ctrl+'-' + { + if (k.Down() AND + Font->PointSize() > 1) + { + Font->PointSize(Font->PointSize() - 1); + OnFontChange(); + Invalidate(); + } + break; + } + case 0xbb: // Ctrl+'+' + { + if (k.Down() AND + Font->PointSize() < 100) + { + Font->PointSize(Font->PointSize() + 1); + OnFontChange(); + Invalidate(); + } + break; + } + case 'a': + case 'A': + { + if (k.Down()) + { + // select all + SelStart = 0; + SelEnd = Size; + Invalidate(); + } + return true; + break; + } + case 'y': + case 'Y': + { + if (!GetReadOnly()) + { + if (k.Down()) + { + Redo(); + } + return true; + } + break; + } + case 'z': + case 'Z': + { + if (!GetReadOnly()) + { + if (k.Down()) + { + if (k.Shift()) + { + Redo(); + } + else + { + Undo(); + } + } + return true; + } + break; + } + case 'x': + case 'X': + { + if (!GetReadOnly()) + { + if (k.Down()) + { + Cut(); + } + return true; + } + break; + } + case 'c': + case 'C': + { + if (k.Down()) + { + Copy(); + } + return true; + break; + } + case 'v': + case 'V': + { + if (!k.Shift() AND + !GetReadOnly()) + { + if (k.Down()) + { + Paste(); + } + return true; + } + break; + } + case 'f': + { + if (k.Down()) + { + DoFind(); + } + return true; + break; + } + case 'g': + case 'G': + { + if (k.Down()) + { + DoGoto(); + } + return true; + break; + } + case 'h': + case 'H': + { + if (k.Down()) + { + DoReplace(); + } + return true; + break; + } + case 'u': + case 'U': + { + if (!GetReadOnly()) + { + if (k.Down()) + { + DoCase(k.Shift()); + } + return true; + } + break; + } + case VK_RETURN: + { + if (!GetReadOnly() AND !k.Shift()) + { + if (k.Down()) + { + OnEnter(k); + } + return true; + } + break; + } + } + } + break; + } + } + } + + return false; +} + +void GTextView3::OnEnter(GKey &k) +{ + // enter + if (SelStart >= 0) + { + DeleteSelection(); + } + + char16 InsertStr[256] = {'\n', 0}; + + GTextLine *CurLine = GetLine(Cursor); + if (CurLine AND AutoIndent) + { + int WsLen = 0; + for (; WsLen < CurLine->Len AND + WsLen < (Cursor - CurLine->Start) AND + strchr(" \t", Text[CurLine->Start + WsLen]); WsLen++); + if (WsLen > 0) + { + memcpy(InsertStr+1, Text+CurLine->Start, WsLen * sizeof(char16)); + InsertStr[WsLen+1] = 0; + } + } + + if (Insert(Cursor, InsertStr, StrlenW(InsertStr))) + { + SetCursor(Cursor + StrlenW(InsertStr), false, true); + } +} + +int GTextView3::TextWidth(GFont *f, char16 *s, int Len, int x, int Origin) +{ + int w = x; + int Size = f->TabSize(); + + for (char16 *c = s; SubtractPtr(c, s) < Len; ) + { + if (*c == 9) + { + w = ((((w-Origin) + Size) / Size) * Size) + Origin; + c++; + } + else + { + char16 *e; + for (e = c; SubtractPtr(e, s) < Len AND *e != 9; e++); + + GDisplayString ds(f, c, SubtractPtr(e, c)); + w += ds.X(); + c = e; + } + } + + return w - x; +} + +int GTextView3::ScrollYLine() +{ + return (VScroll) ? VScroll->Value() : 0; +} + +int GTextView3::ScrollYPixel() +{ + return ScrollYLine() * LineY; +} + +void GTextView3::OnPaint(GSurface *pDC) +{ + #if LGI_EXCEPTIONS + try + { + #endif + // int StartTime = LgiCurrentTime(); + + if (d->LayoutDirty) + { + PourText(d->DirtyStart, d->DirtyLen); + } + + GRect r = GetClient(); + r.x2 += ScrollX; + + int Ox, Oy; + pDC->GetOrigin(Ox, Oy); + pDC->SetOrigin(Ox+ScrollX, Oy); + + #if 0 + // Coverage testing... + pDC->Colour(Rgb24(255, 0, 255), 24); + pDC->Rectangle(); + #endif + + GSurface *pOut = pDC; + bool DrawSel = false; + COLOUR Fore = LC_TEXT; + COLOUR Selected = LC_SELECTION; + GViewFill *BackFill = GetBackgroundFill(); + COLOUR Back = (!ReadOnly) ? (BackFill ? Rgb32To24(BackFill->GetC32()) : LC_WORKSPACE) : BackColour; + + bool HasFocus = Focus(); + if (!HasFocus) + { + COLOUR Work = LC_WORKSPACE; + Selected = Rgb24( (R24(Work)+R24(Selected))/2, + (G24(Work)+G24(Selected))/2, + (B24(Work)+B24(Selected))/2); + } + + if (!Enabled()) + { + Fore = LC_LOW; + Back = LC_MED; + } + + #ifdef DOUBLE_BUFFER_PAINT + GMemDC *pMem = new GMemDC; + pOut = pMem; + #endif + if (Text AND + Font + #ifdef DOUBLE_BUFFER_PAINT + AND + pMem AND + pMem->Create(r.X()-d->Margin.x1, LineY, GdcD->GetBits()) + #endif + ) + { + int SelMin = min(SelStart, SelEnd); + int SelMax = max(SelStart, SelEnd); + + // font properties + Font->Colour(Fore, Back); + Font->Transparent(false); + + // draw margins + pDC->Colour(PAINT_BORDER, 24); + // top margin + pDC->Rectangle(0, 0, r.x2, d->Margin.y1-1); + // left margin + pDC->Rectangle(0, d->Margin.y1, d->Margin.x1-1, r.y2); + + // draw lines of text + int k = ScrollYLine(); + GTextLine *l=Line.ItemAt(k); + int Dy = (l) ? -l->r.y1 : 0; + int NextSelection = (SelStart != SelEnd) ? SelMin : -1; // offset where selection next changes + if (l AND + SelStart >= 0 AND + SelStart < l->Start AND + SelEnd > l->Start) + { + // start of visible area is in selection + // init to selection colour + DrawSel = true; + Font->Colour(LC_SEL_TEXT, Selected); + NextSelection = SelMax; + } + + GTextStyle *NextStyle = GetNextStyle((l) ? l->Start : 0); + + DocOffset = (l) ? l->r.y1 : 0; + + // loop through all visible lines + int y = d->Margin.y1; + for (; l AND l->r.y1+Dy < r.Y(); l=Line.Next()) + { + GRect Tr = l->r; + #ifdef DOUBLE_BUFFER_PAINT + Tr.Offset(-Tr.x1, -Tr.y1); + #else + Tr.Offset(0, y - Tr.y1); + #endif + + // deal with selection change on beginning of line + if (NextSelection == l->Start) + { + // selection change + DrawSel = !DrawSel; + NextSelection = (NextSelection == SelMin) ? SelMax : -1; + } + if (DrawSel) + { + Font->Colour(LC_SEL_TEXT, Selected); + } + else + { + Font->Colour((l->Col&0x80000000)?Fore:l->Col, Back); + } + + // draw text + int Done = 0; // how many chars on this line have we + // processed so far + int TextX = 0; // pixels we have moved so far + + // loop through all sections of similar text on a line + while (Done < l->Len) + { + // decide how big this block is + int RtlTrailingSpace = 0; + int Cur = l->Start + Done; + int Block = l->Len - Done; + + // check for style change + if (NextStyle) + { + // start + if (l->Overlap(NextStyle->Start) AND + NextStyle->Start > Cur AND + NextStyle->Start - Cur < Block) + { + Block = NextStyle->Start - Cur; + } + + // end + int StyleEnd = NextStyle->Start + NextStyle->Len; + if (l->Overlap(StyleEnd) AND + StyleEnd > Cur AND + StyleEnd - Cur < Block) + { + Block = StyleEnd - Cur; + } + } + + // check for next selection change + // this may truncate the style + if (NextSelection > Cur AND + NextSelection - Cur < Block) + { + Block = NextSelection - Cur; + } + + LgiAssert(Block != 0); // sanity check + + int TabOri = Tr.x1 - d->Margin.x1; + + if (NextStyle AND // There is a style + (Cur < SelMin OR Cur >= SelMax) AND // AND we're not drawing a selection block + Cur >= NextStyle->Start AND // AND we're inside the styled area + Cur < NextStyle->Start+NextStyle->Len) + { + if (NextStyle->Font) + { + // draw styled text + NextStyle->Font->Colour(NextStyle->c, Back); + NextStyle->Font->Transparent(false); + + LgiAssert(l->Start + Done >= 0); + + GDisplayString Ds( NextStyle->Font, + MapText(Text + (l->Start + Done), + Block, + RtlTrailingSpace), + Block + RtlTrailingSpace, + 0, + TabOri); + + TextX = Ds.X(); + Ds.Draw(pOut, Tr.x1, Tr.y1, 0); + + if (NextStyle->Style == GTextStyle::DecorSquiggle) + { + pOut->Colour(NextStyle->DecorColour, 24); + for (int i=0; iSet(Tr.x1+i, Tr.y2-(i%2)); + } + + NextStyle->Font->Colour(Fore, Back); + } + } + else + { + // draw a block of normal text + LgiAssert(l->Start + Done >= 0); + + GDisplayString Ds( Font, + MapText(Text + (l->Start + Done), + Block, + RtlTrailingSpace), + Block + RtlTrailingSpace, + 0, + TabOri); + TextX = Ds.X(); + + Ds.Draw(pOut, Tr.x1, Tr.y1, 0); + } + + if (NextStyle AND + Cur+Block >= NextStyle->Start+NextStyle->Len) + { + // end of this styled block + NextStyle = GetNextStyle(); + } + + if (NextSelection == Cur+Block) + { + // selection change + DrawSel = !DrawSel; + if (DrawSel) + { + Font->Colour(LC_SEL_TEXT, Selected); + } + else + { + Font->Colour((l->Col&0x80000000)?Fore:l->Col, Back); + } + NextSelection = (NextSelection == SelMin) ? SelMax : -1; + } + + Tr.x1 += TextX; + Done += Block + RtlTrailingSpace; + + } // end block loop + + // eol processing + int EndOfLine = l->Start+l->Len; + if (EndOfLine >= SelMin AND EndOfLine < SelMax) + { + // draw the '\n' at the end of the line as selected + pOut->Colour(Font->Back(), 24); + pOut->Rectangle(Tr.x2, Tr.y1, Tr.x2+7, Tr.y2); + Tr.x2 += 7; + } + else Tr.x2 = Tr.x1; + + // draw any space after text + pOut->Colour(PAINT_AFTER_LINE, 24); + pOut->Rectangle(Tr.x2, Tr.y1, r.x2, Tr.y2); + + // cursor? + if (Focus()) + { + // draw the cursor if on this line + if (Cursor >= l->Start AND Cursor <= l->Start+l->Len) + { + CursorPos.ZOff(1, LineY-1); + + int At = Cursor-l->Start; + + GDisplayString Ds(Font, MapText(Text+l->Start, At), At); + int CursorX = Ds.X(); + CursorPos.Offset(d->Margin.x1 + CursorX, Tr.y1); + + if (CanScrollX) + { + // Cursor on screen check + GRect Scr = GetClient(); + Scr.Offset(ScrollX, 0); + GRect Cur = CursorPos; + if (Cur.x2 > Scr.x2 - 5) // right edge check + { + ScrollX = ScrollX + Cur.x2 - Scr.x2 + 40; + Invalidate(); + } + else if (Cur.x1 < Scr.x1 AND ScrollX > 0) + { + ScrollX = max(0, Cur.x1 - 40); + Invalidate(); + } + } + + if (Blink) + { + GRect c = CursorPos; + #ifdef DOUBLE_BUFFER_PAINT + c.Offset(-d->Margin.x1, -y); + #endif + + pOut->Colour((!ReadOnly) ? Fore : Rgb24(192, 192, 192), 24); + pOut->Rectangle(&c); + } + + #if WIN32NATIVE + HIMC hIMC = ImmGetContext(Handle()); + if (hIMC) + { + COMPOSITIONFORM Cf; + Cf.dwStyle = CFS_POINT; + Cf.ptCurrentPos.x = CursorPos.x1; + Cf.ptCurrentPos.y = CursorPos.y1; + ImmSetCompositionWindow(hIMC, &Cf); + ImmReleaseContext(Handle(), hIMC); + } + #endif + } + } + + #ifdef DOUBLE_BUFFER_PAINT + // dump to screen + pDC->Blt(d->Margin.x1, y, pOut); + #endif + y += LineY; + + } // end of line loop + + // draw any space under the lines + if (y <= r.y2) + { + // printf("White %i, k=%i Lines=%i\n", r.y2 - y, k, Line.Length()); + + pDC->Colour(Back, 24); + pDC->Rectangle(d->Margin.x1, y, r.x2, r.y2); + } + + #ifdef DOUBLE_BUFFER_PAINT + DeleteObj(pMem); + #endif + } + else + { + // default drawing: nothing + pDC->Colour(Back, 24); + pDC->Rectangle(&r); + } + + // _PaintTime = LgiCurrentTime() - StartTime; + #ifdef PAINT_DEBUG + if (GetNotify()) + { + char s[256]; + sprintf(s, "Pour:%i Style:%i Paint:%i ms", _PourTime, _StyleTime, _PaintTime); + GMessage m = CreateMsg(DEBUG_TIMES_MSG, 0, (int)s); + GetNotify()->OnEvent(&m); + } + #endif + // printf("PaintTime: %ims\n", _PaintTime); + + #if LGI_EXCEPTIONS + } + catch (...) + { + LgiMsg(this, "GTextView3::OnPaint crashed.", "Lgi"); + } + #endif +} + +int GTextView3::OnEvent(GMessage *Msg) +{ + switch (MsgCode(Msg)) + { + case M_CUT: + { + Cut(); + break; + } + case M_COPY: + { + Copy(); + break; + } + case M_PASTE: + { + Paste(); + break; + } + #if defined WIN32 + case WM_GETTEXTLENGTH: + { + return Size; + } + case WM_GETTEXT: + { + int Chars = MsgA(Msg); + char *Out = (char*)MsgB(Msg); + if (Out) + { + char *In = (char*)LgiNewConvertCp(LgiAnsiToLgiCp(), NameW(), LGI_WideCharset, Chars); + if (In) + { + int Len = strlen(In); + memcpy(Out, In, Len); + DeleteArray(In); + return Len; + } + } + return 0; + } + + /* This is broken... the IME returns garbage in the buffer. :( + case WM_IME_COMPOSITION: + { + if (Msg->b & GCS_RESULTSTR) + { + HIMC hIMC = ImmGetContext(Handle()); + if (hIMC) + { + int Size = ImmGetCompositionString(hIMC, GCS_RESULTSTR, NULL, 0); + char *Buf = new char[Size]; + if (Buf) + { + ImmGetCompositionString(hIMC, GCS_RESULTSTR, Buf, Size); + + char16 *Utf = (char16*)LgiNewConvertCp(LGI_WideCharset, Buf, LgiAnsiToLgiCp(), Size); + if (Utf) + { + Insert(Cursor, Utf, StrlenW(Utf)); + DeleteArray(Utf); + } + + DeleteArray(Buf); + } + + ImmReleaseContext(Handle(), hIMC); + } + return 0; + } + break; + } + */ + #endif + } + + return GLayout::OnEvent(Msg); +} + +int GTextView3::OnNotify(GViewI *Ctrl, int Flags) +{ + if (Ctrl->GetId() == IDC_VSCROLL AND VScroll) + { + Invalidate(); + } + + return 0; +} + +void GTextView3::OnPulse() +{ + if (!ReadOnly) + { + Blink = !Blink; + + GRect p = CursorPos; + p.Offset(-ScrollX, 0); + Invalidate(&p); + } +} + +void GTextView3::OnUrl(char *Url) +{ + if (Environment) + { + Environment->OnNavigate(Url); + } +} + +GRect >extView3::GetMargin() +{ + return d->Margin; +} + +void GTextView3::SetMargin(GRect &m) +{ + d->Margin.x1 = max(m.x1, 0); + d->Margin.y1 = max(m.y1, 0); + d->Margin.x2 = max(m.x2, 0); + d->Margin.y2 = max(m.y2, 0); + + Invalidate(); +} + +/////////////////////////////////////////////////////////////////////////////// +class GTextView3_Factory : public GViewFactory +{ + GView *NewView(char *Class, GRect *Pos, char *Text) + { + if (stricmp(Class, "GTextView3") == 0) + { + return new GTextView3(-1, 0, 0, 2000, 2000); + } + + return 0; + } + +} TextView3_Factory;