diff --git a/haiku/Makefile.haiku b/haiku/Makefile.haiku --- a/haiku/Makefile.haiku +++ b/haiku/Makefile.haiku @@ -1,224 +1,224 @@ #!/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 MakeDir := $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST)))) BuildDir = $(Build) CFlags = -MMD -MP -fPIC -fno-inline CppFlags = $(CFlags) -fpermissive -std=c++14 ifeq ($(Build),Debug) CFlags += -g CppFlags += -g Tag = d Defs = -D_DEBUG -DHAIKU -D_REENTRANT -DLGI_LIBRARY -DLGI_UNIT_TESTS -DPOSIX -D_GNU_SOURCE Libs = \ -static-libgcc \ -lgnu \ -lnetwork \ -lbe Inc = \ -I./private/haiku \ -I./private/common \ -I./include/lgi/haiku \ -I./include \ -I../../../../system/develop/headers else CFlags += -s -Os CppFlags += -s -Os Defs = -DHAIKU -D_REENTRANT -DLGI_LIBRARY -DLGI_UNIT_TESTS -DPOSIX -D_GNU_SOURCE Libs = \ -static-libgcc \ -lgnu \ -lnetwork \ -lbe Inc = \ -I./private/haiku \ -I./private/common \ -I./include/lgi/haiku \ -I./include \ -I../../../../system/develop/headers endif # Dependencies Source = src/posix/SubProcess.cpp \ src/haiku/Window.cpp \ src/haiku/Widgets.cpp \ src/haiku/View.cpp \ src/haiku/Thread.cpp \ src/haiku/ShowFileProp_Haiku.cpp \ src/haiku/ScreenDC.cpp \ src/haiku/Printer.cpp \ src/haiku/PrintDC.cpp \ src/haiku/Menu.cpp \ src/haiku/MemDC.cpp \ src/haiku/Mem.cpp \ src/haiku/Layout.cpp \ src/haiku/General.cpp \ src/haiku/Gdc2.cpp \ src/haiku/File.cpp \ src/haiku/DragAndDrop.cpp \ src/haiku/ClipBoard.cpp \ src/haiku/App.cpp \ src/common/Widgets/Tree.cpp \ src/common/Widgets/ToolBar.cpp \ src/common/Widgets/TextLabel.cpp \ src/common/Widgets/TabView.cpp \ src/common/Widgets/TableLayout.cpp \ src/common/Widgets/StatusBar.cpp \ src/common/Widgets/Splitter.cpp \ src/common/Widgets/Slider.cpp \ src/common/Widgets/ScrollBar.cpp \ src/common/Widgets/RadioGroup.cpp \ src/common/Widgets/ProgressDlg.cpp \ src/common/Widgets/Progress.cpp \ src/common/Widgets/Popup.cpp \ src/common/Widgets/Panel.cpp \ src/common/Widgets/List.cpp \ src/common/Widgets/ItemContainer.cpp \ src/common/Widgets/Edit.cpp \ src/common/Widgets/Combo.cpp \ src/common/Widgets/CheckBox.cpp \ src/common/Widgets/Button.cpp \ src/common/Widgets/Box.cpp \ src/common/Widgets/Bitmap.cpp \ src/common/Text/XmlTree.cpp \ src/common/Text/Utf8.cpp \ src/common/Text/Unicode.cpp \ src/common/Text/Token.cpp \ src/common/Text/TextView3.cpp \ src/common/Text/TextConvert.cpp \ src/common/Text/String.cpp \ src/common/Text/DocView.cpp \ src/common/Skins/Gel/Gel.cpp \ src/common/Resource/Res.cpp \ src/common/Resource/LgiRes.cpp \ src/common/Net/Uri.cpp \ src/common/Net/Net.cpp \ src/common/Net/MDStringToDigest.cpp \ src/common/Net/Base64.cpp \ src/common/Lgi/WindowCommon.cpp \ src/common/Lgi/ViewCommon.cpp \ src/common/Lgi/Variant.cpp \ src/common/Lgi/TrayIcon.cpp \ src/common/Lgi/ToolTip.cpp \ src/common/Lgi/ThreadEvent.cpp \ src/common/Lgi/ThreadCommon.cpp \ src/common/Lgi/Stream.cpp \ src/common/Lgi/Rand.cpp \ src/common/Lgi/OptionsFile.cpp \ src/common/Lgi/Object.cpp \ src/common/Lgi/Mutex.cpp \ src/common/Lgi/Mru.cpp \ src/common/Lgi/MenuCommon.cpp \ src/common/Lgi/MemStream.cpp \ src/common/Lgi/LMsg.cpp \ src/common/Lgi/Library.cpp \ src/common/Lgi/LgiCommon.cpp \ src/common/Lgi/Input.cpp \ src/common/Lgi/GuiUtils.cpp \ src/common/Lgi/FontSelect.cpp \ src/common/Lgi/FindReplace.cpp \ src/common/Lgi/FileSelect.cpp \ src/common/Lgi/DragAndDropCommon.cpp \ src/common/Lgi/DataDlg.cpp \ src/common/Lgi/CssTools.cpp \ src/common/Lgi/Css.cpp \ src/common/Lgi/ClipBoardCommon.cpp \ src/common/Lgi/AppCommon.cpp \ src/common/Lgi/Alert.cpp \ src/common/Hash/sha1/sha1.c \ src/common/Hash/md5/md5.c \ src/common/General/Properties.cpp \ src/common/General/Password.cpp \ src/common/General/FileCommon.cpp \ src/common/General/ExeCheck.cpp \ src/common/General/DateTime.cpp \ src/common/General/Containers.cpp \ src/common/Gdc2/Tools/GdcTools.cpp \ src/common/Gdc2/Tools/ColourReduce.cpp \ src/common/Gdc2/Surface.cpp \ src/common/Gdc2/Rect.cpp \ src/common/Gdc2/Path/Path.cpp \ src/common/Gdc2/GdcCommon.cpp \ src/common/Gdc2/Font/TypeFace.cpp \ src/common/Gdc2/Font/StringLayout.cpp \ src/common/Gdc2/Font/FontType.cpp \ src/common/Gdc2/Font/FontSystem.cpp \ src/common/Gdc2/Font/Font.cpp \ src/common/Gdc2/Font/DisplayString.cpp \ src/common/Gdc2/Font/Charset.cpp \ src/common/Gdc2/Filters/Filter.cpp \ src/common/Gdc2/Colour.cpp \ src/common/Gdc2/Alpha.cpp \ src/common/Gdc2/8Bit.cpp \ src/common/Gdc2/32Bit.cpp \ src/common/Gdc2/24Bit.cpp \ src/common/Gdc2/16Bit.cpp \ src/common/Gdc2/15Bit.cpp SourceC := $(filter %.c,$(Source)) ObjectsC := $(SourceC:.c=.o) SourceCpp := $(filter %.cpp,$(Source)) ObjectsCpp := $(SourceCpp:.cpp=.o) Objects := $(notdir $(ObjectsC) $(ObjectsCpp)) Objects := $(addprefix $(BuildDir)/,$(Objects)) Deps := $(patsubst %.o,%.d,$(Objects)) $(BuildDir)/%.o: %.c mkdir -p $(@D) echo $(notdir $<) [$(Build)] - $(CC) $(Inc) $(CFlags) $(Defs) -c $< -o $@ + $(CC) $(Inc) $(CFlags) $(Defs) -c $(abspath $<) -o $@ $(BuildDir)/%.o: %.cpp mkdir -p $(@D) echo $(notdir $<) [$(Build)] - $(CPP) $(Inc) $(CppFlags) $(Defs) -c $< -o $@ + $(CPP) $(Inc) $(CppFlags) $(Defs) -c $(abspath $<) -o $@ # Target TargetFile = lib$(Target)$(Tag).so $(TargetFile) : $(Objects) mkdir -p $(BuildDir) @echo Linking $(TargetFile) [$(Build)]... $(CPP)$s -shared \ \ -o $(BuildDir)/$(TargetFile) \ $(Objects) \ $(Libs) @echo Done. -include $(Objects:.o=.d) # Clean out targets clean : rm -rf $(BuildDir) @echo Cleaned $(BuildDir) VPATH=$(BuildDir) \ ./src/posix \ ./src/haiku \ ./src/common/Widgets \ ./src/common/Text \ ./src/common/Skins/Gel \ ./src/common/Resource \ ./src/common/Net \ ./src/common/Lgi \ ./src/common/Hash/sha1 \ ./src/common/Hash/md5 \ ./src/common/General \ ./src/common/Gdc2/Tools \ ./src/common/Gdc2/Path \ ./src/common/Gdc2/Font \ ./src/common/Gdc2/Filters \ ./src/common/Gdc2 diff --git a/ide/haiku/Makefile.haiku b/ide/haiku/Makefile.haiku --- a/ide/haiku/Makefile.haiku +++ b/ide/haiku/Makefile.haiku @@ -1,429 +1,429 @@ #!/usr/bin/make # # This makefile generated by LgiIde # http://www.memecode.com/lgi.php # .SILENT : CC = gcc CPP = g++ Target = ./lgiide ifndef Build Build = Debug endif MakeDir := $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST)))) BuildDir = $(Build) CFlags = -MMD -MP -fPIC -fno-inline CppFlags = $(CFlags) -fpermissive -std=c++14 ifeq ($(Build),Debug) CFlags += -g CppFlags += -g Tag = d Defs = -D_DEBUG -DHAIKU -D_REENTRANT -DPOSIX -D_GNU_SOURCE Libs = \ -static-libgcc \ -lgnu \ -lnetwork \ -lbe \ -llgi$(Tag) \ -L../$(BuildDir) Inc = \ -I./src \ -I./resources \ -I../private/common \ -I../include/lgi/haiku \ -I../include else CFlags += -s -Os CppFlags += -s -Os Defs = -DHAIKU -D_REENTRANT -DPOSIX -D_GNU_SOURCE Libs = \ -static-libgcc \ -lgnu \ -lnetwork \ -lbe \ -llgi$(Tag) \ -L../$(BuildDir) Inc = \ -I./src \ -I./resources \ -I../private/common \ -I../include/lgi/haiku \ -I../include endif # Dependencies Source = src/WebFldDlg.cpp \ src/SysCharSupport.cpp \ src/SpaceTabConv.cpp \ src/SimpleCppParser.cpp \ src/PythonParser.cpp \ src/ProjectNode.cpp \ src/NewProjectFromTemplate.cpp \ src/MissingFiles.cpp \ src/MemDumpViewer.cpp \ src/LgiUtils.cpp \ src/LgiIde.cpp \ src/JavascriptParser.cpp \ src/IdeProjectSettings.cpp \ src/IdeProject.cpp \ src/IdeFindInFiles.cpp \ src/IdeDoc.cpp \ src/IdeCommon.cpp \ src/History.cpp \ src/FtpThread.cpp \ src/FindSymbol.cpp \ src/DocEditStyling.cpp \ src/DocEdit.cpp \ src/DebugContext.cpp \ src/AddFtpFile.cpp \ ../src/common/Text/TextView4.cpp \ ../src/common/Text/TextConvert.cpp \ ../src/common/Text/HtmlParser.cpp \ ../src/common/Text/HtmlCommon.cpp \ ../src/common/Text/Html.cpp \ ../src/common/Text/Homoglyphs/HomoglyphsTable.cpp \ ../src/common/Text/Homoglyphs/Homoglyphs.cpp \ ../src/common/Text/DocView.cpp \ ../src/common/Net/OpenSSLSocket.cpp \ ../src/common/Net/Http.cpp \ ../src/common/Net/Ftp.cpp \ ../src/common/Lgi/Mdi.cpp \ ../src/common/Lgi/LgiMain.cpp \ ../src/common/Lgi/About.cpp \ ../src/common/Gdc2/Filters/Png.cpp \ ../src/common/Coding/ParseCpp.cpp \ ../src/common/Coding/LexCpp.cpp SourceC := $(filter %.c,$(Source)) ObjectsC := $(SourceC:.c=.o) SourceCpp := $(filter %.cpp,$(Source)) ObjectsCpp := $(SourceCpp:.cpp=.o) Objects := $(notdir $(ObjectsC) $(ObjectsCpp)) Objects := $(addprefix $(BuildDir)/,$(Objects)) Deps := $(patsubst %.o,%.d,$(Objects)) $(BuildDir)/%.o: %.c mkdir -p $(@D) echo $(notdir $<) [$(Build)] - $(CC) $(Inc) $(CFlags) $(Defs) -c $< -o $@ + $(CC) $(Inc) $(CFlags) $(Defs) -c $(abspath $<) -o $@ $(BuildDir)/%.o: %.cpp mkdir -p $(@D) echo $(notdir $<) [$(Build)] - $(CPP) $(Inc) $(CppFlags) $(Defs) -c $< -o $@ + $(CPP) $(Inc) $(CppFlags) $(Defs) -c $(abspath $<) -o $@ # Target # Executable target $(Target) : ../$(BuildDir)/liblgi$(Tag).so $(Objects) mkdir -p $(BuildDir) @echo Linking $(Target) [$(Build)]... $(CPP) -Wl,-export-dynamic,-R. -o \ $(Target) $(Objects) $(Libs) /boot/home/code/lgi/trunk/src/haiku/hvif_to_rdef.py ./resources/icon.hvif $(Build)/lgiide.rdef lgiide rc $(Build)/lgiide.rdef resattr -o $(Target) $(Build)/lgiide.rsrc xres -o $(Target) $(Build)/lgiide.rsrc @echo Done. ../$(BuildDir)/liblgi$(Tag).so : ../include/lgi/common/App.h \ ../include/lgi/common/Array.h \ ../include/lgi/common/AutoPtr.h \ ../include/lgi/common/Base64.h \ ../include/lgi/common/Bitmap.h \ ../include/lgi/common/Box.h \ ../include/lgi/common/Button.h \ ../include/lgi/common/CairoSurface.h \ ../include/lgi/common/Cancel.h \ ../include/lgi/common/Capabilities.h \ ../include/lgi/common/Charset.h \ ../include/lgi/common/CheckBox.h \ ../include/lgi/common/ClipBoard.h \ ../include/lgi/common/Colour.h \ ../include/lgi/common/ColourSpace.h \ ../include/lgi/common/Com.h \ ../include/lgi/common/Combo.h \ ../include/lgi/common/Containers.h \ ../include/lgi/common/Core.h \ ../include/lgi/common/Css.h \ ../include/lgi/common/CssTools.h \ ../include/lgi/common/CurrentTime.h \ ../include/lgi/common/DataDlg.h \ ../include/lgi/common/DateTime.h \ ../include/lgi/common/Dialog.h \ ../include/lgi/common/DisplayString.h \ ../include/lgi/common/DocView.h \ ../include/lgi/common/Dom.h \ ../include/lgi/common/DomFields.h \ ../include/lgi/common/DragAndDrop.h \ ../include/lgi/common/DropFiles.h \ ../include/lgi/common/Edit.h \ ../include/lgi/common/Emoji.h \ ../include/lgi/common/Error.h \ ../include/lgi/common/EventTargetThread.h \ ../include/lgi/common/File.h \ ../include/lgi/common/FileSelect.h \ ../include/lgi/common/Filter.h \ ../include/lgi/common/FindReplaceDlg.h \ ../include/lgi/common/Font.h \ ../include/lgi/common/FontCache.h \ ../include/lgi/common/FontSelect.h \ ../include/lgi/common/Gdc2.h \ ../include/lgi/common/GdcTools.h \ ../include/lgi/common/GdiLeak.h \ ../include/lgi/common/HashTable.h \ ../include/lgi/common/ImageList.h \ ../include/lgi/common/Input.h \ ../include/lgi/common/ItemContainer.h \ ../include/lgi/common/Json.h \ ../include/lgi/common/Layout.h \ ../include/lgi/common/Lgi.h \ ../include/lgi/common/LgiClasses.h \ ../include/lgi/common/LgiCommon.h \ ../include/lgi/common/LgiDefs.h \ ../include/lgi/common/LgiInc.h \ ../include/lgi/common/LgiInterfaces.h \ ../include/lgi/common/LgiMsgs.h \ ../include/lgi/common/LgiRes.h \ ../include/lgi/common/LgiString.h \ ../include/lgi/common/LgiUiBase.h \ ../include/lgi/common/Library.h \ ../include/lgi/common/LibraryUtils.h \ ../include/lgi/common/List.h \ ../include/lgi/common/ListItemCheckBox.h \ ../include/lgi/common/ListItemRadioBtn.h \ ../include/lgi/common/LMallocArray.h \ ../include/lgi/common/Mail.h \ ../include/lgi/common/Matrix.h \ ../include/lgi/common/Mem.h \ ../include/lgi/common/Menu.h \ ../include/lgi/common/Message.h \ ../include/lgi/common/Mime.h \ ../include/lgi/common/Mru.h \ ../include/lgi/common/Mutex.h \ ../include/lgi/common/Net.h \ ../include/lgi/common/Notifications.h \ ../include/lgi/common/OAuth2.h \ ../include/lgi/common/OptionsFile.h \ ../include/lgi/common/Palette.h \ ../include/lgi/common/Panel.h \ ../include/lgi/common/Password.h \ ../include/lgi/common/Path.h \ ../include/lgi/common/PixelRops.h \ ../include/lgi/common/Point.h \ ../include/lgi/common/Popup.h \ ../include/lgi/common/PopupList.h \ ../include/lgi/common/PopupNotification.h \ ../include/lgi/common/Printer.h \ ../include/lgi/common/Profile.h \ ../include/lgi/common/Progress.h \ ../include/lgi/common/ProgressDlg.h \ ../include/lgi/common/ProgressView.h \ ../include/lgi/common/Properties.h \ ../include/lgi/common/RadioGroup.h \ ../include/lgi/common/Range.h \ ../include/lgi/common/Rect.h \ ../include/lgi/common/RectF.h \ ../include/lgi/common/RefCount.h \ ../include/lgi/common/RegKey.h \ ../include/lgi/common/Res.h \ ../include/lgi/common/Rops.h \ ../include/lgi/common/ScrollBar.h \ ../include/lgi/common/SkinEngine.h \ ../include/lgi/common/Slider.h \ ../include/lgi/common/Splitter.h \ ../include/lgi/common/Stat.h \ ../include/lgi/common/StatusBar.h \ ../include/lgi/common/Store3Defs.h \ ../include/lgi/common/Stream.h \ ../include/lgi/common/StringClass.h \ ../include/lgi/common/StringLayout.h \ ../include/lgi/common/StructuredIo.h \ ../include/lgi/common/StructuredLog.h \ ../include/lgi/common/SubProcess.h \ ../include/lgi/common/TableLayout.h \ ../include/lgi/common/TabView.h \ ../include/lgi/common/TextConvert.h \ ../include/lgi/common/TextFile.h \ ../include/lgi/common/TextLabel.h \ ../include/lgi/common/TextLog.h \ ../include/lgi/common/TextView3.h \ ../include/lgi/common/Thread.h \ ../include/lgi/common/ThreadEvent.h \ ../include/lgi/common/TimeZoneInfo.h \ ../include/lgi/common/Token.h \ ../include/lgi/common/ToolBar.h \ ../include/lgi/common/ToolTip.h \ ../include/lgi/common/TrayIcon.h \ ../include/lgi/common/Tree.h \ ../include/lgi/common/TypeToString.h \ ../include/lgi/common/Undo.h \ ../include/lgi/common/Unicode.h \ ../include/lgi/common/UnicodeString.h \ ../include/lgi/common/UnrolledList.h \ ../include/lgi/common/Uri.h \ ../include/lgi/common/Variant.h \ ../include/lgi/common/View.h \ ../include/lgi/common/Widgets.h \ ../include/lgi/common/Window.h \ ../include/lgi/common/XmlTree.h \ ../include/lgi/haiku/LgiOsClasses.h \ ../include/lgi/haiku/LgiOsDefs.h \ ../include/lgi/linux/Gtk/LgiWidget.h \ ../include/lgi/linux/Gtk/LgiWinManGlue.h \ ../include/lgi/mac/cocoa/LCocoaView.h \ ../include/lgi/mac/cocoa/LgiMac.h \ ../include/lgi/mac/cocoa/LgiOs.h \ ../include/lgi/mac/cocoa/LgiOsClasses.h \ ../include/lgi/mac/cocoa/LgiOsDefs.h \ ../include/lgi/mac/cocoa/ObjCWrapper.h \ ../include/lgi/mac/cocoa/SymLookup.h \ ../private/common/FontPriv.h \ ../private/common/ViewPriv.h \ ../private/haiku/AppPriv.h \ ../private/linux/AppPriv.h \ ../src/common/Gdc2/15Bit.cpp \ ../src/common/Gdc2/16Bit.cpp \ ../src/common/Gdc2/24Bit.cpp \ ../src/common/Gdc2/32Bit.cpp \ ../src/common/Gdc2/8Bit.cpp \ ../src/common/Gdc2/Alpha.cpp \ ../src/common/Gdc2/Colour.cpp \ ../src/common/Gdc2/Filters/Filter.cpp \ ../src/common/Gdc2/Font/Charset.cpp \ ../src/common/Gdc2/Font/DisplayString.cpp \ ../src/common/Gdc2/Font/Font.cpp \ ../src/common/Gdc2/Font/FontSystem.cpp \ ../src/common/Gdc2/Font/FontType.cpp \ ../src/common/Gdc2/Font/StringLayout.cpp \ ../src/common/Gdc2/Font/TypeFace.cpp \ ../src/common/Gdc2/GdcCommon.cpp \ ../src/common/Gdc2/Path/Path.cpp \ ../src/common/Gdc2/Rect.cpp \ ../src/common/Gdc2/RopsCases.cpp \ ../src/common/Gdc2/Surface.cpp \ ../src/common/Gdc2/Tools/ColourReduce.cpp \ ../src/common/Gdc2/Tools/GdcTools.cpp \ ../src/common/General/Containers.cpp \ ../src/common/General/DateTime.cpp \ ../src/common/General/ExeCheck.cpp \ ../src/common/General/FileCommon.cpp \ ../src/common/General/Password.cpp \ ../src/common/General/Properties.cpp \ ../src/common/Hash/md5/md5.c \ ../src/common/Hash/md5/md5.h \ ../src/common/Hash/sha1/sha1.c \ ../src/common/Hash/sha1/sha1.h \ ../src/common/Lgi/Alert.cpp \ ../src/common/Lgi/AppCommon.cpp \ ../src/common/Lgi/ClipBoardCommon.cpp \ ../src/common/Lgi/Css.cpp \ ../src/common/Lgi/CssTools.cpp \ ../src/common/Lgi/DataDlg.cpp \ ../src/common/Lgi/DragAndDropCommon.cpp \ ../src/common/Lgi/FileSelect.cpp \ ../src/common/Lgi/FindReplace.cpp \ ../src/common/Lgi/FontSelect.cpp \ ../src/common/Lgi/GuiUtils.cpp \ ../src/common/Lgi/Input.cpp \ ../src/common/Lgi/LgiCommon.cpp \ ../src/common/Lgi/Library.cpp \ ../src/common/Lgi/LMsg.cpp \ ../src/common/Lgi/MemStream.cpp \ ../src/common/Lgi/MenuCommon.cpp \ ../src/common/Lgi/Mru.cpp \ ../src/common/Lgi/Mutex.cpp \ ../src/common/Lgi/Object.cpp \ ../src/common/Lgi/OptionsFile.cpp \ ../src/common/Lgi/Rand.cpp \ ../src/common/Lgi/Stream.cpp \ ../src/common/Lgi/ThreadCommon.cpp \ ../src/common/Lgi/ThreadEvent.cpp \ ../src/common/Lgi/ToolTip.cpp \ ../src/common/Lgi/TrayIcon.cpp \ ../src/common/Lgi/Variant.cpp \ ../src/common/Lgi/ViewCommon.cpp \ ../src/common/Lgi/WindowCommon.cpp \ ../src/common/Net/Base64.cpp \ ../src/common/Net/MDStringToDigest.cpp \ ../src/common/Net/Net.cpp \ ../src/common/Net/Uri.cpp \ ../src/common/Resource/LgiRes.cpp \ ../src/common/Resource/Res.cpp \ ../src/common/Skins/Gel/Gel.cpp \ ../src/common/Text/DocView.cpp \ ../src/common/Text/String.cpp \ ../src/common/Text/TextConvert.cpp \ ../src/common/Text/TextView3.cpp \ ../src/common/Text/Token.cpp \ ../src/common/Text/Unicode.cpp \ ../src/common/Text/Utf8.cpp \ ../src/common/Text/XmlTree.cpp \ ../src/common/Widgets/Bitmap.cpp \ ../src/common/Widgets/Box.cpp \ ../src/common/Widgets/Button.cpp \ ../src/common/Widgets/CheckBox.cpp \ ../src/common/Widgets/Combo.cpp \ ../src/common/Widgets/Edit.cpp \ ../src/common/Widgets/ItemContainer.cpp \ ../src/common/Widgets/List.cpp \ ../src/common/Widgets/Panel.cpp \ ../src/common/Widgets/Popup.cpp \ ../src/common/Widgets/Progress.cpp \ ../src/common/Widgets/ProgressDlg.cpp \ ../src/common/Widgets/RadioGroup.cpp \ ../src/common/Widgets/ScrollBar.cpp \ ../src/common/Widgets/Slider.cpp \ ../src/common/Widgets/Splitter.cpp \ ../src/common/Widgets/StatusBar.cpp \ ../src/common/Widgets/TableLayout.cpp \ ../src/common/Widgets/TabView.cpp \ ../src/common/Widgets/TextLabel.cpp \ ../src/common/Widgets/ToolBar.cpp \ ../src/common/Widgets/Tree.cpp \ ../src/haiku/App.cpp \ ../src/haiku/ClipBoard.cpp \ ../src/haiku/DragAndDrop.cpp \ ../src/haiku/File.cpp \ ../src/haiku/Gdc2.cpp \ ../src/haiku/General.cpp \ ../src/haiku/Layout.cpp \ ../src/haiku/Mem.cpp \ ../src/haiku/MemDC.cpp \ ../src/haiku/Menu.cpp \ ../src/haiku/PrintDC.cpp \ ../src/haiku/Printer.cpp \ ../src/haiku/ScreenDC.cpp \ ../src/haiku/ShowFileProp_Haiku.cpp \ ../src/haiku/Thread.cpp \ ../src/haiku/View.cpp \ ../src/haiku/Widgets.cpp \ ../src/haiku/Window.cpp \ ../src/posix/SubProcess.cpp export Build=$(Build); \ $(MAKE) -C ../ -f ./haiku/Makefile.haiku -include $(Objects:.o=.d) # Clean just this target clean : rm -rf $(BuildDir) $(Target) @echo Cleaned $(BuildDir) $(Target) # Clean all targets cleanall : rm -rf $(BuildDir) $(Target) @echo Cleaned $(BuildDir) $(Target) +make -C "../" -f ./haiku/Makefile.haiku clean VPATH=$(BuildDir) \ ../src/common/Text/Homoglyphs \ ../src/common/Text \ ../src/common/Net \ ../src/common/Lgi \ ../src/common/Gdc2/Filters \ ../src/common/Coding \ ./src diff --git a/ide/src/IdeProject.cpp b/ide/src/IdeProject.cpp --- a/ide/src/IdeProject.cpp +++ b/ide/src/IdeProject.cpp @@ -1,4332 +1,4335 @@ #if defined(WIN32) #include #else #include #endif #include #include "lgi/common/Lgi.h" #include "lgi/common/DragAndDrop.h" #include "lgi/common/Token.h" #include "lgi/common/Combo.h" #include "lgi/common/Net.h" #include "lgi/common/ListItemCheckBox.h" #include "lgi/common/ClipBoard.h" #include "lgi/common/DropFiles.h" #include "lgi/common/SubProcess.h" #include "lgi/common/Css.h" #include "lgi/common/TableLayout.h" #include "lgi/common/TextLabel.h" #include "lgi/common/Button.h" #include "lgi/common/RegKey.h" #include "lgi/common/FileSelect.h" #include "lgi/common/Menu.h" #include "LgiIde.h" #include "resdefs.h" #include "FtpThread.h" #include "ProjectNode.h" #include "WebFldDlg.h" extern const char *Untitled; const char SourcePatterns[] = "*.c;*.h;*.cpp;*.cc;*.java;*.d;*.php;*.html;*.css;*.js"; const char *AddFilesProgress::DefaultExt = "c,cpp,cc,cxx,h,hpp,hxx,html,css,json,js,jsx,txt,png,jpg,jpeg,rc,xml,mk,paths,makefile,py,java,php"; const char *VsBinaries[] = {"devenv.com", "WDExpress.exe"}; #define USE_OPEN_PROGRESS 1 #define STOP_BUILD_TIMEOUT 2000 #ifdef WINDOWS #define LGI_STATIC_LIBRARY_EXT "lib" #else #define LGI_STATIC_LIBRARY_EXT "a" #endif const char *PlatformNames[] = { "Windows", "Linux", "Mac", "Haiku", 0 }; int PlatformCtrlId[] = { IDC_WIN32, IDC_LINUX, IDC_MAC, IDC_HAIKU, 0 }; const char *PlatformDynamicLibraryExt(IdePlatform Platform) { if (Platform == PlatformWin) return "dll"; if (Platform == PlatformMac) return "dylib"; return "so"; } const char *PlatformSharedLibraryExt(IdePlatform Platform) { if (Platform == PlatformWin) return "lib"; return "a"; } const char *PlatformExecutableExt(IdePlatform Platform) { if (Platform == PlatformWin) return ".exe"; return ""; } char *ToUnixPath(char *s) { if (s) { char *c; while ((c = strchr(s, '\\'))) *c = '/'; } return s; } const char *CastEmpty(char *s) { return s ? s : ""; } bool FindInPath(LString &Exe) { LString::Array Path = LString(getenv("PATH")).Split(LGI_PATH_SEPARATOR); for (unsigned i=0; i SubProc; LString::Array BuildConfigs; LString::Array PostBuild; int AppHnd; enum CompilerType { DefaultCompiler, VisualStudio, MingW, Gcc, CrossCompiler, PythonScript, IAR, Nmake, Cygwin, Xcode, } Compiler; enum ArchType { DefaultArch, ArchX32, ArchX64, ArchArm6, ArchArm7, } Arch; public: BuildThread(IdeProject *proj, char *makefile, bool clean, BuildConfig config, bool all, int wordsize); ~BuildThread(); ssize_t Write(const void *Buffer, ssize_t Size, int Flags = 0) override; LString FindExe(); LAutoString WinToMingWPath(const char *path); int Main() override; }; class IdeProjectPrivate { public: AppWnd *App; IdeProject *Project; bool Dirty, UserFileDirty; LString FileName; IdeProject *ParentProject; IdeProjectSettings Settings; LAutoPtr Thread; LHashTbl, ProjectNode*> Nodes; int NextNodeId; ProjectNode *DepParent = NULL; // Threads LAutoPtr CreateMakefile; // User info file LString UserFile; LHashTbl,int> UserNodeFlags; IdeProjectPrivate(AppWnd *a, IdeProject *project, ProjectNode *depParent) : Project(project), Settings(project), DepParent(depParent) { App = a; Dirty = false; UserFileDirty = false; ParentProject = 0; NextNodeId = 1; } void CollectAllFiles(LTreeNode *Base, LArray &Files, bool SubProjects, int Platform); }; LString ToPlatformPath(const char *s, IdePlatform platform) { LString p = s; if (platform == PlatformWin) return p.Replace("/", "\\"); else return p.Replace("\\", "/"); } class MakefileThread : public LThread, public LCancel { IdeProjectPrivate *d; IdeProject *Proj; IdePlatform Platform; LStream *Log; bool BuildAfterwards; bool HasError; void ToNativePath(char *in) { for (auto s = in; *s; s++) { if (Platform == PlatformWin) { if (*s == '/') *s = '\\'; } else { if (*s == '\\') *s = '/'; } } } public: static int Instances; MakefileThread(IdeProjectPrivate *priv, IdePlatform platform, bool Build) : LThread("MakefileThread") { Instances++; d = priv; Proj = d->Project; Platform = platform; BuildAfterwards = Build; HasError = false; Log = d->App->GetBuildLog(); Run(); } ~MakefileThread() { Cancel(); while (!IsExited()) LSleep(1); Instances--; } void OnError(const char *Fmt, ...) { va_list Arg; va_start(Arg, Fmt); LStreamPrintf(Log, 0, Fmt, Arg); va_end(Arg); HasError = true; } void EmbedAppIcon(LStream &m) { // Is there an application icon configured? auto AppIcon = d->Settings.GetStr(ProjApplicationIcon, NULL, Platform); if (!AppIcon) return; if (Platform == PlatformHaiku) { bool showDebugLogging = false; // Find hvif_to_rdef.py LFile::Path scriptPath(LSP_APP_INSTALL); scriptPath += "../src/haiku/hvif_to_rdef.py"; auto script = scriptPath.GetFull(); if (!scriptPath.Exists()) { Log->Print("%s:%i - Failed to find '%s'\n", _FL, script.Get()); return; } // Create a path for the rdef file auto Exe = Proj->GetExecutable(Platform); auto appName = LGetLeaf(Exe); LString rdefPath; rdefPath.Printf("$(Build)/%s.rdef", appName); LString rsrcPath = rdefPath.Replace(".rdef", ".rsrc"); auto Ext = LGetExtension(AppIcon); if (!Stricmp(Ext, "hvif")) { // Convert 'hvif' to 'rdef' m.Print(" %s %s %s %s\n", script.Get(), AppIcon, rdefPath.Get(), appName); // Convert from .rdef to .rsrc using 'rc' m.Print(" rc %s\n", rdefPath.Get()); if (showDebugLogging) m.Print(" ls -l %s\n", rsrcPath.Get()); // Embed in binary using 'resattr' m.Print(" resattr -o $(Target) %s\n" " xres -o $(Target) %s\n", rsrcPath.Get(), rsrcPath.Get()); if (showDebugLogging) // Print result: m.Print(" xres -l $(Target)\n"); } else { Log->Print("%s:%i - No handler '%s' file type.\n", _FL, Ext); } } else { Log->Print("%s:%i - No handler for app icon embedding.\n", _FL); } } int Main() { const char *PlatformName = PlatformNames[Platform]; const char *PlatformLibraryExt = NULL; const char *PlatformStaticLibExt = NULL; const char *PlatformExeExt = ""; const char *CCompilerFlags = "-MMD -MP -fPIC -fno-inline"; const char *CppCompilerFlags = "$(CFlags) -fpermissive -std=c++14"; const char *TargetType = d->Settings.GetStr(ProjTargetType, NULL, Platform); const char *CompilerName = d->Settings.GetStr(ProjCompiler); LString LinkerFlags; LString CCompilerBinary = "gcc"; LString CppCompilerBinary = "g++"; LStream *Log = d->App->GetBuildLog(); bool IsExecutableTarget = TargetType && !stricmp(TargetType, "Executable"); bool IsDynamicLibrary = TargetType && !stricmp(TargetType, "DynamicLibrary"); LAssert(Log); if (!Log) return false; Log->Print("CreateMakefile for '%s'...\n", PlatformName); if (Platform == PlatformWin) { LinkerFlags = ",--enable-auto-import"; } else { if (IsDynamicLibrary) { LinkerFlags = ",-soname,$(TargetFile)"; } LinkerFlags += ",-export-dynamic,-R."; } auto Base = Proj->GetBasePath(); auto MakeFile = Proj->GetMakefile(Platform); LString MakeFilePath; Proj->CheckExists(MakeFile); if (!MakeFile) { MakeFilePath = Base.Get(); LFile::Path p(MakeFilePath, "makefile"); MakeFile = p.GetFull(); } else if (LIsRelativePath(MakeFile)) { LFile::Path p(Base); p += MakeFile; MakeFile = p.GetFull(); p--; MakeFilePath = p.GetFull(); } else { LFile::Path p(MakeFile); p--; MakeFilePath = p.GetFull(); } // LGI_LIBRARY_EXT switch (Platform) { case PlatformWin: PlatformLibraryExt = "dll"; PlatformStaticLibExt = "lib"; PlatformExeExt = ".exe"; break; case PlatformLinux: case PlatformHaiku: PlatformLibraryExt = "so"; PlatformStaticLibExt = "a"; break; case PlatformMac: PlatformLibraryExt = "dylib"; PlatformStaticLibExt = "a"; break; default: LAssert(0); break; } if (CompilerName) { if (!stricmp(CompilerName, "cross")) { LString CBin = d->Settings.GetStr(ProjCCrossCompiler, NULL, Platform); if (CBin && !LFileExists(CBin)) FindInPath(CBin); if (CBin && LFileExists(CBin)) CCompilerBinary = CBin; else Log->Print("%s:%i - Error: C cross compiler '%s' not found.\n", _FL, CBin.Get()); LString CppBin = d->Settings.GetStr(ProjCppCrossCompiler, NULL, Platform); if (CppBin && !LFileExists(CppBin)) FindInPath(CppBin); if (CppBin && LFileExists(CppBin)) CppCompilerBinary = CppBin; else Log->Print("%s:%i - Error: C++ cross compiler '%s' not found.\n", _FL, CppBin.Get()); } } LFile m; if (!m.Open(MakeFile, O_WRITE)) { Log->Print("Error: Failed to open '%s' for writing.\n", MakeFile.Get()); return false; } m.SetSize(0); m.Print("#!/usr/bin/make\n" "#\n" "# This makefile generated by LgiIde\n" "# http://www.memecode.com/lgi.php\n" "#\n" "\n" ".SILENT :\n" "\n" "CC = %s\n" "CPP = %s\n", CCompilerBinary.Get(), CppCompilerBinary.Get()); // Collect all files that require building LArray Files; d->CollectAllFiles ( Proj, Files, false, 1 << Platform ); if (IsExecutableTarget) { auto Exe = Proj->GetExecutable(Platform); if (Exe) { if (LIsRelativePath(Exe)) m.Print("Target = %s\n", ToPlatformPath(Exe, Platform).Get()); else { auto RelExe = LMakeRelativePath(Base, Exe); if (Base && RelExe) { m.Print("Target = %s\n", ToPlatformPath(RelExe, Platform).Get()); } else { Log->Print("%s:%i - Error: Missing path (%s, %s).\n", _FL, Base.Get(), RelExe.Get()); return false; } } } else { Log->Print("%s:%i - Error: No executable name specified (%s, %s).\n", _FL, TargetType, d->FileName.Get()); return false; } } else { LString Target = Proj->GetTargetName(Platform); if (Target) m.Print("Target = %s\n", ToPlatformPath(Target, Platform).Get()); else { Log->Print("%s:%i - Error: No target name specified.\n", _FL); return false; } } // Output the build mode, flags and some paths auto BuildMode = d->App->GetBuildMode(); auto BuildModeName = toString(BuildMode); m.Print("ifndef Build\n" " Build = %s\n" "endif\n", BuildModeName); m.Print("MakeDir := $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))\n"); LString sDefines[BuildMax]; LString sLibs[BuildMax]; LString sIncludes[BuildMax]; LString sCompilerOpts[BuildMax]; const char *ExtraLinkFlags = NULL; const char *ExeFlags = NULL; if (Platform == PlatformWin) { ExtraLinkFlags = ""; ExeFlags = " -mwindows"; m.Print("BuildDir = $(Build)\n" "\n" "CFlags = %s\n" "CppFlags = %s\n", CCompilerFlags, CppCompilerFlags); const char *DefDefs = "-DWIN32 -D_REENTRANT"; sDefines[BuildDebug] = DefDefs; sDefines[BuildRelease] = DefDefs; } else { LString PlatformCap = PlatformName; ExtraLinkFlags = ""; ExeFlags = ""; m.Print("BuildDir = $(Build)\n" "\n" "CFlags = %s\n" "CppFlags = %s\n", CCompilerFlags, CppCompilerFlags); sDefines[0].Printf("-D%s -D_REENTRANT", PlatformCap.Upper().Get()); #ifdef LINUX sDefines[BuildDebug] += " -D_FILE_OFFSET_BITS=64"; // >:-( sDefines[BuildDebug] += " -DPOSIX"; #endif sDefines[BuildRelease] = sDefines[BuildDebug]; } List Deps; Proj->GetChildProjects(Deps); for (int Cfg = BuildDebug; Cfg < BuildMax; Cfg++) { // Set the config auto cfgName = toString((BuildConfig)Cfg); d->Settings.SetCurrentConfig(cfgName); // Get the defines setup auto PDefs = d->Settings.GetStr(ProjDefines, NULL, Platform); if (ValidStr(PDefs)) { LToken Defs(PDefs, " ;,\r\n"); for (int i=0; iSettings.GetStr(ProjCompileOptions, NULL, Platform); if (ValidStr(compOpts)) { sCompilerOpts[Cfg].Printf(" %s", compOpts); } // Collect all dependencies, output their lib names and paths LString PLibPaths = d->Settings.GetStr(ProjLibraryPaths, NULL, Platform); if (ValidStr(PLibPaths)) { LString::Array LibPaths = PLibPaths.Split("\n"); for (auto i: LibPaths) { LString s, in = i.Strip(); if (!in.Length()) continue; if (strchr("`-", in(0))) { s.Printf(" \\\n\t\t%s", in.Get()); } else { LString Rel; if (!LIsRelativePath(in)) Rel = LMakeRelativePath(Base, in); LString Final = Rel ? Rel.Get() : in.Get(); if (!Proj->CheckExists(Final)) OnError("%s:%i - Library path '%s' doesn't exist (from %s).\n", _FL, Final.Get(), Proj->GetFileName()); s.Printf(" \\\n\t\t-L%s", ToUnixPath(Final)); } sLibs[Cfg] += s; } } const char *PLibs = d->Settings.GetStr(ProjLibraries, NULL, Platform); if (ValidStr(PLibs)) { LToken Libs(PLibs, "\r\n"); for (int i=0; iCheckExists(l); s.Printf(" \\\n\t\t-l%s", ToUnixPath(l)); } sLibs[Cfg] += s; } } for (auto dep: Deps) { LString Target = dep->GetTargetName(Platform); if (Target) { char t[MAX_PATH_LEN]; strcpy_s(t, sizeof(t), Target); if (!strnicmp(t, "lib", 3)) memmove(t, t + 3, strlen(t + 3) + 1); char *dot = strrchr(t, '.'); if (dot) *dot = 0; LString s, sTarget = t; Proj->CheckExists(sTarget); s.Printf(" \\\n\t\t-l%s$(Tag)", ToUnixPath(sTarget)); sLibs[Cfg] += s; auto DepBase = dep->GetBasePath(); if (DepBase) { LString DepPath = DepBase.Get(); auto Rel = LMakeRelativePath(Base, DepPath); LString Final = Rel ? Rel.Get() : DepPath.Get(); Proj->CheckExists(Final); s.Printf(" \\\n\t\t-L%s/$(BuildDir)", ToUnixPath(Final.RStrip("/\\"))); sLibs[Cfg] += s; } } } // Includes // Do include paths LHashTbl,bool> Inc; auto ProjIncludes = d->Settings.GetStr(ProjIncludePaths, NULL, Platform); if (ValidStr(ProjIncludes)) { // Add settings include paths. LToken Paths(ProjIncludes, "\r\n"); for (int i=0; iCheckExists(pn)) OnError("%s:%i - Include path '%s' doesn't exist.\n", _FL, pn.Get()); else if (!Inc.Find(pn)) Inc.Add(pn, true); } } const char *SysIncludes = d->Settings.GetStr(ProjSystemIncludes, NULL, Platform); if (ValidStr(SysIncludes)) { // Add settings include paths. LToken Paths(SysIncludes, "\r\n"); for (int i=0; iCheckExists(pn)) OnError("%s:%i - System include path '%s' doesn't exist (from %s).\n", _FL, pn.Get(), Proj->GetFileName()); else if (!Inc.Find(pn)) Inc.Add(pn, true); } } LString::Array Incs; for (auto i: Inc) Incs.New() = i.key; Incs.Sort(); for (auto i: Incs) { LString s; if (*i == '`') { // Pass though shell cmd s.Printf(" \\\n\t\t%s", i.Get()); } else { LFile::Path p; if (LIsRelativePath(i)) { p = Base.Get(); p += i; } else p = i; auto rel = LMakeRelativePath(Base, p.GetFull()); s.Printf(" \\\n\t\t-I%s", ToUnixPath(rel ? rel : i)); } sIncludes[Cfg] += s; } } // Output the defs section for Debug and Release // Debug specific m.Print("\n" "ifeq ($(Build),Debug)\n" " CFlags += -g%s\n" " CppFlags += -g%s\n" " Tag = d\n" " Defs = -D_DEBUG %s\n" " Libs = %s\n" " Inc = %s\n", CastEmpty(sCompilerOpts[BuildDebug].Get()), CastEmpty(sCompilerOpts[BuildDebug].Get()), CastEmpty(sDefines [BuildDebug].Get()), CastEmpty(sLibs [BuildDebug].Get()), CastEmpty(sIncludes [BuildDebug].Get())); // Release specific m.Print("else\n" " CFlags += -s -Os%s\n" " CppFlags += -s -Os%s\n" " Defs = %s\n" " Libs = %s\n" " Inc = %s\n" "endif\n" "\n", CastEmpty(sCompilerOpts[BuildRelease].Get()), CastEmpty(sCompilerOpts[BuildRelease].Get()), CastEmpty(sDefines [BuildRelease].Get()), CastEmpty(sLibs [BuildRelease].Get()), CastEmpty(sIncludes [BuildRelease].Get())); if (Files.Length()) { LArray VPath; // Proj->BuildIncludePaths(VPath, false, false, Platform); auto AddVPath = [&VPath](LString p) { for (auto s: VPath) if (p == s) return; VPath.Add(p); }; // Do source code list... m.Print("# Dependencies\n" "Source =\t"); LString::Array SourceFiles; for (auto &n: Files) { if (n->GetType() == NodeSrc) { auto f = n->GetFileName(); LFile::Path p; if (LIsRelativePath(f)) { p = Base.Get(); p += f; } else { p = f; } auto rel = LMakeRelativePath(Base, p.GetFull()); if (rel) { if (rel.Find("./") == 0) rel = rel(2, -1); } LString path = rel ? rel.Get() : f; ToNativePath(path); SourceFiles.Add(path); if (true) { // Also add path of file to VPath. p--; AddVPath(p.GetFull()); } } } SourceFiles.Sort(); int c = 0; for (auto &src: SourceFiles) { if (c++) m.Print(" \\\n\t\t\t"); m.Print("%s", src.Get()); } + const char *TargetPattern = Platform == PlatformHaiku ? "$(abspath $<)" : "$<"; + m.Print("\n" "\n" "SourceC := $(filter %%.c,$(Source))\n" "ObjectsC := $(SourceC:.c=.o)\n" "SourceCpp := $(filter %%.cpp,$(Source))\n" "ObjectsCpp := $(SourceCpp:.cpp=.o)\n" "Objects := $(notdir $(ObjectsC) $(ObjectsCpp))\n" "Objects := $(addprefix $(BuildDir)/,$(Objects))\n" "Deps := $(patsubst %%.o,%%.d,$(Objects))\n" "\n" "$(BuildDir)/%%.o: %%.c\n" " mkdir -p $(@D)\n" " echo $(notdir $<) [$(Build)]\n" - " $(CC) $(Inc) $(CFlags) $(Defs) -c $< -o $@\n" + " $(CC) $(Inc) $(CFlags) $(Defs) -c %s -o $@\n" "\n" "$(BuildDir)/%%.o: %%.cpp\n" " mkdir -p $(@D)\n" " echo $(notdir $<) [$(Build)]\n" - " $(CPP) $(Inc) $(CppFlags) $(Defs) -c $< -o $@\n" - "\n"); + " $(CPP) $(Inc) $(CppFlags) $(Defs) -c %s -o $@\n" + "\n", + TargetPattern, TargetPattern); // Write out the target stuff m.Print("# Target\n"); LHashTbl,bool> DepFiles; if (TargetType) { if (IsExecutableTarget) { m.Print("# Executable target\n" "$(Target) :"); LStringPipe Rules; IdeProject *Dep; uint64 Last = LCurrentTime(); int Count = 0; auto It = Deps.begin(); for (Dep=*It; Dep && !IsCancelled(); Dep=*(++It), Count++) { // Get dependency to create it's own makefile... Dep->CreateMakefile(Platform, false); // Build a rule to make the dependency if any of the source changes... auto DepBase = Dep->GetBasePath(); auto TargetFile = Dep->GetTargetFile(Platform); if (DepBase && Base && TargetFile) { LString Rel = LMakeRelativePath(Base, DepBase); // Add tag to target name auto Parts = TargetFile.SplitDelimit("."); if (Parts.Length() == 2) TargetFile.Printf("lib%s$(Tag).%s", Parts[0].Get(), Parts[1].Get()); LFile::Path Buf(Rel); Buf += "$(BuildDir)"; Buf += TargetFile; auto FullBuf = Buf.GetFull(); ToNativePath(FullBuf); m.Print(" %s", FullBuf.Get()); LArray AllDeps; Dep->GetAllDependencies(AllDeps, Platform); LAssert(AllDeps.Length() > 0); AllDeps.Sort(StrSort); Rules.Print("%s : ", FullBuf.Get()); for (int i=0; iGetMakefile(Platform); if (Mk) { LString MakefileRel = LMakeRelativePath(DepBase, Mk); if (MakefileRel) { ToNativePath(MakefileRel); Rules.Print(" -f %s", MakefileRel.Get()); } else if (auto DepMakefile = strrchr(Mk, DIR_CHAR)) { Rules.Print(" -f %s", DepMakefile + 1); } } else { Mk = Dep->GetMakefile(Platform); OnError("%s:%i - No makefile for '%s'\n", _FL, Dep->GetFullPath().Get()); } Rules.Print("\n\n"); } uint64 Now = LCurrentTime(); if (Now - Last > 1000) { Last = Now; Log->Print("Building deps %i%%...\n", (int) (((int64)Count+1)*100/Deps.Length())); } } m.Print(" $(Objects)\n" " mkdir -p $(BuildDir)\n" " @echo Linking $(Target) [$(Build)]...\n" " $(CPP)%s%s %s%s -o \\\n" " $(Target) $(Objects) $(Libs)\n", ExtraLinkFlags, ExeFlags, ValidStr(LinkerFlags) ? "-Wl" : "", LinkerFlags.Get()); EmbedAppIcon(m); LString PostBuildCmds = d->Settings.GetStr(ProjPostBuildCommands, NULL, Platform); if (ValidStr(PostBuildCmds)) { LString::Array a = PostBuildCmds.Split("\n"); for (unsigned i=0; iGetMakefile(Platform); if (Mk) { auto DepBase = Dep->GetBasePath(); Dep->CheckExists(DepBase); LString DepFolderPath = LMakeRelativePath(Base, DepBase); if (!DepFolderPath) DepFolderPath = DepBase; ToNativePath(DepFolderPath); m.Print(" +make -C \"%s\"", DepFolderPath.Get()); LString MakefileRel = LMakeRelativePath(DepBase, Mk); if (MakefileRel) { ToNativePath(MakefileRel); m.Print(" -f %s clean\n", MakefileRel.Get()); } else if (auto DepMakefile = strrchr(Mk, DIR_CHAR)) { m.Print(" -f %s clean\n", DepMakefile + 1); } } } m.Print("\n"); } // Shared library else if (!stricmp(TargetType, "DynamicLibrary")) { m.Print("TargetFile = lib$(Target)$(Tag).%s\n" "$(TargetFile) : $(Objects)\n" " mkdir -p $(BuildDir)\n" " @echo Linking $(TargetFile) [$(Build)]...\n" " $(CPP)$s -shared \\\n" " %s%s \\\n" " -o $(BuildDir)/$(TargetFile) \\\n" " $(Objects) \\\n" " $(Libs)\n", PlatformLibraryExt, ValidStr(ExtraLinkFlags) ? "-Wl" : "", ExtraLinkFlags, LinkerFlags.Get()); LString PostBuildCmds = d->Settings.GetStr(ProjPostBuildCommands, NULL, Platform); if (ValidStr(PostBuildCmds)) { LString::Array a = PostBuildCmds.Split("\n"); for (unsigned i=0; iSettings.GetStr(ProjPostBuildCommands, NULL, Platform); if (ValidStr(PostBuildCmds)) { LString::Array a = PostBuildCmds.Split("\n"); for (unsigned i=0; iCheckExists(p); if (p && !strchr(p, '`')) { if (!LIsRelativePath(p)) { auto a = LMakeRelativePath(Base, p); m.Print(" \\\n\t%s", ToPlatformPath(a ? a.Get() : p.Get(), Platform).Get()); } else { m.Print(" \\\n\t%s", ToPlatformPath(p, Platform).Get()); } } } m.Print("\n"); const char *OtherMakefileRules = d->Settings.GetStr(ProjMakefileRules, NULL, Platform); if (ValidStr(OtherMakefileRules)) { m.Print("\n%s\n", OtherMakefileRules); } } } else { m.Print("# No files require building.\n"); } Log->Print("...Done: '%s'\n", MakeFile.Get()); if (BuildAfterwards) { if (!Proj->GetApp()->PostEvent(M_START_BUILD)) printf("%s:%i - PostEvent(M_START_BUILD) failed.\n", _FL); } return HasError; } void OnAfterMain() { Proj->GetApp()->PostEvent(M_MAKEFILES_CREATED, (LMessage::Param)Proj); } }; ///////////////////////////////////////////////////////////////////////////////////// NodeSource::~NodeSource() { if (nView) { nView->nSrc = 0; } } NodeView::~NodeView() { if (nSrc) { nSrc->nView = 0; } } ////////////////////////////////////////////////////////////////////////////////// int NodeSort(LTreeItem *a, LTreeItem *b, NativeInt d) { ProjectNode *A = dynamic_cast(a); ProjectNode *B = dynamic_cast(b); if (A && B) { if ( (A->GetType() == NodeDir) ^ (B->GetType() == NodeDir) ) { return A->GetType() == NodeDir ? -1 : 1; } else { return Stricmp(a->GetText(0), b->GetText(0)); } } return 0; } //////////////////////////////////////////////////////////////////////////////////////////////////// bool ReadVsProjFile(LString File, LString &Ver, LString::Array &Configs) { const char *Ext = LGetExtension(File); if (!Ext || _stricmp(Ext, "vcxproj")) return false; LFile f; if (!f.Open(File, O_READ)) return false; LXmlTree Io; LXmlTag r; if (Io.Read(&r, &f) && r.IsTag("Project")) { Ver = r.GetAttr("ToolsVersion"); LXmlTag *ItemGroup = r.GetChildTag("ItemGroup"); if (ItemGroup) for (auto c: ItemGroup->Children) { if (c->IsTag("ProjectConfiguration")) { char *s = c->GetAttr("Include"); if (s) Configs.New() = s; } } } else return false; return true; } //////////////////////////////////////////////////////////////////////////////////////////////////// BuildThread::BuildThread(IdeProject *proj, char *makefile, bool clean, BuildConfig config, bool all, int wordsize) : LThread("BuildThread") { Proj = proj; Makefile = makefile; Clean = clean; Config = config; All = all; WordSize = wordsize; Arch = DefaultArch; Compiler = DefaultCompiler; AppHnd = Proj->GetApp()->AddDispatch(); LString Cmds = proj->d->Settings.GetStr(ProjPostBuildCommands, NULL); if (ValidStr(Cmds)) PostBuild = Cmds.SplitDelimit("\r\n"); auto Ext = LGetExtension(Makefile); if (Ext && !_stricmp(Ext, "py")) Compiler = PythonScript; else if (Ext && !_stricmp(Ext, "sln")) Compiler = VisualStudio; else if (Ext && !Stricmp(Ext, "xcodeproj")) Compiler = Xcode; else { auto Comp = Proj->GetSettings()->GetStr(ProjCompiler); if (Comp) { // Use the specified compiler... if (!stricmp(Comp, "VisualStudio")) Compiler = VisualStudio; else if (!stricmp(Comp, "MingW")) Compiler = MingW; else if (!stricmp(Comp, "gcc")) Compiler = Gcc; else if (!stricmp(Comp, "cross")) Compiler = CrossCompiler; else if (!stricmp(Comp, "IAR")) Compiler = IAR; else if (!stricmp(Comp, "Cygwin")) Compiler = Cygwin; else { LgiTrace("%s:%i - Unknown compiler '%s' for makefile '%s'\n", _FL, Comp, makefile); LAssert(!"Unknown compiler."); } } } if (Compiler == DefaultCompiler) { // Use default compiler for platform... #ifdef WIN32 Compiler = VisualStudio; #else Compiler = Gcc; #endif } Run(); } BuildThread::~BuildThread() { if (SubProc) { bool b = SubProc->Interrupt(); LgiTrace("%s:%i - Sub process interrupt = %i.\n", _FL, b); } else LgiTrace("%s:%i - No sub process to interrupt.\n", _FL); uint64 Start = LCurrentTime(); bool Killed = false; while (!IsExited()) { LSleep(10); if (LCurrentTime() - Start > STOP_BUILD_TIMEOUT && SubProc) { if (Killed) { // Thread is stuck as well... ok kill that too!!! Argh - kill all the things!!!! Terminate(); LgiTrace("%s:%i - Thread killed.\n", _FL); Proj->GetApp()->PostEvent(M_BUILD_DONE); break; } else { // Kill the sub-process... bool b = SubProc->Kill(); Killed = true; LgiTrace("%s:%i - Sub process killed.\n", _FL, b); Start = LCurrentTime(); } } } } ssize_t BuildThread::Write(const void *Buffer, ssize_t Size, int Flags) { if (Proj->GetApp()) { Proj->GetApp()->PostEvent(M_APPEND_TEXT, (LMessage::Param)NewStr((char*)Buffer, Size), AppWnd::BuildTab); } return Size; } #pragma comment(lib, "version.lib") struct ProjInfo { LString Guid, Name, File; LHashTbl,ssize_t> Configs; }; LString BuildThread::FindExe() { LString::Array p = LGetPath(); if (Compiler == PythonScript) { #if defined(WINDOWS) uint32_t BestVer = 0; #else LString BestVer; #endif static LString Best; if (!Best) { const char *binName[] = {"python3", "python"}; for (int i=0; idwProductVersionMS > BestVer) { BestVer = v->dwProductVersionMS; Best = Path; } } } else if (!Best) { Best = Path; } free(Buf); #else LSubProcess p(Path, "--version"); LStringPipe o; if (p.Start()) p.Communicate(&o); auto Out = o.NewLStr(); auto Ver = Out.SplitDelimit().Last(); printf("Ver=%s\n", Ver.Get()); if (!BestVer || Stricmp(Ver.Get(), BestVer.Get()) > 0) { Best = Path; BestVer = Ver; } break; #endif } } } } return Best; } else if (Compiler == VisualStudio) { // Find the version we need: double fVer = 0.0; ProjInfo *First = NULL; LHashTbl, ProjInfo*> Projects; const char *Ext = LGetExtension(Makefile); if (Ext && !_stricmp(Ext, "sln")) { LFile f; if (f.Open(Makefile, O_READ)) { LString ProjKey = "Project("; LString StartSection = "GlobalSection("; LString EndSection = "EndGlobalSection"; LString Section; ssize_t Pos; LString::Array Ln = f.Read().SplitDelimit("\r\n"); for (size_t i = 0; i < Ln.Length(); i++) { LString s = Ln[i].Strip(); if ((Pos = s.Find(ProjKey)) >= 0) { LString::Array p = s.SplitDelimit("(),="); if (p.Length() > 5) { ProjInfo *i = new ProjInfo; i->Name = p[3].Strip(" \t\""); i->File = p[4].Strip(" \t\'\""); i->Guid = p[5].Strip(" \t\""); if (LIsRelativePath(i->File)) { char f[MAX_PATH_LEN]; LMakePath(f, sizeof(f), Makefile, ".."); LMakePath(f, sizeof(f), f, i->File); if (LFileExists(f)) i->File = f; /* else LAssert(0); */ } if (!First) First = i; Projects.Add(i->Guid, i); } } else if (s.Find(StartSection) >= 0) { auto p = s.SplitDelimit("() \t"); Section = p[1]; } else if (s.Find(EndSection) >= 0) { Section.Empty(); } else if (Section == "ProjectConfigurationPlatforms") { auto p = s.SplitDelimit(". \t"); auto i = Projects.Find(p[0]); if (i) { if (!i->Configs.Find(p[1])) { auto Idx = i->Configs.Length() + 1; i->Configs.Add(p[1], Idx); } } } else if (Section == "SolutionConfigurationPlatforms") { auto p = s.SplitDelimit(); auto config = p[0]; for (auto &it: Projects) { auto proj = it.value; if (!proj->Configs.Find(config)) proj->Configs.Add(config, proj->Configs.Length()+1); } } } } } else if (Ext && !_stricmp(Ext, "vcxproj")) { // ProjFile = Makefile; } else { if (Arch == DefaultArch) { if (sizeof(size_t) == 4) Arch = ArchX32; else Arch = ArchX64; } #ifdef _MSC_VER // Nmake file.. LString NmakePath; switch (_MSC_VER) { #ifdef _MSC_VER_VS2013 case _MSC_VER_VS2013: { if (Arch == ArchX32) NmakePath = "c:\\Program Files (x86)\\Microsoft Visual Studio 12.0\\VC\\bin\\nmake.exe"; else NmakePath = "c:\\Program Files (x86)\\Microsoft Visual Studio 12.0\\VC\\bin\\amd64\\nmake.exe"; break; } #endif #ifdef _MSC_VER_VS2015 case _MSC_VER_VS2015: { if (Arch == ArchX32) NmakePath = "c:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\bin\\nmake.exe"; else NmakePath = "c:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\bin\\amd64\\nmake.exe"; break; } #endif default: { LAssert(!"Impl me."); break; } } if (LFileExists(NmakePath)) { Compiler = Nmake; return NmakePath; } #endif } /* if (ProjFile && LFileExists(ProjFile)) { LString sVer; if (ReadVsProjFile(ProjFile, sVer, BuildConfigs)) { fVer = sVer.Float(); } } */ if (First) { for (auto i: First->Configs) { BuildConfigs[i.value - 1] = i.key; LFile f(First->File, O_READ); LXmlTree t; LXmlTag r; if (t.Read(&r, &f)) { if (r.IsTag("Project")) { auto ToolsVersion = r.GetAttr("ToolsVersion"); if (ToolsVersion) { fVer = atof(ToolsVersion); } } } } } if (fVer > 0.0) { for (int i=0; i LatestVer) { LatestVer = p[2].Float(); Latest = n; } } } if (Latest && LMakePath(p, sizeof(p), p, Latest) && LMakePath(p, sizeof(p), p, "common\\bin\\IarBuild.exe")) { if (LFileExists(p)) return p; } } else if (Compiler == Xcode) { return "/usr/bin/xcodebuild"; } else if (Compiler == Cygwin) { #ifdef WINDOWS LRegKey k(false, "HKEY_CURRENT_USER\\Software\\Cygwin\\Installations"); List n; k.GetValueNames(n); LString s; for (auto i:n) { s = k.GetStr(i); if (s.Find("\\??\\") == 0) s = s(4,-1); if (LDirExists(s)) { CygwinPath = s; break; } } n.DeleteArrays(); LFile::Path p(s, "bin\\make.exe"); if (p.Exists()) return p.GetFull(); #endif } else { if (Compiler == MingW) { // Have a look in the default spot first... const char *Def = "C:\\MinGW\\msys\\1.0\\bin\\make.exe"; if (LFileExists(Def)) { return Def; } } for (int i=0; iGetBasePath(); LString InitDir = InitFolder.Get(); LVariant Jobs; if (!Proj->GetApp()->GetOptions()->GetValue(OPT_Jobs, Jobs) || Jobs.CastInt32() < 1) Jobs = 2; LString TmpArgs, Include, Lib, LibPath, Path; if (Compiler == VisualStudio) { // TmpArgs.Printf("\"%s\" /make \"All - Win32 Debug\"", Makefile.Get()); LString BuildConf = "All - Win32 Debug"; if (BuildConfigs.Length()) { auto Key = toString(Config); for (size_t i=0; i= 0) { if (!BuildConf || (c.Find("x64") >= 0 && BuildConf.Find("x64") < 0)) BuildConf = c; } } } TmpArgs.Printf("\"%s\" %s \"%s\"", Makefile.Get(), Clean ? "/Clean" : "/Build", BuildConf.Get()); } else if (Compiler == Nmake) { const char *DefInc[] = { "C:\\Program Files (x86)\\Microsoft Visual Studio 12.0\\VC\\INCLUDE", "C:\\Program Files (x86)\\Microsoft Visual Studio 12.0\\VC\\ATLMFC\\INCLUDE", "C:\\Program Files (x86)\\Windows Kits\\8.1\\include\\shared", "C:\\Program Files (x86)\\Windows Kits\\8.1\\include\\um", "C:\\Program Files (x86)\\Windows Kits\\8.1\\include\\winrt" }; LString f; #define ADD_PATHS(out, in) \ for (unsigned i=0; iGetChildTag("name") : NULL; if (c) Conf = c->GetContent(); } } TmpArgs.Printf("\"%s\" %s %s -log warnings", Makefile.Get(), Clean ? "-clean" : "-make", Conf.Get()); } else if (Compiler == Xcode) { LString::Array Configs; Configs.SetFixedLength(false); LString a; a.Printf("-list -project \"%s\"", Makefile.Get()); LSubProcess Ls(Exe, a); if (Ls.Start()) { LStringPipe o; Ls.Communicate(&o); auto key = "Build Configurations:"; auto lines = o.NewLStr().SplitDelimit("\n"); int inKey = -1; for (auto l: lines) { int ws = 0; for (int i=0; i=0) inKey = ws; else if (inKey>=0) { if (ws>inKey) { Configs.New() = l.Strip(); } else { inKey = -1; } } // printf("%s\n", l.Get()); } } auto Config = Configs.Length() > 0 ? Configs[0] : LString("Debug"); TmpArgs.Printf("-project \"%s\" -configuration %s", Makefile.Get(), Config.Get()); } else { if (Compiler == Cygwin) { LFile::Path p(CygwinPath, "bin"); Path = p.GetFull(); } if (Compiler == MingW) { LString a; char *Dir = strrchr(MakePath, DIR_CHAR); #if 1 TmpArgs.Printf("/C \"%s\"", Exe.Get()); /* As of MSYS v1.0.18 the support for multiple jobs causes make to hang: http://sourceforge.net/p/mingw/bugs/1950/ http://mingw-users.1079350.n2.nabble.com/MSYS-make-freezes-td7579038.html Apparently it'll be "fixed" in v1.0.19. We'll see. >:-( if (Jobs.CastInt32() > 1) a.Print(" -j %i", Jobs.CastInt32()); */ a.Printf(" -f \"%s\"", Dir ? Dir + 1 : MakePath.Get()); TmpArgs += a; #else TmpArgs.Printf("/C set"); #endif Exe = "C:\\Windows\\System32\\cmd.exe"; } else { if (Jobs.CastInt32()) TmpArgs.Printf("-j %i -f \"%s\"", Jobs.CastInt32(), MakePath.Get()); else TmpArgs.Printf("-f \"%s\"", MakePath.Get()); } if (Clean) { if (All) TmpArgs += " cleanall"; else TmpArgs += " clean"; } if (Config == BuildRelease) TmpArgs += " Build=Release"; } PostThreadEvent(AppHnd, M_SELECT_TAB, AppWnd::BuildTab); LString Msg; // Msg.Printf("InitDir: %s\n", InitDir.Get()); Proj->GetApp()->PostEvent(M_APPEND_TEXT, (LMessage::Param)NewStr(Msg), AppWnd::BuildTab); Print("Making: %s (%s)\n", MakePath.Get(), TmpArgs.Get()); if (SubProc.Reset(new LSubProcess(Exe, TmpArgs))) { SubProc->SetNewGroup(false); SubProc->SetInitFolder(InitDir); if (Include) SubProc->SetEnvironment("INCLUDE", Include); if (Lib) SubProc->SetEnvironment("LIB", Lib); if (LibPath) SubProc->SetEnvironment("LIBPATHS", LibPath); if (Path) { LString Cur = getenv("PATH"); LString New = Cur + LGI_PATH_SEPARATOR + Path; SubProc->SetEnvironment("PATH", New); } // SubProc->SetEnvironment("DLL", "1"); if (Compiler == MingW) SubProc->SetEnvironment("PATH", "c:\\MingW\\bin;C:\\MinGW\\msys\\1.0\\bin;%PATH%"); if ((Status = SubProc->Start(true, false))) { // Read all the output char Buf[256]; ssize_t rd; while ( (rd = SubProc->Read(Buf, sizeof(Buf))) > 0 ) { Write(Buf, rd); } uint32_t ex = SubProc->Wait(); Print("Make exited with %i (0x%x)\n", ex, ex); if (Compiler == IAR && ex == 0 && PostBuild.Length()) { for (auto Cmd : PostBuild) { auto p = Cmd.Split(" ", 1); if (p[0].Equals("cd")) { if (p.Length() > 1) FileDev->SetCurrentFolder(p[1]); else LAssert(!"No folder for cd?"); } else { LSubProcess PostCmd(p[0], p.Length() > 1 ? p[1] : LString()); if (PostCmd.Start(true, false)) { char Buf[256]; ssize_t rd; while ( (rd = PostCmd.Read(Buf, sizeof(Buf))) > 0 ) { Write(Buf, rd); } } } } } } else { // Create a nice error message. LString ErrStr = LErrorCodeToString(SubProc->GetErrorCode()); if (ErrStr) { char *e = ErrStr.Get() + ErrStr.Length(); while (e > ErrStr && strchr(" \t\r\n.", e[-1])) *(--e) = 0; } sprintf_s(ErrBuf, sizeof(ErrBuf), "Running make failed with %i (%s)\n", SubProc->GetErrorCode(), ErrStr.Get()); Err = ErrBuf; } } } else { Err = "Couldn't find program to build makefile."; LgiTrace("%s,%i - %s.\n", _FL, Err); } AppWnd *w = Proj->GetApp(); if (w) { w->PostEvent(M_BUILD_DONE); if (Err) Proj->GetApp()->PostEvent(M_BUILD_ERR, 0, (LMessage::Param)NewStr(Err)); } else LAssert(0); return 0; } ////////////////////////////////////////////////////////////////////////////////// IdeProject::IdeProject(AppWnd *App, ProjectNode *DepParent) : IdeCommon(NULL) { Project = this; d = new IdeProjectPrivate(App, this, DepParent); Tag = NewStr("Project"); } IdeProject::~IdeProject() { d->App->OnProjectDestroy(this); LXmlTag::Empty(true); DeleteObj(d); } bool IdeProject::OnNode(const char *Path, ProjectNode *Node, bool Add) { if (!Path || !Node) { LAssert(0); return false; } char Full[MAX_PATH_LEN]; if (LIsRelativePath(Path)) { LAutoString Base = GetBasePath(); if (LMakePath(Full, sizeof(Full), Base, Path)) { Path = Full; } } bool Status = false; if (Add) Status = d->Nodes.Add(Path, Node); else Status = d->Nodes.Delete(Path); LString p = Path; if (Status && CheckExists(p)) d->App->OnNode(p, Node, Add ? FindSymbolSystem::FileAdd : FindSymbolSystem::FileRemove); return Status; } void IdeProject::ShowFileProperties(const char *File) { ProjectNode *Node = NULL; if (Node) Node->OnProperties(); } const char *IdeProject::GetFileComment() { return d->Settings.GetStr(ProjCommentFile); } const char *IdeProject::GetFunctionComment() { return d->Settings.GetStr(ProjCommentFunction); } IdeProject *IdeProject::GetParentProject() { return d->ParentProject; } void IdeProject::SetParentProject(IdeProject *p) { d->ParentProject = p; } bool IdeProject::GetChildProjects(List &c) { CollectAllSubProjects(c); return c.Length() > 0; } bool IdeProject::RelativePath(LString &Out, const char *In, bool Debug) { if (!In) return false; auto Base = GetBasePath(); if (!Base) return false; CheckExists(Base); if (Debug) LgiTrace("XmlBase='%s'\n In='%s'\n", Base.Get(), In); LToken b(Base, DIR_STR); LToken i(In, DIR_STR); char out[MAX_PATH_LEN] = ""; int outCh = 0; if (Debug) LgiTrace("Len %i-%i\n", b.Length(), i.Length()); auto ILen = i.Length() + (LDirExists(In) ? 0 : 1); auto Max = MIN(b.Length(), ILen); int Common = 0; for (; Common < Max; Common++) { #ifdef WIN32 #define StrCompare stricmp #else #define StrCompare strcmp #endif if (Debug) LgiTrace("Cmd '%s'-'%s'\n", b[Common], i[Common]); if (StrCompare(b[Common], i[Common]) != 0) { break; } } if (Debug) LgiTrace("Common=%i\n", Common); if (Common > 0) { if (Common < b.Length()) { out[outCh = 0] = 0; auto Back = b.Length() - Common; if (Debug) LgiTrace("Back=%i\n", (int)Back); for (int n=0; nSettings.GetStr(ProjExe); LString Exe; if (!PExe) { // Use the default exe name? Exe = GetExecutable(GetCurrentPlatform()); if (Exe) { printf("Exe='%s'\n", Exe.Get()); PExe = Exe; } } if (!PExe) return false; if (LIsRelativePath(PExe)) { auto Base = GetBasePath(); if (Base) LMakePath(Path, Len, Base, PExe); else return false; } else { strcpy_s(Path, Len, PExe); } return true; } LString IdeProject::GetMakefile(IdePlatform Platform) { const char *PMakefile = d->Settings.GetStr(ProjMakefile, NULL, Platform); if (!PMakefile) return LString(); LString Path; if (LIsRelativePath(PMakefile)) { auto Base = GetBasePath(); if (Base) { char p[MAX_PATH_LEN]; LMakePath(p, sizeof(p), Base, PMakefile); Path = p; } } else { Path = PMakefile; } return Path; } void IdeProject::Clean(bool All, BuildConfig Config) { if (!d->Thread && d->Settings.GetStr(ProjMakefile)) { auto m = GetMakefile(PlatformCurrent); if (m) { CheckExists(m); d->Thread.Reset(new BuildThread(this, m, true, Config, All, sizeof(ssize_t)*8)); } } } char *QuoteStr(char *s) { LStringPipe p(256); while (s && *s) { if (*s == ' ') { p.Push("\\ ", 2); } else p.Push(s, 1); s++; } return p.NewStr(); } class ExecuteThread : public LThread, public LStream, public LCancel { IdeProject *Proj; LString Exe, Args, Path; int Len; ExeAction Act; int AppHnd; public: ExecuteThread(IdeProject *proj, const char *exe, const char *args, char *path, ExeAction act) : LThread("ExecuteThread") { Len = 32 << 10; Proj = proj; Act = act; Exe = exe; Args = args; Path = path; DeleteOnExit = true; AppHnd = proj->GetApp()->AddDispatch(); Run(); } ~ExecuteThread() { Cancel(); while (!IsExited()) LSleep(1); } int Main() override { PostThreadEvent(AppHnd, M_SELECT_TAB, AppWnd::OutputTab); PostThreadEvent(AppHnd, M_APPEND_TEXT, 0, AppWnd::OutputTab); if (Exe) { if (Act == ExeDebug) { LSubProcess sub("kdbg", Exe); if (Path) sub.SetInitFolder(Path); if (sub.Start()) sub.Communicate(this, NULL, this); } else if (Act == ExeValgrind) { #ifdef LINUX LString ExePath = Proj->GetExecutable(GetCurrentPlatform()); if (ExePath) { char Path[MAX_PATH_LEN]; char *ExeLeaf = LGetLeaf(Exe); strcpy_s(Path, sizeof(Path), ExeLeaf ? ExeLeaf : Exe.Get()); LTrimDir(Path); const char *Term = NULL; const char *WorkDir = NULL; const char *Execute = NULL; switch (LGetWindowManager()) { case WM_Kde: Term = "konsole"; WorkDir = "--workdir "; Execute = "-e"; break; case WM_Gnome: Term = "gnome-terminal"; WorkDir = "--working-directory="; Execute = "-x"; break; } if (Term && WorkDir && Execute) { char *e = QuoteStr(ExePath); char *p = QuoteStr(Path); const char *a = Proj->GetExeArgs() ? Proj->GetExeArgs() : ""; char Args[512]; sprintf(Args, "%s%s " "--noclose " "%s valgrind --tool=memcheck --num-callers=20 %s %s", WorkDir, p, Execute, e, a); LExecute(Term, Args); } } #endif } else { LSubProcess sub(Exe, Args); if (Path) sub.SetInitFolder(Path); if (sub.Start()) sub.Communicate(this, NULL, this); } } return 0; } ssize_t Write(const void *Buffer, ssize_t Size, int Flags = 0) override { if (Len <= 0) return 0; PostThreadEvent(AppHnd, M_APPEND_TEXT, (LMessage::Param)NewStr((char*)Buffer, Size), AppWnd::OutputTab); Len -= Size; return Size; } }; LDebugContext *IdeProject::Execute(ExeAction Act, LString *ErrMsg) { auto Base = GetBasePath(); if (!Base) { if (ErrMsg) *ErrMsg = "No base path for project."; return NULL; } char e[MAX_PATH_LEN]; if (!GetExePath(e, sizeof(e))) { if (ErrMsg) *ErrMsg = "GetExePath failed."; return NULL; } if (!LFileExists(e)) { if (ErrMsg) ErrMsg->Printf("Executable '%s' doesn't exist.\n", e); return NULL; } const char *Args = d->Settings.GetStr(ProjArgs); const char *Env = d->Settings.GetStr(ProjEnv); LString InitDir = d->Settings.GetStr(ProjInitDir); int RunAsAdmin = d->Settings.GetInt(ProjDebugAdmin); #ifndef HAIKU if (Act == ExeDebug) { if (InitDir && LIsRelativePath(InitDir)) { LFile::Path p(Base); p += InitDir; InitDir = p.GetFull(); } return new LDebugContext(d->App, this, e, Args, RunAsAdmin != 0, Env, InitDir); } #endif new ExecuteThread( this, e, Args, Base, #if defined(HAIKU) || defined(WINDOWS) ExeRun // No gdb or valgrind #else Act #endif ); return NULL; } bool IdeProject::IsMakefileAScript() { auto m = GetMakefile(PlatformCurrent); if (m) { auto Ext = LGetExtension(m); if (!Stricmp(Ext, "py")) { // Not a makefile but a build script... can't update. return true; } } return false; } bool IdeProject::IsMakefileUpToDate() { if (IsMakefileAScript()) return true; // We can't know if it's up to date... List Proj; GetChildProjects(Proj); Proj.Insert(this); for (auto p: Proj) { // Is the project file modified after the makefile? auto Proj = p->GetFullPath(); uint64 ProjModTime = 0, MakeModTime = 0; LDirectory dir; if (dir.First(Proj)) { ProjModTime = dir.GetLastWriteTime(); dir.Close(); } auto m = p->GetMakefile(PlatformCurrent); if (!m) { d->App->GetBuildLog()->Print("Error: no makefile? (%s:%i)\n", _FL); break; } auto Ext = LGetExtension(m); if (!Stricmp(Ext, "py")) { // Not a makefile but a build script... can't update. return true; } if (dir.First(m)) { MakeModTime = dir.GetLastWriteTime(); dir.Close(); } #if 0 printf("IsMakefileUpToDate: Proj=%s - Timestamps " LPrintfInt64 " - " LPrintfInt64 "\n", Proj.Get(), ProjModTime, MakeModTime); #endif if (ProjModTime != 0 && MakeModTime != 0 && ProjModTime > MakeModTime) { // Need to rebuild the makefile... printf("Out of date.\n"); return false; } } return true; } bool IdeProject::FindDuplicateSymbols() { LStream *Log = d->App->GetBuildLog(); Log->Print("FindDuplicateSymbols starting...\n"); List Proj; CollectAllSubProjects(Proj); Proj.Insert(this); int Lines = 0; LHashTbl,int64> Map(200000); int Found = 0; for (auto p: Proj) { LString s = p->GetExecutable(GetCurrentPlatform()); if (s) { LString Args; Args.Printf("--print-size --defined-only -C %s", s.Get()); LSubProcess Nm("nm", Args); if (Nm.Start(true, false)) { char Buf[256]; LStringPipe q; for (ssize_t Rd = 0; (Rd = Nm.Read(Buf, sizeof(Buf))); ) q.Write(Buf, Rd); LString::Array a = q.NewLStr().SplitDelimit("\r\n"); LHashTbl,bool> Local(200000); for (auto &Ln: a) { LString::Array p = Ln.SplitDelimit(" \t", 3); if (!Local.Find(p.Last())) { Local.Add(p.Last(), true); // const char *Sz = p[1]; int64 Ours = p[1].Int(16); int64 Theirs = Map.Find(p.Last()); if (Theirs >= 0) { if (Ours != Theirs) { if (Found++ < 100) Log->Print(" %s (" LPrintfInt64 " -> " LPrintfInt64 ")\n", p.Last().Get(), Ours, Theirs); } } else if (Ours >= 0) { Map.Add(p.Last(), Ours); } else { printf("Bad line: %s\n", Ln.Get()); } } Lines++; } } } else printf("%s:%i - GetExecutable failed.\n", _FL); } /* char *Sym; for (int Count = Map.First(&Sym); Count; Count = Map.Next(&Sym)) { if (Count > 1) Log->Print(" %i: %s\n", Count, Sym); } */ Log->Print("FindDuplicateSymbols finished (%i lines)\n", Lines); return false; } bool IdeProject::FixMissingFiles() { FixMissingFilesDlg(this); return true; } void IdeProject::Build(bool All, BuildConfig Config) { if (d->Thread) { d->App->GetBuildLog()->Print("Error: Already building (%s:%i)\n", _FL); return; } auto m = GetMakefile(PlatformCurrent); CheckExists(m); if (!m) { d->App->GetBuildLog()->Print("Error: no makefile? (%s:%i)\n", _FL); return; } if (GetApp()) GetApp()->PostEvent(M_APPEND_TEXT, 0, 0); SetClean([this, m, All, Config](bool ok) { if (!ok) return; if (!IsMakefileUpToDate()) { CreateMakefile(GetCurrentPlatform(), true); } else { // Start the build thread... d->Thread.Reset ( new BuildThread ( this, m, false, Config, All, sizeof(size_t)*8 ) ); } }); } void IdeProject::StopBuild() { d->Thread.Reset(); } bool IdeProject::Serialize(bool Write) { return true; } AppWnd *IdeProject::GetApp() { return d->App; } const char *IdeProject::GetIncludePaths() { return d->Settings.GetStr(ProjIncludePaths); } const char *IdeProject::GetPreDefinedValues() { return d->Settings.GetStr(ProjDefines); } const char *IdeProject::GetCompileOptions() { return d->Settings.GetStr(ProjCompileOptions); } const char *IdeProject::GetExeArgs() { return d->Settings.GetStr(ProjArgs); } LString IdeProject::GetExecutable(IdePlatform Platform) { LString Bin = d->Settings.GetStr(ProjExe, NULL, Platform); auto TargetType = d->Settings.GetStr(ProjTargetType, NULL, Platform); auto Base = GetBasePath(); if (Bin) { if (LIsRelativePath(Bin) && Base) { char p[MAX_PATH_LEN]; if (LMakePath(p, sizeof(p), Base, Bin)) Bin = p; } return Bin; } // Create binary name from target: auto Target = GetTargetName(Platform); if (Target) { bool IsLibrary = Stristr(TargetType, "library"); int BuildMode = d->App->GetBuildMode(); const char *Name = BuildMode ? "Release" : "Debug"; const char *Postfix = BuildMode ? "" : "d"; switch (Platform) { case PlatformWin: { if (IsLibrary) Bin.Printf("%s%s.dll", Target.Get(), Postfix); else Bin = Target; break; } case PlatformMac: { if (IsLibrary) Bin.Printf("lib%s%s.dylib", Target.Get(), Postfix); else Bin = Target; break; } case PlatformLinux: case PlatformHaiku: { if (IsLibrary) Bin.Printf("lib%s%s.so", Target.Get(), Postfix); else Bin = Target; break; } default: { LAssert(0); printf("%s:%i - Unknown platform.\n", _FL); return LString(); } } // Find the actual file... if (!Base) { printf("%s:%i - GetBasePath failed.\n", _FL); return LString(); } char Path[MAX_PATH_LEN]; LMakePath(Path, sizeof(Path), Base, Name); LMakePath(Path, sizeof(Path), Path, Bin); Bin = Path; // This doesn't need to exist return Bin; } return LString(); } const char *IdeProject::GetFileName() { return d->FileName; } int IdeProject::GetPlatforms() { return PLATFORM_ALL; } LAutoString IdeProject::GetFullPath() { LAutoString Status; if (!d->FileName) { // LAssert(!"No path."); return Status; } LArray sections; IdeProject *proj = this; while ( proj && proj->GetFileName() && LIsRelativePath(proj->GetFileName())) { sections.AddAt(0, proj->GetFileName()); proj = proj->GetParentProject(); } if (!proj) { // LAssert(!"All projects have a relative path?"); return Status; // No absolute path in the parent projects? } char p[MAX_PATH_LEN]; strcpy_s(p, sizeof(p), proj->GetFileName()); // Copy the base path if (sections.Length() > 0) LTrimDir(p); // Trim off the filename for (int i=0; iFileName.Empty(); d->UserFile.Empty(); d->App->GetTree()->Insert(this); ProjectNode *f = new ProjectNode(this); if (f) { f->SetName("Source"); f->SetType(NodeDir); InsertTag(f); } f = new ProjectNode(this); if (f) { f->SetName("Headers"); f->SetType(NodeDir); InsertTag(f); } d->Settings.Set(ProjEditorTabSize, 4); d->Settings.Set(ProjEditorIndentSize, 4); d->Settings.Set(ProjEditorUseHardTabs, true); d->Dirty = true; Expanded(true); } ProjectStatus IdeProject::OpenFile(const char *FileName) { auto Log = d->App->GetBuildLog(); LProfile Prof("IdeProject::OpenFile"); Prof.HideResultsIfBelow(1000); Empty(); Prof.Add("Init"); d->UserNodeFlags.Empty(); if (LIsRelativePath(FileName)) { char p[MAX_PATH_LEN]; getcwd(p, sizeof(p)); LMakePath(p, sizeof(p), p, FileName); d->FileName = p; } else { d->FileName = FileName; } d->UserFile = d->FileName + "." + LCurrentUserName(); if (!d->FileName) { Log->Print("%s:%i - No filename.\n", _FL); return OpenError; } Prof.Add("FileOpen"); LFile f; LString FullPath = d->FileName.Get(); for (unsigned attempt = 0; attempt < 5; attempt++) { if (!CheckExists(FullPath) || !f.Open(FullPath, O_READWRITE)) { Log->Print("%s:%i - Retrying '%s', attempt %i.\n", _FL, FullPath.Get(), attempt+1); LSleep(10); continue; } else break; } if (!f.IsOpen()) { Log->Print("%s:%i - Error: Can't open '%s'.\n", _FL, FullPath.Get()); return OpenError; } Prof.Add("Xml"); LXmlTree x; LXmlTag r; if (!x.Read(&r, &f)) { Log->Print("%s:%i - Error: Can't read XML: %s\n", _FL, x.GetErrorMsg()); return OpenError; } Prof.Add("Progress Setup"); #if DEBUG_OPEN_PROGRESS int64 Nodes = r.CountTags(); LProgressDlg Prog(d->App, 1000); Prog.SetDescription("Loading project..."); Prog.SetLimits(0, Nodes); Prog.SetYieldTime(1000); Prog.SetAlwaysOnTop(true); #endif Prof.Add("UserFile"); if (LFileExists(d->UserFile)) { LFile Uf; if (Uf.Open(d->UserFile, O_READ)) { LString::Array Ln = Uf.Read().SplitDelimit("\n"); for (unsigned i=0; iUserNodeFlags.Add((int)p[0].Int(), (int)p[1].Int(16)); } } } if (!r.IsTag("Project")) { Log->Print("%s:%i - No 'Project' tag.\n", _FL); return OpenError; } Prof.Add("Serialize"); d->Settings.Serialize(&r, false /* read */); Prof.Add("IncludePathProcessing"); LString::Array Inc, Sys; auto plat = PlatformFlagsToEnum(d->App->GetPlatform()); BuildIncludePaths(Inc, &Sys, true, true, plat); d->App->GetFindSym()->SetIncludePaths(Inc, Sys, d->App->GetPlatform()); Prof.Add("OnOpen"); bool Ok = OnOpen( #if DEBUG_OPEN_PROGRESS &Prog, #else NULL, #endif &r); #if DEBUG_OPEN_PROGRESS if (Prog.IsCancelled()) return OpenCancel; else #endif if (!Ok) return OpenError; Prof.Add("Insert"); d->App->GetTree()->Insert(this); Expanded(true); return OpenOk; } bool IdeProject::SaveFile() { auto Full = GetFullPath(); if (ValidStr(Full) && d->Dirty) { LFile f; if (f.Open(Full, O_WRITE)) { f.SetSize(0); LXmlTree x; d->Settings.Serialize(this, true /* write */); if (x.Write(this, &f)) d->Dirty = false; else LgiTrace("%s:%i - Failed to write XML.\n", _FL); } else LgiTrace("%s:%i - Couldn't open '%s' for writing.\n", _FL, Full.Get()); } if (d->UserFileDirty) { LFile f; LAssert(d->UserFile.Get()); if (f.Open(d->UserFile, O_WRITE)) { f.SetSize(0); // Save user file details.. for (auto i : d->UserNodeFlags) f.Print("%i,%x\n", i.key, i.value); d->UserFileDirty = false; } } printf("\tIdeProject::SaveFile %i %i\n", d->Dirty, d->UserFileDirty); return !d->Dirty && !d->UserFileDirty; } void IdeProject::SetDirty() { d->Dirty = true; d->App->OnProjectChange(); } void IdeProject::SetUserFileDirty() { d->UserFileDirty = true; } bool IdeProject::GetExpanded(int Id) { int f = d->UserNodeFlags.Find(Id); return f >= 0 ? f : false; } void IdeProject::SetExpanded(int Id, bool Exp) { if (d->UserNodeFlags.Find(Id) != (int)Exp) { d->UserNodeFlags.Add(Id, Exp); SetUserFileDirty(); } } int IdeProject::AllocateId() { return d->NextNodeId++; } template bool CheckExists(LAutoString Base, T &p, Fn Setter, bool Debug) { LFile::Path Full; bool WasRel = LIsRelativePath(p); if (WasRel) { Full = Base.Get(); Full += p.Get(); } else Full = p.Get(); bool Ret = Full.Exists(); if (!Ret) { // Is the case wrong? for (int i=1; i%s\n", i, Leaf.Get(), dir.GetName()); Leaf = dir.GetName(); Matched = true; break; } } if (!Matched) break; } } if ((Ret = Full.Exists())) { LString Old = p.Get(); if (WasRel) { auto r = LMakeRelativePath(Base, Full); Setter(p, r); } else { Setter(p, Full.GetFull()); } if (Debug) printf("%s -> %s\n", Old.Get(), p.Get()); } } if (Debug) printf("CheckExists '%s' = %i\n", Full.GetFull().Get(), Ret); return Ret; } bool IdeProject::CheckExists(LString &p, bool Debug) { return ::CheckExists(GetBasePath(), p, [](LString &o, const char *i) { o = i; }, Debug); } bool IdeProject::CheckExists(LAutoString &p, bool Debug) { return ::CheckExists(GetBasePath(), p, [](LAutoString &o, const char *i) { o.Reset(NewStr(i)); }, Debug); } bool IdeProject::GetClean() { for (auto i: *this) { ProjectNode *p = dynamic_cast(i); if (p && !p->GetClean()) return false; } return !d->Dirty && !d->UserFileDirty; } void IdeProject::SetClean(std::function OnDone) { auto CleanNodes = [this, OnDone]() { // printf("IdeProject.SetClean.CleanNodes\n"); for (auto i: *this) { ProjectNode *p = dynamic_cast(i); if (!p) break; p->SetClean(); } if (OnDone) OnDone(true); }; // printf("IdeProject.SetClean dirty=%i,%i validfile=%i\n", d->Dirty, d->UserFileDirty, ValidStr(d->FileName)); if (d->Dirty || d->UserFileDirty) { if (ValidStr(d->FileName)) SaveFile(); else { auto s = new LFileSelect; s->Parent(Tree); s->Name("Project.xml"); s->Save([this, OnDone, CleanNodes](auto s, auto ok) { // printf("IdeProject.SetClean.FileSelect ok=%i\n", ok); if (ok) { d->FileName = s->Name(); d->UserFile = d->FileName + "." + LCurrentUserName(); d->App->OnFile(d->FileName, true); Update(); CleanNodes(); } else { if (OnDone) OnDone(false); } delete s; }); return; } } CleanNodes(); } const char *IdeProject::GetText(int Col) { if (d->FileName) { char *s = strrchr(d->FileName, DIR_CHAR); return s ? s + 1 : d->FileName.Get(); } return Untitled; } int IdeProject::GetImage(int Flags) { return 0; } void IdeProject::Empty() { LXmlTag *t; while (Children.Length() > 0 && (t = Children[0])) { ProjectNode *n = dynamic_cast(t); if (n) { n->Remove(); } DeleteObj(t); } } LXmlTag *IdeProject::Create(char *Tag) { if (!stricmp(Tag, TagSettings)) return NULL; return new ProjectNode(this); } void IdeProject::OnMouseClick(LMouse &m) { if (m.IsContextMenu()) { LSubMenu Sub; Sub.AppendItem("New Folder", IDM_NEW_FOLDER); Sub.AppendItem("New Web Folder", IDM_WEB_FOLDER); Sub.AppendItem("Delete", IDM_DELETE, GetParentProject() != NULL); Sub.AppendSeparator(); Sub.AppendItem("Build", IDM_BUILD); Sub.AppendItem("Clean Project", IDM_CLEAN_PROJECT); Sub.AppendItem("Clean All", IDM_CLEAN_ALL); Sub.AppendItem("Rebuild Project", IDM_REBUILD_PROJECT); Sub.AppendItem("Rebuild All", IDM_REBUILD_ALL); Sub.AppendSeparator(); Sub.AppendItem("Sort Children", IDM_SORT_CHILDREN); Sub.AppendSeparator(); Sub.AppendItem("Settings", IDM_SETTINGS, true); Sub.AppendItem("Insert Dependency", IDM_INSERT_DEP); m.ToScreen(); auto c = _ScrollPos(); m.x -= c.x; m.y -= c.y; switch (Sub.Float(Tree, m.x, m.y)) { case IDM_NEW_FOLDER: { auto Name = new LInput(Tree, "", "Name:", AppName); Name->DoModal([this, Name](auto d, auto code) { if (code) GetSubFolder(this, Name->GetStr(), true); delete d; }); break; } case IDM_DELETE: { if (d->DepParent) { d->DepParent->Delete(); // will delete 'this' return; } else LAssert(!"Should be a project node for this dependancy"); break; } case IDM_WEB_FOLDER: { WebFldDlg *Dlg = new WebFldDlg(Tree, 0, 0, 0); Dlg->DoModal([Dlg, this](auto d, auto code) { if (Dlg->Ftp && Dlg->Www) { IdeCommon *f = GetSubFolder(this, Dlg->Name, true); if (f) { f->SetAttr(OPT_Ftp, Dlg->Ftp); f->SetAttr(OPT_Www, Dlg->Www); } } delete Dlg; }); break; } case IDM_BUILD: { StopBuild(); Build(true, d->App->GetBuildMode()); break; } case IDM_CLEAN_PROJECT: { Clean(false, d->App->GetBuildMode()); break; } case IDM_CLEAN_ALL: { Clean(true, d->App->GetBuildMode()); break; } case IDM_REBUILD_PROJECT: { StopBuild(); Clean(false, d->App->GetBuildMode()); Build(false, d->App->GetBuildMode()); break; } case IDM_REBUILD_ALL: { StopBuild(); Clean(true, d->App->GetBuildMode()); Build(true, d->App->GetBuildMode()); break; } case IDM_SORT_CHILDREN: { SortChildren(); Project->SetDirty(); break; } case IDM_SETTINGS: { d->Settings.Edit(Tree, [this]() { SetDirty(); }); break; } case IDM_INSERT_DEP: { LFileSelect *s = new LFileSelect; s->Parent(Tree); s->Type("Project", "*.xml"); s->Open([this](auto s, bool ok) { if (ok) { ProjectNode *New = new ProjectNode(this); if (New) { New->SetFileName(s->Name()); New->SetType(NodeDependancy); InsertTag(New); SetDirty(); } } delete s; }); break; } } } } char *IdeProject::FindFullPath(const char *File, ProjectNode **Node) { char *Full = 0; for (auto i:*this) { ProjectNode *c = dynamic_cast(i); if (!c) break; ProjectNode *n = c->FindFile(File, &Full); if (n) { if (Node) *Node = n; break; } } return Full; } bool IdeProject::HasNode(ProjectNode *Node) { for (auto i:*this) { ProjectNode *c = dynamic_cast(i); if (!c) break; if (c->HasNode(Node)) return true; } return false; } bool IdeProject::GetAllNodes(LArray &Nodes) { for (auto i:*this) { ProjectNode *c = dynamic_cast(i); if (!c) break; c->AddNodes(Nodes); } return true; } bool IdeProject::InProject(bool FuzzyMatch, const char *Path, bool Open, IdeDoc **Doc) { if (!Path) return false; // Search complete path first... auto PlatformFlags = d->App->GetPlatform(); ProjectNode *n = d->Nodes.Find(Path); if (!n && FuzzyMatch) { // No match, do partial matching. const char *Leaf = LGetLeaf(Path); auto PathLen = strlen(Path); auto LeafLen = strlen(Leaf); uint32_t MatchingScore = 0; // Traverse all nodes and try and find the best fit. for (auto Cur: d->Nodes) { int NodePlatforms = Cur.value->GetPlatforms(); uint32_t Score = 0; if (stristr(Cur.key, Path)) { Score += PathLen; } else if (stristr(Cur.key, Leaf)) { Score += LeafLen; } const char *pLeaf = LGetLeaf(Cur.key); if (pLeaf && !stricmp(pLeaf, Leaf)) { Score |= 0x40000000; } if (Score && (NodePlatforms & PlatformFlags) != 0) { Score |= 0x80000000; } if (Score > MatchingScore) { MatchingScore = Score; n = Cur.value; } } } if (n && Doc) { *Doc = n->Open(); } return n != 0; } char *GetQuotedStr(char *Str) { char *s = strchr(Str, '\"'); if (s) { s++; char *e = strchr(s, '\"'); if (e) { return NewStr(s, e - s); } } return 0; } void IdeProject::ImportDsp(const char *File) { if (File && LFileExists(File)) { char Base[256]; strcpy(Base, File); LTrimDir(Base); auto Dsp = LReadFile(File); if (Dsp) { auto Lines = Dsp.SplitDelimit("\r\n"); IdeCommon *Current = this; bool IsSource = false; for (int i=0; iGetSubFolder(this, Folder, true); if (Sub) { Current = Sub; } DeleteArray(Folder); } } else if (strnicmp(L, "# End Group", 11) == 0) { IdeCommon *Parent = dynamic_cast(Current->GetParent()); if (Parent) { Current = Parent; } } else if (strnicmp(L, "# Begin Source", 14) == 0) { IsSource = true; } else if (strnicmp(L, "# End Source", 12) == 0) { IsSource = false; } else if (Current && IsSource && strncmp(L, "SOURCE=", 7) == 0) { ProjectNode *New = new ProjectNode(this); if (New) { char *Src = 0; if (strchr(L, '\"')) { Src = GetQuotedStr(L); } else { Src = NewStr(L + 7); } if (Src) { // Make absolute path char Abs[256]; LMakePath(Abs, sizeof(Abs), Base, ToUnixPath(Src)); // Make relitive path New->SetFileName(Src); DeleteArray(Src); } Current->InsertTag(New); SetDirty(); } } } } } } IdeProjectSettings *IdeProject::GetSettings() { return &d->Settings; } void AddAllSubFolders(LString::Array &out, LString in) { out.SetFixedLength(false); LDirectory dir; for (int b=dir.First(in); b; b=dir.Next()) { if (dir.IsDir() && Strncmp(dir.GetName(), "Qt", 2) // && Strcmp(dir.GetName(), "private") ) { out.Add(dir.FullPath()); AddAllSubFolders(out, dir.FullPath()); } } } bool PkgConfigPaths(LString::Array &out, LString in) { if (in(0) == '`') { // Run config app to get the full path list... in = in.Strip("`"); auto a = in.Split(" ", 1); LSubProcess Proc(a[0], a.Length() > 1 ? a[1].Get() : NULL); if (Proc.Start()) { LStringPipe Buf; Proc.Communicate(&Buf); auto lines = Buf.NewLStr().SplitDelimit(" \t\r\n"); for (auto line: lines) { char *inc = line; if (inc[0] == '-' && inc[1] == 'I') { auto path = line(2,-1); // printf(" inc='%s'\n", path.Get()); out.New() = path; } } } else { LgiTrace("%s:%i - Error: failed to run process for '%s'\n", _FL, in.Get()); return false; } } else { out.New() = in; } return true; } bool IdeProject::BuildIncludePaths(LString::Array &Paths, LString::Array *SysPaths, bool Recurse, bool IncludeSystem, IdePlatform Platform) { List Projects; if (Recurse) GetChildProjects(Projects); Projects.Insert(this, 0); LHashTbl, bool> MapProj, MapSys; for (auto p: Projects) { const auto Base = p->GetBasePath(); LAssert(Base); const char *Delim = ",;\r\n"; LString::Array InProj, InSys, OutProj, OutSys; InProj.SetFixedLength(false); OutProj.SetFixedLength(false); InSys.SetFixedLength(false); OutSys.SetFixedLength(false); LString PlatformInc = d->Settings.GetStr(ProjIncludePaths, NULL, Platform); InProj += PlatformInc.SplitDelimit(Delim); if (IncludeSystem) { auto sys = LString(d->Settings.GetStr(ProjSystemIncludes, NULL, Platform)).SplitDelimit(Delim); for (auto s: sys) PkgConfigPaths(InSys, s); bool recurseSystem = true; if (recurseSystem) { auto sz = InSys.Length(); for (unsigned i=0; i, bool> &Map, LString &p) { char *Path = p; char *Full = 0, Buf[MAX_PATH_LEN]; if ( *Path != '/' && !( IsAlpha(*Path) && Path[1] == ':' ) ) { if (LMakePath(Buf, sizeof(Buf), Base, Path)) { Full = Buf; } else { LAssert(!"Make path err."); return; } } else { Full = Path; } if (!Map.Find(Full)) Map.Add(Full, true); }; for (auto &p: InProj) PreProcessPath(OutProj, p); for (auto &p: InSys) PreProcessPath(OutSys, p); for (auto &p: OutProj) RelativeToFull(MapProj, p); for (auto &p: OutSys) RelativeToFull(MapSys, p); // Add paths for the headers in the project... bit of a hack but it'll // help it find them if the user doesn't specify the paths in the project. LArray Nodes; if (p->GetAllNodes(Nodes)) { auto NodeBase = p->GetFullPath(); if (NodeBase) { LTrimDir(NodeBase); for (auto &n: Nodes) { if (n->GetType() == NodeHeader && // Only look at headers. (n->GetPlatforms() & (1 << Platform)) != 0) // Exclude files not on this platform. { auto f = n->GetFileName(); char p[MAX_PATH_LEN]; if (f && LMakePath(p, sizeof(p), NodeBase, f)) { char *l = strrchr(p, DIR_CHAR); if (l) *l = 0; if (!MapProj.Find(p)) MapProj.Add(p, true); } } } } } } for (auto p: MapProj) { Paths.Add(p.key); // LgiTrace("Proj: %s\n", p.key); } auto SysTarget = SysPaths ? SysPaths : &Paths; for (auto p: MapSys) { SysTarget->Add(p.key); // LgiTrace("Sys: %s\n", p.key); } return true; } void IdeProjectPrivate::CollectAllFiles(LTreeNode *Base, LArray &Files, bool SubProjects, int Platform) { for (auto i: *Base) { IdeProject *Proj = dynamic_cast(i); if (Proj) { if (Proj->GetParentProject() && !SubProjects) { continue; } } else { ProjectNode *p = dynamic_cast(i); if (p) { if (p->GetType() == NodeSrc || p->GetType() == NodeHeader) { if (p->GetPlatforms() & Platform) { Files.Add(p); } } } } CollectAllFiles(i, Files, SubProjects, Platform); } } LString IdeProject::GetTargetName(IdePlatform Platform) { LString Status; const char *t = d->Settings.GetStr(ProjTargetName, NULL, Platform); if (ValidStr(t)) { // Take target name from the settings Status = t; } else { char *s = strrchr(d->FileName, DIR_CHAR); if (s) { // Generate the target executable name char Target[MAX_PATH_LEN]; strcpy_s(Target, sizeof(Target), s + 1); s = strrchr(Target, '.'); if (s) *s = 0; strlwr(Target); s = Target; for (char *i = Target; *i; i++) { if (*i != '.' && *i != ' ') { *s++ = *i; } } *s = 0; Status = Target; } } return Status; } LString IdeProject::GetTargetFile(IdePlatform Platform) { LString Target = GetTargetName(PlatformCurrent); if (!Target) { LAssert(!"No target?"); return LString(); } const char *TargetType = d->Settings.GetStr(ProjTargetType); if (!TargetType) { LAssert(!"Needs a target type."); return LString(); } if (!stricmp(TargetType, "Executable")) { return Target; } else if (!stricmp(TargetType, "DynamicLibrary")) { char t[MAX_PATH_LEN]; auto DefExt = PlatformDynamicLibraryExt(Platform); strcpy_s(t, sizeof(t), Target); auto tCh = strlen(t); char *ext = LGetExtension(t); if (!ext) snprintf(t+tCh, sizeof(t)-tCh, ".%s", DefExt); else if (stricmp(ext, DefExt)) strcpy(ext, DefExt); return t; } else if (!stricmp(TargetType, "StaticLibrary")) { LString Ret; Ret.Printf("%s.%s", Target.Get(), PlatformSharedLibraryExt(Platform)); return Ret; } return LString(); } struct ProjDependency { bool Scanned; LAutoString File; ProjDependency(const char *f) { Scanned = false; File.Reset(NewStr(f)); } }; bool IdeProject::GetAllDependencies(LArray &Files, IdePlatform Platform) { if (!GetTree()->Lock(_FL)) return false; LHashTbl, ProjDependency*> Deps; auto Base = GetBasePath(); // Build list of all the source files... LArray Src; CollectAllSource(Src, Platform); // Get all include paths LString::Array IncPaths; BuildIncludePaths(IncPaths, NULL, false, false, Platform); // Add all source to dependencies for (int i=0; i Unscanned; do { // Find all the unscanned dependencies Unscanned.Length(0); for (auto d : Deps) { if (!d.value->Scanned) Unscanned.Add(d.value); } for (int i=0; iScanned = true; char *Src = d->File; char Full[MAX_PATH_LEN]; if (LIsRelativePath(d->File)) { LMakePath(Full, sizeof(Full), Base, d->File); Src = Full; } LArray SrcDeps; if (GetDependencies(Src, IncPaths, SrcDeps, Platform)) { for (auto File: SrcDeps) { // Add include to dependencies... if (LIsRelativePath(File)) { LMakePath(Full, sizeof(Full), Base, File); File = Full; } if (!Deps.Find(File)) { Deps.Add(File, new ProjDependency(File)); } } SrcDeps.DeleteArrays(); } } } while (Unscanned.Length() > 0); for (auto d : Deps) { Files.Add(d.value->File.Release()); } Deps.DeleteObjects(); GetTree()->Unlock(); return true; } bool IdeProject::GetDependencies(const char *InSourceFile, LString::Array &IncPaths, LArray &Files, IdePlatform Platform) { LString SourceFile = InSourceFile; if (!CheckExists(SourceFile)) { LgiTrace("%s:%i - can't read '%s'\n", _FL, SourceFile.Get()); return false; } auto c8 = LReadFile(SourceFile); if (!c8) return false; LString::Array Headers; LArray AllInc; AllInc.Add(&IncPaths); if (!BuildHeaderList(c8, Headers, false, [&AllInc](auto Name) { return FindHeader(Name, AllInc); })) return false; for (int n=0; nCreateMakefile) { if (d->CreateMakefile->IsExited()) d->CreateMakefile.Reset(); else { d->App->GetBuildLog()->Print("%s:%i - Makefile thread still running.\n", _FL); return false; } } if (Platform == PlatformCurrent) Platform = GetCurrentPlatform(); return d->CreateMakefile.Reset(new MakefileThread(d, Platform, BuildAfterwards)); } void IdeProject::OnMakefileCreated() { if (d->CreateMakefile) { d->CreateMakefile.Reset(); if (MakefileThread::Instances == 0) GetApp()->PostEvent(M_LAST_MAKEFILE_CREATED); } } //////////////////////////////////////////////////////////////////////////////////////////// IdeTree::IdeTree() : LTree(IDC_PROJECT_TREE, 0, 0, 100, 100) { MultiSelect(true); } void IdeTree::OnCreate() { SetWindow(this); } void IdeTree::OnDragExit() { SelectDropTarget(NULL); } int IdeTree::WillAccept(LDragFormats &Formats, LPoint p, int KeyState) { static bool First = true; Formats.SupportsFileDrops(); Formats.Supports(NODE_DROP_FORMAT); First = false; if (Formats.Length() == 0) { LgiTrace("%s:%i - No valid drop formats.\n", _FL); return DROPEFFECT_NONE; } Hit = ItemAtPoint(p.x, p.y); if (!Hit) { SelectDropTarget(NULL); return DROPEFFECT_NONE; } if (Formats.HasFormat(LGI_FileDropFormat)) { SelectDropTarget(Hit); return DROPEFFECT_LINK; } IdeCommon *Src = dynamic_cast(Selection()); IdeCommon *Dst = dynamic_cast(Hit); if (Src && Dst) { // Check this folder is not a child of the src for (IdeCommon *n=Dst; n; n=dynamic_cast(n->GetParent())) { if (n == Src) return DROPEFFECT_NONE; } } // Valid target SelectDropTarget(Hit); return DROPEFFECT_MOVE; } int IdeTree::OnDrop(LArray &Data, LPoint p, int KeyState) { int Ret = DROPEFFECT_NONE; SelectDropTarget(NULL); if (!(Hit = ItemAtPoint(p.x, p.y))) return Ret; for (unsigned n=0; nType == GV_BINARY && Data->Value.Binary.Length == sizeof(ProjectNode*)) { ProjectNode *Src = ((ProjectNode**)Data->Value.Binary.Data)[0]; if (Src) { ProjectNode *Folder = dynamic_cast(Hit); while (Folder && Folder->GetType() != NodeDir) Folder = dynamic_cast(Folder->GetParent()); IdeCommon *Dst = dynamic_cast(Folder?Folder:Hit); if (Dst) { // Check this folder is not a child of the src for (IdeCommon *n=Dst; n; n=dynamic_cast(n->GetParent())) { if (n == Src) return DROPEFFECT_NONE; } // Detach LTreeItem *i = dynamic_cast(Src); i->Detach(); if (Src->LXmlTag::Parent) { LAssert(Src->LXmlTag::Parent->Children.HasItem(Src)); Src->LXmlTag::Parent->Children.Delete(Src); } // Attach Src->LXmlTag::Parent = Dst; Dst->Children.SetFixedLength(false); Dst->Children.Add(Src); Dst->Children.SetFixedLength(true); Dst->Insert(Src); // Dirty Src->GetProject()->SetDirty(); } Ret = DROPEFFECT_MOVE; } } } else if (dd.IsFileDrop()) { ProjectNode *Folder = dynamic_cast(Hit); while (Folder && Folder->GetType() > NodeDir) Folder = dynamic_cast(Folder->GetParent()); IdeCommon *Dst = dynamic_cast(Folder?Folder:Hit); if (Dst) { AddFilesProgress Prog(this); LDropFiles Df(dd); for (int i=0; iAddFiles(&Prog, Df[i])) Ret = DROPEFFECT_LINK; } } } else LgiTrace("%s:%i - Unknown drop format: %s.\n", _FL, dd.Format.Get()); } return Ret; } ///////////////////////////////////////////////////////////////////////////////////////////////// AddFilesProgress::AddFilesProgress(LViewI *par) { v = 0; Cancel = false; Msg = NULL; SetParent(par); Ts = LCurrentTime(); LRect r(0, 0, 140, 100); SetPos(r); MoveSameScreen(par); Name("Importing files..."); LString::Array a = LString(DefaultExt).SplitDelimit(","); for (unsigned i=0; iGetCell(0, 0); c->Add(new LTextLabel(-1, 0, 0, -1, -1, "Loaded:")); c = t->GetCell(1, 0); c->Add(Msg = new LTextLabel(-1, 0, 0, -1, -1, "...")); c = t->GetCell(0, 1, true, 2); c->TextAlign(LCss::Len(LCss::AlignRight)); c->Add(new LButton(IDCANCEL, 0, 0, -1, -1, "Cancel")); } int64 AddFilesProgress::Value() { return v; } void AddFilesProgress::Value(int64 val) { v = val; uint64 Now = LCurrentTime(); if (Visible()) { if (Now - Ts > 200) { if (Msg) { Msg->Value(v); Msg->SendNotify(LNotifyTableLayoutRefresh); } Ts = Now; } } else if (Now - Ts > 1000) { DoModeless(); SetAlwaysOnTop(true); } } int AddFilesProgress::OnNotify(LViewI *c, LNotification n) { if (c->GetId() == IDCANCEL) Cancel = true; return 0; } diff --git a/include/lgi/common/DateTime.h b/include/lgi/common/DateTime.h --- a/include/lgi/common/DateTime.h +++ b/include/lgi/common/DateTime.h @@ -1,478 +1,477 @@ /// \file /// \author Matthew Allen /** * \defgroup Time Time and date handling * \ingroup Lgi */ #ifndef __DATE_TIME_H #define __DATE_TIME_H #include #include "lgi/common/StringClass.h" #define GDTF_DEFAULT 0 /// Format the date as DD/MM/YYYY /// \ingroup Time #define GDTF_DAY_MONTH_YEAR 0x001 /// Format the date as MM/DD/YYYY /// \ingroup Time #define GDTF_MONTH_DAY_YEAR 0x002 /// Format the date as YYYY/MM/DD /// \ingroup Time #define GDTF_YEAR_MONTH_DAY 0x004 /// The bit mask for the date /// \ingroup Time #define GDTF_DATE_MASK 0x00f /// Format the time as HH:MM and an am/pm marker /// \ingroup Time #define GDTF_12HOUR 0x010 /// Format the time as 24 hour time /// \ingroup Time #define GDTF_24HOUR 0x020 /// The bit mask for the time /// \ingroup Time #define GDTF_TIME_MASK 0x0f0 /// Format the date with a leading zero /// \ingroup Time #define GDTF_DAY_LEADINGZ 0x100 /// Format the month with a leading zero /// \ingroup Time #define GDTF_MONTH_LEADINGZ 0x200 class LDateTime; class LgiClass LTimeStamp { uint64_t ts = 0; public: constexpr static uint64_t WINDOWS_TICK = 10000000; constexpr static uint64_t SEC_TO_UNIX_EPOCH = 11644473600LL; LTimeStamp(uint64_t sysTime) { ts = sysTime; } LTimeStamp(time_t unixTime = 0) { if (unixTime) *this = unixTime; } uint64_t Get() const { return ts; } uint64_t &Ref() { return ts; } // Convert from unit time to LGI time stamp LTimeStamp &operator =(const time_t unixTime); // Convert from LDateTime LTimeStamp &operator =(const LDateTime &dt); // Convert from LGI time stamp to unix time time_t Unix() const; // operator bool() const { return ts != 0; } bool Valid() const { return ts != 0; } LTimeStamp operator +(uint64_t offset) { LTimeStamp t; t.Ref() = ts + offset; return t; } bool operator <(const LTimeStamp &b) const { return ts < b.ts; } bool operator <=(const LTimeStamp &b) const { return ts <= b.ts; } bool operator >(const LTimeStamp &b) const { return ts > b.ts; } bool operator >=(const LTimeStamp &b) const { return ts >= b.ts; } int64_t operator -(const LTimeStamp &b) const { return (int64_t)ts - (int64_t)b.ts; } LTimeStamp &operator +=(int64_t i) { ts += i; return *this; } LTimeStamp &operator -=(int64_t i) { ts -= i; return *this; } }; /// A date/time class /// /// This class interacts with system times represented as 64bit ints. The various OS support different /// formats for that 64bit int values. /// - On windows the system times are in 100-nanosecond intervals since /// January 1, 1601 (UTC), as per the FILETIME structure. /// - On Posix systems (Linux/Mac) the 64bit values are in milliseconds since January 1, 1800 UTC. /// This is just (unixTime + Offset1800) * 1000. /// If you are serializing these 64bit values you should take that into account, they are NOT cross platform. /// The LDirectory class uses the same 64bit values as accepted here for the file's last modified timestamp /// etc. To convert the 64bit values to seconds, divide by LDateTime::Second64Bit, useful for calculating /// the time in seconds between 2 LDateTime objects. /// /// \ingroup Time class LgiClass LDateTime // This class can't have a virtual table, because it's used in // LArray's which initialize with all zero bytes. { /// 1 - DaysInMonth int16 _Day; /// #### int16 _Year; /// Milliseconds: 0-999 int16 _Thousands; /// 1-12 int16 _Month; /// 0-59 int16 _Seconds; /// 0-59 int16 _Minutes; /// 0-23 (24hr) int16 _Hours; /// The current timezone of this object, defaults to the system timezone int16 _Tz; // in minutes (+10 == 600 etc) /// Combination of (#GDTF_DAY_MONTH_YEAR or #GDTF_MONTH_DAY_YEAR or #GDTF_YEAR_MONTH_DAY) and (#GDTF_12HOUR or #GDTF_24HOUR) uint16 _Format; /// The default formatting of datetimes static uint16 DefaultFormat; /// The default date separator character static char DefaultSeparator; public: LDateTime(const char *Init = NULL); LDateTime(time_t unixTime); LDateTime(const LTimeStamp &Ts); LDateTime(const LDateTime &dt) { *this = dt; } ~LDateTime(); /// Resolution of a second when using 64 bit int's /// \sa LDateTime::Get(int64), LDateTime::Set(int64) #ifdef WIN32 static constexpr int64_t Second64Bit = 10000000; #else static constexpr int64_t Second64Bit = 1000; #endif static constexpr int64_t MinuteLength = 60; // seconds static constexpr int64_t HourLength = MinuteLength * 60; // seconds static constexpr int64_t DayLength = HourLength * 24; // seconds static constexpr const char *WeekdaysShort[7] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; static constexpr const char *WeekdaysLong[7] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}; static constexpr const char *MonthsShort[12] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; static constexpr const char *MonthsLong[12] = {"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"}; // On Posix systems this allows representation of times before 1/1/1970. // The Lgi epoch is considered to be 1/1/1800 instead and this offset converts // from one to the other. static constexpr int64_t Offset1800 = 5364662400; // Seconds from 1/1/1800 to 1/1/1970 /// Returns true if all the components are in a valid range bool IsValid() const; /// Sets the date to an NULL state void Empty(); /// Returns the day int Day() const { return _Day; } /// Sets the day void Day(int d) { _Day = d; } /// Returns the month int Month() const { return _Month; } /// Sets the month void Month(int m) { _Month = m; } /// Sets the month by it's name void Month(char *m); /// Returns the year int Year() const { return _Year; } /// Sets the year void Year(int y) { _Year = y; } /// Returns the millisecond part of the time int Thousands() const { return _Thousands; } /// Sets the millisecond part of the time void Thousands(int t) { _Thousands = t; } /// Returns the seconds part of the time int Seconds() const { return _Seconds; } /// Sets the seconds part of the time void Seconds(int s) { _Seconds = s; } /// Returns the minutes part of the time int Minutes() const { return _Minutes; } /// Sets the minutes part of the time void Minutes(int m) { _Minutes = m; } /// Returns the hours part of the time int Hours() const { return _Hours; } /// Sets the hours part of the time void Hours(int h) { _Hours = h; } /// Returns the timezone of this current date time object in minutes (+10 = 600) int GetTimeZone() const { return _Tz; } /// Returns the timezone in hours double GetTimeZoneHours() const { return (double)_Tz / 60.0; } /// Sets the timezone of this current object.in minutes (+10 = 600) void SetTimeZone ( /// The new timezone (in minutes, 600 = +10:00) int Tz, /// True if you want to convert the date and time to the new zone, /// False if you want to leave the date/time as it is. bool ConvertTime ); /// Figure out the right TZ offset for a given date according to the DST /// rules for the current timezone. bool InferTimeZone(bool ConvertTime = true); /// Set this object to UTC timezone, changing the other members as /// needed LDateTime &ToUtc(bool AssumeLocal = false) { if (AssumeLocal) _Tz = SystemTimeZone(); SetTimeZone(0, true); return *this; } /// \returns the UTC version of this object. LDateTime Utc() const { LDateTime dt = *this; return dt.ToUtc(); } /// Changes the timezone to the local zone, changing other members /// as needed. LDateTime &ToLocal(bool AssumeUtc = false) { if (AssumeUtc) _Tz = 0; SetTimeZone(SystemTimeZone(), true); return *this; } /// \returns the local time version of this object. LDateTime Local() const { LDateTime dt = *this; return dt.ToLocal(); } /// Gets the current formatting of the date, the format only effects /// the representation of the date when converted to/from a string. /// \returns a bit mask of (#GDTF_DAY_MONTH_YEAR or #GDTF_MONTH_DAY_YEAR or #GDTF_YEAR_MONTH_DAY) and (#GDTF_12HOUR or #GDTF_24HOUR) uint16 GetFormat() const { return _Format; } /// Sets the current formatting of the date, the format only effects /// the representation of the date when converted to/from a string void SetFormat ( /// a bit mask of (#GDTF_DAY_MONTH_YEAR or #GDTF_MONTH_DAY_YEAR or #GDTF_YEAR_MONTH_DAY) and (#GDTF_12HOUR or #GDTF_24HOUR) uint16 f ) { _Format = f; } /// \returns zero based index of weekday, or -1 if not found. static int IsWeekDay(const char *s); /// \returns zero based index of month, or -1 if not found. static int IsMonth(const char *s); /// The default format for the date when formatted as a string static uint16 GetDefaultFormat(); /// Sets the default format for the date when formatted as a string static void SetDefaultFormat(uint16 f) { DefaultFormat = f; } /// Gets the data and time as a LString LString Get() const; /// Gets the date and time as a string /// \sa LDateTime::GetFormat() void Get(char *Str, size_t SLen) const; /// Gets the data and time as a 64 bit int (os specific) bool Get(LTimeStamp &s) const; /// Gets just the date as a string /// \sa LDateTime::GetFormat() /// \returns The number of characters written to 'Str' int GetDate(char *Str, size_t SLen) const; /// Gets just the date as a LString /// \sa LDateTime::GetFormat() LString GetDate() const; /// Gets just the time as a string /// \sa LDateTime::GetFormat() /// \returns The number of characters written to 'Str' int GetTime(char *Str, size_t SLen) const; /// Gets just the time as a LString /// \sa LDateTime::GetFormat() LString GetTime() const; // Unix epoch support - uint64_t GetUnix(); - bool SetUnix(uint64_t s); + time_t GetUnix(); + bool SetUnix(time_t tt); /// Returns the 64bit LTimeStamp. LTimeStamp Ts() const; /// Get the timestamp in a format compatible with the current operating system APIs. /// \returns the timestamp or zero on error. uint64_t OsTime() const; bool OsTime(uint64_t ts); /// Get the current time... static LDateTime Now(); /// Sets the date and time to the system clock LDateTime &SetNow(); /// Parses a date time from a string /// \sa LDateTime::GetFormat() bool Set(const char *Str); /// Sets the date and time from a 64 bit int (os specific) bool Set(const LTimeStamp &s); - /// Sets the time from a unit time_t - bool Set(time_t tt); /// Parses the date from a string /// \sa LDateTime::GetFormat() bool SetDate(const char *Str); /// Parses the time from a string /// \sa LDateTime::GetFormat() bool SetTime(const char *Str); /// Parses the date time from a free form string bool Parse(LString s); /// Set to a tm struct bool Set(struct tm *t, bool inferTimezone); /// Describes the perios between this and 'to' in the form: /// ##d ##h ##m ##s /// Order of the dates isn't important. LString DescribePeriod(LDateTime to); static LString DescribePeriod(double seconds); /// \returns true if 'd' is on the same day as this object bool IsSameDay(LDateTime &d) const; /// \returns true if 'd' is on the same month as this object bool IsSameMonth(LDateTime &d) const; /// \returns true if 'd' is on the same year as this object bool IsSameYear(LDateTime &d) const; /// \returns the start of day, same date but time: 00:00:00 LDateTime StartOfDay() const; /// \returns the end of day, same date but time: 23:59:59 LDateTime EndOfDay() const; /// \returns whether a year is a leap year or not bool IsLeapYear ( /// Pass a specific year here, or ignore to return if the current Date/Time is in a leap year. int Year = -1 ) const; /// Returns the day of the week as an index, 0=sun, 1=mon, 2=teus etc int DayOfWeek() const; /// \returns the number of days in the current month int DaysInMonth() const; /// Adds a number of seconds to the current date/time void AddSeconds(int64 Seconds); /// Adds a number of minutes to the current date/time void AddMinutes(int64 Minutes); /// Adds a number of hours to the current date/time void AddHours(int64 Hours); /// Adds a number of days to the current date/time bool AddDays(int64 Days); /// Adds a number of months to the current date/time void AddMonths(int64 Months); /// Adds years void AddYears(int yr) { _Year += yr; } /// The system timezone including daylight savings offset in minutes, +10 would return 600 static int SystemTimeZone(bool ForceUpdate = false); /// Any daylight savings offset applied to TimeZone(), in minutes. e.g. to retreive the /// timezone uneffected by DST use TimeZone() - TimeZoneOffset(). static int SystemTimeZoneOffset(); /// Daylight savings info record struct LgiClass LDstInfo { /// Timestamp where the DST timezone changes to 'Offset' LTimeStamp Utc; /// The new offset in minutes (e.g. 600 = +10 hours) int Offset; LDateTime GetLocal(); }; /// Retreives daylight savings start and end events for a given period. One event will be emitted /// for the current DST/TZ setting at the datetime specified by 'Start', followed by any changes that occur /// between that and the 'End' datetime. To retreive just the DST info for start, use NULL for end. static bool GetDaylightSavingsInfo ( /// [Out] The array to receive DST info. At minimum one record will be returned /// matching the TZ in place for the start datetime. LArray &Out, /// [In] The start date that you want DST info for. LDateTime &Start, /// [Optional In] The end of the period you want DST info for. LDateTime *End = 0 ); /// Using the DST info this will convert 'dt' from UTC to local static bool DstToLocal(LArray &Dst, LDateTime &dt); /// Decodes an email date into the current instance bool Decode(const char *In); /// Returns a month index from a month name static int MonthFromName(const char *Name); // File int Sizeof(); bool Serialize(class LFile &f, bool Write); bool Serialize(class LDom *Props, char *Name, bool Write); // operators bool operator <(const LDateTime &dt) const; bool operator <=(const LDateTime &dt) const; bool operator >(const LDateTime &dt) const; bool operator >=(const LDateTime &dt) const; bool operator ==(const LDateTime &dt) const; bool operator !=(const LDateTime &dt) const; int Compare(const LDateTime *d) const; LDateTime operator -(const LDateTime &dt); LDateTime operator +(const LDateTime &dt); int DiffMonths(const LDateTime &dt); LDateTime &operator =(struct tm *t); LDateTime &operator =(const LDateTime &t); LDateTime &operator =(LDateTime const *t) { if (t) *this = *t; return *this; } /// LDom interface. /// /// Even though we don't inherit from a LDom class this class supports the same /// interface for ease of use. Currently there are cases where LDateTime is used /// in LArray's which don't implement calling a constructor (they init with all /// zeros). bool GetVariant(const char *Name, class LVariant &Value, char *Array = NULL); bool SetVariant(const char *Name, class LVariant &Value, char *Array = NULL); bool CallMethod(const char *Name, class LVariant *ReturnValue, LArray &Args); #ifdef WINDOWS operator SYSTEMTIME() const; LDateTime &operator =(const SYSTEMTIME &st); #endif + operator struct tm() const; + + #ifdef _DEBUG + static bool UnitTests(); + #endif }; /// Time zone information struct LTimeZone { public: /// The offset from UTC float Offset; /// The name of the zone const char *Text; }; /// A list of all known timezones. extern LTimeZone LTimeZones[]; -#ifdef _DEBUG -LgiFunc bool LDateTime_Test(); #endif - -#endif diff --git a/include/lgi/common/TimeZoneInfo.h b/include/lgi/common/TimeZoneInfo.h --- a/include/lgi/common/TimeZoneInfo.h +++ b/include/lgi/common/TimeZoneInfo.h @@ -1,522 +1,522 @@ // For reading timezone info files: // https://www.rfc-editor.org/rfc/rfc8536.html #pragma once #ifdef HAIKU #include #endif #include "lgi/common/DateTime.h" class LTimeZoneInfo { static int TimeToSeconds(LString t) { auto parts = t.SplitDelimit(":"); auto hrs = parts[0].Int(); auto min = parts.IdxCheck(1) ? parts[1].Int() : 0; auto sec = parts.IdxCheck(2) ? parts[2].Int() : 0; return (hrs * 3600) + (min * 60) + sec; } struct Header { char magic[4]; char version; char reserved[15]; uint32_t UtCount; uint32_t StdCount; uint32_t LeapCount; uint32_t TimeCount; uint32_t TypeCount; uint32_t CharCount; }; struct LocalTimeType { int32_t UtOffset; uint8_t IsDst; uint8_t Idx; }; struct LeapSecond1 { uint32_t occur; uint32_t corr; }; struct LeapSecond2 { uint64_t occur; uint32_t corr; }; struct PosixTransition { int JulianIdx = -1; // 0 - 365 int Month = 0; // 1 - 12 int Week = 0; // 1 - 5 int Day = 0; // 0 - 6 LString time; int OffsetSeconds(int StdOffsetSeconds) { int sec = StdOffsetSeconds; if (time) { auto TwoAm = 2 * 3600; auto TimeOff = TimeToSeconds(time); auto Offset = TimeOff - TwoAm; sec += Offset; } return sec; } LDateTime Get(int year) { LDateTime dt; if (JulianIdx >= 0) { dt.SetTimeZone(0, false); dt.Year(year); dt.Month(1); dt.Day(1); dt.AddDays(JulianIdx); } else if (Month) { dt.SetTimeZone(0, false); dt.Year(year); dt.Month(Month); dt.Day(1); if (Week == 5) { // Find last instance of 'Day' dt.Day(dt.DaysInMonth()); while (dt.Day() > 1 && dt.DayOfWeek() != Day) dt.AddDays(-1); } else { // Find 'Week'th instance of 'Day' int CurWk = Week; int MonthsDays = dt.DaysInMonth(); while (dt.Day() <= MonthsDays) { int CurDay = dt.DayOfWeek(); if (CurDay == Day) { if (--CurWk == 0) break; } dt.AddDays(1); } } } else LAssert(0); if (time) dt.Hours(time.Int()); else dt.Hours(2); return dt; } bool Set(LString spec) { auto parts = spec.SplitDelimit("/"); auto s = parts[0]; if (parts.Length() > 1) time = parts[1]; if (s(0) == 'J') { JulianIdx = s(1, -1).Int() - 1; return JulianIdx >= 0 && JulianIdx < 365; } else if (s(0) == 'M') { auto parts = s(1, -1).SplitDelimit("."); if (parts.Length() != 3) return false; Month = parts[0].Int(); Week = parts[1].Int(); Day = parts[2].Int(); return Month >= 1 && Month <= 12; } else { JulianIdx = s.Int(); return JulianIdx >= 0 && JulianIdx < 365; } } }; enum TVersion { Version1 = 0, Version2 = '2', Version3 = '3', }; LString data; LPointer ptr; char *end = NULL; bool Eof() { return ptr.c >= end; } ssize_t Remaining() { return end - ptr.c; } Header hdr; LArray TransitionTimes1; LArray TransitionTimes2; LArray TransitionTypes; LArray LocalTimeTypes; LString TimeZoneDesignations; LArray LeapSeconds1; LArray LeapSeconds2; LArray StandardWall; LArray UtLocal; // Format: // https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html#tag_08_03 // Examples: // https://support.cyberdata.net/portal/en/kb/articles/010d63c0cfce3676151e1f2d5442e311 LString footer; bool Read(Header &h) { h.magic[0] = *ptr.c++; h.magic[1] = *ptr.c++; h.magic[2] = *ptr.c++; h.magic[3] = *ptr.c++; if (Strncmp(h.magic, "TZif", 4)) return OnError("bad magic value"); h.version = *ptr.u8++; ptr.c += 15; h.UtCount = ntohl(*ptr.u32++); h.StdCount = ntohl(*ptr.u32++); h.LeapCount = ntohl(*ptr.u32++); h.TimeCount = ntohl(*ptr.u32++); h.TypeCount = ntohl(*ptr.u32++); h.CharCount = ntohl(*ptr.u32++); return !Eof(); } bool Read(LocalTimeType &t) { if (Remaining() < 6) return false; t.UtOffset = ntohl(*ptr.s32++); t.IsDst = *ptr.u8++; t.Idx = *ptr.u8++; return true; } bool Read(LeapSecond1 &ls) { if (Remaining() < 8) return false; ls.occur = ntohl(*ptr.u32++); ls.corr = ntohl(*ptr.u32++); return true; } bool Read(LeapSecond2 &ls) { if (Remaining() < 12) return false; ls.occur = ntohll(*ptr.u64++); ls.corr = ntohl(*ptr.u32++); return true; } template bool Read(LArray &out, size_t elem) { if (elem == 0) return true; size_t bytes = sizeof(T) * elem; if (!out.Length(elem)) return OnError("alloc failed"); if (Remaining() < bytes) return OnError("not enough data left"); memcpy(out.AddressOf(), ptr.vp, bytes); ptr.c += bytes; return true; } bool Read(LString &s) { char *start = ptr.c; while (!Eof() && *ptr.c) ptr.c++; if (ptr.c > start) if (!s.Set(start, ptr.c - start)) return OnError("alloc failed"); ptr.c++; return true; } bool Read(TVersion ver) { if (ver == Version1) { if (!Read(TransitionTimes1, hdr.TimeCount)) return OnError("failed to read transitions"); } else if (ver == Version2) { if (!Read(TransitionTimes2, hdr.TimeCount)) return OnError("failed to read transitions"); for (auto &i: TransitionTimes2) i = ntohll(i); } else return OnError("unsupported version"); if (!Read(TransitionTypes, hdr.TimeCount)) return OnError("failed to read transition types"); if (!LocalTimeTypes.Length(hdr.TypeCount)) return OnError("alloc failed"); for (unsigned i=0; i &Info, LDateTime &Start, LDateTime *End) { auto InRange = [&](LDateTime &dt) { if (End) return dt >= Start && dt < *End; return dt.Year() == (Start.Year()-1) || dt.Year() == Start.Year(); }; for (unsigned i=0; iGet().Get() : NULL); #endif for (int year = Start.Year()-1; year <= (End ? End->Year() : Start.Year()); year++) { // Figure out the start and end DST for 'year' int DstOffset = endTrans.OffsetSeconds(StdOffsetSeconds) / 60; auto startDt = startTrans.Get(year); startDt.SetTimeZone(StdOffsetSeconds / 60, false); if (InRange(startDt)) { auto &s = Info.New(); - s.UtcTimeStamp = startDt.Ts(); + s.Utc = startDt.Ts(); s.Offset = DstOffset; } auto endDt = endTrans.Get(year); endDt.SetTimeZone(StdOffsetSeconds / 60, false); #if 0 printf("\tstart=%s %i, end=%s %i\n", startDt.Get().Get(), InRange(startDt), endDt.Get().Get(), InRange(endDt)); #endif if (InRange(endDt)) { auto &e = Info.New(); - e.UtcTimeStamp = endDt.Ts(); + e.Utc = endDt.Ts(); e.Offset = StdOffsetSeconds / 60; } } } return Info.Length() > 0; } }; diff --git a/src/common/General/DateTime.cpp b/src/common/General/DateTime.cpp --- a/src/common/General/DateTime.cpp +++ b/src/common/General/DateTime.cpp @@ -1,2439 +1,2581 @@ /* ** FILE: LDateTime.cpp ** AUTHOR: Matthew Allen ** DATE: 11/11/98 ** DESCRIPTION: Scribe Date Time Object ** ** Copyright (C) 1998, Matthew Allen ** fret@memecode.com */ #define _DEFAULT_SOURCE #include #include #include #include #include #if defined(MAC) #include #endif #ifdef WINDOWS #include +#define timegm _mkgmtime #endif #include "lgi/common/Lgi.h" #include "lgi/common/DateTime.h" #include "lgi/common/DocView.h" constexpr const char *LDateTime::WeekdaysShort[7]; constexpr const char *LDateTime::WeekdaysLong[7]; constexpr const char *LDateTime::MonthsShort[12]; constexpr const char *LDateTime::MonthsLong[12]; #if !defined(WINDOWS) #define MIN_YEAR 1800 #endif #if defined(LINUX) #define USE_ZDUMP 1 #elif defined(HAIKU) #include "lgi/common/TimeZoneInfo.h" #endif #define DEBUG_DST_INFO 0 ////////////////////////////////////////////////////////////////////////////// uint16 LDateTime::DefaultFormat = GDTF_DEFAULT; char LDateTime::DefaultSeparator = '/'; uint16 LDateTime::GetDefaultFormat() { if (DefaultFormat == GDTF_DEFAULT) { #ifdef WIN32 TCHAR s[80] = _T("1"); GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_IDATE, s, CountOf(s)); switch (_tstoi(s)) { case 0: DefaultFormat = GDTF_MONTH_DAY_YEAR; break; default: case 1: DefaultFormat = GDTF_DAY_MONTH_YEAR; break; case 2: DefaultFormat = GDTF_YEAR_MONTH_DAY; break; } GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_ITIME, s, sizeof(s)); if (_tstoi(s) == 1) { DefaultFormat |= GDTF_24HOUR; } else { DefaultFormat |= GDTF_12HOUR; } if (GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SDATE, s, sizeof(s))) DefaultSeparator = (char)s[0]; if (GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SSHORTDATE, s, sizeof(s))) { char Sep[] = { DefaultSeparator, '/', '\\', '-', '.', 0 }; LString Str = s; auto t = Str.SplitDelimit(Sep); for (int i=0; i= low && (v) <= high) bool LDateTime::IsValid() const { return InRange(_Day, 1, 31 ) && InRange(_Year, 1600, 2100) && InRange(_Thousands, 0, 999 ) && InRange(_Month, 1, 12 ) && InRange(_Seconds, 0, 59 ) && InRange(_Minutes, 0, 59 ) && InRange(_Hours, 0, 23 ) && InRange(_Tz, -780, 780 ); } void LDateTime::SetTimeZone(int NewTz, bool ConvertTime) { if (ConvertTime && NewTz != _Tz) AddMinutes(NewTz - _Tz); _Tz = NewTz; } +LDateTime::operator struct tm() const +{ + struct tm t = {}; + + t.tm_year = _Year - 1900; + t.tm_mon = _Month - 1; + t.tm_mday = _Day; + t.tm_hour = _Hours; + t.tm_min = _Minutes; + t.tm_sec = _Seconds; + t.tm_isdst = -1; + + return t; +} + #ifdef WINDOWS LDateTime::operator SYSTEMTIME() const { SYSTEMTIME System = {}; System.wYear = _Year; System.wMonth = limit(_Month, 1, 12); System.wDay = limit(_Day, 1, 31); System.wHour = limit(_Hours, 0, 23); System.wMinute = limit(_Minutes, 0, 59); System.wSecond = limit(_Seconds, 0, 59); System.wMilliseconds = limit(_Thousands, 0, 999); System.wDayOfWeek = DayOfWeek(); return System; } LDateTime &LDateTime::operator =(const SYSTEMTIME &st) { _Year = st.wYear; _Month = st.wMonth; _Day = st.wDay; _Hours = st.wHour; _Minutes = st.wMinute; _Seconds = st.wSecond; _Thousands = st.wMilliseconds; return *this; } #endif bool LDateTime::InferTimeZone(bool ConvertTime) { #ifdef WINDOWS SYSTEMTIME System = *this; // Compare the non-year parts of the SYSTEMTIMEs auto SysCmp = [](SYSTEMTIME &a, SYSTEMTIME &b) { #define CMP(name) if (auto d = a.name - b.name) return d; CMP(wMonth); CMP(wDay); CMP(wHour); CMP(wMinute); CMP(wSecond); CMP(wMilliseconds); return 0; }; + auto MakeAbsolute = [this](SYSTEMTIME &s) + { + if (s.wYear == 0) + { + // Convert from relative to absolute... + LDateTime dt = *this; + dt.Month(s.wMonth); + int days = dt.DaysInMonth(); + if (s.wDay == 5) + { + // Use the final day of the month + for (int day = days; day > 0; day--) + { + dt.Day(day); + if (dt.DayOfWeek() == s.wDayOfWeek) + break; + } + s.wDay = dt.Day(); + } + else if (s.wDay > 0) + { + // Find the 'wDay'th instance of 'wDayOfWeek' + int week = 1; + for (int day = 1; day <= days; day++) + { + dt.Day(day); + if (dt.DayOfWeek() == s.wDayOfWeek) + { + if (week++ == s.wDay) + break; + } + } + s.wDay = dt.Day(); + } + else LAssert(!"unexpected relative date"); + } + }; + // 'System' is currently in 'UTC' but we want the local version of the time, // not in the _Tz timezone, but the effective timezone for that // actual moment in question, which could have a different DST offset. TIME_ZONE_INFORMATION tzi = {}; if (!GetTimeZoneInformationForYear(_Year, NULL, &tzi)) return false; + MakeAbsolute(tzi.StandardDate); + MakeAbsolute(tzi.DaylightDate); auto order = SysCmp(tzi.DaylightDate, tzi.StandardDate); // order > 0 = DaylightDate is after StandardDate // order < 0 = DaylightDate is before StandardDate LAssert(order != 0); auto a = SysCmp(System, tzi.StandardDate); // a > 0 = System is after StandardDate // a < 0 = System is before StandardDate auto b = SysCmp(System, tzi.DaylightDate); // b > 0 = System is after DaylightDate // b < 0 = System is before DaylightDate int tz = (int16)-tzi.Bias; if (order > 0) { // year is: DST -> Normal -> DST if (a < 0 || b > 0) tz -= (int16)tzi.DaylightBias; } else { // year is: Normal -> DST -> Normal if (a < 0 && b > 0) tz -= (int16)tzi.DaylightBias; } if (ConvertTime) - SetTimeZone(tz -_Tz, true); + SetTimeZone(tz - _Tz, true); + else + _Tz = tz; return true; #else - #warning "Impl me." + auto tt = GetUnix(); + auto local = localtime(&tt); + if (!local) + return false; + + auto tz = local->tm_gmtoff / 60; + if (ConvertTime) + SetTimeZone(tz - _Tz, true); + else + _Tz = (int16)tz; + return true; #endif return false; } int LDateTime::SystemTimeZone(bool ForceUpdate) { if (ForceUpdate || CurTz == NO_ZONE) { CurTz = 0; CurTzOff = 0; #ifdef MAC #ifdef LGI_COCOA NSTimeZone *timeZone = [NSTimeZone localTimeZone]; if (timeZone) { NSDate *Now = [NSDate date]; CurTz = (int) [timeZone secondsFromGMTForDate:Now] / 60; CurTzOff = [timeZone daylightSavingTimeOffsetForDate:Now] / 60; CurTz -= CurTzOff; } #elif defined LGI_CARBON CFTimeZoneRef tz = CFTimeZoneCopySystem(); CFAbsoluteTime now = CFAbsoluteTimeGetCurrent(); Boolean dst = CFTimeZoneIsDaylightSavingTime(tz, now); if (dst) { CFAbsoluteTime next = CFTimeZoneGetNextDaylightSavingTimeTransition(tz, now); CurTz = CFTimeZoneGetSecondsFromGMT(tz, next + 100) / 60; } else { CurTz = CFTimeZoneGetSecondsFromGMT(tz, now) / 60; } CurTzOff = CFTimeZoneGetDaylightSavingTimeOffset(tz, now) / 60; CFRelease(tz); #endif #elif defined(WIN32) timeb tbTime; ftime(&tbTime); CurTz = -tbTime.timezone; TIME_ZONE_INFORMATION Tzi; if (GetTimeZoneInformation(&Tzi) == TIME_ZONE_ID_DAYLIGHT) CurTzOff = -Tzi.DaylightBias; #elif defined(LINUX) || defined(HAIKU) int six_months = (365 * 24 * 60 * 60) / 2; time_t now = 0, then = 0; time (&now); then = now - six_months; tm now_tz, then_tz; tm *t = localtime_r(&now, &now_tz); if (t) { localtime_r(&then, &then_tz); CurTz = now_tz.tm_gmtoff / 60; if (now_tz.tm_isdst) { CurTzOff = (now_tz.tm_gmtoff - then_tz.tm_gmtoff) / 60; CurTz = then_tz.tm_gmtoff / 60; } else // This is not DST so there is no offset right? CurTzOff = 0; // (then_tz.tm_gmtoff - now_tz.tm_gmtoff) / 60; } else return NO_ZONE; #else #error "Impl me." #endif } return CurTz + CurTzOff; } int LDateTime::SystemTimeZoneOffset() { if (CurTz == NO_ZONE) SystemTimeZone(); return CurTzOff; } #if defined WIN32 LDateTime ConvertSysTime(SYSTEMTIME &st, int year) { LDateTime n; if (st.wYear) { n.Year(st.wYear); n.Month(st.wMonth); n.Day(st.wDay); } else { n.Year(year); n.Month(st.wMonth); // Find the 'nth' matching weekday, starting from the first day in the month n.Day(1); LDateTime c = n; for (int i=0; iCompare(b); } #elif USE_ZDUMP static bool ParseValue(char *s, LString &var, LString &val) { if (!s) return false; char *e = strchr(s, '='); if (!e) return false; *e++ = 0; var = s; val = e; *e = '='; return var != 0 && val != 0; } #endif /* Testing code... LDateTime Start, End; LArray Info; Start.Set("1/1/2010"); End.Set("31/12/2014"); LDateTime::GetDaylightSavingsInfo(Info, Start, &End); LStringPipe p; for (int i=0; i,int> { MonthHash() { for (int i=0; i &Info, LDateTime &Start, LDateTime *End) { bool Status = false; #if defined(WIN32) TIME_ZONE_INFORMATION Tzi; auto r = GetTimeZoneInformation(&Tzi); if (r > TIME_ZONE_ID_UNKNOWN) { Info.Length(0); // Find the dates for the previous year from Start. This allows // us to cover the start of the current year. LDateTime s = ConvertSysTime(Tzi.StandardDate, Start.Year() - 1); LDateTime d = ConvertSysTime(Tzi.DaylightDate, Start.Year() - 1); // Create initial Info entry, as the last change in the previous year auto *i = &Info.New(); if (s < d) { // Year is: Daylight->Standard->Daylight LDateTime tmp = d; i->Offset = -(Tzi.Bias + Tzi.DaylightBias); tmp.AddMinutes(-i->Offset); i->Utc = tmp; } else { // Year is: Standard->Daylight->Standard LDateTime tmp = s; i->Offset = -(Tzi.Bias + Tzi.StandardBias); tmp.AddMinutes(-i->Offset); i->Utc = tmp; } for (auto y=Start.Year(); y<=(End?End->Year():Start.Year()); y++) { if (s < d) { // Cur year, first event: end of DST i = &Info.New(); auto tmp = ConvertSysTime(Tzi.StandardDate, y); i->Offset = -(Tzi.Bias + Tzi.StandardBias); tmp.AddMinutes(-i->Offset); i->Utc = tmp; // Cur year, second event: start of DST i = &Info.New(); tmp = ConvertSysTime(Tzi.DaylightDate, y); i->Offset = -(Tzi.Bias + Tzi.DaylightBias); tmp.AddMinutes(-i->Offset); i->Utc = tmp; } else { // Cur year, first event: start of DST i = &Info.New(); auto tmp = ConvertSysTime(Tzi.DaylightDate, Start.Year()); i->Offset = -(Tzi.Bias + Tzi.DaylightBias); tmp.AddMinutes(-i->Offset); i->Utc = tmp; // Cur year, second event: end of DST i = &Info.New(); tmp = ConvertSysTime(Tzi.StandardDate, Start.Year()); i->Offset = -(Tzi.Bias + Tzi.StandardBias); tmp.AddMinutes(-i->Offset); i->Utc = tmp; } } Status = true; } #elif defined(MAC) LDateTime From = Start; From.AddMonths(-6); LDateTime To = End ? *End : Start; To.AddMonths(6); auto ToUnix = To.GetUnix(); auto tz = [NSTimeZone systemTimeZone]; - auto startDate = [[NSDate alloc] initWithTimeIntervalSince1970:(From.Ts() / Second64Bit) - Offset1800]; + auto startDate = [[NSDate alloc] initWithTimeIntervalSince1970:(From.Ts().Get() / Second64Bit) - Offset1800]; while (startDate) { auto next = [tz nextDaylightSavingTimeTransitionAfterDate:startDate]; auto &i = Info.New(); auto nextTs = [next timeIntervalSince1970]; - i.UtcTimeStamp = (nextTs + Offset1800) * Second64Bit; + i.Utc = (nextTs + Offset1800) * Second64Bit; i.Offset = (int)([tz secondsFromGMTForDate:[next dateByAddingTimeInterval:60]]/60); #if DEBUG_DST_INFO { LDateTime dt; dt.Set(i.UtcTimeStamp); LgiTrace("%s:%i - Ts=%s Off=%i\n", _FL, dt.Get().Get(), i.Offset); } #endif if (nextTs >= ToUnix) break; [startDate release]; startDate = next; } if (startDate) [startDate release]; #elif USE_ZDUMP if (!Zdump.Length()) { static bool First = true; auto linkLoc = "/etc/localtime"; #if defined(LINUX) auto zoneLoc = "/usr/share/zoneinfo"; #elif defined(HAIKU) auto zoneLoc = "/boot/system/data/zoneinfo"; #else #error "Impl me" #endif if (!LFileExists(linkLoc)) { if (First) { LgiTrace("%s:%i - LDateTime::GetDaylightSavingsInfo error: '%s' doesn't exist.\n" " It should link to something in the '%s' tree.\n", _FL, linkLoc, zoneLoc); #ifdef HAIKU LgiTrace(" To fix that: pkgman install timezone_data and then create the '%s' link.\n", linkLoc); #endif } return First = false; } auto f = popen(LString::Fmt("zdump -v %s", linkLoc), "r"); if (f) { char s[1024]; size_t r; LStringPipe p(1024); while ((r = fread(s, 1, sizeof(s), f)) > 0) p.Write(s, (int)r); fclose(f); Zdump = p.NewLStr().Split("\n"); } else { if (First) { LgiTrace("%s:%i - LDateTime::GetDaylightSavingsInfo error: zdump didn't run.\n", _FL); #ifdef HAIKU LgiTrace("To fix that: pkgman install timezone_data\n"); #endif } return First = false; } } MonthHash Lut; LDateTime Prev; int PrevOff = 0; for (auto Line: Zdump) { auto l = Line.SplitDelimit(" \t"); if (l.Length() >= 16 && l[0].Equals("/etc/localtime")) { // /etc/localtime Sat Oct 3 15:59:59 2037 UTC = Sun Oct 4 01:59:59 2037 EST isdst=0 gmtoff=36000 // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 LDateTime Utc; Utc.Year(l[5].Int()); #if DEBUG_DST_INFO if (Utc.Year() < 2020) continue; // printf("DST: %s\n", Line.Get()); #endif auto Tm = l[4].SplitDelimit(":"); if (Tm.Length() != 3) { #if DEBUG_DST_INFO printf("%s:%i - Tm '%s' has wrong parts: %s\n", _FL, l[4].Get(), Line.Get()); #endif continue; } Utc.Hours(Tm[0].Int()); Utc.Minutes(Tm[1].Int()); Utc.Seconds(Tm[2].Int()); if (Utc.Minutes() < 0) { #if DEBUG_DST_INFO printf("%s:%i - Mins is zero: %s\n", _FL, l[4].Get()); #endif continue; } int m = Lut.Find(l[2]); if (!m) { #if DEBUG_DST_INFO printf("%s:%i - Unknown month '%s'\n", _FL, l[2].Get()); #endif continue; } Utc.Day(l[3].Int()); Utc.Month(m); LString Var, Val; if (!ParseValue(l[14], Var, Val) || Var != "isdst") { #if DEBUG_DST_INFO printf("%s:%i - Unknown value for isdst\n", _FL); #endif continue; } if (!ParseValue(l[15], Var, Val) || Var != "gmtoff") { #if DEBUG_DST_INFO printf("%s:%i - Unknown value for isdst\n", _FL); #endif continue; } int Off = atoi(Val) / 60; if (Utc.Ts().Valid() == 0) continue; if (Prev.Year() && Prev < Start && Start < Utc) { // Emit initial entry for 'start' auto &inf = Info.New(); Prev.Get(inf.Utc); inf.Offset = PrevOff; #if DEBUG_DST_INFO printf("Info: Start=%s %i\n", Prev.Get().Get(), inf.Offset); #endif } if (Utc > Start) { // Emit furthur entries for DST events between start and end. auto &inf = Info.New(); Utc.Get(inf.Utc); inf.Offset = Off; #if DEBUG_DST_INFO printf("Info: Next=%s %i\n", Utc.Get().Get(), inf.Offset); #endif if (End && Utc > *End) { // printf("Utc after end: %s > %s\n", Utc.Get().Get(), End->Get().Get()); break; } } Prev = Utc; PrevOff = Off; } } Status = Info.Length() > 1; #elif defined(HAIKU) LTimeZoneInfo tzinfo; if (!tzinfo.Read()) { #if DEBUG_DST_INFO LgiTrace("%s:%i - info read failed.\n", _FL); #endif return false; } Status = tzinfo.GetDaylightSavingsInfo(Info, Start, End); #if DEBUG_DST_INFO if (!Status) printf("%s:%i - GetDaylightSavingsInfo failed.\n", _FL); #endif #else LAssert(!"Not implemented."); #endif return Status; } bool LDateTime::DstToLocal(LArray &Dst, LDateTime &dt) { if (dt.GetTimeZone()) { LAssert(!"Should be a UTC date."); return true; } #if DEBUG_DST_INFO LgiTrace("DstToLocal: %s\n", dt.Get().Get()); #endif LAssert(Dst.Length() > 1); // Needs to have at least 2 entries...? for (size_t i=0; i= start && dt < end; if (InRange) { dt.SetTimeZone(a.Offset, true); #if DEBUG_DST_INFO LgiTrace("\tRng[%i]: %s -> %s, SetTimeZone(%g), dt=%s\n", (int)i, start.Get().Get(), end.Get().Get(), (double)a.Offset/60.0, dt.Get().Get()); #endif return true; } } auto Last = Dst.Last(); LDateTime d; d.Set(Last.Utc); if (dt >= d && dt.Year() == d.Year()) { // If it's after the last DST change but in the same year... it's ok... // Just use the last offset. dt.SetTimeZone(Last.Offset, true); return true; } #if DEBUG_DST_INFO for (auto d: Dst) LgiTrace("Dst: %s = %i\n", d.GetLocal().Get().Get(), d.Offset); #endif LgiTrace("%s:%i - No valid DST range for: %s\n", _FL, dt.Get().Get()); LAssert(!"No valid DST range for this date."); return false; } int LDateTime::DayOfWeek() const { int Index = 0; int Day = IsLeapYear() ? 29 : 28; switch (_Year / 100) { case 19: { Index = 3; break; } case 20: { Index = 2; break; } } // get year right int y = _Year % 100; int r = y % 12; Index = (Index + (y / 12) + r + (r / 4)) % 7; // get month right if (_Month % 2 == 0) { // even month if (_Month > 2) Day = _Month; } else { // odd month switch (_Month) { case 1: { Day = 31; if (IsLeapYear()) { Index = Index > 0 ? Index - 1 : Index + 6; } break; } case 11: case 3: { Day = 7; break; } case 5: { Day = 9; break; } case 7: { Day = 11; break; } case 9: { Day = 5; break; } } } // get day right int Diff = Index - (Day - _Day); while (Diff < 0) Diff += 7; return Diff % 7; } LDateTime LDateTime::Now() { LDateTime dt; dt.SetNow(); return dt; } LDateTime &LDateTime::SetNow() { #ifdef WIN32 SYSTEMTIME stNow; auto sysTz = SystemTimeZone(); if (_Tz == sysTz) GetLocalTime(&stNow); else GetSystemTime(&stNow); #if 1 *this = stNow; if (_Tz && _Tz != sysTz) { // Adjust to this objects timezone... auto tz = _Tz; _Tz = 0; SetTimeZone(tz, true); } #else // This is actually less efficient. FILETIME ftNow; SystemTimeToFileTime(&stNow, &ftNow); uint64 i64 = ((uint64)ftNow.dwHighDateTime << 32) | ftNow.dwLowDateTime; OsTime(i64); #endif #else time_t now; time(&now); struct tm *time = localtime(&now); if (time) *this = time; #ifndef LGI_STATIC else { LgiTrace("%s:%i - Error: localtime failed, now=%u\n", _FL, now); } #endif #endif return *this; } -#define Convert24HrTo12Hr(h) ( (h) == 0 ? 12 : (h) > 12 ? (h) % 12 : (h) ) +#define Convert24HrTo12Hr(h) ( (h) == 0 ? 12 : (h) > 12 ? (h) % 12 : (h) ) #define Convert24HrToAmPm(h) ( (h) >= 12 ? "p" : "a" ) LString LDateTime::GetDate() const { char s[32]; int Ch = GetDate(s, sizeof(s)); return LString(s, Ch); } int LDateTime::GetDate(char *Str, size_t SLen) const { int Ch = 0; if (Str && SLen > 0) { switch (_Format & GDTF_DATE_MASK) { case GDTF_MONTH_DAY_YEAR: Ch += sprintf_s(Str+Ch, SLen-Ch, _Format&GDTF_MONTH_LEADINGZ?"%2.2i" :"%i" , _Month); Ch += sprintf_s(Str+Ch, SLen-Ch, _Format&GDTF_DAY_LEADINGZ ?"%c%2.2i":"%c%i", DefaultSeparator, _Day); Ch += sprintf_s(Str+Ch, SLen-Ch, "%c%i", DefaultSeparator, _Year); break; default: case GDTF_DAY_MONTH_YEAR: Ch += sprintf_s(Str+Ch, SLen-Ch, _Format&GDTF_DAY_LEADINGZ ?"%2.2i" :"%i" , _Day); Ch += sprintf_s(Str+Ch, SLen-Ch, _Format&GDTF_MONTH_LEADINGZ?"%c%2.2i":"%c%i", DefaultSeparator, _Month); Ch += sprintf_s(Str+Ch, SLen-Ch, "%c%i", DefaultSeparator, _Year); break; case GDTF_YEAR_MONTH_DAY: Ch += sprintf_s(Str+Ch, SLen-Ch, "%i", _Year); Ch += sprintf_s(Str+Ch, SLen-Ch, _Format&GDTF_MONTH_LEADINGZ?"%c%2.2i":"%c%i", DefaultSeparator, _Month); Ch += sprintf_s(Str+Ch, SLen-Ch, _Format&GDTF_DAY_LEADINGZ ?"%c%2.2i":"%c%i", DefaultSeparator, _Day); break; } } return Ch; } LString LDateTime::GetTime() const { char s[32]; int Ch = GetTime(s, sizeof(s)); return LString(s, Ch); } int LDateTime::GetTime(char *Str, size_t SLen) const { int Ch = 0; if (Str && SLen > 0) { switch (_Format & GDTF_TIME_MASK) { case GDTF_12HOUR: default: { Ch += sprintf_s(Str, SLen, "%i:%2.2i:%2.2i%s", Convert24HrTo12Hr(_Hours), _Minutes, _Seconds, Convert24HrToAmPm(_Hours)); break; } case GDTF_24HOUR: { Ch += sprintf_s(Str, SLen, "%i:%2.2i:%2.2i", _Hours, _Minutes, _Seconds); break; } } } return Ch; } LTimeStamp LDateTime::Ts() const { LTimeStamp ts; Get(ts); return ts; } -uint64_t LDateTime::GetUnix() +time_t LDateTime::GetUnix() { + /* This isn't unit time? LTimeStamp s; Get(s); #if defined(WINDOWS) return s.Get() / LDateTime::Second64Bit / 116445168000000000LL; #else return s.Get() / LDateTime::Second64Bit - Offset1800; #endif + */ + + struct tm t = *this; + time_t tt = timegm(&t); + if (_Tz) + tt -= _Tz * 60; + + return tt; } -bool LDateTime::SetUnix(uint64 s) +bool LDateTime::SetUnix(time_t tt) { + /* This isn't unit time? #if defined(WINDOWS) return Set(s * LDateTime::Second64Bit + 116445168000000000LL); #else return Set((s + Offset1800) * LDateTime::Second64Bit); #endif + */ + + struct tm *t; + + if (_Tz) + tt += _Tz * 60; + + #if !defined(_MSC_VER) || _MSC_VER < _MSC_VER_VS2005 + t = gmtime(&tt); + if (t) + #else + struct tm tmp; + if (_gmtime64_s(t = &tmp, &tt) == 0) + #endif + { + return Set(t, false); + } + + return false; } bool LDateTime::Set(const LTimeStamp &s) { #if defined WIN32 - FILETIME Utc; - SYSTEMTIME System; - - // Adjust to the desired timezone - uint64 u = s.Get(); - Utc.dwHighDateTime = u >> 32; - Utc.dwLowDateTime = u & 0xffffffff; - - if (!FileTimeToSystemTime(&Utc, &System)) - return false; - - *this = System; - return true; + return OsTime(s.Get()); #else - Set(s.Unix()); + SetUnix(s.Unix()); _Thousands = s.Get() % Second64Bit; return true; #endif } bool LDateTime::Set(struct tm *t, bool inferTimezone) { if (!t) return false; _Year = t->tm_year + 1900; _Month = t->tm_mon + 1; _Day = t->tm_mday; _Hours = t->tm_hour; _Minutes = t->tm_min; _Seconds = t->tm_sec; _Thousands = 0; if (inferTimezone) { - #ifdef WINDOWS - #define timegm _mkgmtime - #endif auto diff = timegm(t) - mktime(t); _Tz = (int16)(diff / 60); } return true; } -bool LDateTime::Set(time_t tt) -{ - struct tm *t; - - if (_Tz) - tt += _Tz * 60; - -#if !defined(_MSC_VER) || _MSC_VER < _MSC_VER_VS2005 - t = gmtime(&tt); - if (t) -#else - struct tm tmp; - if (_gmtime64_s(t = &tmp, &tt) == 0) -#endif - { - return Set(t, false); - } - - return false; -} - uint64_t LDateTime::OsTime() const { #ifdef WINDOWS FILETIME Utc; SYSTEMTIME System = *this; if (SystemTimeToFileTime(&System, &Utc)) { uint64_t s = ((uint64_t)Utc.dwHighDateTime << 32) | Utc.dwLowDateTime; if (_Tz) // Adjust for timezone s -= (int64)_Tz * 60 * Second64Bit; return s; } else { DWORD Err = GetLastError(); LAssert(!"SystemTimeToFileTime failed."); } #else if (_Year < MIN_YEAR) return 0; struct tm t; ZeroObj(t); t.tm_year = _Year - 1900; t.tm_mon = _Month - 1; t.tm_mday = _Day; t.tm_hour = _Hours; t.tm_min = _Minutes; t.tm_sec = _Seconds; t.tm_isdst = -1; time_t sec = timegm(&t); if (sec == -1) return 0; if (_Tz) { // Adjust the output to UTC from the current timezone. sec -= _Tz * 60; } return sec; #endif return 0; } bool LDateTime::OsTime(uint64_t ts) { - return Set((time_t)ts); + #ifdef WINDOWS + + if (_Tz) + // Adjust for timezone + ts += (int64)_Tz * 60 * Second64Bit; + + FILETIME Utc; + Utc.dwHighDateTime = ts >> 32; + Utc.dwLowDateTime = ts & 0xffffffff; + + SYSTEMTIME System; + if (!FileTimeToSystemTime(&Utc, &System)) + { + DWORD Err = GetLastError(); + LAssert(!"FileTimeToSystemTime failed."); + return false; + } + + *this = System; + return true; + + #else + + return SetUnix((time_t)ts); + + #endif } bool LDateTime::Get(LTimeStamp &s) const { #ifdef WINDOWS if (!IsValid()) { LAssert(!"Needs a valid date."); return false; } s.Ref() = OsTime(); if (!s.Valid()) return false; return true; #else if (_Year < MIN_YEAR) return false; auto sec = OsTime(); s.Ref() = (uint64)(sec + Offset1800) * Second64Bit + _Thousands; return true; #endif } LString LDateTime::Get() const { char buf[32]; int Ch = GetDate(buf, sizeof(buf)); buf[Ch++] = ' '; Ch += GetTime(buf+Ch, sizeof(buf)-Ch); return LString(buf, Ch); } void LDateTime::Get(char *Str, size_t SLen) const { if (Str) { GetDate(Str, SLen); size_t len = strlen(Str); if (len < SLen - 1) { Str[len++] = ' '; GetTime(Str+len, SLen-len); } } } bool LDateTime::Set(const char *Str) { if (!Str) return false; if (Strlen(Str) > 100) return false; char Local[256]; strcpy_s(Local, sizeof(Local), Str); char *Sep = strchr(Local, ' '); if (Sep) { *Sep++ = 0; if (!SetTime(Sep)) return false; } if (!SetDate(Local)) return false; return true; } void LDateTime::Month(char *m) { int i = IsMonth(m); if (i >= 0) _Month = i + 1; } int DateComponent(const char *s) { int64 i = Atoi(s); return i ? (int)i : LDateTime::IsMonth(s); } bool LDateTime::SetDate(const char *Str) { bool Status = false; if (Str) { auto T = LString(Str).SplitDelimit("/-.,_\\"); if (T.Length() == 3) { int i[3] = { DateComponent(T[0]), DateComponent(T[1]), DateComponent(T[2]) }; int fmt = _Format & GDTF_DATE_MASK; // Do some guessing / overrides. // Don't let _Format define the format completely. if (i[0] > 1000) { fmt = GDTF_YEAR_MONTH_DAY; } else if (i[2] > 1000) { if (i[0] > 12) fmt = GDTF_DAY_MONTH_YEAR; else if (i[1] > 12) fmt = GDTF_MONTH_DAY_YEAR; } switch (fmt) { case GDTF_MONTH_DAY_YEAR: { _Month = i[0]; _Day = i[1]; _Year = i[2]; break; } case GDTF_DAY_MONTH_YEAR: { _Day = i[0]; _Month = i[1]; _Year = i[2]; break; } case GDTF_YEAR_MONTH_DAY: { _Year = i[0]; _Month = i[1]; _Day = i[2]; break; } default: { _Year = i[2]; if ((DefaultFormat & GDTF_DATE_MASK) == GDTF_MONTH_DAY_YEAR) { // Assume m/d/yyyy _Day = i[1]; _Month = i[0]; } else { // Who knows??? // Assume d/m/yyyy _Day = i[0]; _Month = i[1]; } break; } } if (_Year < 100) { LAssert(_Day < 1000 && _Month < 1000); if (_Year >= 80) _Year += 1900; else _Year += 2000; } Status = true; } else { // Fall back to fuzzy matching auto T = LString(Str).SplitDelimit(" ,"); MonthHash Lut; int FMonth = 0; int FDay = 0; int FYear = 0; for (unsigned i=0; i 0) { if (i >= 1000) { FYear = i; } else if (i < 32) { FDay = i; } } } else { int i = Lut.Find(p); if (i) FMonth = i; } } if (FMonth && FDay) { Day(FDay); Month(FMonth); } if (FYear) { Year(FYear); } else { LDateTime Now; Now.SetNow(); Year(Now.Year()); } } } return Status; } bool LDateTime::SetTime(const char *Str) { if (!Str) return false; auto T = LString(Str).SplitDelimit(":."); if (T.Length() < 2 || T.Length() > 4) return false; #define SetClamp(out, in, minVal, maxVal) \ out = (int)Atoi(in.Get(), 10, 0); \ if (out > maxVal) out = maxVal; \ else if (out < minVal) out = minVal SetClamp(_Hours, T[0], 0, 23); SetClamp(_Minutes, T[1], 0, 59); SetClamp(_Seconds, T[2], 0, 59); _Thousands = 0; const char *s = T.Last(); if (s) { if (strchr(s, 'p') || strchr(s, 'P')) { if (_Hours != 12) _Hours += 12; } else if (strchr(s, 'a') || strchr(s, 'A')) { if (_Hours == 12) _Hours -= 12; } } if (T.Length() > 3) { LString t = "0."; t += s; _Thousands = (int) (t.Float() * 1000); } return true; } int LDateTime::IsWeekDay(const char *s) { for (unsigned n=0; n= 4) { Year((int)t[0].Int()); Month((int)t[1].Int()); Day((int)t[2].Int()); } else if (t[2].Length() >= 4) { Day((int)t[0].Int()); Month((int)t[1].Int()); Year((int)t[2].Int()); } else { LAssert(!"Unknown date format?"); return false; } } } else if (a[i].Length() == 4) Year((int)a[i].Int()); else if (!Day()) Day((int)a[i].Int()); } else if (IsAlpha(*c)) { int WkDay = IsWeekDay(c); if (WkDay >= 0) continue; int Mnth = IsMonth(c); if (Mnth >= 0) Month(Mnth + 1); } else if (*c == '-' || *c == '+') { c++; if (strlen(c) == 4) { // Timezone.. int64 Tz = a[i].Int(); int Hrs = (int) (Tz / 100); int Min = (int) (Tz % 100); SetTimeZone(Hrs * 60 + Min, false); } } } return IsValid(); } int LDateTime::Sizeof() { return sizeof(int) * 7; } bool LDateTime::Serialize(LFile &f, bool Write) { int32 i; if (Write) { #define wf(fld) i = fld; f << i; wf(_Day); wf(_Month); wf(_Year); wf(_Thousands); wf(_Seconds); wf(_Minutes); wf(_Hours); } else { #define rf(fld) f >> i; fld = i; rf(_Day); rf(_Month); rf(_Year); rf(_Thousands); rf(_Seconds); rf(_Minutes); rf(_Hours); } return true; } /* bool LDateTime::Serialize(ObjProperties *Props, char *Name, bool Write) { #ifndef LGI_STATIC if (Props && Name) { struct _Date { uint8_t Day; uint8_t Month; int16_t Year; uint8_t Hour; uint8_t Minute; uint16_t ThouSec; }; LAssert(sizeof(_Date) == 8); if (Write) { _Date d; d.Day = _Day; d.Month = _Month; d.Year = _Year; d.Hour = _Hours; d.Minute = _Minutes; d.ThouSec = (_Seconds * 1000) + _Thousands; return Props->Set(Name, &d, sizeof(d)); } else // Read { void *Ptr; int Len; if (Props->Get(Name, Ptr, Len) && sizeof(_Date) == Len) { _Date *d = (_Date*) Ptr; _Day = d->Day; _Month = d->Month; _Year = d->Year; _Hours = d->Hour; _Minutes = d->Minute; _Seconds = d->ThouSec / 1000; _Thousands = d->ThouSec % 1000; return true; } } } #endif return false; } */ int LDateTime::Compare(const LDateTime *Date) const { // this - *Date auto ThisTs = IsValid() ? Ts() : LTimeStamp(); auto DateTs = Date->IsValid() ? Date->Ts() : LTimeStamp(); if (ThisTs.Get() & 0x800000000000000) { Get(ThisTs); } // If these ever fire, the cast to int64_t will overflow LAssert((ThisTs.Get() & 0x800000000000000) == 0); LAssert((DateTs.Get() & 0x800000000000000) == 0); auto Diff = ThisTs - DateTs; if (Diff < 0) return -1; return Diff > 0 ? 1 : 0; } #define DATETIME_OP(op) \ bool LDateTime::operator op(const LDateTime &dt) const \ { \ auto a = Ts(); \ auto b = dt.Ts(); \ return a op b; \ } DATETIME_OP(<) DATETIME_OP(<=) DATETIME_OP(>) DATETIME_OP(>=) bool LDateTime::operator ==(const LDateTime &dt) const { return _Year == dt._Year && _Month == dt._Month && _Day == dt._Day && _Hours == dt._Hours && _Minutes == dt._Minutes && _Seconds == dt._Seconds && _Thousands == dt._Thousands; } bool LDateTime::operator !=(const LDateTime &dt) const { return _Year != dt._Year || _Month != dt._Month || _Day != dt._Day || _Hours != dt._Hours || _Minutes != dt._Minutes || _Seconds != dt._Seconds || _Thousands != dt._Thousands; } int LDateTime::DiffMonths(const LDateTime &dt) { int a = (Year() * 12) + Month(); int b = (dt.Year() * 12) + dt.Month(); return b - a; } LDateTime LDateTime::operator -(const LDateTime &dt) { LTimeStamp a, b; Get(a); dt.Get(b); /// Resolution of a second when using 64 bit timestamps int64 Sec = Second64Bit; int64 Min = 60 * Sec; int64 Hr = 60 * Min; int64 Day = 24 * Hr; int64 d = (int64)a.Get() - (int64)b.Get(); LDateTime r; r._Day = (int16) (d / Day); d -= r._Day * Day; r._Hours = (int16) (d / Hr); d -= r._Hours * Hr; r._Minutes = (int16) (d / Min); d -= r._Minutes * Min; r._Seconds = (int16) (d / Sec); #ifdef WIN32 d -= r._Seconds * Sec; r._Thousands = (int16) (d / 10000); #else r._Thousands = 0; #endif return r; } LDateTime LDateTime::operator +(const LDateTime &dt) { LDateTime s = *this; s.AddMonths(dt.Month()); s.AddDays(dt.Day()); s.AddHours(dt.Hours()); s.AddMinutes(dt.Minutes()); // s.AddSeconds(dt.Seconds()); return s; } LDateTime &LDateTime::operator =(const LDateTime &t) { _Day = t._Day; _Year = t._Year; _Thousands = t._Thousands; _Month = t._Month; _Seconds = t._Seconds; _Minutes = t._Minutes; _Hours = t._Hours; _Tz = t._Tz; _Format = t._Format; return *this; } LDateTime &LDateTime::operator =(struct tm *time) { if (time) { _Seconds = time->tm_sec; _Minutes = time->tm_min; _Hours = time->tm_hour; _Day = time->tm_mday; _Month = time->tm_mon + 1; _Year = time->tm_year + 1900; } else Empty(); return *this; } bool LDateTime::IsSameDay(LDateTime &d) const { return Day() == d.Day() && Month() == d.Month() && Year() == d.Year(); } bool LDateTime::IsSameMonth(LDateTime &d) const { return Day() == d.Day() && Month() == d.Month(); } bool LDateTime::IsSameYear(LDateTime &d) const { return Year() == d.Year(); } LDateTime LDateTime::StartOfDay() const { LDateTime dt = *this; dt.Hours(0); dt.Minutes(0); dt.Seconds(0); dt.Thousands(0); return dt; } LDateTime LDateTime::EndOfDay() const { LDateTime dt = *this; dt.Hours(23); dt.Minutes(59); dt.Seconds(59); dt.Thousands(999); return dt; } bool LDateTime::IsLeapYear(int Year) const { if (Year < 0) Year = _Year; if (Year % 4 != 0) return false; if (Year % 400 == 0) return true; if (Year % 100 == 0) return false; return true; } int LDateTime::DaysInMonth() const { if (_Month == 2 && IsLeapYear()) { return 29; } short DaysInMonth[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; return _Month >= 1 && _Month <= 12 ? DaysInMonth[_Month-1] : 0; } void LDateTime::AddSeconds(int64 Seconds) { LTimeStamp i; if (Get(i)) { i.Ref() += Seconds * Second64Bit; Set(i); } } void LDateTime::AddMinutes(int64 Minutes) { LTimeStamp i; if (Get(i)) { int64 delta = Minutes * 60 * Second64Bit; i.Ref() += delta; Set(i); } } void LDateTime::AddHours(int64 Hours) { LTimeStamp i; if (Get(i)) { i.Ref() += Hours * HourLength * Second64Bit; Set(i); } } bool LDateTime::AddDays(int64 Days) { if (!Days) return true; LTimeStamp Ts; if (!Get(Ts)) return false; Ts.Ref() += Days * LDateTime::DayLength * Second64Bit; bool b = Set(Ts); return b; } void LDateTime::AddMonths(int64 Months) { int64 m = _Month + Months; do { if (m < 1) { _Year--; m += 12; } else if (m > 12) { _Year++; m -= 12; } else { break; } } while (1); _Month = (int16) m; if (_Day > DaysInMonth()) _Day = DaysInMonth(); } LString LDateTime::DescribePeriod(double seconds) { int mins = (int) (seconds / 60); seconds -= mins * 60; int hrs = mins / 60; mins -= hrs * 60; int days = hrs / 24; hrs -= days * 24; LString s; if (days > 0) s.Printf("%id %ih %im %is", days, hrs, mins, (int)seconds); else if (hrs > 0) s.Printf("%ih %im %is", hrs, mins, (int)seconds); else if (mins > 0) s.Printf("%im %is", mins, (int)seconds); else s.Printf("%is", (int)seconds); return s; } LString LDateTime::DescribePeriod(LDateTime to) { auto ThisTs = Ts(); auto ToTs = to.Ts(); auto diff = ThisTs < ToTs ? ToTs - ThisTs : ThisTs - ToTs; auto seconds = (double)diff / LDateTime::Second64Bit; return DescribePeriod(seconds); } int LDateTime::MonthFromName(const char *Name) { if (Name) { for (int m=0; m<12; m++) { if (strnicmp(Name, MonthsShort[m], strlen(MonthsShort[m])) == 0) { return m + 1; break; } } } return -1; } bool LDateTime::Decode(const char *In) { // Test data: // // Tue, 6 Dec 2005 1:25:32 -0800 Empty(); if (!In) { LAssert(0); return false; } bool Status = false; // Tokenize delimited by whitespace LString::Array T = LString(In).SplitDelimit(", \t\r\n"); if (T.Length() < 2) { if (T[0].IsNumeric()) { // Some sort of timestamp? uint64_t Ts = Atoi(T[0].Get()); if (Ts > 0) { return SetUnix(Ts); } else return false; } else { // What now? return false; } } else { bool GotDate = false; for (unsigned i=0; i 31) { // Y/M/D? Year((int)Date[0].Int()); Day((int)Date[2].Int()); } else if (Date[2].Int() > 31) { // D/M/Y? Day((int)Date[0].Int()); Year((int)Date[2].Int()); } else { // Ambiguous year... bool YrFirst = true; if (Date[0].Length() == 1) YrFirst = false; // else we really can't tell.. just go with year first if (YrFirst) { Year((int)Date[0].Int()); Day((int)Date[2].Int()); } else { Day((int)Date[0].Int()); Year((int)Date[2].Int()); } LDateTime Now; Now.SetNow(); if (Year() + 2000 <= Now.Year()) Year(2000 + Year()); else Year(1900 + Year()); } if (Date[1].IsNumeric()) Month((int)Date[1].Int()); else { int m = MonthFromName(Date[1]); if (m > 0) Month(m); } GotDate = true; Status = true; } else if (s.Find(":") >= 0) { // whole time // Do some validation bool Valid = true; for (char *c = s; *c && Valid; c++) { if (!(IsDigit(*c) || *c == ':')) Valid = false; } if (Valid) { LString::Array Time = s.Split(":"); if (Time.Length() == 2 || Time.Length() == 3) { // Hour int i = (int) Time[0].Int(); if (i >= 0) Hours(i); if (s.Lower().Find("p") >= 0) { if (Hours() < 12) Hours(Hours() + 12); } // Minute i = (int) Time[1].Int(); if (i >= 0) Minutes(i); if (Time.Length() == 3) { // Second i = (int) Time[2].Int(); if (i >= 0) Seconds(i); } Status = true; } } } else if (IsAlpha(s(0))) { // text int m = MonthFromName(s); if (m > 0) Month(m); } else if (strchr("+-", *s)) { // timezone DoTimeZone: LDateTime Now; double OurTmz = (double)Now.SystemTimeZone() / 60; if (s && strchr("-+", *s) && strlen(s) == 5) { #if 1 int i = atoi(s); int hr = i / 100; int min = i % 100; SetTimeZone(hr * 60 + min, false); #else // adjust for timezone char Buf[32]; memcpy(Buf, s, 3); Buf[3] = 0; double TheirTmz = atof(Buf); memcpy(Buf+1, s + 3, 2); TheirTmz += (atof(Buf) / 60); if (Tz) { *Tz = TheirTmz; } double AdjustHours = OurTmz - TheirTmz; AddMinutes((int) (AdjustHours * 60)); #endif } else { // assume GMT AddMinutes((int) (OurTmz * 60)); } } else if (s.IsNumeric()) { int Count = 0; for (char *c = s; *c; c++) { if (!IsDigit(*c)) break; Count++; } if (Count <= 2) { if (Day()) { // We already have a day... so this might be // a 2 digit year... LDateTime Now; Now.SetNow(); int Yr = atoi(s); if (2000 + Yr <= Now.Year()) Year(2000 + Yr); else Year(1900 + Yr); } else { // A day number (hopefully)? Day((int)s.Int()); } } else if (Count == 4) { if (!Year()) { // A year! Year((int)s.Int()); Status = true; } else { goto DoTimeZone; } // My one and only Y2K fix // d.Year((Yr < 100) ? (Yr > 50) ? 1900+Yr : 2000+Yr : Yr); } } } } return Status; } bool LDateTime::GetVariant(const char *Name, LVariant &Dst, char *Array) { LDomProperty p = LStringToDomProp(Name); switch (p) { case DateYear: // Type: Int32 Dst = Year(); break; case DateMonth: // Type: Int32 Dst = Month(); break; case DateDay: // Type: Int32 Dst = Day(); break; case DateHour: // Type: Int32 Dst = Hours(); break; case DateMinute: // Type: Int32 Dst = Minutes(); break; case DateSecond: // Type: Int32 Dst = Seconds(); break; case DateDate: // Type: String { char s[32]; GetDate(s, sizeof(s)); Dst = s; break; } case DateTime: // Type: String { char s[32]; GetTime(s, sizeof(s)); Dst = s; break; } case TypeString: // Type: String case DateDateAndTime: // Type: String { char s[32]; Get(s, sizeof(s)); Dst = s; break; } case TypeInt: // Type: Int64 case DateTimestamp: // Type: Int64 { LTimeStamp i; if (Get(i)) Dst = (int64)i.Get(); break; } case DateSecond64Bit: { Dst = Second64Bit; break; } default: { return false; } } return true; } bool LDateTime::SetVariant(const char *Name, LVariant &Value, char *Array) { LDomProperty p = LStringToDomProp(Name); switch (p) { case DateYear: Year(Value.CastInt32()); break; case DateMonth: Month(Value.CastInt32()); break; case DateDay: Day(Value.CastInt32()); break; case DateHour: Hours(Value.CastInt32()); break; case DateMinute: Minutes(Value.CastInt32()); break; case DateSecond: Seconds(Value.CastInt32()); break; case DateDate: SetDate(Value.Str()); break; case DateTime: SetTime(Value.Str()); break; case DateDateAndTime: Set(Value.Str()); break; case DateTimestamp: Set((uint64)Value.CastInt64()); break; default: return false; } return true; } bool LDateTime::CallMethod(const char *Name, LVariant *ReturnValue, LArray &Args) { switch (LStringToDomProp(Name)) { case DateSetNow: SetNow(); if (ReturnValue) *ReturnValue = true; break; case DateSetStr: if (Args.Length() < 1) return false; bool Status; if (Args[0]->Type == GV_INT64) Status = Set((uint64) Args[0]->Value.Int64); else Status = Set(Args[0]->Str()); if (ReturnValue) *ReturnValue = Status; break; case DateGetStr: { char s[256] = ""; Get(s, sizeof(s)); if (ReturnValue) *ReturnValue = s; break; } default: return false; } return true; } #ifdef _DEBUG #define DATE_ASSERT(i) \ if (!(i)) \ { \ LAssert(!"LDateTime unit test failed."); \ return false; \ } -bool LDateTime_Test() +static bool CompareDateStr(LString a, LString b) { + auto delim = "/: ap"; + auto aParts = a.SplitDelimit(delim); + auto bParts = a.SplitDelimit(delim); + if (aParts.Length() != 6 || + bParts.Length() != 6) + return false; + for (int i=0; i= 8); + // Check 64bit get/set - LDateTime t("1/1/2017 0:0:0"); - LTimeStamp i; - DATE_ASSERT(t.Get(i)); - LgiTrace("Get='%s'\n", t.Get().Get()); - auto i2 = i + (24ULL * 60 * 60 * LDateTime::Second64Bit); - LDateTime t2; - t2.SetFormat(GDTF_DAY_MONTH_YEAR); - t2.Set(i2); - LString s = t2.Get(); - LgiTrace("Set='%s'\n", s.Get()); - DATE_ASSERT(!stricmp(s, "2/1/2017 12:00:00a") || - !stricmp(s, "2/01/2017 12:00:00a")); + LDateTime t("1/1/2017 0:0:0"), t2; + LTimeStamp i; + DATE_ASSERT(t.Get(i)); + // LgiTrace("Get='%s'\n", t.Get().Get()); + auto i2 = i + (24ULL * 60 * 60 * LDateTime::Second64Bit); + t2.SetFormat(GDTF_DAY_MONTH_YEAR); + t2.Set(i2); + LString s = t2.Get(); + // LgiTrace("Set='%s'\n", s.Get()); + DATE_ASSERT(CompareDateStr(s, "2/1/2017 12:00:00a")); + + t.SetNow(); + // LgiTrace("Now.Local=%s Tz=%.2f\n", t.Get().Get(), t.GetTimeZoneHours()); + t2 = t; + t2.ToUtc(); + // LgiTrace("Now.Utc=%s Tz=%.2f\n", t2.Get().Get(), t2.GetTimeZoneHours()); + t2.ToLocal(); + // LgiTrace("Now.Local=%s Tz=%.2f\n", t2.Get().Get(), t2.GetTimeZoneHours()); + DATE_ASSERT(t == t2); - t.SetNow(); - LgiTrace("Now.Local=%s Tz=%.2f\n", t.Get().Get(), t.GetTimeZoneHours()); - t2 = t; - t2.ToUtc(); - LgiTrace("Now.Utc=%s Tz=%.2f\n", t2.Get().Get(), t2.GetTimeZoneHours()); - t2.ToLocal(); - LgiTrace("Now.Local=%s Tz=%.2f\n", t2.Get().Get(), t2.GetTimeZoneHours()); - DATE_ASSERT(t == t2); - + // Check get/set Unix time + t.ToUtc(); + t.SetUnix(tt = 1704067200); + s = t.Get(); + DATE_ASSERT(CompareDateStr(s, Jan1)); + auto new_tt = t.GetUnix(); + DATE_ASSERT(new_tt == tt); + // Now do it with a timezone set... + t.SetTimeZone(t.SystemTimeZone(), false); + t.SetUnix(tt); + DATE_ASSERT(CompareDateStr(s, Jan1)); + new_tt = t.GetUnix(); + DATE_ASSERT(new_tt == tt); + + // Check various Add### functions + + + // Check infer timezone + t.Empty(); + t.Set(Jan1); + DATE_ASSERT(t.InferTimeZone(false)); + DATE_ASSERT(t.GetTimeZone() == 660); + t.SetTimeZone(660, false); + + // Now try something right on the edge of the DST change... just before... + t.SetUnix(1712412000); // 7/4/2024 1:00:00 +1100 + t.SetTimeZone(0, true); + DATE_ASSERT(t.InferTimeZone(false)); + DATE_ASSERT(t.GetTimeZone() == 660); + // Just after... + t.SetUnix(1712426400); // 7/4/2024 4:00:00 +1000 + t.SetTimeZone(0, true); + DATE_ASSERT(t.InferTimeZone(false)); + DATE_ASSERT(t.GetTimeZone() == 600); + + return true; } #endif //////////////////////////////////////////////////////////////////////////////////////////////////// LTimeStamp <imeStamp::operator =(const time_t unixTime) { #if defined(WINDOWS) ts = (unixTime + SEC_TO_UNIX_EPOCH) * WINDOWS_TICK; #else ts = (unixTime + LDateTime::Offset1800) * LDateTime::Second64Bit; #endif return *this; } LTimeStamp <imeStamp::operator =(const LDateTime &dt) { dt.Get(*this); return *this; } time_t LTimeStamp::Unix() const { #if defined(WINDOWS) return (ts / WINDOWS_TICK) - SEC_TO_UNIX_EPOCH; #else return (ts / LDateTime::Second64Bit) - LDateTime::Offset1800; #endif } diff --git a/src/common/Net/Uri.cpp b/src/common/Net/Uri.cpp --- a/src/common/Net/Uri.cpp +++ b/src/common/Net/Uri.cpp @@ -1,439 +1,439 @@ #include "lgi/common/Lgi.h" #include "lgi/common/Net.h" #include "lgi/common/RegKey.h" #include "lgi/common/Uri.h" ///////////////////////////////////////////////////////////////////////////////// static const char *Ws = " \t\r\n"; #define SkipWs(s) while (*s && strchr(Ws, *s)) s++; LUri::LUri(const char *uri) { if (uri) Set(uri); } LUri::LUri ( const char *proto, const char *user, const char *pass, const char *host, int port, const char *path, const char *anchor ) { sProtocol = proto; sUser = user; sPass = pass; sHost = host; Port = port; sPath = path; sAnchor = anchor; } LUri::~LUri() { Empty(); } LUri &LUri::operator +=(const char *s) { // Add segment to path if (!s) return *this; if (*s == '/') sPath.Empty(); // reset auto parts = sPath.SplitDelimit("/\\"); parts.SetFixedLength(false); for (auto p: LString(s).SplitDelimit("/\\")) { if (p.Equals("..")) parts.PopLast(); else if (p.Equals(".")) ; else parts.Add(p); } sPath = LString(IsFile() ? DIR_STR : "/").Join(parts); return *this; } LUri &LUri::operator =(const LUri &u) { Empty(); sProtocol = u.sProtocol; sUser = u.sUser; sPass = u.sPass; sHost = u.sHost; sPath = u.sPath; sAnchor = u.sAnchor; Port = u.Port; return *this; } void LUri::Empty() { Port = 0; sProtocol.Empty(); sUser.Empty(); sPass.Empty(); sHost.Empty(); sPath.Empty(); sAnchor.Empty(); } LUri::operator bool() { return IsFile() ? !sPath.IsEmpty() : !sHost.IsEmpty(); } LString LUri::LocalPath() { if (!IsFile()) - return NULL; + return LString(); #ifdef WINDOWS if (sPath.Length() > 0 && sPath(0) == '/') return sPath(1, -1).Replace("/", DIR_STR); #endif return sPath.Replace("/", DIR_STR); } LString LUri::ToString() { LStringPipe p; if (sProtocol) p.Print("%s://", sProtocol.Get()); if (sUser || sPass) { auto UserEnc = EncodeStr(sUser, "@:"); auto PassEnc = EncodeStr(sPass, "@:"); p.Print("%s:%s@", UserEnc?UserEnc.Get():"", PassEnc?PassEnc.Get():""); } if (sHost) p.Write(sHost); if (Port) p.Print(":%i", Port); if (sPath) { auto e = EncodeStr(sPath); char *s = e ? e : sPath; p.Print("%s%s", *s == '/' ? "" : "/", s); } if (sAnchor) p.Print("#%s", sAnchor.Get()); return p.NewLStr(); } bool LUri::Set(const char *uri) { if (!uri) return false; Empty(); const char *s = uri; SkipWs(s); // Scan ahead and check for protocol... const char *hasProto = NULL; const char *hasAt = NULL; const char *hasPath = NULL; const char *hasColon = NULL; for (auto c = s; *c; c++) { if (c[0] == ':' && c[1] == '/' && c[2] == '/') { if (!hasProto) { hasProto = c; c += 2; } } else if (c[0] == '@' && !hasAt) { hasAt = c; // keep the first '@' } else if (c[0] == ':') { hasColon = c; } else if ((c[0] == '/' || c[0] == '\\') && !hasPath) { hasPath = c; break; // anything after this is path... } } if (hasProto) { sProtocol.Set(s, hasProto - s); s = hasProto + 3; } if (hasAt) { if (hasAt >= s) { auto p = LString(s, hasAt - s).SplitDelimit(":", 1); if (p.Length() == 2) { sUser = DecodeStr(p[0]); sPass = DecodeStr(p[1]); } else if (p.Length() == 1) { sUser = DecodeStr(p[0]); } s = hasAt + 1; } else LAssert(!"hasAt should be > s"); } bool hasHost = hasProto || hasAt || hasColon || !hasPath; if (hasHost) { auto p = LString(s, hasPath ? hasPath - s : -1).SplitDelimit(":", 1); if (p.Length() == 2) { sHost = p[0]; Port = p[1].Int(); } else if (p.Length() == 1) { sHost = p[0]; } } else { hasPath = s; } if (hasPath) { sPath = hasPath; } if (sPath) { auto anchor = sPath.Find("#"); if (anchor >= 0) { sAnchor = sPath(anchor, -1); sPath.Length(anchor); } } return sHost || sPath; } LString LUri::EncodeStr(const char *s, const char *ExtraCharsToEncode) { LStringPipe p(256); if (s) { while (*s) { if (*s == ' ' || (ExtraCharsToEncode && strchr(ExtraCharsToEncode, *s))) { char h[4]; sprintf_s(h, sizeof(h), "%%%2.2X", (uint32_t)(uchar)*s++); p.Write(h, 3); } else { p.Write(s++, 1); } } } return p.NewLStr(); } LUri::StrMap LUri::Params() { StrMap m; if (sPath) { const char *q = strchr(sPath, '?'); if (q++) { auto Parts = LString(q).SplitDelimit("&"); for (auto p : Parts) { auto Var = p.Split("=", 1); if (Var.Length() == 2) m.Add(Var[0], Var[1]); } } } return m; } LString LUri::DecodeStr(const char *s) { LStringPipe p(256); if (s) { while (*s) { if (s[0] == '%' && s[1] && s[2]) { char h[3] = { s[1], s[2], 0 }; char c = htoi(h); p.Write(&c, 1); s += 3; } else { p.Write(s++, 1); } } } return p.NewLStr(); } struct UriUnitCase { const char *str; LUri uri; }; bool LUri::UnitTests() { UriUnitCase Parse[] = { {"http://user:pass@host:1234/somePath/seg/file.png", LUri("http", "user", "pass", "host", 1234, "somePath/seg/file.png")}, {"user:pass@host:1234/somePath/seg/file.png", LUri(NULL, "user", "pass", "host", 1234, "somePath/seg/file.png") }, {"user@host:1234/somePath/seg/file.png", LUri(NULL, "user", NULL, "host", 1234, "somePath/seg/file.png") }, {"user@host/somePath/seg/file.png", LUri(NULL, "user", NULL, "host", 0, "somePath/seg/file.png") }, {"user@host", LUri(NULL, "user", NULL, "host", 0, NULL) }, {"host", LUri(NULL, NULL, NULL, "host", 0, NULL) }, {"host:1234", LUri(NULL, NULL, NULL, "host", 1234, NULL) }, {"somePath/seg/file.png", LUri(NULL, NULL, NULL, NULL, 0, "somePath/seg/file.png") }, }; for (auto &test: Parse) { LUri u(test.str); if (u != test.uri) { LAssert(!"test failed"); return false; } } return true; } /////////////////////////////////////////////////////////////////////////////////////////////////////// #if defined LGI_CARBON int CFNumberRefToInt(CFNumberRef r, int Default = 0) { int i = Default; if (r && CFGetTypeID(r) == CFNumberGetTypeID()) { CFNumberGetValue(r, kCFNumberIntType, &r); } return i; } #endif LProxyUri::LProxyUri() { #if defined(WIN32) LRegKey k(false, "HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings"); if (k.IsOk()) { uint32_t Enabled = 0; if (k.GetInt("ProxyEnable", Enabled) && Enabled) { char *p = k.GetStr("ProxyServer"); if (p) { Set(p); } } } #elif defined LINUX char *HttpProxy = getenv("http_proxy"); if (HttpProxy) { Set(HttpProxy); } #elif defined MAC // CFDictionaryRef Proxies = SCDynamicStoreCopyProxies(0); // if (!Proxies) // LgiTrace("%s:%i - SCDynamicStoreCopyProxies failed.\n", _FL); // else // { // int enable = CFNumberRefToInt((CFNumberRef) CFDictionaryGetValue(Proxies, kSCPropNetProxiesHTTPEnable)); // if (enable) // { // #ifdef LGI_COCOA // LAssert(!"Fixme"); // #else // Host = CFStringToUtf8((CFStringRef) CFDictionaryGetValue(Proxies, kSCPropNetProxiesHTTPProxy)); // #endif // Port = CFNumberRefToInt((CFNumberRef) CFDictionaryGetValue(Proxies, kSCPropNetProxiesHTTPPort)); // } // // CFRelease(Proxies); // } #elif defined(HAIKU) // There doesn't seem to be a system wide proxy setting, so for the time being // lets just put a setting in the Lgi config and use that: if (!LAppInst) { LgiTrace("%s:%i - No LApp instance yet?\n", _FL); } else { auto p = LAppInst->GetConfig(LApp::CfgNetworkHttpProxy); if (p) { Set(p); } else { static bool First = true; if (First) { First = false; LgiTrace("%s:%i No HTTP Proxy configured in '%s'.\n", _FL, LAppInst->GetConfigPath().Get()); } } } #else #warning "Impl getting OS proxy here." #endif } diff --git a/src/common/Widgets/ItemContainer.cpp b/src/common/Widgets/ItemContainer.cpp --- a/src/common/Widgets/ItemContainer.cpp +++ b/src/common/Widgets/ItemContainer.cpp @@ -1,1315 +1,1315 @@ #include "lgi/common/Lgi.h" #include "lgi/common/ItemContainer.h" #include "lgi/common/DisplayString.h" #include "lgi/common/SkinEngine.h" #include "lgi/common/ScrollBar.h" #include "lgi/common/Edit.h" #include "lgi/common/CssTools.h" // Colours #if defined(__GTK_H__) #define DOUBLE_BUFFER_COLUMN_DRAWING 1 #else #define DOUBLE_BUFFER_COLUMN_DRAWING 0 #endif #if defined(WIN32) #if !defined(WS_EX_LAYERED) #define WS_EX_LAYERED 0x80000 #endif #if !defined(LWA_ALPHA) #define LWA_ALPHA 2 #endif typedef BOOL (__stdcall *_SetLayeredWindowAttributes)(HWND hwnd, COLORREF crKey, BYTE bAlpha, DWORD dwFlags); #endif class LItemColumnPrivate { LDisplayString *Txt = NULL; public: LRect Pos; bool Down = false; bool Drag = false; #define _(name) bool name = false; COLUMN_FLAGS() #undef _ LItemContainer *Parent = NULL; LString cName; int cWidth = 0; LSurface *cIcon = NULL; int cImage = -1; bool OwnIcon = false; bool CanResize = true; LItemColumnPrivate(LItemContainer *parent) { Parent = parent; } ~LItemColumnPrivate() { DeleteObj(Txt); if (OwnIcon) { DeleteObj(cIcon); } } void SetName(const char *n) { cName = n; DeleteObj(Txt); } // Try and delay the creation of the display string till // the code is being called from an event, and in the window's thread // on Haiku. That way it gets a thread specific font handle. LDisplayString *&GetDs() { if (!Txt) { auto f = Parent && Parent->GetFont() && Parent->GetFont()->Handle() ? Parent->GetFont() : LSysFont; Txt = new LDisplayString(f, cName); } return Txt; } }; static LColour cActiveCol(0x86, 0xba, 0xe9); static void FillStops(LArray &Stops, LRect &r, bool Active) { if (Active) { Stops[0].Set(0.0f, LColour(0xd0, 0xe2, 0xf5)); Stops[1].Set(2.0f / (r.Y() - 2), LColour(0x98, 0xc1, 0xe9)); Stops[2].Set(0.5f, LColour(0x86, 0xba, 0xe9)); Stops[3].Set(0.51f, LColour(0x68, 0xaf, 0xea)); Stops[4].Set(1.0f, LColour(0xbb, 0xfc, 0xff)); } else { LColour cMed(L_MED), cWs(L_WORKSPACE); if (cWs.GetGray() < 96) { cMed = cMed.Mix(LColour::White, 0.25f); cWs = cWs.Mix(LColour::White, 0.25f); } Stops[0].Set(0.0f, cWs); Stops[1].Set(0.5f, cMed.Mix(cWs)); Stops[2].Set(0.51f, cMed); Stops[3].Set(1.0f, cWs); } } ////////////////////////////////////////////////////////////////////////////////////// LItemContainer::LItemContainer() { ColumnHeader.ZOff(-1, -1); Columns.SetFixedLength(true); } LItemContainer::~LItemContainer() { DeleteObj(ItemEdit); DeleteObj(DragCol); Columns.DeleteObjects(); } void LItemContainer::PaintColumnHeadings(LSurface *pDC) { // Draw column headings if (!ColumnHeaders() || !ColumnHeader.Valid()) return; LSurface *ColDC = pDC; LRect cr; #if DOUBLE_BUFFER_COLUMN_DRAWING LMemDC Bmp; if (!pDC->SupportsAlphaCompositing() && Bmp.Create(ColumnHeader.X(), ColumnHeader.Y(), System32BitColourSpace)) { ColDC = &Bmp; Bmp.Op(GDC_ALPHA); cr = ColumnHeader.ZeroTranslate(); } else #endif { cr = ColumnHeader; pDC->ClipRgn(&cr); } // Draw columns int cx = cr.x1; if (IconCol) { cr.x1 = cx; cr.x2 = cr.x1 + IconCol->Width() - 1; IconCol->SetPos(cr); IconCol->OnPaint(ColDC, cr); cx += IconCol->Width(); } // Draw other columns for (int i=0; iWidth() - 1; c->SetPos(cr); c->OnPaint(ColDC, cr); cx += c->Width(); } else LAssert(0); } // Draw ending piece cr.x1 = cx; cr.x2 = ColumnHeader.x2 + 2; if (cr.Valid()) { // Draw end section where there are no columns #ifdef MAC LArray Stops; LRect j(cr.x1, cr.y1, cr.x2-1, cr.y2-1); FillStops(Stops, j, false); LFillGradient(ColDC, j, true, Stops); ColDC->Colour(L_LOW); ColDC->Line(cr.x1, cr.y2, cr.x2, cr.y2); #else if (LApp::SkinEngine) { LSkinState State; State.pScreen = ColDC; State.Rect = cr; State.Enabled = Enabled(); State.View = this; LApp::SkinEngine->OnPaint_ListColumn(0, 0, &State); } else { LWideBorder(ColDC, cr, DefaultRaisedEdge); ColDC->Colour(LColour(L_MED)); ColDC->Rectangle(&cr); } #endif } #if DOUBLE_BUFFER_COLUMN_DRAWING if (!pDC->SupportsAlphaCompositing()) pDC->Blt(ColumnHeader.x1, ColumnHeader.y1, &Bmp); else #endif pDC->ClipRgn(0); } LItemColumn *LItemContainer::AddColumn(const char *Name, int Width, int Where) { LItemColumn *c = 0; if (Lock(_FL)) { c = new LItemColumn(this, Name, Width); if (c) { Columns.SetFixedLength(false); Columns.AddAt(Where, c); Columns.SetFixedLength(true); UpdateAllItems(); SendNotify(LNotifyItemColumnsChanged); } Unlock(); } return c; } bool LItemContainer::AddColumn(LItemColumn *Col, int Where) { bool Status = false; if (Col && Lock(_FL)) { Columns.SetFixedLength(false); Status = Columns.AddAt(Where, Col); Columns.SetFixedLength(true); if (Status) { UpdateAllItems(); SendNotify(LNotifyItemColumnsChanged); } Unlock(); } return Status; } void LItemContainer::DragColumn(int Index) { DeleteObj(DragCol); if (Index >= 0) { DragCol = new LDragColumn(this, Index); if (DragCol) { Capture(true); DragMode = DRAG_COLUMN; } } } int LItemContainer::ColumnAtX(int x, LItemColumn **Col, int *Offset) { LItemColumn *Column = NULL; if (!Col) Col = &Column; int Cx = GetImageList() ? 16 : 0; int c; for (c=0; c= Cx && x < Cx + (*Col)->Width()) { if (Offset) *Offset = Cx; return c; } Cx += (*Col)->Width(); } return -1; } void LItemContainer::EmptyColumns() { Columns.DeleteObjects(); Invalidate(&ColumnHeader); SendNotify(LNotifyItemColumnsChanged); } int LItemContainer::HitColumn(int x, int y, LItemColumn *&Resize, LItemColumn *&Over) { int Index = -1; Resize = 0; Over = 0; if (ColumnHeaders() && ColumnHeader.Overlap(x, y)) { // Clicked on a column heading int cx = ColumnHeader.x1 + ((IconCol) ? IconCol->Width() : 0); for (int n = 0; n < Columns.Length(); n++) { LItemColumn *c = Columns[n]; cx += c->Width(); if (abs(x-cx) < 5) { if (c->Resizable()) { Resize = c; Index = n; break; } } else if (c->d->Pos.Overlap(x, y)) { Over = c; Index = n; break; } } } return Index; } void LItemContainer::OnColumnClick(int Col, LMouse &m) { ColClick = Col; ColMouse = m; LNotification n(LNotifyItemColumnClicked); n.Int[0] = Col; SendNotify(n); } bool LItemContainer::GetColumnClickInfo(int &Col, LMouse &m) { if (ColClick < 0) { Col = ColumnAtX(m.x); return true; } else if (ColClick >= 0) { Col = ColClick; m = ColMouse; return true; } return false; } void LItemContainer::GetColumnSizes(ColSizes &cs) { // Read in the current sizes cs.FixedPx = 0; cs.ResizePx = 0; for (int i=0; iResizable()) { ColInfo &Inf = cs.Info.New(); Inf.Col = c; Inf.Idx = i; Inf.ContentPx = c->GetContentSize(); Inf.WidthPx = c->Width(); cs.ResizePx += Inf.ContentPx; } else { cs.FixedPx += c->Width(); } } } void LItemContainer::SetSortingMark(int ColIdx, bool Up) { for (int i=0; iUpArrow(ColIdx == i && Up); c->DownArrow(ColIdx == i && !Up); } } LMessage::Result LItemContainer::OnEvent(LMessage *Msg) { switch (Msg->Msg()) { case M_RESIZE_TO_CONTENT: { ResizeColumnsToContent((int)Msg->A()); break; } default: break; } return LLayout::OnEvent(Msg); } void LItemContainer::ResizeColumnsToContent(int Border) { if (!InThread()) { PostEvent(M_RESIZE_TO_CONTENT, Border); return; } if (Lock(_FL)) { // Read in the current sizes ColSizes Sizes; GetColumnSizes(Sizes); // Allocate space int AvailablePx = GetClient().X() - 5; if (VScroll) AvailablePx -= VScroll->X(); int ExpandPx = AvailablePx - Sizes.FixedPx; Sizes.Info.Sort([](auto a, auto b) { int AGrowPx = a->GrowPx(); int BGrowPx = b->GrowPx(); return AGrowPx - BGrowPx; }); for (int i=0; iResizable()) { if (ExpandPx > Sizes.ResizePx) { // Everything fits... Inf.Col->Width(Inf.ContentPx + Border); } else { int Cx = GetClient().X(); double Ratio = Cx ? (double)Inf.ContentPx / Cx : 1.0; if (Ratio < 0.25) { Inf.Col->Width(Inf.ContentPx + Border); } else { // Need to scale to fit... int Px = Inf.ContentPx * ExpandPx / Sizes.ResizePx; Inf.Col->Width(Px + Border); } } ClearDs(Inf.Idx); } } Unlock(); } Invalidate(); } ////////////////////////////////////////////////////////////////////////////// LDragColumn::LDragColumn(LItemContainer *list, int col) { List = list; Index = col; Offset = 0; #ifdef LINUX Back = 0; #endif Col = List->ColumnAt(Index); if (Col) { Col->d->Down = false; Col->d->Drag = true; LRect r = Col->d->Pos; r.y1 = 0; r.y2 = List->Y()-1; List->Invalidate(&r, true); #if WINNATIVE LArray Ver; bool Layered = ( LGetOs(&Ver) == LGI_OS_WIN32 || LGetOs(&Ver) == LGI_OS_WIN64 ) && Ver[0] >= 5; SetStyle(WS_POPUP); SetExStyle(GetExStyle() | WS_EX_TOOLWINDOW); if (Layered) { SetExStyle(GetExStyle() | WS_EX_LAYERED | WS_EX_TRANSPARENT); } #endif Attach(0); #if WINNATIVE if (Layered) { SetWindowLong(Handle(), GWL_EXSTYLE, GetWindowLong(Handle(), GWL_EXSTYLE) | WS_EX_LAYERED); LLibrary User32("User32"); _SetLayeredWindowAttributes SetLayeredWindowAttributes = (_SetLayeredWindowAttributes)User32.GetAddress("SetLayeredWindowAttributes"); if (SetLayeredWindowAttributes) { if (!SetLayeredWindowAttributes(Handle(), 0, DRAG_COL_ALPHA, LWA_ALPHA)) { DWORD Err = GetLastError(); } } } #elif defined(__GTK_H__) Gtk::GtkWindow *w = WindowHandle(); if (w) { gtk_window_set_decorated(w, FALSE); gtk_widget_set_opacity(GtkCast(w, gtk_widget, GtkWidget), DRAG_COL_ALPHA / 255.0); } #endif LMouse m; List->GetMouse(m); Offset = m.x - r.x1; List->PointToScreen(ListScrPos); r.Offset(ListScrPos.x, ListScrPos.y); SetPos(r); Visible(true); } } LDragColumn::~LDragColumn() { Visible(false); if (Col) { Col->d->Drag = false; } List->Invalidate(); } #if LINUX_TRANS_COL void LDragColumn::OnPosChange() { Invalidate(); } #endif void LDragColumn::OnPaint(LSurface *pScreen) { #if LINUX_TRANS_COL LSurface *Buf = new LMemDC(X(), Y(), GdcD->GetBits()); LSurface *pDC = new LMemDC(X(), Y(), GdcD->GetBits()); #else LSurface *pDC = pScreen; #endif pDC->SetOrigin(Col->d->Pos.x1, 0); if (Col) Col->d->Drag = false; List->OnPaint(pDC); if (Col) Col->d->Drag = true; pDC->SetOrigin(0, 0); #if LINUX_TRANS_COL if (Buf && pDC) { LRect p = GetPos(); // Fill the buffer with the background Buf->Blt(ListScrPos.x - p.x1, 0, Back); // Draw painted column over the back with alpha Buf->Op(GDC_ALPHA); LApplicator *App = Buf->Applicator(); if (App) { App->SetVar(GAPP_ALPHA_A, DRAG_COL_ALPHA); } Buf->Blt(0, 0, pDC); // Put result on the screen pScreen->Blt(0, 0, Buf); } DeleteObj(Buf); DeleteObj(pDC); #endif } /////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////// // List column LItemColumn::LItemColumn(LItemContainer *parent, const char *name, int width) : ResObject(Res_Column) { d = new LItemColumnPrivate(parent); d->cWidth = width; if (name) Name(name); } LItemColumn::~LItemColumn() { if (d->Drag) { d->Parent->DragColumn(-1); } DeleteObj(d); } LItemContainer *LItemColumn::GetList() { return d->Parent; } void LItemColumn::Image(int i) { d->cImage = i; } int LItemColumn::Image() { return d->cImage; } bool LItemColumn::Resizable() { return d->CanResize; } void LItemColumn::Resizable(bool i) { d->CanResize = i; } bool LItemColumn::InDrag() { return d->Drag; } LRect LItemColumn::GetPos() { return d->Pos; } void LItemColumn::SetPos(LRect &r) { d->Pos = r; } void LItemColumn::Name(const char *n) { d->SetName(n); if (d->Parent) d->Parent->Invalidate(&d->Parent->ColumnHeader); } char *LItemColumn::Name() { return d->cName; } int LItemColumn::GetIndex() { if (d->Parent) { return (int)d->Parent->Columns.IndexOf(this); } return -1; } int LItemColumn::GetContentSize() { return d->Parent->GetContentSize(GetIndex()); } void LItemColumn::Width(int i) { if (d->cWidth != i) { d->cWidth = i; // If we are attached to a list... if (d->Parent) { /* FIXME int MyIndex = GetIndex(); // Clear all the cached strings for this column for (List::I it=d->Parent->Items.Start(); it.In(); it++) { DeleteObj((*it)->d->Display[MyIndex]); } if (d->Parent->IsAttached()) { // Update the screen from this column across LRect Up = d->Parent->GetClient(); Up.x1 = d->Pos.x1; d->Parent->Invalidate(&Up); } */ } // Notify listener auto p = d->Parent; if (p) p->SendNotify(LNotifyItemColumnsResized); } } int LItemColumn::Width() { return d->cWidth; } #define _(name) \ bool LItemColumn::name() \ { \ return d->name; \ } \ \ void LItemColumn::name(bool b) \ { \ d->name = b; \ if (d->Parent) \ d->Parent->Invalidate(&d->Parent->ColumnHeader); \ } COLUMN_FLAGS() #undef _ void LItemColumn::Icon(LSurface *i, bool Own) { if (d->OwnIcon) { DeleteObj(d->cIcon); } d->cIcon = i; d->OwnIcon = Own; if (d->Parent) { d->Parent->Invalidate(&d->Parent->ColumnHeader); } } LSurface *LItemColumn::Icon() { return d->cIcon; } bool LItemColumn::Value() { return d->Down; } void LItemColumn::Value(bool i) { d->Down = i; } void LItemColumn::OnPaint_Content(LSurface *pDC, LRect &r, bool FillBackground) { if (d->Drag) return; LCssTools Tools(d->Parent); auto Fore = Tools.GetFore(); auto cMed = LColour(L_MED); int Off = d->Down ? 1 : 0; int Mx = r.x1 + 8, My = r.y1 + ((r.Y() - 8) / 2); if (d->cIcon) { if (FillBackground) { pDC->Colour(cMed); pDC->Rectangle(&r); } int x = (r.X()-d->cIcon->X()) / 2; pDC->Blt( r.x1 + x + Off, r.y1 + ((r.Y()-d->cIcon->Y())/2) + Off, d->cIcon); if (HasSort()) { Mx += x + d->cIcon->X() + 4; } } else if (d->cImage >= 0 && d->Parent) { LColour Background = cMed; if (FillBackground) { pDC->Colour(Background); pDC->Rectangle(&r); } if (d->Parent->GetImageList()) { LRect *b = d->Parent->GetImageList()->GetBounds(); int x = r.x1; int y = r.y1; if (b) { b += d->cImage; x = r.x1 + ((r.X()-b->X()) / 2) - b->x1; y = r.y1 + ((r.Y()-b->Y()) / 2) - b->y1; } d->Parent->GetImageList()->Draw(pDC, x + Off, y + Off, d->cImage, Background); } if (HasSort()) { Mx += d->Parent->GetImageList()->TileX() + 4; } } else if (ValidStr(d->cName)) { auto Ds = d->GetDs(); if (!Ds || !Ds->GetFont()) { LAssert(0); return; } auto f = Ds->GetFont(); LColour cText = Fore; #ifdef MAC // Contrast check - if (d->cMark && (cText - cActiveCol) < 64) + if (HasSort() && (cText - cActiveCol) < 64) cText = cText.Invert(); #endif f->Transparent(!FillBackground); f->Colour(cText, cMed); int ty = Ds->Y(); int ry = r.Y(); int y = r.y1 + ((ry - ty) >> 1); // d->Txt->_debug = true; Ds->Draw(pDC, r.x1 + Off + 3, y + Off, &r); if (HasSort()) { Mx += Ds->X(); } } else { if (FillBackground) { pDC->Colour(cMed); pDC->Rectangle(&r); } } #define ARROW_SIZE 9 pDC->Colour(Fore); Mx += Off; My += Off - 1; if (UpArrow()) { pDC->Line(Mx + 2, My, Mx + 2, My + ARROW_SIZE - 1); pDC->Line(Mx, My + 2, Mx + 2, My); pDC->Line(Mx + 2, My, Mx + 4, My + 2); } else if (DownArrow()) { pDC->Line(Mx + 2, My, Mx + 2, My + ARROW_SIZE - 1); pDC->Line( Mx, My + ARROW_SIZE - 3, Mx + 2, My + ARROW_SIZE - 1); pDC->Line( Mx + 2, My + ARROW_SIZE - 1, Mx + 4, My + ARROW_SIZE - 3); } } void ColumnPaint(void *UserData, LSurface *pDC, LRect &r, bool FillBackground) { ((LItemColumn*)UserData)->OnPaint_Content(pDC, r, FillBackground); } void LItemColumn::OnPaint(LSurface *pDC, LRect &Rgn) { LRect r = Rgn; if (d->Drag) { pDC->Colour(DragColumnColour); pDC->Rectangle(&r); } else { #ifdef MAC LArray Stops; LRect j(r.x1, r.y1, r.x2-1, r.y2-1); - FillStops(Stops, j, d->cMark != 0); + FillStops(Stops, j, HasSort()); LFillGradient(pDC, j, true, Stops); - if (d->cMark) + if (HasSort()) pDC->Colour(Rgb24(0x66, 0x93, 0xc0), 24); else pDC->Colour(Rgb24(178, 178, 178), 24); pDC->Line(r.x1, r.y2, r.x2, r.y2); pDC->Line(r.x2, r.y1, r.x2, r.y2); LRect n = r; n.Inset(2, 2); OnPaint_Content(pDC, n, false); #else if (LApp::SkinEngine) { LSkinState State; State.pScreen = pDC; State.ptrText = &d->GetDs(); State.Rect = Rgn; State.Value = Value(); State.Enabled = GetList()->Enabled(); State.View = d->Parent; LApp::SkinEngine->OnPaint_ListColumn(ColumnPaint, this, &State); } else { if (d->Down) { LThinBorder(pDC, r, DefaultSunkenEdge); LFlatBorder(pDC, r, 1); } else { LWideBorder(pDC, r, DefaultRaisedEdge); } OnPaint_Content(pDC, r, true); } #endif } } /////////////////////////////////////////////////////////////////////////////////////////// LItem::LItem() { SelectionStart = SelectionEnd = -1; } LItem::~LItem() { } LView *LItem::EditLabel(int Col) { LItemContainer *c = GetContainer(); if (!c) return NULL; c->Capture(false); if (!c->ItemEdit) { c->ItemEdit = new LItemEdit(c, this, Col, SelectionStart, SelectionEnd); SelectionStart = SelectionEnd = -1; } return c->ItemEdit; } void LItem::OnEditLabelEnd() { LItemContainer *c = GetContainer(); if (c) c->ItemEdit = NULL; } void LItem::SetEditLabelSelection(int SelStart, int SelEnd) { SelectionStart = SelStart; SelectionEnd = SelEnd; } //////////////////////////////////////////////////////////////////////////////////////////// #define M_END_POPUP (M_USER+0x1500) #define M_LOSING_FOCUS (M_USER+0x1501) class LItemEditBox : public LEdit { LItemEdit *ItemEdit; public: LItemEditBox(LItemEdit *i, int x, int y, const char *s) : LEdit(100, 1, 1, x-3, y-3, s) { ItemEdit = i; Sunken(false); MultiLine(false); #ifndef LINUX SetPos(GetPos()); #endif } const char *GetClass() { return "LItemEditBox"; } void OnCreate() { LEdit::OnCreate(); Focus(true); } void OnFocus(bool f) { if (!f && GetParent()) { #if DEBUG_EDIT_LABEL LgiTrace("%s:%i - LItemEditBox posting M_LOSING_FOCUS\n", _FL); #endif GetParent()->PostEvent(M_LOSING_FOCUS); } LEdit::OnFocus(f); } bool OnKey(LKey &k) { /* This should be handled by LEdit::OnKey now. Which will send a LNotifyEscapeKey or LNotifyReturnKey up to the ItemEdit OnNotify handler. switch (k.vkey) { case LK_RETURN: case LK_ESCAPE: { if (k.Down()) ItemEdit->OnNotify(this, k.c16); return true; } } */ return LEdit::OnKey(k); } bool SetScrollBars(bool x, bool y) { return false; } }; ////////////////////////////////////////////////////////////////////////////////////////// class LItemEditPrivate { public: LItem *Item; LEdit *Edit; int Index; bool Esc; LItemEditPrivate() { Esc = false; Item = 0; Index = 0; } }; LItemEdit::LItemEdit(LView *parent, LItem *item, int index, int SelStart, int SelEnd) : LPopup(parent) { d = new LItemEditPrivate; d->Item = item; d->Index = index; _BorderSize = 0; Sunken(false); Raised(false); #if DEBUG_EDIT_LABEL LgiTrace("%s:%i - LItemEdit(%p/%s, %i, %i, %i)\n", _FL, parent, parent?parent->GetClass():0, index, SelStart, SelEnd); #endif LPoint p; SetParent(parent); GetParent()->PointToScreen(p); LRect r = d->Item->GetPos(d->Index); int MinY = 6 + LSysFont->GetHeight(); if (r.Y() < MinY) r.y2 = r.y1 + MinY - 1; r.Offset(p.x, p.y); SetPos(r); if (Attach(parent)) { d->Edit = new LItemEditBox(this, r.X(), r.Y(), d->Item->GetText(d->Index)); if (d->Edit) { d->Edit->Attach(this); d->Edit->Focus(true); if (SelStart >= 0) { d->Edit->Select(SelStart, SelEnd-SelStart+1); } } Visible(true); } } LItemEdit::~LItemEdit() { if (d->Item) { if (d->Edit && !d->Esc) { auto Str = d->Edit->Name(); #if DEBUG_EDIT_LABEL LgiTrace("%s:%i - ~LItemEdit, updating item(%i) with '%s'\n", _FL, d->Index, Str); #endif LItemContainer *c = d->Item->GetContainer(); if (d->Item->SetText(Str, d->Index)) { d->Item->Update(); } else { // Item is deleting itself... // Make sure there is no dangling ptr on the container.. if (c) c->ItemEdit = NULL; // And we don't touch the no longer existant item.. d->Item = NULL; } } #if DEBUG_EDIT_LABEL else LgiTrace("%s:%i - Edit=%p Esc=%i\n", _FL, d->Edit, d->Esc); #endif if (d->Item) d->Item->OnEditLabelEnd(); } #if DEBUG_EDIT_LABEL else LgiTrace("%s:%i - Error: No item?\n", _FL); #endif DeleteObj(d); } LItem *LItemEdit::GetItem() { return d->Item; } void LItemEdit::OnPaint(LSurface *pDC) { pDC->Colour(L_BLACK); pDC->Rectangle(); } int LItemEdit::OnNotify(LViewI *v, LNotification n) { switch (v->GetId()) { case 100: { if (n.Type == LNotifyEscapeKey) { d->Esc = true; #if DEBUG_EDIT_LABEL LgiTrace("%s:%i - LItemEdit got escape\n", _FL); #endif } if (n.Type == LNotifyEscapeKey || n.Type == LNotifyReturnKey) { #if DEBUG_EDIT_LABEL LgiTrace("%s:%i - LItemEdit hiding on esc/enter\n", _FL); #endif d->Edit->KeyProcessed(); Visible(false); } break; } } return 0; } void LItemEdit::Visible(bool i) { LPopup::Visible(i); if (!i) { #if DEBUG_EDIT_LABEL LgiTrace("%s:%i - LItemEdit posting M_END_POPUP\n", _FL); #endif PostEvent(M_END_POPUP); } } bool LItemEdit::OnKey(LKey &k) { if (d->Edit) return d->Edit->OnKey(k); return false; } void LItemEdit::OnFocus(bool f) { if (f && d->Edit) d->Edit->Focus(true); } LMessage::Result LItemEdit::OnEvent(LMessage *Msg) { switch (Msg->Msg()) { case M_LOSING_FOCUS: { #if DEBUG_EDIT_LABEL LgiTrace("%s:%i - LItemEdit get M_LOSING_FOCUS\n", _FL); #endif // One of us has to retain focus... don't care which control. if (Focus() || d->Edit->Focus()) break; // else fall thru to end the popup #if DEBUG_EDIT_LABEL LgiTrace("%s:%i - LItemEdit falling thru to M_END_POPUP\n", _FL); #endif } case M_END_POPUP: { #if DEBUG_EDIT_LABEL LgiTrace("%s:%i - LItemEdit got M_END_POPUP, quiting\n", _FL); #endif if (d->Item && d->Item->GetContainer()) { d->Item->GetContainer()->Focus(true); } Quit(); return 0; } } return LPopup::OnEvent(Msg); } diff --git a/src/common/Widgets/List.cpp b/src/common/Widgets/List.cpp --- a/src/common/Widgets/List.cpp +++ b/src/common/Widgets/List.cpp @@ -1,2721 +1,2725 @@ /*hdr ** FILE: LList.cpp ** AUTHOR: Matthew Allen ** DATE: 14/2/2000 ** DESCRIPTION: Lgi self-drawn listbox ** ** Copyright (C) 2000 Matthew Allen ** fret@memecode.com */ #include #include #include #include "lgi/common/Lgi.h" #include "lgi/common/SkinEngine.h" #include "lgi/common/List.h" #include "lgi/common/ScrollBar.h" #include "lgi/common/DisplayString.h" #include "lgi/common/LgiRes.h" #include "lgi/common/CssTools.h" // Debug defines #define DEBUG_EDIT_LABEL 1 // Number of pixels you have to move the mouse until a drag is initiated. #define DRAG_THRESHOLD 4 // Switches for various profiling code.. #define LList_POUR_PROFILE 1 #define LList_ONPAINT_PROFILE 0 // Options #define DOUBLE_BUFFER_PAINT 0 #define ForAllItems(Var) for (auto Var : Items) #define ForAllItemsReverse(Var) Iterator ItemIter(&Items); for (LListItem *Var = ItemIter.Last(); Var; Var = ItemIter.Prev()) #define VisibleItems() CompletelyVisible // (LastVisible - FirstVisible + 1) #define MaxScroll() MAX((int)Items.Length() - CompletelyVisible, 0) class LListPrivate { public: // Mode LListMode Mode; int Columns; int VisibleColumns; // This is a pointer to a flag, that gets set when the object // is deleted. Used to trap events deleting the window. If an // event handler deletes the current window we can't touch any // of the member variables anymore, so we need to know to quit/return // ASAP. bool *DeleteFlag; // If this is true the ctrl is selecting lots of things // and we only want to notify once. bool NoSelectEvent; // Drag'n'drop LPoint DragStart; int DragData; // Kayboard search uint64 KeyLast; char16 *KeyBuf; // Class LListPrivate() { DragData = 0; KeyBuf = 0; DeleteFlag = 0; Columns = 0; VisibleColumns = 0; Mode = LListDetails; NoSelectEvent = false; } ~LListPrivate() { if (DeleteFlag) *DeleteFlag = true; DeleteArray(KeyBuf); } }; class LListItemPrivate { public: bool Selected = false; bool Visible = true; int ListItem_Image = -1; List Cols; LArray Str; LArray Display; int16 LayoutColumn = -1; LListItemPrivate() { } ~LListItemPrivate() { Cols.DeleteObjects(); EmptyStrings(); EmptyDisplay(); } void EmptyStrings() { Str.DeleteArrays(); } void EmptyDisplay() { Display.DeleteObjects(); } }; //////////////////////////////////////////////////////////////////////////////////////////// LListItemColumn::LListItemColumn(LListItem *item, int col) { _Column = col; _Item = item; _Value = 0; _Item->d->Cols.Insert(this); } LList *LListItemColumn::GetList() { return _Item ? _Item->Parent : 0; } LItemContainer *LListItemColumn::GetContainer() { return GetList(); } LListT *LListItemColumn::GetAllItems() { return GetList() ? &GetList()->Items : 0; } void LListItemColumn::Value(int64 i) { if (i != _Value) { _Value = i; _Item->OnColumnNotify(_Column, _Value); } } LListItemColumn *LListItemColumn::GetItemCol(LListItem *i, int Col) { if (i) { for (auto c: i->d->Cols) { if (c->_Column == Col) { return c; } } } return 0; } //////////////////////////////////////////////////////////////////////////////////////////// // List item LListItem::LListItem(const char *initStr) { d = new LListItemPrivate; Pos.ZOff(-1, -1); if (initStr) SetText(initStr); } LListItem::~LListItem() { if (Parent) Parent->Remove(this); DeleteObj(d); } void LListItem::SetImage(int i) { d->ListItem_Image = i; } int LListItem::GetImage(int Flags) { return d->ListItem_Image; } LItemContainer *LListItem::GetContainer() { return Parent; } List *LListItem::GetItemCols() { return &d->Cols; } /* Calling this to store your data is optional. Just override the "GetText" function to return your own data to avoid duplication in memory. */ bool LListItem::SetText(const char *s, int i) { if (i < 0) return false; // Delete any existing column DeleteArray((char*&)d->Str[i]); DeleteObj(d->Display[i]); // Add new string in d->Str[i] = NewStr(s); if (Parent) Parent->SendNotify(LNotifyItemChange); return true; } // User can override this if they want to use their own data const char *LListItem::GetText(int i) { return d->Str[i]; } bool LListItem::Select() { return d->Selected; } LRect *LListItem::GetPos(int Col) { static LRect r; r = Pos; if (Parent->GetMode() == LListDetails) { if (Col >= 0) { LItemColumn *Column = 0; int Cx = Parent->GetImageList() ? 16 : 0; for (int c=0; cColumnAt(c); if (Column) { Cx += Column->Width(); } } Column = Parent->ColumnAt(Col); if (Column) { r.x1 = Cx; r.x2 = Cx + Column->Width() - 1; } } } else { r.Offset(16, 0); } return &r; } void LListItem::Select(bool b) { if (d->Selected != b) { d->Selected = b; Update(); if (Parent && d->Selected && !Parent->d->NoSelectEvent) { LArray Items; Items.Add(this); Parent->OnItemSelect(Items); } } } void LListItem::ScrollTo() { if (Parent) { if (Parent->GetMode() == LListDetails && Parent->VScroll) { ssize_t n = Parent->Items.IndexOf(this); if (n < Parent->FirstVisible) { Parent->VScroll->Value(n); Parent->Invalidate(&Parent->ItemsPos); } else if (n >= Parent->LastVisible) { Parent->VScroll->Value(n - (Parent->LastVisible - Parent->FirstVisible) + 1); Parent->Invalidate(&Parent->ItemsPos); } } else if (Parent->GetMode() == LListColumns && Parent->HScroll) { ssize_t n = Parent->Items.IndexOf(this); if (n < Parent->FirstVisible) { Parent->HScroll->Value(d->LayoutColumn); Parent->Invalidate(&Parent->ItemsPos); } else if (n >= Parent->LastVisible) { ssize_t Range = Parent->HScroll->Page(); Parent->HScroll->Value(d->LayoutColumn - Range); Parent->Invalidate(&Parent->ItemsPos); } } } } void LListItem::Update() { if (Parent) { if (Parent->Lock(_FL)) { d->EmptyDisplay(); LPoint Info; OnMeasure(&Info); LRect r = Pos; if (r.Valid()) { if (Info.y != r.Y()) { Pos.y2 = Pos.y1 + Info.y - 1; Parent->PourAll(); r.y1 = MIN(r.y1, Pos.y1); r.y2 = Parent->ItemsPos.y2; } Parent->Invalidate(&r); } Parent->Unlock(); } } else { d->EmptyDisplay(); } } void LListItem::OnMeasure(LPoint *Info) { if (!Info) return; if (Parent && Parent->GetMode() == LListDetails) { Info->x = 4 << 10; } else { auto s = GetDs(0); Info->x = 22 + (s ? s->X() : 0); } LFont *f = Parent ? Parent->GetFont() : LSysFont; Info->y = MAX(16, f->GetHeight() + 2); // the default height } bool LListItem::GridLines() { return (Parent) ? Parent->GridLines : false; } void LListItem::OnMouseClick(LMouse &m) { int Col = Parent ? Parent->ColumnAtX(m.x) : -1; for (auto h: d->Cols) { if (Col == h->GetColumn()) { h->OnMouseClick(m); } } } LDisplayString *LListItem::GetDs(int Col, int FitTo) { if (!d->Display[Col]) { LFont *f = GetFont(); if (!f && Parent) f = Parent->GetFont(); if (!f) f = LSysFont; const char *Text = d->Str[Col] ? d->Str[Col] : GetText(Col); LAssert((NativeInt)Text != 0xcdcdcdcd && (NativeInt)Text != 0xfdfdfdfd); d->Display[Col] = new LDisplayString(f, Text?Text:(char*)""); if (d->Display[Col] && FitTo > 0) { d->Display[Col]->TruncateWithDots(FitTo); } } return d->Display[Col]; } void LListItem::ClearDs(int Col) { if (Col >= 0) { DeleteObj(d->Display[Col]); } else { d->Display.DeleteObjects(); } } void LListItem::OnPaintColumn(LItem::ItemPaintCtx &Ctx, int i, LItemColumn *c) { LSurface *&pDC = Ctx.pDC; if (!pDC) return; LRect ng = Ctx; // non-grid area if (c && c->InDrag()) { pDC->Colour(DragColumnColour); pDC->Rectangle(&ng); } else { LColour Background = Ctx.Back; if (Parent->GetMode() == LListDetails && (c && c->HasSort()) && !d->Selected) { Background = GdcMixColour(LColour(0, 24), Background, (double)1/32); } if (GridLines()) { ng.x2--; ng.y2--; } if (!c || !c->HasImage()) { auto Ds = GetDs(i, Ctx.X()); if (Ds) { Ds->GetFont()->TabSize(0); Ds->GetFont()->Transparent(false); Ds->GetFont()->Colour(Ctx.Fore, Background); switch (Ctx.Align.Type) { case LCss::AlignCenter: Ds->Draw(pDC, ng.x1+((ng.X()-Ds->X())/2), ng.y1+1, &ng); break; case LCss::AlignRight: Ds->Draw(pDC, ng.x2-Ds->X()-1, ng.y1+1, &ng); break; default: // Left or inherit Ds->Draw(pDC, ng.x1+1, ng.y1+1, &ng); break; } } else { pDC->Colour(Background); pDC->Rectangle(&ng); } } else { pDC->Colour(Background); pDC->Rectangle(&ng); if (Parent->GetImageList()) { int Img = GetImage(); if (Img >= 0) { int CenterY = Ctx.y1 + ((Ctx.Y() - Parent->GetImageList()->TileY()) >> 1); LAssert(CenterY >= 0); Parent->GetImageList()->Draw(pDC, Ctx.x1+1, CenterY, Img, Background); } } } if (GridLines()) { pDC->Colour(L_LOW); pDC->Line(Ctx.x1, Ctx.y2, Ctx.x2, Ctx.y2); pDC->Line(Ctx.x2, Ctx.y1, Ctx.x2, Ctx.y2); } } } void LListItem::OnPaint(LItem::ItemPaintCtx &Ctx) { if (!Parent || !d->Visible) return; int x = Ctx.x1; auto CtxX = Ctx.X(); LAutoPtr Prev; if (GetCss()) { Prev.Reset(new ItemPaintCtx(Ctx)); LCss::ColorDef Fill = GetCss()->Color(); if (Fill.Type == LCss::ColorRgb) Ctx.Fore.Set(Fill.Rgb32, 32); if (!Select()) { Fill = GetCss()->BackgroundColor(); if (Fill.Type == LCss::ColorRgb) Ctx.Back.Set(Fill.Rgb32, 32); } } // Icon? if (Parent->IconCol) { LItem::ItemPaintCtx IcoCtx = Ctx; IcoCtx.Set(x, Ctx.y1, x + Parent->IconCol->Width()-1, Ctx.y2); // draw icon OnPaintColumn(IcoCtx, -1, Parent->IconCol); x = IcoCtx.x2 + 1; } // draw columns auto It = d->Cols.begin(); LListItemColumn *h = *It; LItem::ItemPaintCtx ColCtx = Ctx; if (Parent->Columns.Length()) { for (int i=0; iColumns.Length(); i++) { LItemColumn *c = Parent->Columns[i]; if (Parent->GetMode() == LListColumns) ColCtx.Set(x, Ctx.y1, Ctx.x2, Ctx.y2); else ColCtx.Set(x, Ctx.y1, x + c->Width()-1, Ctx.y2); ColCtx.Align = c->TextAlign(); OnPaintColumn(ColCtx, i, c); if (h && i == h->GetColumn()) { h->OnPaintColumn(ColCtx, i, c); h = *(++It); } x = ColCtx.x2 + 1; if (Parent->GetMode() == LListColumns) break; } } else { // One fake column for the whole control: ColCtx.Set(x, Ctx.y1, x+CtxX-1, Ctx.y2); ColCtx.Align = LCss::AlignLeft; OnPaintColumn(ColCtx, 0, NULL); x = ColCtx.x2 + 1; } // after columns if (x <= Ctx.x2) { Ctx.pDC->Colour(Ctx.Back); Ctx.pDC->Rectangle(x, Ctx.y1, Ctx.x2, Ctx.y2); } if (Prev) Ctx = *Prev; } ////////////////////////////////////////////////////////////////////////////// // List control LList::LList(int id, int x, int y, int cx, int cy, const char *name) : ResObject(Res_ListView) { d = new LListPrivate; SetId(id); Name(name); ItemsPos.ZOff(-1, -1); Buf = 0; GridLines = false; FirstVisible = -1; LastVisible = -1; EditLabels = false; MultiSelect(true); CompletelyVisible = 0; Keyboard = -1; Sunken(true); Name("LList"); #if WINNATIVE SetStyle(GetStyle() | WS_TABSTOP); SetDlgCode(DLGC_WANTARROWS); Cursor = 0; #endif SetTabStop(true); LRect r(x, y, x+cx, y+cy); SetPos(r); LResources::StyleElement(this); } LList::~LList() { DeleteObj(Buf); Empty(); EmptyColumns(); DeleteObj(d); } LListMode LList::GetMode() { return d->Mode; } void LList::SetMode(LListMode m) { if (d->Mode ^ m) { d->Mode = m; if (IsAttached()) { PourAll(); Invalidate(); } } } void LList::OnItemClick(LListItem *Item, LMouse &m) { if (Item) Item->OnMouseClick(m); } void LList::OnItemBeginDrag(LListItem *Item, LMouse &m) { if (Item) Item->OnBeginDrag(m); } void LList::OnItemSelect(LArray &It) { if (It.Length()) { Keyboard = (int)Items.IndexOf(It[0]); LAssert(Keyboard >= 0); LHashTbl, bool> Sel; for (int n=0; nOnSelect(); if (!MultiSelect()) Sel.Add(It[n], true); } if (!MultiSelect()) { // deselect all other items ForAllItems(i) { if (!Sel.Find(i)) { if (i->d->Selected) { /* i->d->Selected = false; i->Update(); */ i->Select(false); } } } } } // Notify selection change SendNotify(LNotifyItemSelect); } bool LItemContainer::DeleteColumn(LItemColumn *Col) { bool Status = false; if (Col && Lock(_FL)) { if (Columns.HasItem(Col)) { Columns.Delete(Col); DeleteObj(Col); UpdateAllItems(); SendNotify(LNotifyItemColumnsChanged); Status = true; } Unlock(); } return Status; } LMessage::Result LList::OnEvent(LMessage *Msg) { switch (Msg->Msg()) { #ifdef WIN32 case WM_VSCROLL: { if (VScroll) return VScroll->OnEvent(Msg); break; } #endif } return LItemContainer::OnEvent(Msg); } int LList::OnNotify(LViewI *Ctrl, LNotification n) { if ( (Ctrl->GetId() == IDC_VSCROLL && VScroll) || (Ctrl->GetId() == IDC_HSCROLL && HScroll) ) { if (n.Type == LNotifyScrollBarCreate) UpdateScrollBars(); Invalidate(&ItemsPos); } return LLayout::OnNotify(Ctrl, n); } LRect &LList::GetClientRect() { static LRect r; r = GetPos(); r.Offset(-r.x1, -r.y1); return r; } LListItem *LList::HitItem(int x, int y, int *Index) { int n=0; ForAllItems(i) { if ( ( // Is list mode we consider the item to have infinite width. // This helps with multi-selection when the cursor falls outside // the window's bounds but is still receiving mouse move messages // because of mouse capture. d->Mode == LListDetails && y >= i->Pos.y1 && y <= i->Pos.y2 ) || ( i->Pos.Overlap(x, y) ) ) { if (Index) *Index = n; return i; } n++; } return NULL; } void LList::ClearDs(int Col) { ForAllItems(i) { i->ClearDs(Col); } } void LList::KeyScroll(int iTo, int iFrom, bool SelectItems) { int Start = -1, End = -1, i = 0; { ForAllItems(n) { if (n->Select()) { if (Start < 0) { Start = i; } } else if (Start >= 0 && End < 0) { End = i - 1; } i++; } if (End < 0) End = i - 1; } if (Items.Length() == 0) return; iTo = limit(iTo, 0, (int)Items.Length()-1); iFrom = limit(iFrom, 0, (int)Items.Length()-1); LListItem *To = Items.ItemAt(iTo); LListItem *From = Items.ItemAt(iFrom); // int Inc = (iTo < iFrom) ? -1 : 1; if (To && From && iTo != iFrom) { // LListItem *Item = 0; if (SelectItems) { int OtherEnd = Keyboard == End ? Start : End; int Min = MIN(OtherEnd, iTo); int Max = MAX(OtherEnd, iTo); i = 0; d->NoSelectEvent = true; LArray Sel; ForAllItems(n) { bool s = i>=Min && i<=Max; n->Select(s); if (s) Sel.Add(n); i++; } d->NoSelectEvent = false; OnItemSelect(Sel); } else { Select(To); } To->ScrollTo(); Keyboard = iTo; } } bool LList::OnMouseWheel(double Lines) { if (VScroll) { int64 Old = VScroll->Value(); VScroll->Value(Old + (int)Lines); if (Old != VScroll->Value()) { Invalidate(&ItemsPos); } } if (HScroll) { int64 Old = HScroll->Value(); HScroll->Value(Old + (int)(Lines / 3)); if (Old != HScroll->Value()) { Invalidate(&ItemsPos); } } return true; } bool LList::OnKey(LKey &k) { bool Status = false; LListItem *Item = GetSelected(); if (Item) { Status = Item->OnKey(k); } if (k.vkey != LK_UP && k.vkey != LK_DOWN && k.CtrlCmd()) { switch (k.c16) { case 'A': case 'a': { if (k.Down()) SelectAll(); Status = true; break; } } } else { switch (k.vkey) { case LK_RETURN: { #if WINNATIVE if (!k.IsChar) #endif { if (k.Down()) SendNotify(LNotification(k)); } break; } case LK_BACKSPACE: case LK_DELETE: case LK_ESCAPE: { if (k.Down()) SendNotify(LNotification(k)); break; } case LK_UP: { // int i = Value(); #ifdef MAC if (k.Ctrl()) goto LList_PageUp; else if (k.System()) goto LList_Home; #endif if (k.Down()) KeyScroll(Keyboard-1, Keyboard, k.Shift()); Status = true; break; } case LK_DOWN: { #ifdef MAC if (k.Ctrl()) goto LList_PageDown; else if (k.System()) goto LList_End; #endif if (k.Down()) KeyScroll(Keyboard+1, Keyboard, k.Shift()); Status = true; break; } case LK_LEFT: { if (GetMode() == LListColumns) { if (k.Down()) { LListItem *Hit = GetSelected(); if (Hit) { LListItem *To = 0; int ToDist = 0x7fffffff; for (auto It = Items.begin(FirstVisible); It != Items.end(); ++It) { LListItem *i = *It; if (!i->Pos.Valid()) break; if (i->Pos.x2 < Hit->Pos.x1) { int Dx = i->Pos.x1 - Hit->Pos.x1; int Dy = i->Pos.y1 - Hit->Pos.y1; int IDist = Dx * Dx + Dy * Dy; if (!To || IDist < ToDist) { To = i; ToDist = IDist; } } } if (!To && HScroll) { if (Hit->d->LayoutColumn == HScroll->Value() + 1) { // Seek back to the start of the column before the // first visible column for (auto it = Items.begin(FirstVisible); it.In(); it--) { LListItem *i = *it; if (i->d->LayoutColumn < HScroll->Value()) { it++; break; } } // Now find the entry at the right height } } if (To) { Select(0); To->Select(true); To->ScrollTo(); } } } Status = true; } break; } case LK_RIGHT: { if (GetMode() == LListColumns) { if (k.Down()) { LListItem *Hit = GetSelected(); if (Hit) { LListItem *To = 0; int ToDist = 0x7fffffff; for (auto It = Items.begin(FirstVisible); It != Items.end(); ++It) { LListItem *i = *It; if (!i->Pos.Valid()) break; if (i->Pos.x1 > Hit->Pos.x2) { int Dx = i->Pos.x1 - Hit->Pos.x1; int Dy = i->Pos.y1 - Hit->Pos.y1; int IDist = Dx * Dx + Dy * Dy; if (!To || IDist < ToDist) { To = i; ToDist = IDist; } } } if (To) { Select(0); To->Select(true); To->ScrollTo(); } } } Status = true; } break; } case LK_PAGEUP: { #ifdef MAC LList_PageUp: #endif if (k.Down()) { int Vis = VisibleItems(); Vis = MAX(Vis, 0); KeyScroll(Keyboard-Vis, Keyboard, k.Shift()); } Status = true; break; } case LK_PAGEDOWN: { #ifdef MAC LList_PageDown: #endif if (k.Down()) { int Vis = VisibleItems(); Vis = MAX(Vis, 0); KeyScroll(Keyboard+Vis, Keyboard, k.Shift()); } Status = true; break; } case LK_END: { #ifdef MAC LList_End: #endif if (k.Down()) KeyScroll((int)Items.Length()-1, Keyboard, k.Shift()); Status = true; break; } case LK_HOME: { #ifdef MAC LList_Home: #endif if (k.Down()) KeyScroll(0, Keyboard, k.Shift()); Status = true; break; } #ifdef VK_APPS case VK_APPS: { if (k.Down()) { LListItem *s = GetSelected(); if (s) { LRect *r = &s->Pos; if (r) { LMouse m; LListItem *FirstVisible = ItemAt((VScroll) ? (int)VScroll->Value() : 0); m.x = 32 + ItemsPos.x1; m.y = r->y1 + (r->Y() >> 1) - (FirstVisible ? FirstVisible->Pos.y1 : 0) + ItemsPos.y1; m.Target = this; m.ViewCoords = true; m.Down(true); m.Right(true); OnMouseClick(m); } Status = true; } } break; } #endif default: { if ( !Status && k.IsChar && ( IsDigit(k.c16) || IsAlpha(k.c16) || strchr("_.-", k.c16) ) ) { if (k.Down()) { uint64 Now = LCurrentTime(); LStringPipe p; if (d->KeyBuf && Now < d->KeyLast + 1500) { p.Push(d->KeyBuf); } DeleteArray(d->KeyBuf); d->KeyLast = Now; p.Push(&k.c16, 1); d->KeyBuf = p.NewStrW(); if (d->KeyBuf) { char *c8 = WideToUtf8(d->KeyBuf); if (c8) { int Col = 0; bool Ascend = true; for (int i=0; iHasSort()) { Col = i; if (c->UpArrow()) { Ascend = false; } } } bool Selected = false; auto It = Ascend ? Items.begin() : Items.rbegin(); for (; It.In(); Ascend ? ++It : --It) { LListItem *i = *It; if (!Selected) { const char *t = i->GetText(Col); if (t && stricmp(t, c8) >= 0) { i->Select(true); i->ScrollTo(); Selected = true; } else { i->Select(false); } } else { i->Select(false); } } DeleteArray(c8); } } } Status = true; } break; } } } return Status; } LCursor LList::GetCursor(int x, int y) { LItemColumn *Resize, *Over; HitColumn(x, y, Resize, Over); if (Resize) return LCUR_SizeHor; return LCUR_Normal; } void LList::OnMouseClick(LMouse &m) { // m.Trace("LList::OnMouseClick"); if (Lock(_FL)) { if (m.Down()) { Focus(true); DragMode = DRAG_NONE; d->DragStart.x = m.x; d->DragStart.y = m.y; if (ColumnHeaders() && ColumnHeader.Overlap(m.x, m.y)) { // Clicked on a column heading LItemColumn *Resize, *Over; int Index = HitColumn(m.x, m.y, Resize, Over); if (Resize) { if (m.Double()) { if (m.CtrlCmd()) { ResizeColumnsToContent(); } else { ColSizes Sizes; GetColumnSizes(Sizes); int AvailablePx = GetClient().X() - 5; if (VScroll) AvailablePx -= VScroll->X(); int ExpandPx = AvailablePx - (Sizes.FixedPx + Sizes.ResizePx); if (ExpandPx > 0) { int MaxPx = Resize->GetContentSize() + DEFAULT_COLUMN_SPACING; int AddPx = MIN(ExpandPx, MaxPx - Resize->Width()); if (AddPx > 0) { Resize->Width(Resize->Width() + AddPx); ClearDs(Index); Invalidate(); } } } } else { DragMode = RESIZE_COLUMN; d->DragData = (int)Columns.IndexOf(Resize); Capture(true); } } else { DragMode = CLICK_COLUMN; d->DragData = (int)Columns.IndexOf(Over); if (Over) { Over->Value(true); LRect r = Over->GetPos(); Invalidate(&r); Capture(true); } } } else if (ItemsPos.Overlap(m.x, m.y)) { // Clicked in the items area bool HandlerHung = false; int ItemIndex = -1; LListItem *Item = HitItem(m.x, m.y, &ItemIndex); // LViewI *Notify = Item ? (GetNotify()) ? GetNotify() : GetParent() : 0; d->DragData = ItemIndex; if (Item && Item->Select()) { // Click on selected item if (m.CtrlCmd()) { Item->Select(false); OnItemClick(Item, m); } else { // Could be drag'n'drop operation // Or just a select int64 StartHandler = LCurrentTime(); // this will get set if 'this' is deleted. bool DeleteFlag = false; // Setup the delete flag pointer d->DeleteFlag = &DeleteFlag; // Do the event... may delete 'this' object, or hang for a long time OnItemClick(Item, m); // If the object has been deleted... exit out of here NOW! if (DeleteFlag) { return; } // Shut down the delete flag pointer... it'll point to invalid stack soon. d->DeleteFlag = 0; // Check if the handler hung for a long time... uint64 Now = LCurrentTime(); HandlerHung = Now - StartHandler > 200; if (!HandlerHung && !m.Double() && !m.IsContextMenu()) { // Start d'n'd watcher pulse... SetPulse(100); Capture(true); DragMode = CLICK_ITEM; } if (!IsCapturing()) { // If capture failed then we reset the dragmode... DragMode = DRAG_NONE; } } } else { // Selection change if (m.Shift() && MultiSelect()) { int n = 0; int a = MIN(ItemIndex, Keyboard); int b = MAX(ItemIndex, Keyboard); LArray Sel; ForAllItems(i) { bool s = n >= a && n <= b; if (i->d->Selected ^ s) { i->d->Selected = s; i->Update(); } if (s) Sel.Add(i); n++; } OnItemSelect(Sel); if (Item) Item->Select(true); } else { bool PostSelect = false; bool SelectionChanged = false; // Temporaily turn off selection events... // and just send one at the end. // d->NoSelectEvent = true; ForAllItems(i) { if (Item == i) // clicked item { if (m.CtrlCmd()) { // Toggle selected state if (!i->Select()) { Keyboard = (int)Items.IndexOf(i); } i->Select(!i->Select()); SelectionChanged = true; } else { // Select this after we have delselected everything else PostSelect = true; } } else if (!m.CtrlCmd() || !MultiSelect()) { if (i->Select()) { i->Select(false); SelectionChanged = true; } } } if (PostSelect) { SelectionChanged |= Item->Select() == false; Item->Select(true); Keyboard = (int)Items.IndexOf(Item); } if (!m.CtrlCmd() && Items.Length() && !m.IsContextMenu()) { DragMode = SELECT_ITEMS; SetPulse(100); Capture(true); } if (SelectionChanged) { SendNotify(LNotifyItemSelect); } // d->NoSelectEvent = false; } OnItemClick(Item, m); } if (!HandlerHung) { if (m.IsContextMenu()) SendNotify(LNotification(m, LNotifyItemContextMenu)); else if (Item || m.Double()) SendNotify(LNotification(m)); else SendNotify(LNotification(m, LNotifyContainerClick)); } } } else // Up Click { switch (DragMode) { case CLICK_COLUMN: { if (d->DragData < 0) break; LItemColumn *c = Columns[d->DragData]; if (c) { c->Value(false); LRect cpos = c->GetPos(); Invalidate(&cpos); if (cpos.Overlap(m.x, m.y)) { OnColumnClick((int)Columns.IndexOf(c), m); } } else { OnColumnClick(-1, m); } break; } case CLICK_ITEM: { // This code allows the user to change a larger selection // down to a single item, by clicking on that item. This // can't be done on the down click because the user may also // be clicking the selected items to drag them somewhere and // if we de-selected all but the clicked item on the down // click they would never be able to drag and drop more than // one item. // // However we also do not want this to select items after the // contents of the list box have changed since the down click if (d->DragData >= Items.Length()) break; auto Item = Items.ItemAt(d->DragData); if (Item) { bool Change = false; LArray s; ForAllItems(i) { bool Sel = Item == i; if (Sel ^ i->Select()) { Change = true; i->Select(Sel); if (Sel) { s.Add(i); } } } if (Change) OnItemSelect(s); } break; } case DRAG_COLUMN: { // End column drag if (DragCol) { LRect DragPos = DragCol->GetPos(); LPoint p(DragPos.x1 + (DragPos.X()/2), 0); PointToView(p); int OldIndex = DragCol->GetIndex(); int Best = 100000000, NewIndex = OldIndex, i=0, delta; for (i=0; iGetPos().x1); if (delta < Best) { Best = delta; NewIndex = i - (i > OldIndex ? 1 : 0); } } delta = abs(p.x - Columns.Last()->GetPos().x2); if (delta < Best) { NewIndex = i; } LItemColumn *Col = DragCol->GetColumn(); if (OldIndex != NewIndex && OnColumnReindex(Col, OldIndex, NewIndex)) { Columns.SetFixedLength(false); Columns.Delete(Col, true); Columns.AddAt(OldIndex < NewIndex ? NewIndex-1 : NewIndex, Col); Columns.SetFixedLength(true); UpdateAllItems(); } DragCol->Quit(); DragCol = NULL; } Invalidate(); break; } + default: + break; } LListItem *Item = HitItem(m.x, m.y); if (Item) { OnItemClick(Item, m); } if (IsCapturing()) { Capture(false); } DragMode = DRAG_NONE; } Unlock(); } } void LList::OnPulse() { if (!Lock(_FL)) return; if (IsCapturing()) { LMouse m; bool HasMs = GetMouse(m); // m.Trace("LList::OnPulse"); if (HasMs && (m.y < 0 || m.y >= Y())) { switch (DragMode) { case SELECT_ITEMS: { int OverIndex = 0; LListItem *Over = 0; if (m.y < 0) { int Space = -m.y; int n = FirstVisible - 1; for (auto It = Items.begin(n); It != Items.end(); --It, n--) { LListItem *i = *It; LPoint Info; i->OnMeasure(&Info); if (Space > Info.y) { Space -= Info.y; } else { OverIndex = n; Over = i; break; } } if (!Over) { Over = Items[0]; OverIndex = 0; } } else if (m.y >= Y()) { int Space = m.y - Y(); int n = LastVisible + 1; for (auto It = Items.begin(n); It != Items.end(); ++It, n++) { LListItem *i = *It; LPoint Info; i->OnMeasure(&Info); if (Space > Info.y) { Space -= Info.y; } else { OverIndex = n; Over = i; break; } } if (!Over) { Over = *Items.rbegin(); OverIndex = (int)Items.Length()-1; } } int Min = MIN(d->DragData, OverIndex); int Max = MAX(d->DragData, OverIndex); int n = Min; for (auto It = Items.begin(Min); It != Items.end() && n <= Max; ++It, n++) { LListItem *i = *It; if (!i->Select()) i->Select(true); } if (Over) { Over->ScrollTo(); } break; } + default: + break; } } } else { DragMode = DRAG_NONE; SetPulse(); } Unlock(); } void LList::OnMouseMove(LMouse &m) { if (!Lock(_FL)) return; // m.Trace("LList::OnMouseMove"); switch (DragMode) { case DRAG_COLUMN: { if (DragCol) { LPoint p; PointToScreen(p); LRect r = DragCol->GetPos(); r.Offset(-p.x, -p.y); // to view co-ord r.Offset(m.x - DragCol->GetOffset() - r.x1, 0); if (r.x1 < 0) r.Offset(-r.x1, 0); if (r.x2 > X()-1) r.Offset((X()-1)-r.x2, 0); r.Offset(p.x, p.y); // back to screen co-ord DragCol->SetPos(r, true); r = DragCol->GetPos(); } break; } case RESIZE_COLUMN: { LItemColumn *c = Columns[d->DragData]; if (c) { // int OldWidth = c->Width(); int NewWidth = m.x - c->GetPos().x1; c->Width(MAX(NewWidth, 4)); ClearDs(d->DragData); Invalidate(); } break; } case CLICK_COLUMN: { if (d->DragData < 0 || d->DragData >= Columns.Length()) break; LItemColumn *c = Columns[d->DragData]; if (c) { if (abs(m.x - d->DragStart.x) > DRAG_THRESHOLD || abs(m.y - d->DragStart.y) > DRAG_THRESHOLD) { OnColumnDrag(d->DragData, m); } else { bool Over = c->GetPos().Overlap(m.x, m.y); if (m.Down() && Over != c->Value()) { c->Value(Over); LRect r = c->GetPos(); Invalidate(&r); } } } break; } case SELECT_ITEMS: { int n=0; // bool Selected = m.y < ItemsPos.y1; if (IsCapturing()) { if (MultiSelect()) { int Over = -1; HitItem(m.x, m.y, &Over); if (m.y < ItemsPos.y1 && FirstVisible == 0) { Over = 0; } else { int n = FirstVisible; for (auto it = Items.begin(n); it != Items.end(); it++) { auto k = *it; if (!k->OnScreen()) break; if ((m.y >= k->Pos.y1) && (m.y <= k->Pos.y2)) { Over = n; break; } n++; } } if (Over >= 0) { n = 0; int Start = MIN(Over, d->DragData); int End = MAX(Over, d->DragData); ForAllItems(i) { i->Select(n >= Start && n <= End); n++; } } } else { ForAllItems(i) { i->Select(i->Pos.Overlap(m.x, m.y)); } } } break; } case CLICK_ITEM: { if (d->DragData >= Items.Length()) break; auto Cur = Items.ItemAt(d->DragData); if (Cur) { Cur->OnMouseMove(m); if (IsCapturing() && (abs(d->DragStart.x-m.x) > DRAG_THRESHOLD || abs(d->DragStart.y-m.y) > DRAG_THRESHOLD)) { Capture(false); OnItemBeginDrag(Cur, m); DragMode = DRAG_NONE; } } break; } default: { List s; if (GetSelection(s)) { for (auto c: s) { LMouse ms = m; ms.x -= c->Pos.x1; ms.y -= c->Pos.y1; c->OnMouseMove(ms); } } break; } } Unlock(); } int64 LList::Value() { int n=0; ForAllItems(i) { if (i->Select()) { return n; } n++; } return -1; } void LList::Value(int64 Index) { int n=0; ForAllItems(i) { if (n == Index) { i->Select(true); Keyboard = n; } else { i->Select(false); } n++; } } void LList::SelectAll() { if (Lock(_FL)) { ForAllItems(i) { i->d->Selected = true; } Unlock(); Invalidate(); } } bool LList::Select(LListItem *Obj) { bool Status = false; ForAllItems(i) { i->Select(Obj == i); if (Obj == i) Status = true; } return true; } LListItem *LList::GetSelected() { LListItem *n = 0; if (Lock(_FL)) { ForAllItems(i) { if (i->Select()) { n = i; break; } } Unlock(); } return n; } bool LList::GetUpdateRegion(LListItem *i, LRegion &r) { r.Empty(); if (d->Mode == LListDetails) { if (i->Pos.Valid()) { LRect u = i->Pos; u.y2 = ItemsPos.y2; r.Union(&u); return true; } } else if (d->Mode == LListColumns) { if (i->Pos.Valid()) { LRect u = i->Pos; u.y2 = ItemsPos.y2; r.Union(&u); u.x1 = u.x2 + 1; u.y1 = ItemsPos.y1; r.Union(&u); return true; } } return false; } bool LList::Insert(LListItem *i, int Index, bool Update) { List l; l.Insert(i); return Insert(l, Index, Update); } bool LList::Insert(List &l, int Index, bool Update) { bool Status = false; if (Lock(_FL)) { bool First = Items.Length() == 0; // Insert list of items for (auto i: l) { if (i->Parent != this) { i->Parent = this; i->Select(false); Items.Insert(i, Index); i->OnInsert(); if (Index >= 0) Index++; if (First) { First = false; Keyboard = 0; i->Select(true); } } } Status = true; Unlock(); if (Update) { // Update screen PourAll(); Invalidate(); // Notify SendNotify(LNotifyItemInsert); } } return Status; } bool LList::Delete(ssize_t Index) { return Delete(Items.ItemAt(Index)); } bool LList::Delete(LListItem *i) { bool Status = false; if (Lock(_FL)) { if (Remove(i)) { // Delete DeleteObj(i); Status = true; } Unlock(); } return Status; } bool LList::Remove(LListItem *i) { bool Status = false; if (Lock(_FL)) { if (i && i->GetList() == this) { LRegion Up; bool Visible = GetUpdateRegion(i, Up); bool Selected = i->Select(); int Index = (int)Items.IndexOf(i); int64 Pos = (VScroll) ? VScroll->Value() : 0; // Remove from list Items.Delete(i); i->OnRemove(); i->Parent = 0; UpdateScrollBars(); // Update screen if ((VScroll && VScroll->Value() != Pos) || Index < FirstVisible) { Invalidate(&ItemsPos); } else if (Visible) { Up.y2 = ItemsPos.y2; Invalidate(&Up); } // Notify LViewI *Note = GetNotify() ? GetNotify() : GetParent(); if (Note) { if (Selected) { LArray s; OnItemSelect(s); } LNotification n(LNotifyItemDelete); Note->OnNotify(this, n); } Status = true; } Unlock(); } return Status; } bool LList::HasItem(LListItem *Obj) { return Items.HasItem(Obj); } int LList::IndexOf(LListItem *Obj) { return (int)Items.IndexOf(Obj); } LListItem *LList::ItemAt(size_t Index) { return Index < Items.Length() ? Items.ItemAt(Index) : NULL; } void LList::ScrollToSelection() { if (VScroll) { int n=0; int Vis = VisibleItems(); ForAllItems(i) { if (i->Select()) { if (n < FirstVisible || n > LastVisible) { int k = n - (Vis/2); VScroll->Value(MAX(k, 0)); Invalidate(&ItemsPos); break; } } n++; } } } void LList::Empty() { if (Lock(_FL)) { ForAllItems(i) { LAssert(i->Parent == this); i->Parent = 0; DeleteObj(i); } Items.Empty(); FirstVisible = LastVisible = -1; DragMode = DRAG_NONE; if (VScroll) { VScroll->Value(0); VScroll->SetRange(0); } Invalidate(); DeleteArray(d->KeyBuf); Unlock(); } } void LList::RemoveAll() { if (Lock(_FL)) { if (Items.Length()) { LArray s; OnItemSelect(s); } for (auto i: Items) { i->OnRemove(); i->Parent = 0; } Items.Empty(); FirstVisible = LastVisible = -1; DragMode = DRAG_NONE; if (VScroll) { // these have to be in this order because // "SetLimits" can cause the VScroll object to // be deleted and becoming NULL VScroll->Value(0); VScroll->SetRange(0); } Invalidate(); DeleteArray(d->KeyBuf); Unlock(); } } void LList::OnPosChange() { LLayout::OnPosChange(); } void LList::UpdateScrollBars() { static bool Processing = false; if (!Processing && InThread()) { Processing = true; if (VScroll) { int Vis = VisibleItems(); int Max = MaxScroll(); if (VScroll->Value() > MAX(Max, 0)) { VScroll->Value(Max); } VScroll->SetPage(Vis); VScroll->SetRange(Items.Length()); } if (HScroll) { HScroll->SetPage(d->VisibleColumns); HScroll->SetRange(d->Columns); } Processing = false; } } void LList::PourAll() { #if LList_POUR_PROFILE LProfile Prof("PourAll()", 100); #endif // Layout all the elements LRect Client = GetClient(); LFont *Font = GetFont(); if (d->Mode == LListDetails) { if (ColumnHeaders()) { ColumnHeader = Client; ColumnHeader.y2 = ColumnHeader.y1 + Font->GetHeight() + 4; ItemsPos = Client; ItemsPos.y1 = ColumnHeader.y2 + 1; } else { ItemsPos = Client; ColumnHeader.ZOff(-1, -1); } int n = 0; int y = ItemsPos.y1; int Max = MaxScroll(); FirstVisible = (VScroll) ? (int)VScroll->Value() : 0; if (FirstVisible > Max) FirstVisible = Max; LastVisible = 0x7FFFFFFF; CompletelyVisible = 0; bool SomeHidden = false; // Process visible flag ForAllItems(i) { auto css = i->GetCss(); i->d->Visible = !css || css->Display() != LCss::DispNone; } #if LList_POUR_PROFILE Prof.Add("List items"); #endif ForAllItems(i) { if (!i->d->Visible) { i->Pos.Set(-1, -1, -2, -2); SomeHidden = true; continue; // Don't increment 'n' } if (n < FirstVisible || n > LastVisible) { i->Pos.Set(-1, -1, -2, -2); SomeHidden = true; } else { LPoint Info; i->OnMeasure(&Info); if (i->Pos.Valid() && Info.y != i->Pos.Y()) { // This detects changes in item height and invalidates the items below this one. LRect in(0, y+Info.y, X()-1, Y()-1); Invalidate(&in); } i->Pos.Set(ItemsPos.x1, y, ItemsPos.x2, y+Info.y-1); y = y+Info.y; if (i->Pos.y2 > ItemsPos.y2) { LastVisible = n; SomeHidden = true; } else { CompletelyVisible++; } } n++; } if (LastVisible >= Items.Length()) { LastVisible = (int)Items.Length() - 1; } SetScrollBars(false, SomeHidden); UpdateScrollBars(); } else if (d->Mode == LListColumns) { ColumnHeader.ZOff(-1, -1); ItemsPos = Client; FirstVisible = 0; int CurX = 0; int CurY = 0; int MaxX = 16; LArray Col; d->Columns = 1; d->VisibleColumns = 0; int64 ScrollX = HScroll ? HScroll->Value() : 0; int64 OffsetY = HScroll ? 0 : LScrollBar::GetScrollSize(); FirstVisible = -1; int n = 0; #if LList_POUR_PROFILE Prof.Add("List cols"); #endif ForAllItems(i) { LPoint Info; i->OnMeasure(&Info); if (d->Columns <= ScrollX || CurX > ItemsPos.X()) { i->Pos.ZOff(-1, -1); i->d->LayoutColumn = d->Columns; if (ItemsPos.y1 + CurY + Info.y > ItemsPos.y2 - OffsetY) { CurY = 0; d->Columns++; if (d->Columns > ScrollX && CurX < ItemsPos.X()) { goto FlowItem; } } } else { FlowItem: if (ItemsPos.y1 + CurY + Info.y > ItemsPos.y2 - OffsetY) { // wrap to next column for (int n=0; nPos.x2 = CurX + MaxX - 1; } Col.Length(0); CurX += MaxX; CurY = 0; d->Columns++; if (CurX < ItemsPos.X()) { d->VisibleColumns++; } } if (FirstVisible < 0) FirstVisible = n; LastVisible = n; i->d->LayoutColumn = d->Columns; i->Pos.ZOff(Info.x-1, Info.y-1); i->Pos.Offset(ItemsPos.x1 + CurX, ItemsPos.y1 + CurY); Col[Col.Length()] = i; MaxX = MAX(MaxX, Info.x); CompletelyVisible++; } CurY += Info.y; n++; } d->VisibleColumns = MAX(1, d->VisibleColumns); // pour remaining items... for (n=0; nPos.x2 = CurX + MaxX - 1; } Col.Length(0); if (CurX + MaxX < ItemsPos.X()) { d->VisibleColumns++; } // printf("%u - ScrollX=%i VisCol=%i Cols=%i\n", (uint32)LCurrentTime(), ScrollX, d->VisibleColumns, d->Columns); SetScrollBars(d->VisibleColumns < d->Columns, false); UpdateScrollBars(); } } static LColour Tint(LColour back, double amt) { bool Darken = back.GetGray() >= 128; LColour Mixer = Darken ? LColour::Black : LColour::White; return back.Mix(Mixer, (float)(1.0f - amt)); } void LList::OnPaint(LSurface *pDC) { #if LList_ONPAINT_PROFILE int Start = LCurrentTime(), t1, t2, t3, t4, t5; #endif if (!Lock(_FL)) return; LCssTools Tools(this); LColour DisabledTint(L_MED); LColour Workspace(L_WORKSPACE); LColour NonFocusBack(L_NON_FOCUS_SEL_BACK); LColour Fore = Enabled() ? Tools.GetFore() : Tools.GetFore().Mix(DisabledTint); LColour Back = Tools.GetBack(&Workspace, 0); double NonFocusBackAmt = (double)NonFocusBack.GetGray() / Workspace.GetGray(); if (!Enabled()) Back = Back.Mix(DisabledTint); LColour SelFore(Focus() ? L_FOCUS_SEL_FORE : L_NON_FOCUS_SEL_FORE); LColour SelBack(Focus() ? L_FOCUS_SEL_BACK : (Enabled() ? Tint(Back, NonFocusBackAmt) : DisabledTint)); PourAll(); // printf("ListPaint SelFore=%s SelBack=%s Back=%s %f NonFocusBack=%s\n", SelFore.GetStr(), SelBack.GetStr(), Back.GetStr(), NonFocusBackAmt, NonFocusBack.GetStr()); #if LList_ONPAINT_PROFILE t1 = LCurrentTime(); #endif // Check icon column status then draw if (AskImage() && !IconCol) { IconCol.Reset(new LItemColumn(this, 0, 18)); if (IconCol) { IconCol->Resizable(false); IconCol->HasImage(true); } } else if (!AskImage()) IconCol.Reset(); PaintColumnHeadings(pDC); #if LList_ONPAINT_PROFILE t2 = LCurrentTime(); #endif // Draw items if (!Buf) Buf = new LMemDC; LRect r = ItemsPos; int n = FirstVisible; int LastY = r.y1; LCss::ColorDef Fill; int LastSelected = -1; LItem::ItemPaintCtx Ctx; Ctx.pDC = pDC; LRegion Rgn(ItemsPos); if (Items.Length()) { for (auto It = Items.begin(n); It != Items.end(); ++It, n++) { LListItem *i = *It; if (i->Pos.Valid()) { // Setup painting colours in the context if (LastSelected ^ (int)i->Select()) { if ((LastSelected = i->Select())) { Ctx.Fore = SelFore; Ctx.Back = SelBack; } else { Ctx.Fore = Fore; Ctx.Back = Back; } } // tell the item what colour to use #if DOUBLE_BUFFER_PAINT if (Buf->X() < i->Pos.X() || Buf->Y() < i->Pos.Y()) { Buf->Create(i->Pos.X(), i->Pos.Y(), GdcD->GetBits()); } Ctx = i->Pos; Ctx.r.Offset(-Ctx.r.x1, -Ctx.r.y1); i->OnPaint(Ctx); pDC->Blt(i->Pos.x1, i->Pos.y1, Buf, &Ctx.r); #else (LRect&)Ctx = i->Pos; i->OnPaint(Ctx); #endif Rgn.Subtract(&i->Pos); LastY = i->Pos.y2 + 1; } } } pDC->Colour(Back); for (LRect *w=Rgn.First(); w; w=Rgn.Next()) { pDC->Rectangle(w); } Unlock(); #if LList_ONPAINT_PROFILE int64 End = LCurrentTime(); printf("LList::OnPaint() pour=%i headers=%i items=%i\n", (int) (t1-Start), (int) (t2-t1), (int) (End-t2)); #endif } void LList::OnFocus(bool b) { LListItem *s = GetSelected(); if (Items.Length()) { if (!s) { s = Items[0]; if (s) s->Select(true); } for (auto It = Items.begin(FirstVisible); It != Items.end(); ++It) { auto i = *It; if (i->Pos.Valid() && i->d->Selected) { Invalidate(&i->Pos); } } } LLayout::OnFocus(b); if (!b && IsCapturing()) { Capture(false); } } void LList::UpdateAllItems() { if (Lock(_FL)) { bool needsRepour = false; ForAllItems(i) { auto css = i->GetCss(); bool vis = !css || css->Display() != LCss::DispNone; if (i->d->Visible != vis) needsRepour = true; i->d->EmptyDisplay(); } Unlock(); if (needsRepour) PourAll(); Invalidate(); } } int LList::GetContentSize(int Index) { int Max = 0; for (auto It = Items.begin(); It.In(); It++) { LListItem *i = *It; LDisplayString *s = i->d->Display[Index]; LDisplayString *Mem = 0; // If no cached string, create it for the list item if (!s || s->IsTruncated()) { LFont *f = i->GetFont(); if (!f) f = GetFont(); if (!f) f = LSysFont; const char *Text = i->d->Str[Index] ? i->d->Str[Index] : i->GetText(Index); if (s && s->IsTruncated()) { s = Mem = new LDisplayString(f, Text?Text:(char*)""); } else { s = i->d->Display[Index] = new LDisplayString(f, Text?Text:(char*)""); } } // Measure it if (s) { Max = MAX(Max, s->X()); } DeleteObj(Mem); } // Measure the heading too LItemColumn *Col = Columns[Index]; LFont *f = GetFont(); LAssert(f != 0); if (f) { LDisplayString h(f, Col->Name()); int Hx = h.X() + (Col->HasSort() ? 10 : 0); Max = MAX(Max, Hx); } return Max; } diff --git a/src/haiku/Window.cpp b/src/haiku/Window.cpp --- a/src/haiku/Window.cpp +++ b/src/haiku/Window.cpp @@ -1,1326 +1,1326 @@ #include #include "lgi/common/Lgi.h" #include "lgi/common/DragAndDrop.h" #include "lgi/common/Token.h" #include "lgi/common/Popup.h" #include "lgi/common/Panel.h" #include "lgi/common/Notifications.h" #include "lgi/common/Menu.h" #include "ViewPriv.h" #include "MenuBar.h" #define DEBUG_SETFOCUS 0 #define DEBUG_HANDLEVIEWKEY 0 #define DEBUG_WAIT_THREAD 0 #define DEBUG_SERIALIZE_STATE 0 #if DEBUG_WAIT_THREAD #define WAIT_LOG(...) LgiTrace(__VA_ARGS__) #else #define WAIT_LOG(...) #endif LString ToString(BRect &r) { LString s; s.Printf("%g,%g,%g,%g", r.left, r.top, r.right, r.bottom); return s; } /////////////////////////////////////////////////////////////////////// class HookInfo { public: LWindowHookType Flags; LView *Target; }; enum LAttachState { LUnattached, LAttaching, LAttached, LDetaching, }; class LWindowPrivate : public BWindow { public: LWindow *Wnd; bool SnapToEdge = false; LArray Hooks; LWindow *ModalParent = NULL; LWindow *ModalChild = NULL; bool ShowTitleBar = true; bool ThreadMsgDone = false; LString MakeName(LWindow *w) { LString s; s.Printf("%p::con::%s", w, w->GetClass()); // printf("%s\n", s.Get()); return s; } LWindowPrivate(LWindow *wnd) : BWindow(BRect(100,100,400,400), MakeName(wnd), B_DOCUMENT_WINDOW_LOOK, B_NORMAL_WINDOW_FEEL, 0), Wnd(wnd) { } ~LWindowPrivate() { #if 0 printf("%p::~LWindowPrivate start, locking=%i, cur=%i, thread=%i\n", this, LockingThread(), LCurrentThreadId(), Thread()); #endif LAssert(ModalChild == NULL); DeleteObj(Wnd->Menu); if (Thread() >= 0 && IsMinimized()) Wnd->_PrevZoom = LZoomMin; Wnd->d = NULL; Lock(); // printf("%p::~LWindowPrivate end\n", this); } window_look DefaultLook() { if (ShowTitleBar) return B_DOCUMENT_WINDOW_LOOK; else return B_NO_BORDER_WINDOW_LOOK; } int GetHookIndex(LView *Target, bool Create = false) { for (int i=0; iTarget = Target; n->Flags = LNoEvents; return Hooks.Length() - 1; } } return -1; } void FrameMoved(BPoint newPosition) { auto Pos = Frame(); if (Pos != Wnd->Pos) { Wnd->Pos = Pos; Wnd->OnPosChange(); } BWindow::FrameMoved(newPosition); } void FrameResized(float newWidth, float newHeight) { auto Pos = Frame(); if (Pos != Wnd->Pos) { Wnd->Pos.SetSize(newWidth, newHeight); Wnd->OnPosChange(); } BWindow::FrameResized(newWidth, newHeight); } bool QuitRequested() { /* printf("%p::QuitRequested() starting.. pos=%s cls=%s\n", Wnd, Wnd->Pos.GetStr(), Wnd->GetClass()); */ auto r = Wnd->OnRequestClose(false); // printf("%s::QuitRequested()=%i\n", Wnd->GetClass(), r); return r; } void MessageReceived(BMessage *message) { if (message->what == M_LWINDOW_DELETE) { // printf("Processing M_LWINDOW_DELETE th=%u\n", LCurrentThreadId()); Wnd->Handle()->RemoveSelf(); Quit(); // printf("Processed M_LWINDOW_DELETE\n"); } else { BWindow::MessageReceived(message); LView *view = NULL; auto r = message->FindPointer(LMessage::PropView, (void**)&view); if (r == B_OK) { if (!LView::RecentlyDeleted(view)) view->OnEvent((LMessage*)message); /* else printf("%s:%i - Dropping message to deleted view.\n", _FL); */ } else { Wnd->OnEvent((LMessage*)message); } } } void WindowActivated(bool focus) { // printf("%s::WindowActivated %i\n", Wnd->GetClass(), focus); if (!ThreadMsgDone) { ThreadMsgDone = true; LString n; n.Printf("%s/%s", Wnd->GetClass(), Wnd->Name()); LThread::RegisterThread(Thread(), n); } if (ModalChild && focus) { printf("%s:%i - %s dropping activate, has modal: %s.\n", _FL, Wnd->GetClass(), ModalChild->GetClass()); SendBehind(ModalChild->WindowHandle()); auto w = Wnd; while (auto p = w->GetModalParent()) { p->WindowHandle()->SendBehind(w->WindowHandle()); w = p; } } else { BWindow::WindowActivated(focus); } } }; /////////////////////////////////////////////////////////////////////// LWindow::LWindow() : LView(0) { d = new LWindowPrivate(this); _QuitOnClose = false; Menu = NULL; _Default = 0; _Window = this; ClearFlag(WndFlags, GWF_VISIBLE); } LWindow::~LWindow() { if (LAppInst->AppWnd == this) LAppInst->AppWnd = NULL; LAssert(!Menu); WaitThread(); } LWindow *LWindow::GetModalParent() { return d->ModalParent; } bool LWindow::SetModalParent(LWindow *p) { if (p) { if (d->ModalParent || p->GetModalChild()) { printf("%s:%i - this=%s p=%s d->ModalParent=%s p->GetModalChild()=%s\n", _FL, GetClass(), p?p->GetClass():NULL, d->ModalParent?d->ModalParent->GetClass():NULL, p->GetModalChild()?p->GetModalChild()->GetClass():NULL); LAssert(!"Already set!"); return false; } d->ModalParent = p; p->d->ModalChild = this; } else { if (d->ModalParent) { if (d->ModalParent->GetModalChild() != this) { LAssert(!"Wrong linkage."); return false; } d->ModalParent->d->ModalChild = NULL; d->ModalParent = NULL; } } return true; } LWindow *LWindow::GetModalChild() { return d->ModalChild; } bool LWindow::SetModalChild(LWindow *c) { if (c) { if (d->ModalChild || c->GetModalParent()) { LAssert(!"Already set."); return false; } d->ModalChild = c; c->d->ModalParent = this; } else { if (d->ModalChild) { if (d->ModalChild->GetModalParent() != this) { LAssert(!"Wrong linkage."); return false; } d->ModalChild->d->ModalParent = NULL; d->ModalChild = NULL; } } return true; } int LWindow::WaitThread() { if (!d) return -1; thread_id id = d->Thread(); bool thisThread = id == LCurrentThreadId(); WAIT_LOG("%s::~LWindow thread=%u lock=%u\n", Name(), LCurrentThreadId(), d->LockingThread()); if (thisThread) { // We are in thread... can delete easily. if (d->Lock()) { // printf("%s::~LWindow Quiting\n", Name()); Handle()->RemoveSelf(); d->Quit(); // printf("%s::~LWindow Quit finished\n", Name()); } DeleteObj(d); return 0; // If we're in thread... no need to wait. } status_t value = B_OK; if (d->Thread() >= 0) { // Post event to the window's thread to delete itself... WAIT_LOG("%s::~LWindow posting M_LWINDOW_DELETE from th=%u\n", Name(), LCurrentThreadId()); d->PostMessage(new BMessage(M_LWINDOW_DELETE)); WAIT_LOG("wait_for_thread(%u) start..\n", id); wait_for_thread(id, &value); WAIT_LOG("wait_for_thread(%u) end=%i\n", id, value); } else printf("%s:%i - No thread to wait for: %s\n", _FL, GetClass()); DeleteObj(d); return value; } OsWindow LWindow::WindowHandle() { return d; } bool LWindow::SetTitleBar(bool ShowTitleBar) { if (!d) return false; d->ShowTitleBar = ShowTitleBar; LLocker lck(d, _FL); auto r = d->SetLook(d->DefaultLook()); if (r) printf("%s:%i - SetFeel failed: %i\n", _FL, r); return r == B_OK; } bool LWindow::SetIcon(const char *FileName) { LString a; if (!LFileExists(FileName)) { if (a = LFindFile(FileName)) FileName = a; } return false; } bool LWindow::GetSnapToEdge() { return d->SnapToEdge; } void LWindow::SetSnapToEdge(bool s) { d->SnapToEdge = s; } bool LWindow::IsActive() { LLocker lck(d, _FL); if (!lck.Lock()) return false; return d->IsActive(); } bool LWindow::SetActive() { LLocker lck(d, _FL); if (!lck.Lock()) return false; d->Activate(); return true; } bool LWindow::Visible() { LLocker lck(d, _FL); if (!lck.WaitForLock()) return false; return !d->IsHidden(); } void LWindow::Visible(bool i) { LLocker lck(d, _FL); if (!lck.WaitForLock()) { printf("%s:%i - Can't lock.\n", _FL); return; } if (i) { if (d->IsHidden()) { d->MoveTo(Pos.x1, Pos.y1); d->ResizeTo(Pos.X(), Pos.Y()); d->Show(); } else printf("%s already shown\n", GetClass()); } else { if (!d->IsHidden()) d->Hide(); } } bool LWindow::Obscured() { return false; } bool DndPointMap(LViewI *&v, LPoint &p, LDragDropTarget *&t, LWindow *Wnd, int x, int y) { LRect cli = Wnd->GetClient(); t = NULL; v = Wnd->WindowFromPoint(x - cli.x1, y - cli.y1, false); if (!v) { LgiTrace("%s:%i - @ %i,%i\n", _FL, x, y); return false; } v->WindowVirtualOffset(&p); p.x = x - p.x; p.y = y - p.y; for (LViewI *view = v; !t && view; view = view->GetParent()) t = view->DropTarget(); if (t) return true; LgiTrace("%s:%i - No target for %s\n", _FL, v->GetClass()); return false; } void LWindow::UpdateRootView() { auto rootView = Handle(); if (!rootView) { printf("%s:%i - Can't update root view: no view handle.\n", _FL); return; } auto wnd = WindowHandle(); auto looper = rootView->Looper(); auto isLocked = looper->IsLocked(); if (!isLocked) { printf("%s:%i - Can't update root view: not locked.\n", _FL); return; } if (rootView->IsHidden()) { printf("%s::UpdateRootView() curThread=%i/%s wndThread=%i/%s\n", GetClass(), LCurrentThreadId(), LThread::GetThreadName(LCurrentThreadId()), wnd->Thread(), LThread::GetThreadName(wnd->Thread()) ); rootView->Show(); } auto menu = wnd->KeyMenuBar(); BRect menuPos = menu ? menu->Frame() : BRect(0, 0, 0, 0); auto f = wnd->Frame(); rootView->ResizeTo(f.Width(), f.Height() - menuPos.Height()); if (menu) rootView->MoveTo(0, menuPos.Height()); rootView->SetResizingMode(B_FOLLOW_ALL_SIDES); } bool LWindow::Attach(LViewI *p) { LLocker lck(d, _FL); if (!lck.Lock()) return false; auto rootView = Handle(); auto wnd = WindowHandle(); // printf("%s:%i attach %p to %p\n", _FL, Handle(), WindowHandle()); if (rootView && wnd) { wnd->AddChild(rootView); UpdateRootView(); } // Setup default button... if (!_Default) _Default = FindControl(IDOK); // Do a rough layout of child windows PourAll(); return true; } bool LWindow::OnRequestClose(bool OsShuttingDown) { if (GetQuitOnClose()) LCloseApp(); return LView::OnRequestClose(OsShuttingDown); } bool LWindow::HandleViewMouse(LView *v, LMouse &m) { if (d->ModalChild) { /* printf("%s:%i - %s ignoring mouse event while %s is shown.\n", _FL, GetClass(), d->ModalDlg->GetClass()); */ return false; } if (m.Down() && !m.IsMove()) { bool InPopup = false; for (LViewI *p = v; p; p = p->GetParent()) { if (dynamic_cast(p)) { InPopup = true; break; } } if (!InPopup && LPopup::CurrentPopups.Length()) { for (int i=0; iVisible()) p->Visible(false); } } } for (int i=0; iHooks.Length(); i++) { if (d->Hooks[i].Flags & LMouseEvents) { if (!d->Hooks[i].Target->OnViewMouse(v, m)) { return false; } } } return true; } bool LWindow::HandleViewKey(LView *v, LKey &k) { bool Status = false; LViewI *Ctrl = 0; #if DEBUG_HANDLEVIEWKEY bool Debug = 1; // k.vkey == LK_RETURN; char SafePrint = k.c16 < ' ' ? ' ' : k.c16; // if (Debug) { LgiTrace("%s/%p::HandleViewKey=%i ischar=%i %s%s%s%s\n", v->GetClass(), v, k.c16, k.IsChar, (char*)(k.Down()?" Down":" Up"), (char*)(k.Shift()?" Shift":""), (char*)(k.Alt()?" Alt":""), (char*)(k.Ctrl()?" Ctrl":"")); } #endif if (d->ModalChild) { /* printf("%s:%i - %s ignoring key event while %s is shown.\n", _FL, GetClass(), d->ModalChild->GetClass()); */ return false; } // Any window in a popup always gets the key... LViewI *p; for (p = v->GetParent(); p; p = p->GetParent()) { if (dynamic_cast(p)) { #if DEBUG_HANDLEVIEWKEY if (Debug) LgiTrace("\tSending key to popup\n"); #endif return v->OnKey(k); } } // Give key to popups if (LAppInst && LAppInst->GetMouseHook() && LAppInst->GetMouseHook()->OnViewKey(v, k)) { #if DEBUG_HANDLEVIEWKEY if (Debug) LgiTrace("\tMouseHook got key\n"); #endif goto AllDone; } // Allow any hooks to see the key... for (int i=0; iHooks.Length(); i++) { #if DEBUG_HANDLEVIEWKEY // if (Debug) LgiTrace("\tHook[%i]\n", i); #endif if (d->Hooks[i].Flags & LKeyEvents) { LView *Target = d->Hooks[i].Target; #if DEBUG_HANDLEVIEWKEY if (Debug) LgiTrace("\tHook[%i].Target=%p %s\n", i, Target, Target->GetClass()); #endif if (Target->OnViewKey(v, k)) { Status = true; #if DEBUG_HANDLEVIEWKEY if (Debug) LgiTrace("\tHook[%i] ate '%c'(%i) down=%i alt=%i ctrl=%i sh=%i\n", i, SafePrint, k.c16, k.Down(), k.Alt(), k.Ctrl(), k.Shift()); #endif goto AllDone; } } } // Give the key to the window... if (v->OnKey(k)) { #if DEBUG_HANDLEVIEWKEY if (Debug) LgiTrace("\tView ate '%c'(%i) down=%i alt=%i ctrl=%i sh=%i\n", SafePrint, k.c16, k.Down(), k.Alt(), k.Ctrl(), k.Shift()); #endif Status = true; goto AllDone; } #if DEBUG_HANDLEVIEWKEY else if (Debug) LgiTrace("\t%s didn't eat '%c'(%i) down=%i alt=%i ctrl=%i sh=%i\n", v->GetClass(), SafePrint, k.c16, k.Down(), k.Alt(), k.Ctrl(), k.Shift()); #endif // Window didn't want the key... switch (k.vkey) { case LK_RETURN: #ifdef LK_KEYPADENTER case LK_KEYPADENTER: #endif { Ctrl = _Default; break; } case LK_ESCAPE: { Ctrl = FindControl(IDCANCEL); break; } } // printf("Ctrl=%p\n", Ctrl); if (Ctrl) { if (Ctrl->Enabled()) { if (Ctrl->OnKey(k)) { Status = true; #if DEBUG_HANDLEVIEWKEY if (Debug) LgiTrace("\tDefault Button ate '%c'(%i) down=%i alt=%i ctrl=%i sh=%i\n", SafePrint, k.c16, k.Down(), k.Alt(), k.Ctrl(), k.Shift()); #endif goto AllDone; } // else printf("OnKey()=false\n"); } // else printf("Ctrl=disabled\n"); } #if DEBUG_HANDLEVIEWKEY else if (Debug) LgiTrace("\tNo default ctrl to handle key.\n"); #endif if (Menu) { Status = Menu->OnKey(v, k); if (Status) { #if DEBUG_HANDLEVIEWKEY if (Debug) LgiTrace("\tMenu ate '%c' down=%i alt=%i ctrl=%i sh=%i\n", k.c16, k.Down(), k.Alt(), k.Ctrl(), k.Shift()); #endif } } // Tab through controls if (k.vkey == LK_TAB && k.Down() && !k.IsChar) { LViewI *Wnd = GetNextTabStop(v, k.Shift()); #if DEBUG_HANDLEVIEWKEY if (Debug) LgiTrace("\tTab moving focus shift=%i Wnd=%p\n", k.Shift(), Wnd); #endif if (Wnd) Wnd->Focus(true); } // Control shortcut? if (k.Down() && k.Alt() && k.c16 > ' ') { ShortcutMap Map; BuildShortcuts(Map); LViewI *c = Map.Find(ToUpper(k.c16)); if (c) { c->OnNotify(c, LNotifyActivate); return true; } } AllDone: return Status; } void LWindow::Raise() { } LWindowZoom LWindow::GetZoom() { if (!d) return _PrevZoom; LLocker lck(d, _FL); if (!IsAttached() || lck.Lock()) { if (d->IsMinimized()) return LZoomMin; } return LZoomNormal; } void LWindow::SetZoom(LWindowZoom i) { LLocker lck(d, _FL); if (lck.Lock()) { d->Minimize(i == LZoomMin); } } LViewI *LWindow::GetDefault() { return _Default; } void LWindow::SetDefault(LViewI *v) { if (v && v->GetWindow() == this) { if (_Default != v) { LViewI *Old = _Default; _Default = v; if (Old) Old->Invalidate(); if (_Default) _Default->Invalidate(); } } else { _Default = 0; } } bool LWindow::Name(const char *n) { LLocker lck(d, _FL); if (lck.Lock()) d->SetTitle(n); return LBase::Name(n); } const char *LWindow::Name() { return LBase::Name(); } -LPoint LWindow::GetDpi() +LPoint LWindow::GetDpi() const { return LPoint(96, 96); } LPointF LWindow::GetDpiScale() { auto Dpi = GetDpi(); return LPointF((double)Dpi.x/96.0, (double)Dpi.y/96.0); } LRect &LWindow::GetClient(bool ClientSpace) { static LRect r; r = Pos.ZeroTranslate(); LLocker lck(WindowHandle(), _FL); if (lck.Lock()) { LRect br = Handle()->Bounds(); if (br.Valid()) { r = br; auto frm = d->Frame(); frm.OffsetBy(-frm.left, -frm.top); // printf("%s:%i - LWindow::GetClient: Frame=%s Bounds=%s r=%s\n", _FL, ToString(frm).Get(), br.GetStr(), r.GetStr()); } // else printf("%s:%i - LWindow::GetClient: Bounds not valid.\n", _FL); lck.Unlock(); } // else printf("%s:%i - LWindow::GetClient: no lock.\n", _FL); return r; } #if DEBUG_SERIALIZE_STATE #define SERIALIZE_LOG(...) printf(__VA_ARGS__) #else #define SERIALIZE_LOG(...) #endif bool LWindow::SerializeState(LDom *Store, const char *FieldName, bool Load) { if (!Store || !FieldName) return false; if (Load) { ::LVariant v; if (Store->GetValue(FieldName, v) && v.Str()) { LRect Position(0, 0, -1, -1); LWindowZoom State = LZoomNormal; auto vars = LString(v.Str()).SplitDelimit(";"); SERIALIZE_LOG("SerializeState: %s=%s, vars=%i\n", FieldName, v.Str(), (int)vars.Length()); for (auto var: vars) { auto parts = var.SplitDelimit("=", 1); SERIALIZE_LOG("SerializeState: parts=%i\n", (int)parts.Length()); if (parts.Length() == 2) { if (parts[0].Equals("State")) { State = (LWindowZoom) parts[1].Int(); SERIALIZE_LOG("SerializeState: part=%s state=%i\n", parts[0].Get(), State); } else if (parts[0].Equals("Pos")) { Position.SetStr(parts[1]); SERIALIZE_LOG("SerializeState: part=%s pos=%s\n", parts[0].Get(), Position.GetStr()); } } } if (Position.Valid()) { SERIALIZE_LOG("SerializeState setpos %s\n", Position.GetStr()); SetPos(Position); } SetZoom(State); } else { SERIALIZE_LOG("SerializeState: no '%s' var\n", FieldName); return false; } } else { char s[256]; LWindowZoom State = GetZoom(); sprintf_s(s, sizeof(s), "State=%i;Pos=%s", State, GetPos().GetStr()); LVariant v = s; SERIALIZE_LOG("SerializeState: saving '%s' = '%s'\n", FieldName, s); if (!Store->SetValue(FieldName, v)) { SERIALIZE_LOG("SerializeState: SetValue failed\n"); return false; } } return true; } LRect &LWindow::GetPos() { return Pos; } bool LWindow::SetPos(LRect &p, bool Repaint) { Pos = p; LLocker lck(d, _FL); if (lck.Lock()) { d->MoveTo(Pos.x1, Pos.y1); d->ResizeTo(Pos.X(), Pos.Y()); } else printf("%s:%i - Failed to lock.\n", _FL); return true; } void LWindow::OnChildrenChanged(LViewI *Wnd, bool Attaching) { // Force repour Invalidate(); } void LWindow::OnCreate() { AttachChildren(); } void LWindow::OnPaint(LSurface *pDC) { pDC->Colour(L_MED); pDC->Rectangle(); } void LWindow::OnPosChange() { LLocker lck(WindowHandle(), _FL); if (lck.Lock()) { auto frame = WindowHandle()->Bounds(); auto menu = WindowHandle()->KeyMenuBar(); auto menuPos = menu ? menu->Frame() : BRect(0, 0, 0, 0); auto rootPos = Handle()->Frame(); if (menu) { if (menu->IsHidden()) // Why? { menu->Show(); if (menu->IsHidden()) { // printf("Can't show menu?\n"); for (auto p = menu->Parent(); p; p = p->Parent()) printf(" par=%s %i\n", p->Name(), p->IsHidden()); } } if (menuPos.Width() < 1) // Again... WHHHHY? FFS { float x = 0.0f, y = 0.0f; menu->GetPreferredSize(&x, &y); // printf("Pref=%g,%g\n", x, y); if (y > 0.0f) menu->ResizeTo(frame.Width(), y); } } #if 0 printf("frame=%s menu=%p,%i,%s rootpos=%s\n", ToString(frame).Get(), menu, menu?menu->IsHidden():0, ToString(menuPos).Get(), ToString(rootPos).Get()); #endif int rootTop = menu ? menuPos.bottom + 1 : 0; if (rootPos.top != rootTop) { Handle()->MoveTo(0, rootTop); Handle()->ResizeTo(rootPos.Width(), frame.Height() - menuPos.Height()); } lck.Unlock(); } LView::OnPosChange(); PourAll(); } #define IsTool(v) \ ( \ dynamic_cast(v) \ && \ dynamic_cast(v)->_IsToolBar \ ) void LWindow::PourAll() { LRect c = GetClient(); LRegion Client(c); LViewI *MenuView = 0; LRegion Update(Client); bool HasTools = false; LViewI *v; List::I Lst = Children.begin(); { LRegion Tools; for (v = *Lst; v; v = *++Lst) { bool IsMenu = MenuView == v; if (!IsMenu && IsTool(v)) { LRect OldPos = v->GetPos(); if (OldPos.Valid()) Update.Union(&OldPos); if (HasTools) { // 2nd and later toolbars if (v->Pour(Tools)) { if (!v->Visible()) { v->Visible(true); } auto vpos = v->GetPos(); if (OldPos != vpos) { // position has changed update... v->Invalidate(); } // Has it increased the size of the toolbar area? auto b = Tools.Bound(); if (vpos.y2 >= b.y2) { LRect Bar = Client; Bar.y2 = vpos.y2; Client.Subtract(&Bar); // LgiTrace("IncreaseToolbar=%s\n", Bar.GetStr()); } Tools.Subtract(&vpos); Update.Subtract(&vpos); // LgiTrace("vpos=%s\n", vpos.GetStr()); } } else { // First toolbar if (v->Pour(Client)) { HasTools = true; if (!v->Visible()) { v->Visible(true); } if (OldPos != v->GetPos()) { v->Invalidate(); } LRect Bar(v->GetPos()); // LgiTrace("%s = %s\n", v->GetClass(), Bar.GetStr()); Bar.x2 = GetClient().x2; Tools = Bar; Tools.Subtract(&v->GetPos()); Client.Subtract(&Bar); Update.Subtract(&Bar); } } } } } Lst = Children.begin(); for (LViewI *v = *Lst; v; v = *++Lst) { bool IsMenu = MenuView == v; if (!IsMenu && !IsTool(v)) { LRect OldPos = v->GetPos(); if (OldPos.Valid()) Update.Union(&OldPos); if (v->Pour(Client)) { // LRect p = v->GetPos(); // LgiTrace("%s = %s\n", v->GetClass(), p.GetStr()); if (!v->Visible()) v->Visible(true); v->Invalidate(); Client.Subtract(&v->GetPos()); Update.Subtract(&v->GetPos()); } else { // non-pourable } } } for (int i=0; iMsg()) { case M_CLOSE: { if (OnRequestClose(false)) { Quit(); return 0; } break; } } return LView::OnEvent(m); } bool LWindow::RegisterHook(LView *Target, LWindowHookType EventType, int Priority) { bool Status = false; if (Target && EventType) { int i = d->GetHookIndex(Target, true); if (i >= 0) { d->Hooks[i].Flags = EventType; Status = true; } } return Status; } bool LWindow::UnregisterHook(LView *Target) { int i = d->GetHookIndex(Target); if (i >= 0) { d->Hooks.DeleteAt(i); return true; } return false; } void LWindow::OnFrontSwitch(bool b) { } bool LWindow::SetWillFocus(bool f) { if (!d) return false; LLocker lck(d, _FL); auto flags = d->Flags(); if (f) flags &= ~B_AVOID_FOCUS; // clear flag else flags |= B_AVOID_FOCUS; // set flag auto r = d->SetFlags(flags); if (r) printf("%s:%i - SetFlags failed: %i\n", _FL, r); return true; } LViewI *LWindow::GetFocus() { if (!d) return NULL; LLocker lck(d, _FL); if (!lck.WaitForLock()) return NULL; auto f = d->CurrentFocus(); return LViewFromHandle(f); } void LWindow::SetFocus(LViewI *ctrl, FocusType type) { if (!ctrl) return; LLocker lck(d, _FL); if (lck.WaitForLock()) { auto h = ctrl->Handle(); h->MakeFocus(type == GainFocus ? true : false); } } void LWindow::SetDragHandlers(bool On) { } void LWindow::OnTrayClick(LMouse &m) { if (m.Down() || m.IsContextMenu()) { LSubMenu RClick; OnTrayMenu(RClick); if (GetMouse(m, true)) { int Result = RClick.Float(this, m.x, m.y); OnTrayMenuResult(Result); } } } void LWindow::SetAlwaysOnTop(bool b) { if (!d) return; LLocker lck(d, _FL); status_t r; if (b) r = d->SetFeel(B_FLOATING_APP_WINDOW_FEEL); else r = d->SetFeel(B_NORMAL_WINDOW_FEEL); if (r) printf("%s:%i - SetFeel failed: %i\n", _FL, r); } diff --git a/src/mac/cocoa/General.mm b/src/mac/cocoa/General.mm --- a/src/mac/cocoa/General.mm +++ b/src/mac/cocoa/General.mm @@ -1,603 +1,603 @@ // Mac Implementation of General LGI functions #include #include #include #include #include // #define _POSIX_TIMERS #include #include "lgi/common/Lgi.h" #include "lgi/common/Process.h" #include "lgi/common/TextLabel.h" #include "lgi/common/Button.h" #include "lgi/common/Net.h" #include "lgi/common/Uri.h" #include #include #include //////////////////////////////////////////////////////////////// // Local helper functions CFStringRef Utf8ToCFString(const char *s, ssize_t len = -1) { if (s && len < 0) len = strlen(s); return CFStringCreateWithBytes(kCFAllocatorDefault, (const UInt8*)s, len, kCFStringEncodingUTF8, false); } char *CFStringToUtf8(CFStringRef r) { if (r == NULL) return 0; char *Buffer = 0; CFRange g = { 0, CFStringGetLength(r) }; CFIndex Used; if (CFStringGetBytes(r, g, kCFStringEncodingUTF8, 0, false, 0, 0, &Used)) { if ((Buffer = new char[Used+1])) { CFStringGetBytes(r, g, kCFStringEncodingUTF8, 0, false, (UInt8*)Buffer, Used, &Used); Buffer[Used] = 0; } } return Buffer; } bool _lgi_check_file(char *Path) { if (Path) { if (LFileExists(Path)) { // file is there return true; } else { // shortcut? char *e = Path + strlen(Path); strcpy(e, ".lnk"); if (LFileExists(Path)) { // resolve shortcut char Link[256]; if (LResolveShortcut(Path, Link, sizeof(Link))) { // check destination of link if (LFileExists(Link)) { strcpy(Path, Link); return true; } } } *e = 0; } } return false; } void LSleep(uint32 i) { struct timespec request, remain; ZeroObj(request); ZeroObj(remain); request.tv_sec = i / 1000; request.tv_nsec = (i % 1000) * 1000000; while (nanosleep(&request, &remain) == -1) { request = remain; } } char *p2c(unsigned char *s) { if (s) { s[1+s[0]] = 0; return (char*)s + 1; } return 0; } void c2p255(Str255 &d, char *s) { if (s) { size_t Len = strlen(s); if (Len > 255) Len = 255; d[0] = Len; for (int i=0; iHandle(); [hnd.p performSelectorOnMainThread:@selector(assert:) withObject:ca waitUntilDone:true]; switch (ca.result) { case NSAlertFirstButtonReturn: // Debug/Break Result = 2; break; case NSAlertSecondButtonReturn: // Ingore/Continue Result = 3; break; case NSAlertThirdButtonReturn: // Exit/Abort Result = 1; break; } [ca release]; } #else - GAlert a(0, "Assert Failed", Assert.Msg, "Abort", "Debug", "Ignore"); + LAlert a(0, "Assert Failed", Assert.Msg, "Abort", "Debug", "Ignore"); Result = a.DoModal(); #endif switch (Result) { default: { exit(-1); break; } case 2: { // Crash here to bring up the debugger... int *p = 0; *p = 0; break; } case 3: { break; } } #endif Asserting = false; } } //////////////////////////////////////////////////////////////////////// // Implementations LMessage CreateMsg(int m, LMessage::Param a, LMessage::Param b) { static class LMessage Msg(0); Msg.Set(m, a, b); return Msg; } OsView DefaultOsView(LView *v) { return NULL; } LString LGetFileMimeType(const char *File) { return LAppInst ? LAppInst->GetFileMimeType(File) : NULL; } bool _GetIniField(char *Grp, char *Field, char *In, char *Out, int OutSize) { if (ValidStr(In)) { bool InGroup = false; auto t = LString(In).SplitDelimit("\r\n"); for (int i=0; i Ver; LgiGetOs(Ver); if (Ver.Length() > 1) { if (Ver[0] < 10 || Ver[1] < 6) { IsAppBundle = false; } } } */ } struct stat s; int st = stat(File, &s); if (IsAppBundle) { char cmd[512]; if (ValidStr((char*)Args)) snprintf(cmd, sizeof(cmd), "open -a \"%s\" %s", File, Args); else snprintf(cmd, sizeof(cmd), "open -a \"%s\"", File); system(cmd); } else if (st == 0 && S_ISREG(s.st_mode) && (s.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) { // This is an executable file if (!fork()) { if (Dir) chdir(Dir); LArray a; a.Add(File); char *p; while ((p = LTokStr(Args))) { a.Add(p); } a.Add(0); char *env[] = {0}; execve(File, (char*const*)&a[0], env); } return true; } else { // Document #if LGI_CARBON e = FinderLaunch(1, &r); if (e) printf("%s:%i - FinderLaunch faied with %i\n", _FL, (int)e); else Status = true; #elif LGI_COCOA LString file = File; auto url = [[NSURL alloc] initFileURLWithPath:file.NsStr()]; Status = [[NSWorkspace sharedWorkspace] openURL:url]; #endif } } } } return Status; } bool LGetMimeTypeExtensions(const char *Mime, LArray &Ext) { size_t Start = Ext.Length(); #define HardCodeExtention(Mime, Ext1, Ext2) \ else if (!stricmp(Mime, Mime)) \ { if (Ext1) Ext.Add(Ext1); \ if (Ext2) Ext.Add(Ext2); } if (!Mime); HardCodeExtention("text/calendar", "ics", (const char*)NULL) HardCodeExtention("text/x-vcard", "vcf", (const char*)NULL) HardCodeExtention("text/mbox", "mbx", "mbox"); return Ext.Length() > Start; } LString LCurrentUserName() { struct passwd *pw = getpwuid(geteuid()); if (pw) return pw->pw_name; return ""; } diff --git a/src/mac/cocoa/LgiCocoa.xcodeproj/project.pbxproj b/src/mac/cocoa/LgiCocoa.xcodeproj/project.pbxproj --- a/src/mac/cocoa/LgiCocoa.xcodeproj/project.pbxproj +++ b/src/mac/cocoa/LgiCocoa.xcodeproj/project.pbxproj @@ -1,1204 +1,1210 @@ // !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 54; objects = { /* Begin PBXBuildFile section */ 340833152698F8FF0028012F /* FontPriv.h in Headers */ = {isa = PBXBuildFile; fileRef = 340833122698F8FE0028012F /* FontPriv.h */; }; 340833162698F8FF0028012F /* FontType.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 340833132698F8FE0028012F /* FontType.cpp */; }; 340833172698F8FF0028012F /* TypeFace.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 340833142698F8FE0028012F /* TypeFace.cpp */; }; 3409C0CA27580ED60050302A /* ListItemRadioBtn.h in Headers */ = {isa = PBXBuildFile; fileRef = 3409C0C727580ED60050302A /* ListItemRadioBtn.h */; }; 3409C0CB27580ED60050302A /* List.h in Headers */ = {isa = PBXBuildFile; fileRef = 3409C0C827580ED60050302A /* List.h */; }; 3409C0CC27580ED60050302A /* ListItemCheckBox.h in Headers */ = {isa = PBXBuildFile; fileRef = 3409C0C927580ED60050302A /* ListItemCheckBox.h */; }; 3409C0D0275812CB0050302A /* Profile.h in Headers */ = {isa = PBXBuildFile; fileRef = 3409C0CF275812CB0050302A /* Profile.h */; }; 34109857217A9805007020CE /* LCocoaView.h in Sources */ = {isa = PBXBuildFile; fileRef = 34109856217A9805007020CE /* LCocoaView.h */; }; 34114C8B23091E6E00F342B1 /* LgiClasses.h in Headers */ = {isa = PBXBuildFile; fileRef = 34114C8A23091E6E00F342B1 /* LgiClasses.h */; }; 3413170E23068FFE008CE982 /* FileSelect.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3413170D23068FFE008CE982 /* FileSelect.cpp */; }; 34131710230694D1008CE982 /* Gel.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3413170F230694D1008CE982 /* Gel.cpp */; }; 3415168F26EC32E7007EE35F /* TextView4.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3415168E26EC32E7007EE35F /* TextView4.cpp */; }; 3415169326EC32FF007EE35F /* TextView3.h in Headers */ = {isa = PBXBuildFile; fileRef = 3415169126EC32FF007EE35F /* TextView3.h */; }; 3415169426EC32FF007EE35F /* TextView4.h in Headers */ = {isa = PBXBuildFile; fileRef = 3415169226EC32FF007EE35F /* TextView4.h */; }; 3418EBCF25D9FEA600EA168A /* Progress.h in Headers */ = {isa = PBXBuildFile; fileRef = 3418EBCE25D9FEA600EA168A /* Progress.h */; }; 3418EBD125D9FEF500EA168A /* ProgressDlg.h in Headers */ = {isa = PBXBuildFile; fileRef = 3418EBD025D9FEF500EA168A /* ProgressDlg.h */; }; 3427064727A0E20C0043F733 /* CocoaView.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3427064627A0E20C0043F733 /* CocoaView.mm */; }; 343874B5230F46B200CF96B4 /* Window.h in Headers */ = {isa = PBXBuildFile; fileRef = 343874B1230F46B200CF96B4 /* Window.h */; }; 343874B6230F46B200CF96B4 /* View.h in Headers */ = {isa = PBXBuildFile; fileRef = 343874B2230F46B200CF96B4 /* View.h */; }; 343874B7230F46B200CF96B4 /* App.h in Headers */ = {isa = PBXBuildFile; fileRef = 343874B3230F46B200CF96B4 /* App.h */; }; 343874B8230F46B200CF96B4 /* Layout.h in Headers */ = {isa = PBXBuildFile; fileRef = 343874B4230F46B200CF96B4 /* Layout.h */; }; 343BE8C72355D0CE00742E21 /* PopupList.h in Headers */ = {isa = PBXBuildFile; fileRef = 343BE8C62355D0CE00742E21 /* PopupList.h */; }; 3449B8CE26BBF6B10022A9B8 /* Notifications.h in Headers */ = {isa = PBXBuildFile; fileRef = 3449B8CD26BBF6B10022A9B8 /* Notifications.h */; }; 344CFBC8237365B800AE6B35 /* ClipBoard.h in Headers */ = {isa = PBXBuildFile; fileRef = 344CFBC7237365B800AE6B35 /* ClipBoard.h */; }; 345920FF1F25649000098DFD /* Font.h in Headers */ = {isa = PBXBuildFile; fileRef = 345920FC1F25649000098DFD /* Font.h */; }; 345921001F25649000098DFD /* FontCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 345920FD1F25649000098DFD /* FontCache.h */; }; 345921011F25649000098DFD /* FontSelect.h in Headers */ = {isa = PBXBuildFile; fileRef = 345920FE1F25649000098DFD /* FontSelect.h */; }; 345EB84E235C6C01007D05DB /* Popup.h in Headers */ = {isa = PBXBuildFile; fileRef = 345EB84D235C6C01007D05DB /* Popup.h */; }; 346DDEC1240C882900751380 /* RadioGroup.h in Headers */ = {isa = PBXBuildFile; fileRef = 346DDEC0240C882900751380 /* RadioGroup.h */; }; 346FACE71D3C720B00FFEBCE /* Message.h in Headers */ = {isa = PBXBuildFile; fileRef = 346FACE61D3C720B00FFEBCE /* Message.h */; }; 346FACEB1D3C75AE00FFEBCE /* Uri.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 346FACE91D3C75AE00FFEBCE /* Uri.cpp */; }; 346FACEC1D3C75AE00FFEBCE /* MDStringToDigest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 346FACEA1D3C75AE00FFEBCE /* MDStringToDigest.cpp */; }; 3471868823A9AF8900F8C952 /* SubProcess.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3471868723A9AF8900F8C952 /* SubProcess.cpp */; }; 3477C2751CBF07DD0028B84B /* App.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3477C2741CBF07DD0028B84B /* App.mm */; }; 3477C2781CBF08050028B84B /* ClipBoard.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3477C2761CBF08050028B84B /* ClipBoard.mm */; }; 3477C2821CBF086A0028B84B /* General.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3477C27A1CBF086A0028B84B /* General.mm */; }; 3477C2831CBF086A0028B84B /* Layout.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3477C27B1CBF086A0028B84B /* Layout.mm */; }; 3477C2841CBF086A0028B84B /* Menu.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3477C27C1CBF086A0028B84B /* Menu.mm */; }; 3477C2851CBF086A0028B84B /* Printer.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3477C27D1CBF086A0028B84B /* Printer.mm */; }; 3477C2861CBF086A0028B84B /* Thread.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3477C27E1CBF086A0028B84B /* Thread.mm */; }; 3477C2871CBF086A0028B84B /* View.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3477C27F1CBF086A0028B84B /* View.mm */; }; 3477C2881CBF086A0028B84B /* Widgets.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3477C2801CBF086A0028B84B /* Widgets.mm */; }; 3477C2891CBF086A0028B84B /* Window.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3477C2811CBF086A0028B84B /* Window.mm */; }; 3477C28F1CBF08C10028B84B /* Gdc2.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3477C28B1CBF08C10028B84B /* Gdc2.mm */; }; 3477C2901CBF08C10028B84B /* MemDC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3477C28C1CBF08C10028B84B /* MemDC.cpp */; }; 3477C2911CBF08C10028B84B /* PrintDC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3477C28D1CBF08C10028B84B /* PrintDC.cpp */; }; 3477C2921CBF08C10028B84B /* ScreenDC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3477C28E1CBF08C10028B84B /* ScreenDC.cpp */; }; 3477C2971CBF09320028B84B /* File.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3477C2941CBF09320028B84B /* File.mm */; }; 3477C2981CBF09320028B84B /* Mem.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3477C2951CBF09320028B84B /* Mem.mm */; }; 3477C2991CBF09320028B84B /* ShowFileProp_Mac.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3477C2961CBF09320028B84B /* ShowFileProp_Mac.mm */; }; 3477C2A11CBF0A920028B84B /* Mem.h in Headers */ = {isa = PBXBuildFile; fileRef = 3477C29B1CBF0A920028B84B /* Mem.h */; }; 3477C2A21CBF0A920028B84B /* SymLookup.h in Headers */ = {isa = PBXBuildFile; fileRef = 3477C29C1CBF0A920028B84B /* SymLookup.h */; }; 3477C2A31CBF0A920028B84B /* LgiMac.h in Headers */ = {isa = PBXBuildFile; fileRef = 3477C29D1CBF0A920028B84B /* LgiMac.h */; }; 3477C2A41CBF0A920028B84B /* LgiOs.h in Headers */ = {isa = PBXBuildFile; fileRef = 3477C29E1CBF0A920028B84B /* LgiOs.h */; }; 3477C2A51CBF0A920028B84B /* LgiOsClasses.h in Headers */ = {isa = PBXBuildFile; fileRef = 3477C29F1CBF0A920028B84B /* LgiOsClasses.h */; }; 3477C2A61CBF0A920028B84B /* LgiOsDefs.h in Headers */ = {isa = PBXBuildFile; fileRef = 3477C2A01CBF0A920028B84B /* LgiOsDefs.h */; }; 3477C2A81CBF0AAF0028B84B /* Lgi.h in Headers */ = {isa = PBXBuildFile; fileRef = 3477C2A71CBF0AAF0028B84B /* Lgi.h */; }; 3477C2AA1CBF22A00028B84B /* File.h in Headers */ = {isa = PBXBuildFile; fileRef = 3477C2A91CBF22A00028B84B /* File.h */; }; 3477C2AC1CBF72170028B84B /* ViewPriv.h in Headers */ = {isa = PBXBuildFile; fileRef = 3477C2AB1CBF72170028B84B /* ViewPriv.h */; }; 3477C2AF1CBF96B90028B84B /* LgiRes.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3477C2AE1CBF96B90028B84B /* LgiRes.cpp */; }; 3477C2B11CBF96C40028B84B /* Res.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3477C2B01CBF96C40028B84B /* Res.cpp */; }; 3477C2B41CBF96F20028B84B /* LMsg.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3477C2B31CBF96F20028B84B /* LMsg.cpp */; }; 3477C2B71CBF973F0028B84B /* GuiUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3477C2B61CBF973F0028B84B /* GuiUtils.cpp */; }; 3477C2B91CBF977F0028B84B /* LgiCommon.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3477C2B81CBF977F0028B84B /* LgiCommon.cpp */; }; 3477C2BB1CBF97C80028B84B /* GdcCommon.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3477C2BA1CBF97C80028B84B /* GdcCommon.cpp */; }; 3477C2C11CBF9A040028B84B /* String.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3477C2C01CBF9A040028B84B /* String.cpp */; }; 3477C2C81CC046310028B84B /* TextView3.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3477C2C41CC046310028B84B /* TextView3.cpp */; }; 3477C2C91CC046310028B84B /* Unicode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3477C2C51CC046310028B84B /* Unicode.cpp */; }; 3477C2CA1CC046310028B84B /* Utf8.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3477C2C61CC046310028B84B /* Utf8.cpp */; }; 3477C2CB1CC046310028B84B /* XmlTree.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3477C2C71CC046310028B84B /* XmlTree.cpp */; }; 3477C2CE1CC047BE0028B84B /* Charset.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3477C2CD1CC047BE0028B84B /* Charset.cpp */; }; 3477C2D01CC047F50028B84B /* ToolBar.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3477C2CF1CC047F50028B84B /* ToolBar.cpp */; }; 3477C2D31CC048100028B84B /* Containers.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3477C2D21CC048100028B84B /* Containers.cpp */; }; 3477C2D51CC0481B0028B84B /* Array.h in Headers */ = {isa = PBXBuildFile; fileRef = 3477C2D41CC0481B0028B84B /* Array.h */; }; 3477C2D71CC048390028B84B /* Popup.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3477C2D61CC048390028B84B /* Popup.cpp */; }; 3477C2DB1CC048F90028B84B /* DisplayString.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3477C2D81CC048F90028B84B /* DisplayString.cpp */; }; 3477C2DC1CC048F90028B84B /* Font.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3477C2D91CC048F90028B84B /* Font.cpp */; }; 3477C2DD1CC048F90028B84B /* FontSystem.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3477C2DA1CC048F90028B84B /* FontSystem.cpp */; }; 3477C2DF1CC04A030028B84B /* ScrollBar.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3477C2DE1CC04A030028B84B /* ScrollBar.cpp */; }; 3477C2E11CC04A3C0028B84B /* FontSelect.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3477C2E01CC04A3C0028B84B /* FontSelect.cpp */; }; 3477C2E31CC04A5C0028B84B /* Stream.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3477C2E21CC04A5C0028B84B /* Stream.cpp */; }; 3477C2E51CC04A7F0028B84B /* ViewCommon.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3477C2E41CC04A7F0028B84B /* ViewCommon.cpp */; }; 3477C2E71CC04B1E0028B84B /* Surface.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3477C2E61CC04B1E0028B84B /* Surface.cpp */; }; 3477C2E91CC04B5D0028B84B /* Rand.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3477C2E81CC04B5D0028B84B /* Rand.cpp */; }; 3477C2EB1CC04B890028B84B /* Filter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3477C2EA1CC04B890028B84B /* Filter.cpp */; }; 3477C2EF1CC04BBB0028B84B /* ItemContainer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3477C2EC1CC04BBB0028B84B /* ItemContainer.cpp */; }; 3477C2F01CC04BBB0028B84B /* List.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3477C2ED1CC04BBB0028B84B /* List.cpp */; }; 3477C2F11CC04BBB0028B84B /* Tree.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3477C2EE1CC04BBB0028B84B /* Tree.cpp */; }; 3477C2FF1CC04BF00028B84B /* Bitmap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3477C2F21CC04BF00028B84B /* Bitmap.cpp */; }; 3477C3001CC04BF00028B84B /* Box.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3477C2F31CC04BF00028B84B /* Box.cpp */; }; 3477C3011CC04BF00028B84B /* Button.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3477C2F41CC04BF00028B84B /* Button.cpp */; }; 3477C3021CC04BF00028B84B /* CheckBox.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3477C2F51CC04BF00028B84B /* CheckBox.cpp */; }; 3477C3031CC04BF00028B84B /* Combo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3477C2F61CC04BF00028B84B /* Combo.cpp */; }; 3477C3041CC04BF00028B84B /* Edit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3477C2F71CC04BF00028B84B /* Edit.cpp */; }; 3477C3051CC04BF00028B84B /* Panel.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3477C2F81CC04BF00028B84B /* Panel.cpp */; }; 3477C3061CC04BF00028B84B /* Progress.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3477C2F91CC04BF00028B84B /* Progress.cpp */; }; 3477C3071CC04BF00028B84B /* RadioGroup.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3477C2FA1CC04BF00028B84B /* RadioGroup.cpp */; }; 3477C3081CC04BF00028B84B /* Splitter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3477C2FB1CC04BF00028B84B /* Splitter.cpp */; }; 3477C3091CC04BF00028B84B /* TableLayout.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3477C2FC1CC04BF00028B84B /* TableLayout.cpp */; }; 3477C30A1CC04BF00028B84B /* TabView.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3477C2FD1CC04BF00028B84B /* TabView.cpp */; }; 3477C30B1CC04BF00028B84B /* TextLabel.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3477C2FE1CC04BF00028B84B /* TextLabel.cpp */; }; 3477C30D1CC04C560028B84B /* FindReplace.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3477C30C1CC04C560028B84B /* FindReplace.cpp */; }; 3477C3101CC04C790028B84B /* Css.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3477C30E1CC04C790028B84B /* Css.cpp */; }; 3477C3111CC04C790028B84B /* CssTools.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3477C30F1CC04C790028B84B /* CssTools.cpp */; }; 3477C3131CC04CB10028B84B /* Rect.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3477C3121CC04CB10028B84B /* Rect.cpp */; }; 3477C3161CC04CCB0028B84B /* Library.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3477C3151CC04CCB0028B84B /* Library.cpp */; }; 3477C3191CC04CF10028B84B /* ThreadCommon.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3477C3181CC04CF10028B84B /* ThreadCommon.cpp */; }; 3477C31D1CC04D3F0028B84B /* Net.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3477C31B1CC04D3F0028B84B /* Net.cpp */; }; 3477C3201CC04D630028B84B /* ThreadEvent.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3477C31F1CC04D630028B84B /* ThreadEvent.cpp */; }; 3477C3221CC04DA00028B84B /* Colour.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3477C3211CC04DA00028B84B /* Colour.cpp */; }; 3477C3241CC04DD70028B84B /* Object.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3477C3231CC04DD70028B84B /* Object.cpp */; }; 3477C3261CC04DE20028B84B /* Mutex.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3477C3251CC04DE20028B84B /* Mutex.cpp */; }; 3477C3281CC04E020028B84B /* Variant.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3477C3271CC04E020028B84B /* Variant.cpp */; }; 3477C3311CC04E120028B84B /* 8Bit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3477C3291CC04E120028B84B /* 8Bit.cpp */; }; 3477C3321CC04E120028B84B /* 64Bit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3477C32A1CC04E120028B84B /* 64Bit.cpp */; }; 3477C3331CC04E120028B84B /* 16Bit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3477C32B1CC04E120028B84B /* 16Bit.cpp */; }; 3477C3341CC04E120028B84B /* 24Bit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3477C32C1CC04E120028B84B /* 24Bit.cpp */; }; 3477C3351CC04E120028B84B /* 32Bit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3477C32D1CC04E120028B84B /* 32Bit.cpp */; }; 3477C3361CC04E120028B84B /* 48Bit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3477C32E1CC04E120028B84B /* 48Bit.cpp */; }; 3477C3381CC04E120028B84B /* Alpha.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3477C3301CC04E120028B84B /* Alpha.cpp */; }; 3477C33A1CC04ED90028B84B /* DragAndDrop.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3477C3391CC04ED90028B84B /* DragAndDrop.mm */; }; 3477C33D1CC088590028B84B /* FileCommon.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3477C33C1CC088590028B84B /* FileCommon.cpp */; }; 3477C33F1CC0A6780028B84B /* Token.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3477C33E1CC0A6780028B84B /* Token.cpp */; }; 3477C3411CC0A68E0028B84B /* DateTime.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3477C3401CC0A68E0028B84B /* DateTime.cpp */; }; 3477C3431CC0A6B90028B84B /* DocView.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3477C3421CC0A6B90028B84B /* DocView.cpp */; }; 3477C3451CC0A9DE0028B84B /* Input.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3477C3441CC0A9DE0028B84B /* Input.cpp */; }; 3477C3471CC0AA220028B84B /* WindowCommon.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3477C3461CC0AA220028B84B /* WindowCommon.cpp */; }; 3477C3491CC0AA7E0028B84B /* ProgressDlg.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3477C3481CC0AA7E0028B84B /* ProgressDlg.cpp */; }; 3477C34B1CC0AB0B0028B84B /* ToolTip.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3477C34A1CC0AB0B0028B84B /* ToolTip.cpp */; }; 3477C34D1CC0ACED0028B84B /* md5.c in Sources */ = {isa = PBXBuildFile; fileRef = 3477C34C1CC0ACED0028B84B /* md5.c */; }; 3477C3501CC0C3D60028B84B /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3477C34F1CC0C3D60028B84B /* Cocoa.framework */; }; 3477C35B1CC1AEEA0028B84B /* LgiInc.h in Headers */ = {isa = PBXBuildFile; fileRef = 3477C35A1CC1AEEA0028B84B /* LgiInc.h */; }; 3477C35D1CC2253E0028B84B /* LgiInterfaces.h in Headers */ = {isa = PBXBuildFile; fileRef = 3477C35C1CC2253E0028B84B /* LgiInterfaces.h */; }; 3477C43A1CC234750028B84B /* MemStream.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3477C4391CC234750028B84B /* MemStream.cpp */; }; 3477C46D1CC26DEB0028B84B /* Alert.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3477C46C1CC26DEB0028B84B /* Alert.cpp */; }; 3477C4ED1CC27F1C0028B84B /* TrayIcon.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3477C4EC1CC27F1C0028B84B /* TrayIcon.cpp */; }; 3477C5171CC303B70028B84B /* LgiUiBase.h in Headers */ = {isa = PBXBuildFile; fileRef = 3477C5161CC303B70028B84B /* LgiUiBase.h */; }; 347AFB9827A0DC5E00BE4ABE /* 15Bit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 347AFB9727A0DC5E00BE4ABE /* 15Bit.cpp */; }; 348362421F233A77003B2A6D /* Stream.h in Headers */ = {isa = PBXBuildFile; fileRef = 348362411F233A77003B2A6D /* Stream.h */; }; 34837C4925C677ED00D103B4 /* AppPriv.h in Headers */ = {isa = PBXBuildFile; fileRef = 34837C4825C677ED00D103B4 /* AppPriv.h */; }; 348CD46A25C63E56001AA32B /* AppCommon.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 348CD46925C63E56001AA32B /* AppCommon.cpp */; }; 348E99CE2876850F0067E92A /* ObjCWrapper.h in Headers */ = {isa = PBXBuildFile; fileRef = 348E99CD2876850F0067E92A /* ObjCWrapper.h */; }; 3491A28523678DB800604C29 /* DropFiles.h in Headers */ = {isa = PBXBuildFile; fileRef = 3491A28423678DB700604C29 /* DropFiles.h */; }; + 349F939E2B92CBAD002410A2 /* TextConvert.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 349F939D2B92CBAD002410A2 /* TextConvert.cpp */; }; 34A3ACE820568D5800B1D62A /* StringLayout.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 34A3ACE720568D5800B1D62A /* StringLayout.cpp */; }; 34A3ACEA20568D9700B1D62A /* MenuCommon.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 34A3ACE920568D9700B1D62A /* MenuCommon.cpp */; }; 34B14E3426951C6E004C22CC /* Slider.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 34B14E3326951C6E004C22CC /* Slider.cpp */; }; 34B531D822F65734002B50F7 /* Menu.h in Headers */ = {isa = PBXBuildFile; fileRef = 34B531D722F65734002B50F7 /* Menu.h */; }; 34B6B19123CFD0F100C24906 /* DragAndDropCommon.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 34B6B19023CFD0F100C24906 /* DragAndDropCommon.cpp */; }; 34BF15072871503B001B4CA5 /* DrawListSurface.h in Headers */ = {isa = PBXBuildFile; fileRef = 34BF15062871503B001B4CA5 /* DrawListSurface.h */; }; 34C072C12369878100E1E222 /* DropFiles.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 34C072C02369878100E1E222 /* DropFiles.cpp */; }; 34C81A5A25DA0AA10053F93A /* Base64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 34C81A5925DA0AA10053F93A /* Base64.cpp */; }; 34CD9F7223D5768B0039F259 /* Unicode.h in Headers */ = {isa = PBXBuildFile; fileRef = 34CD9F7123D5768B0039F259 /* Unicode.h */; }; 34D1B3C121748A2800BC6B58 /* Path.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 34D1B3C021748A2800BC6B58 /* Path.cpp */; }; 34D21CEC217981AE003D1EA6 /* EventTargetThread.h in Headers */ = {isa = PBXBuildFile; fileRef = 34D21CEB217981AE003D1EA6 /* EventTargetThread.h */; }; 34D28926275B3D9F005961A8 /* DateTime.h in Headers */ = {isa = PBXBuildFile; fileRef = 34D28925275B3D9F005961A8 /* DateTime.h */; }; 34D28929275B3DB0005961A8 /* Variant.h in Headers */ = {isa = PBXBuildFile; fileRef = 34D28928275B3DB0005961A8 /* Variant.h */; }; 34D6AA1722D3749A005D739E /* Gdc2.h in Headers */ = {isa = PBXBuildFile; fileRef = 34D6AA1622D3749A005D739E /* Gdc2.h */; }; 34EE8BB12748A55600F12915 /* TextFile.h in Headers */ = {isa = PBXBuildFile; fileRef = 34EE8BB02748A55600F12915 /* TextFile.h */; }; 34F3EC4329BD374E00BB9B58 /* Rect.h in Headers */ = {isa = PBXBuildFile; fileRef = 34F3EC4229BD374E00BB9B58 /* Rect.h */; }; 34F6151F27E55C5D00FE5B0C /* Thread.h in Headers */ = {isa = PBXBuildFile; fileRef = 34F6151D27E55C5D00FE5B0C /* Thread.h */; }; 34F6152027E55C5D00FE5B0C /* ThreadEvent.h in Headers */ = {isa = PBXBuildFile; fileRef = 34F6151E27E55C5D00FE5B0C /* ThreadEvent.h */; }; 34F6153E27E5628600FE5B0C /* Token.h in Headers */ = {isa = PBXBuildFile; fileRef = 34F6153D27E5628600FE5B0C /* Token.h */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ 340833122698F8FE0028012F /* FontPriv.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FontPriv.h; path = ../../../private/common/FontPriv.h; sourceTree = SOURCE_ROOT; }; 340833132698F8FE0028012F /* FontType.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = FontType.cpp; path = ../../common/Gdc2/Font/FontType.cpp; sourceTree = SOURCE_ROOT; }; 340833142698F8FE0028012F /* TypeFace.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = TypeFace.cpp; path = ../../common/Gdc2/Font/TypeFace.cpp; sourceTree = SOURCE_ROOT; }; 3409C0C727580ED60050302A /* ListItemRadioBtn.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ListItemRadioBtn.h; path = ../../../include/lgi/common/ListItemRadioBtn.h; sourceTree = SOURCE_ROOT; }; 3409C0C827580ED60050302A /* List.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = List.h; path = ../../../include/lgi/common/List.h; sourceTree = SOURCE_ROOT; }; 3409C0C927580ED60050302A /* ListItemCheckBox.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ListItemCheckBox.h; path = ../../../include/lgi/common/ListItemCheckBox.h; sourceTree = SOURCE_ROOT; }; 3409C0CF275812CB0050302A /* Profile.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = Profile.h; path = ../../../include/lgi/common/Profile.h; sourceTree = ""; }; 34109853217A975E007020CE /* View.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = View.h; path = ../../../include/lgi/common/View.h; sourceTree = SOURCE_ROOT; }; 34109856217A9805007020CE /* LCocoaView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LCocoaView.h; path = ../../../include/lgi/mac/cocoa/LCocoaView.h; sourceTree = SOURCE_ROOT; }; 34114C8A23091E6E00F342B1 /* LgiClasses.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LgiClasses.h; path = ../../../include/lgi/common/LgiClasses.h; sourceTree = ""; }; 3413170D23068FFE008CE982 /* FileSelect.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = FileSelect.cpp; path = ../../common/Lgi/FileSelect.cpp; sourceTree = SOURCE_ROOT; }; 3413170F230694D1008CE982 /* Gel.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Gel.cpp; path = ../../common/Skins/Gel/Gel.cpp; sourceTree = SOURCE_ROOT; }; 3415168E26EC32E7007EE35F /* TextView4.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = TextView4.cpp; path = ../../common/Text/TextView4.cpp; sourceTree = SOURCE_ROOT; }; 3415169126EC32FF007EE35F /* TextView3.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TextView3.h; path = ../../../include/lgi/common/TextView3.h; sourceTree = SOURCE_ROOT; }; 3415169226EC32FF007EE35F /* TextView4.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TextView4.h; path = ../../../include/lgi/common/TextView4.h; sourceTree = SOURCE_ROOT; }; 3418EBCE25D9FEA600EA168A /* Progress.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Progress.h; path = ../../../include/lgi/common/Progress.h; sourceTree = ""; }; 3418EBD025D9FEF500EA168A /* ProgressDlg.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ProgressDlg.h; path = ../../../include/lgi/common/ProgressDlg.h; sourceTree = ""; }; 3427064627A0E20C0043F733 /* CocoaView.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = CocoaView.mm; sourceTree = SOURCE_ROOT; }; 343874B1230F46B200CF96B4 /* Window.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Window.h; path = ../../../include/lgi/common/Window.h; sourceTree = SOURCE_ROOT; }; 343874B2230F46B200CF96B4 /* View.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = View.h; path = ../../../include/lgi/common/View.h; sourceTree = ""; }; 343874B3230F46B200CF96B4 /* App.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = App.h; path = ../../../include/lgi/common/App.h; sourceTree = SOURCE_ROOT; }; 343874B4230F46B200CF96B4 /* Layout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Layout.h; path = ../../../include/lgi/common/Layout.h; sourceTree = ""; }; 343BE8C62355D0CE00742E21 /* PopupList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PopupList.h; path = ../../../include/lgi/common/PopupList.h; sourceTree = ""; }; 3449B8CD26BBF6B10022A9B8 /* Notifications.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Notifications.h; path = ../../../include/lgi/common/Notifications.h; sourceTree = ""; }; 344CFBC7237365B800AE6B35 /* ClipBoard.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ClipBoard.h; path = ../../../include/lgi/common/ClipBoard.h; sourceTree = ""; }; 345920FC1F25649000098DFD /* Font.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Font.h; path = ../../../include/lgi/common/Font.h; sourceTree = SOURCE_ROOT; }; 345920FD1F25649000098DFD /* FontCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FontCache.h; path = ../../../include/lgi/common/FontCache.h; sourceTree = SOURCE_ROOT; }; 345920FE1F25649000098DFD /* FontSelect.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FontSelect.h; path = ../../../include/lgi/common/FontSelect.h; sourceTree = SOURCE_ROOT; }; 345EB84D235C6C01007D05DB /* Popup.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Popup.h; path = ../../../include/lgi/common/Popup.h; sourceTree = SOURCE_ROOT; }; 346DDEC0240C882900751380 /* RadioGroup.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RadioGroup.h; path = ../../../include/lgi/common/RadioGroup.h; sourceTree = SOURCE_ROOT; }; 346FACE61D3C720B00FFEBCE /* Message.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Message.h; path = ../../../include/lgi/common/Message.h; sourceTree = ""; }; 346FACE91D3C75AE00FFEBCE /* Uri.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Uri.cpp; path = ../../common/Net/Uri.cpp; sourceTree = SOURCE_ROOT; }; 346FACEA1D3C75AE00FFEBCE /* MDStringToDigest.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MDStringToDigest.cpp; path = ../../common/Net/MDStringToDigest.cpp; sourceTree = SOURCE_ROOT; }; 3471868723A9AF8900F8C952 /* SubProcess.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp; fileEncoding = 4; name = SubProcess.cpp; path = ../../posix/SubProcess.cpp; sourceTree = SOURCE_ROOT; }; 3477C2681CBF020F0028B84B /* LgiCocoa.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = LgiCocoa.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 3477C26D1CBF020F0028B84B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = SOURCE_ROOT; }; 3477C2741CBF07DD0028B84B /* App.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = App.mm; sourceTree = SOURCE_ROOT; }; 3477C2761CBF08050028B84B /* ClipBoard.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ClipBoard.mm; sourceTree = SOURCE_ROOT; }; 3477C27A1CBF086A0028B84B /* General.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = General.mm; sourceTree = SOURCE_ROOT; }; 3477C27B1CBF086A0028B84B /* Layout.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = Layout.mm; sourceTree = SOURCE_ROOT; }; 3477C27C1CBF086A0028B84B /* Menu.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = Menu.mm; sourceTree = SOURCE_ROOT; }; 3477C27D1CBF086A0028B84B /* Printer.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = Printer.mm; sourceTree = SOURCE_ROOT; }; 3477C27E1CBF086A0028B84B /* Thread.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = Thread.mm; sourceTree = SOURCE_ROOT; }; 3477C27F1CBF086A0028B84B /* View.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = View.mm; sourceTree = SOURCE_ROOT; }; 3477C2801CBF086A0028B84B /* Widgets.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = Widgets.mm; sourceTree = SOURCE_ROOT; }; 3477C2811CBF086A0028B84B /* Window.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = Window.mm; sourceTree = SOURCE_ROOT; }; 3477C28B1CBF08C10028B84B /* Gdc2.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = Gdc2.mm; sourceTree = SOURCE_ROOT; }; 3477C28C1CBF08C10028B84B /* MemDC.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp; fileEncoding = 4; name = MemDC.cpp; path = ../common/MemDC.cpp; sourceTree = SOURCE_ROOT; }; 3477C28D1CBF08C10028B84B /* PrintDC.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp; fileEncoding = 4; name = PrintDC.cpp; path = ../common/PrintDC.cpp; sourceTree = SOURCE_ROOT; }; 3477C28E1CBF08C10028B84B /* ScreenDC.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp; fileEncoding = 4; name = ScreenDC.cpp; path = ../common/ScreenDC.cpp; sourceTree = SOURCE_ROOT; }; 3477C2941CBF09320028B84B /* File.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = File.mm; sourceTree = SOURCE_ROOT; }; 3477C2951CBF09320028B84B /* Mem.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = Mem.mm; sourceTree = SOURCE_ROOT; }; 3477C2961CBF09320028B84B /* ShowFileProp_Mac.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ShowFileProp_Mac.mm; sourceTree = SOURCE_ROOT; }; 3477C29B1CBF0A920028B84B /* Mem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Mem.h; path = ../../../include/lgi/common/Mem.h; sourceTree = ""; }; 3477C29C1CBF0A920028B84B /* SymLookup.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SymLookup.h; path = ../../../include/lgi/mac/cocoa/SymLookup.h; sourceTree = ""; }; 3477C29D1CBF0A920028B84B /* LgiMac.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LgiMac.h; path = ../../../include/lgi/mac/cocoa/LgiMac.h; sourceTree = ""; }; 3477C29E1CBF0A920028B84B /* LgiOs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LgiOs.h; path = ../../../include/lgi/mac/cocoa/LgiOs.h; sourceTree = ""; }; 3477C29F1CBF0A920028B84B /* LgiOsClasses.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LgiOsClasses.h; path = ../../../include/lgi/mac/cocoa/LgiOsClasses.h; sourceTree = ""; }; 3477C2A01CBF0A920028B84B /* LgiOsDefs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LgiOsDefs.h; path = ../../../include/lgi/mac/cocoa/LgiOsDefs.h; sourceTree = ""; }; 3477C2A71CBF0AAF0028B84B /* Lgi.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Lgi.h; path = ../../../include/lgi/common/Lgi.h; sourceTree = ""; }; 3477C2A91CBF22A00028B84B /* File.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = File.h; path = ../../../include/lgi/common/File.h; sourceTree = SOURCE_ROOT; }; 3477C2AB1CBF72170028B84B /* ViewPriv.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ViewPriv.h; path = ../../../private/common/ViewPriv.h; sourceTree = SOURCE_ROOT; }; 3477C2AE1CBF96B90028B84B /* LgiRes.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = LgiRes.cpp; path = ../../common/Resource/LgiRes.cpp; sourceTree = SOURCE_ROOT; }; 3477C2B01CBF96C40028B84B /* Res.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Res.cpp; path = ../../common/Resource/Res.cpp; sourceTree = SOURCE_ROOT; }; 3477C2B31CBF96F20028B84B /* LMsg.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp; fileEncoding = 4; name = LMsg.cpp; path = ../../common/Lgi/LMsg.cpp; sourceTree = SOURCE_ROOT; }; 3477C2B61CBF973F0028B84B /* GuiUtils.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp; fileEncoding = 4; name = GuiUtils.cpp; path = ../../common/Lgi/GuiUtils.cpp; sourceTree = SOURCE_ROOT; }; 3477C2B81CBF977F0028B84B /* LgiCommon.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp; fileEncoding = 4; name = LgiCommon.cpp; path = ../../common/Lgi/LgiCommon.cpp; sourceTree = SOURCE_ROOT; }; 3477C2BA1CBF97C80028B84B /* GdcCommon.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp; fileEncoding = 4; name = GdcCommon.cpp; path = ../../common/Gdc2/GdcCommon.cpp; sourceTree = SOURCE_ROOT; }; 3477C2C01CBF9A040028B84B /* String.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = String.cpp; path = ../../common/Text/String.cpp; sourceTree = SOURCE_ROOT; }; 3477C2C41CC046310028B84B /* TextView3.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = TextView3.cpp; path = ../../common/Text/TextView3.cpp; sourceTree = SOURCE_ROOT; }; 3477C2C51CC046310028B84B /* Unicode.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Unicode.cpp; path = ../../common/Text/Unicode.cpp; sourceTree = SOURCE_ROOT; }; 3477C2C61CC046310028B84B /* Utf8.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Utf8.cpp; path = ../../common/Text/Utf8.cpp; sourceTree = SOURCE_ROOT; }; 3477C2C71CC046310028B84B /* XmlTree.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = XmlTree.cpp; path = ../../common/Text/XmlTree.cpp; sourceTree = SOURCE_ROOT; }; 3477C2CD1CC047BE0028B84B /* Charset.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Charset.cpp; path = ../../common/Gdc2/Font/Charset.cpp; sourceTree = SOURCE_ROOT; }; 3477C2CF1CC047F50028B84B /* ToolBar.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ToolBar.cpp; path = ../../common/Widgets/ToolBar.cpp; sourceTree = SOURCE_ROOT; }; 3477C2D21CC048100028B84B /* Containers.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Containers.cpp; path = ../../common/General/Containers.cpp; sourceTree = SOURCE_ROOT; }; 3477C2D41CC0481B0028B84B /* Array.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Array.h; path = ../../../include/lgi/common/Array.h; sourceTree = SOURCE_ROOT; }; 3477C2D61CC048390028B84B /* Popup.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp.preprocessed; fileEncoding = 4; name = Popup.cpp; path = ../../common/Widgets/Popup.cpp; sourceTree = SOURCE_ROOT; }; 3477C2D81CC048F90028B84B /* DisplayString.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DisplayString.cpp; path = ../../common/Gdc2/Font/DisplayString.cpp; sourceTree = SOURCE_ROOT; }; 3477C2D91CC048F90028B84B /* Font.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Font.cpp; path = ../../common/Gdc2/Font/Font.cpp; sourceTree = SOURCE_ROOT; }; 3477C2DA1CC048F90028B84B /* FontSystem.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp; fileEncoding = 4; name = FontSystem.cpp; path = ../../common/Gdc2/Font/FontSystem.cpp; sourceTree = SOURCE_ROOT; }; 3477C2DE1CC04A030028B84B /* ScrollBar.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ScrollBar.cpp; path = ../../common/Widgets/ScrollBar.cpp; sourceTree = SOURCE_ROOT; }; 3477C2E01CC04A3C0028B84B /* FontSelect.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = FontSelect.cpp; path = ../../common/Lgi/FontSelect.cpp; sourceTree = SOURCE_ROOT; }; 3477C2E21CC04A5C0028B84B /* Stream.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Stream.cpp; path = ../../common/Lgi/Stream.cpp; sourceTree = SOURCE_ROOT; }; 3477C2E41CC04A7F0028B84B /* ViewCommon.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp; fileEncoding = 4; name = ViewCommon.cpp; path = ../../common/Lgi/ViewCommon.cpp; sourceTree = SOURCE_ROOT; }; 3477C2E61CC04B1E0028B84B /* Surface.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Surface.cpp; path = ../../common/Gdc2/Surface.cpp; sourceTree = SOURCE_ROOT; }; 3477C2E81CC04B5D0028B84B /* Rand.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Rand.cpp; path = ../../common/Lgi/Rand.cpp; sourceTree = SOURCE_ROOT; }; 3477C2EA1CC04B890028B84B /* Filter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Filter.cpp; path = ../../common/Gdc2/Filters/Filter.cpp; sourceTree = SOURCE_ROOT; }; 3477C2EC1CC04BBB0028B84B /* ItemContainer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ItemContainer.cpp; path = ../../common/Widgets/ItemContainer.cpp; sourceTree = SOURCE_ROOT; }; 3477C2ED1CC04BBB0028B84B /* List.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = List.cpp; path = ../../common/Widgets/List.cpp; sourceTree = SOURCE_ROOT; }; 3477C2EE1CC04BBB0028B84B /* Tree.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Tree.cpp; path = ../../common/Widgets/Tree.cpp; sourceTree = SOURCE_ROOT; }; 3477C2F21CC04BF00028B84B /* Bitmap.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Bitmap.cpp; path = ../../common/Widgets/Bitmap.cpp; sourceTree = SOURCE_ROOT; }; 3477C2F31CC04BF00028B84B /* Box.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Box.cpp; path = ../../common/Widgets/Box.cpp; sourceTree = SOURCE_ROOT; }; 3477C2F41CC04BF00028B84B /* Button.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Button.cpp; path = ../../common/Widgets/Button.cpp; sourceTree = SOURCE_ROOT; }; 3477C2F51CC04BF00028B84B /* CheckBox.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CheckBox.cpp; path = ../../common/Widgets/CheckBox.cpp; sourceTree = SOURCE_ROOT; }; 3477C2F61CC04BF00028B84B /* Combo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Combo.cpp; path = ../../common/Widgets/Combo.cpp; sourceTree = SOURCE_ROOT; }; 3477C2F71CC04BF00028B84B /* Edit.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Edit.cpp; path = ../../common/Widgets/Edit.cpp; sourceTree = SOURCE_ROOT; }; 3477C2F81CC04BF00028B84B /* Panel.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Panel.cpp; path = ../../common/Widgets/Panel.cpp; sourceTree = SOURCE_ROOT; }; 3477C2F91CC04BF00028B84B /* Progress.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Progress.cpp; path = ../../common/Widgets/Progress.cpp; sourceTree = SOURCE_ROOT; }; 3477C2FA1CC04BF00028B84B /* RadioGroup.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = RadioGroup.cpp; path = ../../common/Widgets/RadioGroup.cpp; sourceTree = SOURCE_ROOT; }; 3477C2FB1CC04BF00028B84B /* Splitter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Splitter.cpp; path = ../../common/Widgets/Splitter.cpp; sourceTree = SOURCE_ROOT; }; 3477C2FC1CC04BF00028B84B /* TableLayout.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = TableLayout.cpp; path = ../../common/Widgets/TableLayout.cpp; sourceTree = SOURCE_ROOT; }; 3477C2FD1CC04BF00028B84B /* TabView.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = TabView.cpp; path = ../../common/Widgets/TabView.cpp; sourceTree = SOURCE_ROOT; }; 3477C2FE1CC04BF00028B84B /* TextLabel.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = TextLabel.cpp; path = ../../common/Widgets/TextLabel.cpp; sourceTree = SOURCE_ROOT; }; 3477C30C1CC04C560028B84B /* FindReplace.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = FindReplace.cpp; path = ../../common/Lgi/FindReplace.cpp; sourceTree = SOURCE_ROOT; }; 3477C30E1CC04C790028B84B /* Css.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Css.cpp; path = ../../common/Lgi/Css.cpp; sourceTree = SOURCE_ROOT; }; 3477C30F1CC04C790028B84B /* CssTools.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CssTools.cpp; path = ../../common/Lgi/CssTools.cpp; sourceTree = SOURCE_ROOT; }; 3477C3121CC04CB10028B84B /* Rect.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Rect.cpp; path = ../../common/Gdc2/Rect.cpp; sourceTree = SOURCE_ROOT; }; 3477C3151CC04CCB0028B84B /* Library.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Library.cpp; path = ../../common/Lgi/Library.cpp; sourceTree = SOURCE_ROOT; }; 3477C3181CC04CF10028B84B /* ThreadCommon.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ThreadCommon.cpp; path = ../../common/Lgi/ThreadCommon.cpp; sourceTree = SOURCE_ROOT; }; 3477C31B1CC04D3F0028B84B /* Net.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Net.cpp; path = ../../common/Net/Net.cpp; sourceTree = SOURCE_ROOT; }; 3477C31F1CC04D630028B84B /* ThreadEvent.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ThreadEvent.cpp; path = ../../common/Lgi/ThreadEvent.cpp; sourceTree = SOURCE_ROOT; }; 3477C3211CC04DA00028B84B /* Colour.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Colour.cpp; path = ../../common/Gdc2/Colour.cpp; sourceTree = SOURCE_ROOT; }; 3477C3231CC04DD70028B84B /* Object.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Object.cpp; path = ../../common/Lgi/Object.cpp; sourceTree = SOURCE_ROOT; }; 3477C3251CC04DE20028B84B /* Mutex.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Mutex.cpp; path = ../../common/Lgi/Mutex.cpp; sourceTree = SOURCE_ROOT; }; 3477C3271CC04E020028B84B /* Variant.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Variant.cpp; path = ../../common/Lgi/Variant.cpp; sourceTree = SOURCE_ROOT; }; 3477C3291CC04E120028B84B /* 8Bit.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = 8Bit.cpp; path = ../../common/Gdc2/8Bit.cpp; sourceTree = SOURCE_ROOT; }; 3477C32A1CC04E120028B84B /* 64Bit.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = 64Bit.cpp; path = ../../common/Gdc2/64Bit.cpp; sourceTree = SOURCE_ROOT; }; 3477C32B1CC04E120028B84B /* 16Bit.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = 16Bit.cpp; path = ../../common/Gdc2/16Bit.cpp; sourceTree = SOURCE_ROOT; }; 3477C32C1CC04E120028B84B /* 24Bit.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = 24Bit.cpp; path = ../../common/Gdc2/24Bit.cpp; sourceTree = SOURCE_ROOT; }; 3477C32D1CC04E120028B84B /* 32Bit.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = 32Bit.cpp; path = ../../common/Gdc2/32Bit.cpp; sourceTree = SOURCE_ROOT; }; 3477C32E1CC04E120028B84B /* 48Bit.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = 48Bit.cpp; path = ../../common/Gdc2/48Bit.cpp; sourceTree = SOURCE_ROOT; }; 3477C3301CC04E120028B84B /* Alpha.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Alpha.cpp; path = ../../common/Gdc2/Alpha.cpp; sourceTree = SOURCE_ROOT; }; 3477C3391CC04ED90028B84B /* DragAndDrop.mm */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp; fileEncoding = 4; path = DragAndDrop.mm; sourceTree = SOURCE_ROOT; }; 3477C33C1CC088590028B84B /* FileCommon.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = FileCommon.cpp; path = ../../common/General/FileCommon.cpp; sourceTree = SOURCE_ROOT; }; 3477C33E1CC0A6780028B84B /* Token.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Token.cpp; path = ../../common/Text/Token.cpp; sourceTree = SOURCE_ROOT; }; 3477C3401CC0A68E0028B84B /* DateTime.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp; fileEncoding = 4; name = DateTime.cpp; path = ../../common/General/DateTime.cpp; sourceTree = SOURCE_ROOT; }; 3477C3421CC0A6B90028B84B /* DocView.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DocView.cpp; path = ../../common/Text/DocView.cpp; sourceTree = SOURCE_ROOT; }; 3477C3441CC0A9DE0028B84B /* Input.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Input.cpp; path = ../../common/Lgi/Input.cpp; sourceTree = SOURCE_ROOT; }; 3477C3461CC0AA220028B84B /* WindowCommon.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp; fileEncoding = 4; name = WindowCommon.cpp; path = ../../common/Lgi/WindowCommon.cpp; sourceTree = SOURCE_ROOT; }; 3477C3481CC0AA7E0028B84B /* ProgressDlg.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ProgressDlg.cpp; path = ../../common/Widgets/ProgressDlg.cpp; sourceTree = SOURCE_ROOT; }; 3477C34A1CC0AB0B0028B84B /* ToolTip.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ToolTip.cpp; path = ../../common/Lgi/ToolTip.cpp; sourceTree = SOURCE_ROOT; }; 3477C34C1CC0ACED0028B84B /* md5.c */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = md5.c; path = ../../common/Hash/md5/md5.c; sourceTree = SOURCE_ROOT; }; 3477C34F1CC0C3D60028B84B /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = ../../../../../../../../System/Library/Frameworks/Cocoa.framework; sourceTree = ""; }; 3477C35A1CC1AEEA0028B84B /* LgiInc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LgiInc.h; path = ../../../include/lgi/common/LgiInc.h; sourceTree = ""; }; 3477C35C1CC2253E0028B84B /* LgiInterfaces.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LgiInterfaces.h; path = ../../../include/lgi/common/LgiInterfaces.h; sourceTree = ""; }; 3477C4391CC234750028B84B /* MemStream.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MemStream.cpp; path = ../../common/Lgi/MemStream.cpp; sourceTree = SOURCE_ROOT; }; 3477C46C1CC26DEB0028B84B /* Alert.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Alert.cpp; path = ../../common/Lgi/Alert.cpp; sourceTree = SOURCE_ROOT; }; 3477C4EC1CC27F1C0028B84B /* TrayIcon.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp; fileEncoding = 4; name = TrayIcon.cpp; path = ../../common/Lgi/TrayIcon.cpp; sourceTree = SOURCE_ROOT; }; 3477C5161CC303B70028B84B /* LgiUiBase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LgiUiBase.h; path = ../../../include/lgi/common/LgiUiBase.h; sourceTree = ""; }; 347AFB9727A0DC5E00BE4ABE /* 15Bit.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = 15Bit.cpp; path = ../../common/Gdc2/15Bit.cpp; sourceTree = SOURCE_ROOT; }; 348362411F233A77003B2A6D /* Stream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Stream.h; path = ../../../include/lgi/common/Stream.h; sourceTree = SOURCE_ROOT; }; 34837C4825C677ED00D103B4 /* AppPriv.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppPriv.h; path = ../../../private/mac/AppPriv.h; sourceTree = SOURCE_ROOT; }; 348CD46925C63E56001AA32B /* AppCommon.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp; fileEncoding = 4; name = AppCommon.cpp; path = ../../common/Lgi/AppCommon.cpp; sourceTree = SOURCE_ROOT; }; 348E99CD2876850F0067E92A /* ObjCWrapper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ObjCWrapper.h; path = ../../../include/lgi/mac/cocoa/ObjCWrapper.h; sourceTree = ""; }; 3491A28423678DB700604C29 /* DropFiles.h */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp; fileEncoding = 4; name = DropFiles.h; path = ../../../include/lgi/common/DropFiles.h; sourceTree = SOURCE_ROOT; }; + 349F939D2B92CBAD002410A2 /* TextConvert.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = TextConvert.cpp; path = ../../../common/Text/TextConvert.cpp; sourceTree = ""; }; 34A3ACE720568D5800B1D62A /* StringLayout.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = StringLayout.cpp; path = ../../common/Gdc2/Font/StringLayout.cpp; sourceTree = SOURCE_ROOT; }; 34A3ACE920568D9700B1D62A /* MenuCommon.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MenuCommon.cpp; path = ../../common/Lgi/MenuCommon.cpp; sourceTree = SOURCE_ROOT; }; 34B14E3326951C6E004C22CC /* Slider.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Slider.cpp; path = ../../common/Widgets/Slider.cpp; sourceTree = SOURCE_ROOT; }; 34B531D722F65734002B50F7 /* Menu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Menu.h; path = ../../../include/lgi/common/Menu.h; sourceTree = ""; }; 34B6B19023CFD0F100C24906 /* DragAndDropCommon.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DragAndDropCommon.cpp; path = ../../common/Lgi/DragAndDropCommon.cpp; sourceTree = SOURCE_ROOT; }; 34BF15062871503B001B4CA5 /* DrawListSurface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DrawListSurface.h; path = ../../../../../lgi/trunk/include/lgi/common/DrawListSurface.h; sourceTree = ""; }; 34C072C02369878100E1E222 /* DropFiles.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp; fileEncoding = 4; name = DropFiles.cpp; path = ../../common/Lgi/DropFiles.cpp; sourceTree = SOURCE_ROOT; }; 34C81A5925DA0AA10053F93A /* Base64.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Base64.cpp; path = ../../common/Net/Base64.cpp; sourceTree = SOURCE_ROOT; }; 34CD9F7123D5768B0039F259 /* Unicode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Unicode.h; path = ../../../include/lgi/common/Unicode.h; sourceTree = SOURCE_ROOT; }; 34D1B3C021748A2800BC6B58 /* Path.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Path.cpp; path = ../../common/Gdc2/Path/Path.cpp; sourceTree = SOURCE_ROOT; }; 34D21CEB217981AE003D1EA6 /* EventTargetThread.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = EventTargetThread.h; path = ../../../include/lgi/common/EventTargetThread.h; sourceTree = SOURCE_ROOT; }; 34D28925275B3D9F005961A8 /* DateTime.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DateTime.h; path = ../../../include/lgi/common/DateTime.h; sourceTree = SOURCE_ROOT; }; 34D28928275B3DB0005961A8 /* Variant.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Variant.h; path = ../../../include/lgi/common/Variant.h; sourceTree = SOURCE_ROOT; }; 34D6AA1622D3749A005D739E /* Gdc2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Gdc2.h; path = ../../../include/lgi/common/Gdc2.h; sourceTree = SOURCE_ROOT; }; 34EE8BB02748A55600F12915 /* TextFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TextFile.h; path = ../../../include/lgi/common/TextFile.h; sourceTree = ""; }; 34F3EC4229BD374E00BB9B58 /* Rect.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Rect.h; path = ../../../include/lgi/common/Rect.h; sourceTree = SOURCE_ROOT; }; 34F6151D27E55C5D00FE5B0C /* Thread.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Thread.h; path = ../../../../../lgi/trunk/include/lgi/common/Thread.h; sourceTree = ""; }; 34F6151E27E55C5D00FE5B0C /* ThreadEvent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ThreadEvent.h; path = ../../../../../lgi/trunk/include/lgi/common/ThreadEvent.h; sourceTree = ""; }; 34F6153D27E5628600FE5B0C /* Token.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Token.h; path = ../../../../../lgi/trunk/include/lgi/common/Token.h; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ 3477C2641CBF020F0028B84B /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 3477C3501CC0C3D60028B84B /* Cocoa.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ 3415168D26EC32C8007EE35F /* TextCtrls */ = { isa = PBXGroup; children = ( 3415169126EC32FF007EE35F /* TextView3.h */, 3415169226EC32FF007EE35F /* TextView4.h */, 3415168E26EC32E7007EE35F /* TextView4.cpp */, 3477C2C41CC046310028B84B /* TextView3.cpp */, ); name = TextCtrls; sourceTree = ""; }; 345BABCE2B78952D005795A0 /* Frameworks */ = { isa = PBXGroup; children = ( ); name = Frameworks; sourceTree = ""; }; 346FACE81D3C723700FFEBCE /* Resources */ = { isa = PBXGroup; children = ( 3477C26D1CBF020F0028B84B /* Info.plist */, ); name = Resources; sourceTree = ""; }; 3477C25E1CBF020F0028B84B = { isa = PBXGroup; children = ( 346FACE81D3C723700FFEBCE /* Resources */, 3477C26A1CBF020F0028B84B /* Classes */, 3477C29A1CBF0A700028B84B /* Headers */, 3477C34E1CC0C3A30028B84B /* Link */, 3477C2691CBF020F0028B84B /* Products */, 345BABCE2B78952D005795A0 /* Frameworks */, ); sourceTree = ""; }; 3477C2691CBF020F0028B84B /* Products */ = { isa = PBXGroup; children = ( 3477C2681CBF020F0028B84B /* LgiCocoa.framework */, ); name = Products; sourceTree = ""; }; 3477C26A1CBF020F0028B84B /* Classes */ = { isa = PBXGroup; children = ( 3477C2B21CBF96E60028B84B /* Dialogs */, 3477C33B1CC0881E0028B84B /* Files */, 3477C2931CBF09240028B84B /* General */, 3477C28A1CBF08760028B84B /* Graphics */, 3477C2731CBF07410028B84B /* Interface_Cocoa */, 3477C2B51CBF972B0028B84B /* Interface_XP */, 3477C3141CC04CC00028B84B /* Libraries */, 3477C2D11CC048000028B84B /* Memory */, 3477C31A1CC04D290028B84B /* Network */, 3477C4051CC22F920028B84B /* Process */, 3477C2AD1CBF96AA0028B84B /* Resources */, 3477C2BF1CBF99F00028B84B /* Text */, 3477C3171CC04CDE0028B84B /* Threads */, 3477C2CC1CC046370028B84B /* Widgets */, ); name = Classes; path = Lgi; sourceTree = SOURCE_ROOT; usesTabs = 1; }; 3477C2731CBF07410028B84B /* Interface_Cocoa */ = { isa = PBXGroup; children = ( 343874B3230F46B200CF96B4 /* App.h */, 3477C2741CBF07DD0028B84B /* App.mm */, 344CFBC7237365B800AE6B35 /* ClipBoard.h */, 3477C2761CBF08050028B84B /* ClipBoard.mm */, 3427064627A0E20C0043F733 /* CocoaView.mm */, 3477C3391CC04ED90028B84B /* DragAndDrop.mm */, 3477C27A1CBF086A0028B84B /* General.mm */, 343874B4230F46B200CF96B4 /* Layout.h */, 3477C27B1CBF086A0028B84B /* Layout.mm */, 34109856217A9805007020CE /* LCocoaView.h */, 3477C27C1CBF086A0028B84B /* Menu.mm */, 3477C27D1CBF086A0028B84B /* Printer.mm */, 343874B2230F46B200CF96B4 /* View.h */, 3477C27F1CBF086A0028B84B /* View.mm */, 3477C2AB1CBF72170028B84B /* ViewPriv.h */, 3477C2801CBF086A0028B84B /* Widgets.mm */, 343874B1230F46B200CF96B4 /* Window.h */, 3477C2811CBF086A0028B84B /* Window.mm */, 34109853217A975E007020CE /* View.h */, ); name = Interface_Cocoa; sourceTree = ""; }; 3477C28A1CBF08760028B84B /* Graphics */ = { isa = PBXGroup; children = ( 34F3EC4229BD374E00BB9B58 /* Rect.h */, 3413170F230694D1008CE982 /* Gel.cpp */, 34D6AA1622D3749A005D739E /* Gdc2.h */, 34D1B3C021748A2800BC6B58 /* Path.cpp */, 3477C3211CC04DA00028B84B /* Colour.cpp */, 3477C2BA1CBF97C80028B84B /* GdcCommon.cpp */, 3477C2EA1CC04B890028B84B /* Filter.cpp */, 3477C3121CC04CB10028B84B /* Rect.cpp */, 3477C28B1CBF08C10028B84B /* Gdc2.mm */, 3477C2BE1CBF97E20028B84B /* Applicators */, 3477C2BD1CBF97DA0028B84B /* Fonts */, 3477C2BC1CBF97D10028B84B /* Surfaces */, ); name = Graphics; sourceTree = ""; }; 3477C2931CBF09240028B84B /* General */ = { isa = PBXGroup; children = ( 34C81A5925DA0AA10053F93A /* Base64.cpp */, 3477C3401CC0A68E0028B84B /* DateTime.cpp */, 34D28925275B3D9F005961A8 /* DateTime.h */, 3477C3421CC0A6B90028B84B /* DocView.cpp */, 3477C2951CBF09320028B84B /* Mem.mm */, 3477C34C1CC0ACED0028B84B /* md5.c */, 3477C3231CC04DD70028B84B /* Object.cpp */, 3477C2E81CC04B5D0028B84B /* Rand.cpp */, 3477C2961CBF09320028B84B /* ShowFileProp_Mac.mm */, 3477C3271CC04E020028B84B /* Variant.cpp */, 34D28928275B3DB0005961A8 /* Variant.h */, ); name = General; sourceTree = ""; }; 3477C29A1CBF0A700028B84B /* Headers */ = { isa = PBXGroup; children = ( 348E99CD2876850F0067E92A /* ObjCWrapper.h */, 34BF15062871503B001B4CA5 /* DrawListSurface.h */, 3477C2A71CBF0AAF0028B84B /* Lgi.h */, 34114C8A23091E6E00F342B1 /* LgiClasses.h */, 3477C35A1CC1AEEA0028B84B /* LgiInc.h */, 3477C35C1CC2253E0028B84B /* LgiInterfaces.h */, 3477C29D1CBF0A920028B84B /* LgiMac.h */, 3477C29E1CBF0A920028B84B /* LgiOs.h */, 3477C29F1CBF0A920028B84B /* LgiOsClasses.h */, 3477C2A01CBF0A920028B84B /* LgiOsDefs.h */, 3477C5161CC303B70028B84B /* LgiUiBase.h */, 3477C29B1CBF0A920028B84B /* Mem.h */, 3449B8CD26BBF6B10022A9B8 /* Notifications.h */, 3409C0CF275812CB0050302A /* Profile.h */, 3477C29C1CBF0A920028B84B /* SymLookup.h */, 34EE8BB02748A55600F12915 /* TextFile.h */, 34F6151D27E55C5D00FE5B0C /* Thread.h */, 34F6151E27E55C5D00FE5B0C /* ThreadEvent.h */, 34F6153D27E5628600FE5B0C /* Token.h */, ); name = Headers; sourceTree = ""; }; 3477C2AD1CBF96AA0028B84B /* Resources */ = { isa = PBXGroup; children = ( 3477C2B01CBF96C40028B84B /* Res.cpp */, 3477C2AE1CBF96B90028B84B /* LgiRes.cpp */, ); name = Resources; path = ../../common/Gdc2/Rect.cpp; sourceTree = SOURCE_ROOT; }; 3477C2B21CBF96E60028B84B /* Dialogs */ = { isa = PBXGroup; children = ( 3413170D23068FFE008CE982 /* FileSelect.cpp */, 3477C46C1CC26DEB0028B84B /* Alert.cpp */, 3477C3481CC0AA7E0028B84B /* ProgressDlg.cpp */, 3477C3441CC0A9DE0028B84B /* Input.cpp */, 3477C30C1CC04C560028B84B /* FindReplace.cpp */, 3477C2E01CC04A3C0028B84B /* FontSelect.cpp */, 3477C2B31CBF96F20028B84B /* LMsg.cpp */, ); name = Dialogs; sourceTree = ""; }; 3477C2B51CBF972B0028B84B /* Interface_XP */ = { isa = PBXGroup; children = ( 34837C4825C677ED00D103B4 /* AppPriv.h */, 348CD46925C63E56001AA32B /* AppCommon.cpp */, 34B6B19023CFD0F100C24906 /* DragAndDropCommon.cpp */, 3491A28423678DB700604C29 /* DropFiles.h */, 34C072C02369878100E1E222 /* DropFiles.cpp */, 34B531D722F65734002B50F7 /* Menu.h */, 34A3ACE920568D9700B1D62A /* MenuCommon.cpp */, 346FACE61D3C720B00FFEBCE /* Message.h */, 3477C4EC1CC27F1C0028B84B /* TrayIcon.cpp */, 3477C34A1CC0AB0B0028B84B /* ToolTip.cpp */, 3477C3461CC0AA220028B84B /* WindowCommon.cpp */, 3477C2E41CC04A7F0028B84B /* ViewCommon.cpp */, 3477C2B81CBF977F0028B84B /* LgiCommon.cpp */, 3477C2B61CBF973F0028B84B /* GuiUtils.cpp */, ); name = Interface_XP; sourceTree = ""; }; 3477C2BC1CBF97D10028B84B /* Surfaces */ = { isa = PBXGroup; children = ( 3477C2E61CC04B1E0028B84B /* Surface.cpp */, 3477C28E1CBF08C10028B84B /* ScreenDC.cpp */, 3477C28C1CBF08C10028B84B /* MemDC.cpp */, 3477C28D1CBF08C10028B84B /* PrintDC.cpp */, ); name = Surfaces; sourceTree = ""; }; 3477C2BD1CBF97DA0028B84B /* Fonts */ = { isa = PBXGroup; children = ( 3477C2CD1CC047BE0028B84B /* Charset.cpp */, 3477C2D81CC048F90028B84B /* DisplayString.cpp */, 3477C2D91CC048F90028B84B /* Font.cpp */, 345920FC1F25649000098DFD /* Font.h */, 345920FD1F25649000098DFD /* FontCache.h */, 340833122698F8FE0028012F /* FontPriv.h */, 345920FE1F25649000098DFD /* FontSelect.h */, 3477C2DA1CC048F90028B84B /* FontSystem.cpp */, 340833132698F8FE0028012F /* FontType.cpp */, 34A3ACE720568D5800B1D62A /* StringLayout.cpp */, 340833142698F8FE0028012F /* TypeFace.cpp */, ); name = Fonts; sourceTree = ""; }; 3477C2BE1CBF97E20028B84B /* Applicators */ = { isa = PBXGroup; children = ( 3477C3291CC04E120028B84B /* 8Bit.cpp */, 347AFB9727A0DC5E00BE4ABE /* 15Bit.cpp */, 3477C32B1CC04E120028B84B /* 16Bit.cpp */, 3477C32C1CC04E120028B84B /* 24Bit.cpp */, 3477C32D1CC04E120028B84B /* 32Bit.cpp */, 3477C32E1CC04E120028B84B /* 48Bit.cpp */, 3477C32A1CC04E120028B84B /* 64Bit.cpp */, 3477C3301CC04E120028B84B /* Alpha.cpp */, ); name = Applicators; sourceTree = ""; }; 3477C2BF1CBF99F00028B84B /* Text */ = { isa = PBXGroup; children = ( + 349F939D2B92CBAD002410A2 /* TextConvert.cpp */, 3477C30E1CC04C790028B84B /* Css.cpp */, 3477C30F1CC04C790028B84B /* CssTools.cpp */, 3477C2C01CBF9A040028B84B /* String.cpp */, 3477C33E1CC0A6780028B84B /* Token.cpp */, 3477C2C51CC046310028B84B /* Unicode.cpp */, 34CD9F7123D5768B0039F259 /* Unicode.h */, 3477C2C61CC046310028B84B /* Utf8.cpp */, 3477C2C71CC046310028B84B /* XmlTree.cpp */, ); name = Text; sourceTree = ""; }; 3477C2CC1CC046370028B84B /* Widgets */ = { isa = PBXGroup; children = ( 3477C2F21CC04BF00028B84B /* Bitmap.cpp */, 3477C2F31CC04BF00028B84B /* Box.cpp */, 3477C2F41CC04BF00028B84B /* Button.cpp */, 3477C2F51CC04BF00028B84B /* CheckBox.cpp */, 3477C2F61CC04BF00028B84B /* Combo.cpp */, 3477C2F71CC04BF00028B84B /* Edit.cpp */, 3477C2EC1CC04BBB0028B84B /* ItemContainer.cpp */, 3477C2ED1CC04BBB0028B84B /* List.cpp */, 3409C0C827580ED60050302A /* List.h */, 3409C0C927580ED60050302A /* ListItemCheckBox.h */, 3409C0C727580ED60050302A /* ListItemRadioBtn.h */, 3477C2F81CC04BF00028B84B /* Panel.cpp */, 3477C2D61CC048390028B84B /* Popup.cpp */, 345EB84D235C6C01007D05DB /* Popup.h */, 343BE8C62355D0CE00742E21 /* PopupList.h */, 3477C2F91CC04BF00028B84B /* Progress.cpp */, 3418EBCE25D9FEA600EA168A /* Progress.h */, 3418EBD025D9FEF500EA168A /* ProgressDlg.h */, 3477C2FA1CC04BF00028B84B /* RadioGroup.cpp */, 346DDEC0240C882900751380 /* RadioGroup.h */, 3477C2DE1CC04A030028B84B /* ScrollBar.cpp */, 34B14E3326951C6E004C22CC /* Slider.cpp */, 3477C2FB1CC04BF00028B84B /* Splitter.cpp */, 3477C2FC1CC04BF00028B84B /* TableLayout.cpp */, 3477C2FD1CC04BF00028B84B /* TabView.cpp */, 3415168D26EC32C8007EE35F /* TextCtrls */, 3477C2FE1CC04BF00028B84B /* TextLabel.cpp */, 3477C2CF1CC047F50028B84B /* ToolBar.cpp */, 3477C2EE1CC04BBB0028B84B /* Tree.cpp */, ); name = Widgets; sourceTree = ""; }; 3477C2D11CC048000028B84B /* Memory */ = { isa = PBXGroup; children = ( 3477C2D41CC0481B0028B84B /* Array.h */, 3477C2D21CC048100028B84B /* Containers.cpp */, 3477C4391CC234750028B84B /* MemStream.cpp */, 3477C2E21CC04A5C0028B84B /* Stream.cpp */, 348362411F233A77003B2A6D /* Stream.h */, ); name = Memory; sourceTree = ""; }; 3477C3141CC04CC00028B84B /* Libraries */ = { isa = PBXGroup; children = ( 3477C3151CC04CCB0028B84B /* Library.cpp */, ); name = Libraries; sourceTree = ""; }; 3477C3171CC04CDE0028B84B /* Threads */ = { isa = PBXGroup; children = ( 34D21CEB217981AE003D1EA6 /* EventTargetThread.h */, 3477C27E1CBF086A0028B84B /* Thread.mm */, 3477C3251CC04DE20028B84B /* Mutex.cpp */, 3477C31F1CC04D630028B84B /* ThreadEvent.cpp */, 3477C3181CC04CF10028B84B /* ThreadCommon.cpp */, ); name = Threads; sourceTree = ""; }; 3477C31A1CC04D290028B84B /* Network */ = { isa = PBXGroup; children = ( 346FACE91D3C75AE00FFEBCE /* Uri.cpp */, 346FACEA1D3C75AE00FFEBCE /* MDStringToDigest.cpp */, 3477C31B1CC04D3F0028B84B /* Net.cpp */, ); name = Network; sourceTree = ""; }; 3477C33B1CC0881E0028B84B /* Files */ = { isa = PBXGroup; children = ( 3477C33C1CC088590028B84B /* FileCommon.cpp */, 3477C2A91CBF22A00028B84B /* File.h */, 3477C2941CBF09320028B84B /* File.mm */, ); name = Files; sourceTree = ""; }; 3477C34E1CC0C3A30028B84B /* Link */ = { isa = PBXGroup; children = ( 3477C34F1CC0C3D60028B84B /* Cocoa.framework */, ); name = Link; sourceTree = ""; }; 3477C4051CC22F920028B84B /* Process */ = { isa = PBXGroup; children = ( 3471868723A9AF8900F8C952 /* SubProcess.cpp */, ); name = Process; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ 3477C2651CBF020F0028B84B /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( 344CFBC8237365B800AE6B35 /* ClipBoard.h in Headers */, 3449B8CE26BBF6B10022A9B8 /* Notifications.h in Headers */, 343874B8230F46B200CF96B4 /* Layout.h in Headers */, 3477C2AC1CBF72170028B84B /* ViewPriv.h in Headers */, 3409C0CA27580ED60050302A /* ListItemRadioBtn.h in Headers */, 3477C35D1CC2253E0028B84B /* LgiInterfaces.h in Headers */, 34F6153E27E5628600FE5B0C /* Token.h in Headers */, 3477C2A51CBF0A920028B84B /* LgiOsClasses.h in Headers */, 3491A28523678DB800604C29 /* DropFiles.h in Headers */, 3415169326EC32FF007EE35F /* TextView3.h in Headers */, 34B531D822F65734002B50F7 /* Menu.h in Headers */, 34114C8B23091E6E00F342B1 /* LgiClasses.h in Headers */, 34D28926275B3D9F005961A8 /* DateTime.h in Headers */, 3477C35B1CC1AEEA0028B84B /* LgiInc.h in Headers */, 34F6152027E55C5D00FE5B0C /* ThreadEvent.h in Headers */, 348362421F233A77003B2A6D /* Stream.h in Headers */, 3477C2A61CBF0A920028B84B /* LgiOsDefs.h in Headers */, 3409C0CC27580ED60050302A /* ListItemCheckBox.h in Headers */, 345EB84E235C6C01007D05DB /* Popup.h in Headers */, 34BF15072871503B001B4CA5 /* DrawListSurface.h in Headers */, 34F6151F27E55C5D00FE5B0C /* Thread.h in Headers */, 34D21CEC217981AE003D1EA6 /* EventTargetThread.h in Headers */, 343BE8C72355D0CE00742E21 /* PopupList.h in Headers */, 34EE8BB12748A55600F12915 /* TextFile.h in Headers */, 34CD9F7223D5768B0039F259 /* Unicode.h in Headers */, 34837C4925C677ED00D103B4 /* AppPriv.h in Headers */, 34F3EC4329BD374E00BB9B58 /* Rect.h in Headers */, 3409C0D0275812CB0050302A /* Profile.h in Headers */, 3477C2A11CBF0A920028B84B /* Mem.h in Headers */, 3477C2D51CC0481B0028B84B /* Array.h in Headers */, 3477C5171CC303B70028B84B /* LgiUiBase.h in Headers */, 3477C2AA1CBF22A00028B84B /* File.h in Headers */, 343874B5230F46B200CF96B4 /* Window.h in Headers */, 34D28929275B3DB0005961A8 /* Variant.h in Headers */, 348E99CE2876850F0067E92A /* ObjCWrapper.h in Headers */, 3477C2A21CBF0A920028B84B /* SymLookup.h in Headers */, 34D6AA1722D3749A005D739E /* Gdc2.h in Headers */, 3477C2A41CBF0A920028B84B /* LgiOs.h in Headers */, 340833152698F8FF0028012F /* FontPriv.h in Headers */, 343874B7230F46B200CF96B4 /* App.h in Headers */, 3418EBCF25D9FEA600EA168A /* Progress.h in Headers */, 346DDEC1240C882900751380 /* RadioGroup.h in Headers */, 345921011F25649000098DFD /* FontSelect.h in Headers */, 346FACE71D3C720B00FFEBCE /* Message.h in Headers */, 3477C2A31CBF0A920028B84B /* LgiMac.h in Headers */, 3418EBD125D9FEF500EA168A /* ProgressDlg.h in Headers */, 343874B6230F46B200CF96B4 /* View.h in Headers */, 345920FF1F25649000098DFD /* Font.h in Headers */, 3415169426EC32FF007EE35F /* TextView4.h in Headers */, 3477C2A81CBF0AAF0028B84B /* Lgi.h in Headers */, 345921001F25649000098DFD /* FontCache.h in Headers */, 3409C0CB27580ED60050302A /* List.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXHeadersBuildPhase section */ /* Begin PBXNativeTarget section */ 3477C2671CBF020F0028B84B /* LgiCocoa */ = { isa = PBXNativeTarget; buildConfigurationList = 3477C2701CBF020F0028B84B /* Build configuration list for PBXNativeTarget "LgiCocoa" */; buildPhases = ( 3477C2631CBF020F0028B84B /* Sources */, 3477C2641CBF020F0028B84B /* Frameworks */, 3477C2651CBF020F0028B84B /* Headers */, 3477C2661CBF020F0028B84B /* Resources */, ); buildRules = ( ); dependencies = ( ); name = LgiCocoa; productName = Lgi; productReference = 3477C2681CBF020F0028B84B /* LgiCocoa.framework */; productType = "com.apple.product-type.framework"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ 3477C25F1CBF020F0028B84B /* Project object */ = { isa = PBXProject; attributes = { LastUpgradeCheck = 1420; ORGANIZATIONNAME = Memecode; TargetAttributes = { 3477C2671CBF020F0028B84B = { CreatedOnToolsVersion = 7.3; }; }; }; buildConfigurationList = 3477C2621CBF020F0028B84B /* Build configuration list for PBXProject "LgiCocoa" */; compatibilityVersion = "Xcode 12.0"; developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, Base, ); mainGroup = 3477C25E1CBF020F0028B84B; productRefGroup = 3477C2691CBF020F0028B84B /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( 3477C2671CBF020F0028B84B /* LgiCocoa */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ 3477C2661CBF020F0028B84B /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ 3477C2631CBF020F0028B84B /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 349F939E2B92CBAD002410A2 /* TextConvert.cpp in Sources */, 3477C2CE1CC047BE0028B84B /* Charset.cpp in Sources */, 3477C3111CC04C790028B84B /* CssTools.cpp in Sources */, 34A3ACE820568D5800B1D62A /* StringLayout.cpp in Sources */, 3477C2DB1CC048F90028B84B /* DisplayString.cpp in Sources */, 34B14E3426951C6E004C22CC /* Slider.cpp in Sources */, 3477C33A1CC04ED90028B84B /* DragAndDrop.mm in Sources */, 3477C2DC1CC048F90028B84B /* Font.cpp in Sources */, 3477C2831CBF086A0028B84B /* Layout.mm in Sources */, 3477C34D1CC0ACED0028B84B /* md5.c in Sources */, 3477C2C81CC046310028B84B /* TextView3.cpp in Sources */, 3477C2861CBF086A0028B84B /* Thread.mm in Sources */, 3477C2821CBF086A0028B84B /* General.mm in Sources */, 3477C3131CC04CB10028B84B /* Rect.cpp in Sources */, 3477C3451CC0A9DE0028B84B /* Input.cpp in Sources */, 34B6B19123CFD0F100C24906 /* DragAndDropCommon.cpp in Sources */, 3477C31D1CC04D3F0028B84B /* Net.cpp in Sources */, 3477C28F1CBF08C10028B84B /* Gdc2.mm in Sources */, 3477C30A1CC04BF00028B84B /* TabView.cpp in Sources */, 3427064727A0E20C0043F733 /* CocoaView.mm in Sources */, 3477C33F1CC0A6780028B84B /* Token.cpp in Sources */, 3477C2D01CC047F50028B84B /* ToolBar.cpp in Sources */, 3477C3011CC04BF00028B84B /* Button.cpp in Sources */, 3477C3061CC04BF00028B84B /* Progress.cpp in Sources */, 3477C3001CC04BF00028B84B /* Box.cpp in Sources */, 3477C2871CBF086A0028B84B /* View.mm in Sources */, 3477C3021CC04BF00028B84B /* CheckBox.cpp in Sources */, 3477C2BB1CBF97C80028B84B /* GdcCommon.cpp in Sources */, 3477C2E51CC04A7F0028B84B /* ViewCommon.cpp in Sources */, 347AFB9827A0DC5E00BE4ABE /* 15Bit.cpp in Sources */, 34131710230694D1008CE982 /* Gel.cpp in Sources */, 3477C2D71CC048390028B84B /* Popup.cpp in Sources */, 3477C3091CC04BF00028B84B /* TableLayout.cpp in Sources */, 3477C2E11CC04A3C0028B84B /* FontSelect.cpp in Sources */, 3477C2DF1CC04A030028B84B /* ScrollBar.cpp in Sources */, 3477C2F11CC04BBB0028B84B /* Tree.cpp in Sources */, 3413170E23068FFE008CE982 /* FileSelect.cpp in Sources */, 3477C3281CC04E020028B84B /* Variant.cpp in Sources */, 3477C2AF1CBF96B90028B84B /* LgiRes.cpp in Sources */, 3471868823A9AF8900F8C952 /* SubProcess.cpp in Sources */, 3477C3161CC04CCB0028B84B /* Library.cpp in Sources */, 3477C3051CC04BF00028B84B /* Panel.cpp in Sources */, 3477C2901CBF08C10028B84B /* MemDC.cpp in Sources */, 34C072C12369878100E1E222 /* DropFiles.cpp in Sources */, 3477C30D1CC04C560028B84B /* FindReplace.cpp in Sources */, 3477C34B1CC0AB0B0028B84B /* ToolTip.cpp in Sources */, 3415168F26EC32E7007EE35F /* TextView4.cpp in Sources */, 3477C3201CC04D630028B84B /* ThreadEvent.cpp in Sources */, 3477C3031CC04BF00028B84B /* Combo.cpp in Sources */, 3477C30B1CC04BF00028B84B /* TextLabel.cpp in Sources */, 3477C2B71CBF973F0028B84B /* GuiUtils.cpp in Sources */, 3477C2911CBF08C10028B84B /* PrintDC.cpp in Sources */, 3477C2B91CBF977F0028B84B /* LgiCommon.cpp in Sources */, 34D1B3C121748A2800BC6B58 /* Path.cpp in Sources */, 3477C2CA1CC046310028B84B /* Utf8.cpp in Sources */, 3477C2971CBF09320028B84B /* File.mm in Sources */, 3477C43A1CC234750028B84B /* MemStream.cpp in Sources */, 3477C3191CC04CF10028B84B /* ThreadCommon.cpp in Sources */, 3477C2781CBF08050028B84B /* ClipBoard.mm in Sources */, 346FACEC1D3C75AE00FFEBCE /* MDStringToDigest.cpp in Sources */, 3477C3491CC0AA7E0028B84B /* ProgressDlg.cpp in Sources */, 346FACEB1D3C75AE00FFEBCE /* Uri.cpp in Sources */, 3477C2B11CBF96C40028B84B /* Res.cpp in Sources */, 340833162698F8FF0028012F /* FontType.cpp in Sources */, 3477C4ED1CC27F1C0028B84B /* TrayIcon.cpp in Sources */, 3477C2991CBF09320028B84B /* ShowFileProp_Mac.mm in Sources */, 3477C2921CBF08C10028B84B /* ScreenDC.cpp in Sources */, 3477C2981CBF09320028B84B /* Mem.mm in Sources */, 3477C2CB1CC046310028B84B /* XmlTree.cpp in Sources */, 3477C2EF1CC04BBB0028B84B /* ItemContainer.cpp in Sources */, 3477C3081CC04BF00028B84B /* Splitter.cpp in Sources */, 34109857217A9805007020CE /* LCocoaView.h in Sources */, 3477C46D1CC26DEB0028B84B /* Alert.cpp in Sources */, 3477C3381CC04E120028B84B /* Alpha.cpp in Sources */, 3477C2EB1CC04B890028B84B /* Filter.cpp in Sources */, 3477C3321CC04E120028B84B /* 64Bit.cpp in Sources */, 3477C3041CC04BF00028B84B /* Edit.cpp in Sources */, 3477C3361CC04E120028B84B /* 48Bit.cpp in Sources */, 3477C3261CC04DE20028B84B /* Mutex.cpp in Sources */, 3477C2851CBF086A0028B84B /* Printer.mm in Sources */, 3477C33D1CC088590028B84B /* FileCommon.cpp in Sources */, 3477C3471CC0AA220028B84B /* WindowCommon.cpp in Sources */, 3477C3071CC04BF00028B84B /* RadioGroup.cpp in Sources */, 3477C2FF1CC04BF00028B84B /* Bitmap.cpp in Sources */, 3477C2E91CC04B5D0028B84B /* Rand.cpp in Sources */, 3477C2E71CC04B1E0028B84B /* Surface.cpp in Sources */, 3477C3241CC04DD70028B84B /* Object.cpp in Sources */, 3477C2E31CC04A5C0028B84B /* Stream.cpp in Sources */, 34A3ACEA20568D9700B1D62A /* MenuCommon.cpp in Sources */, 3477C3341CC04E120028B84B /* 24Bit.cpp in Sources */, 3477C3351CC04E120028B84B /* 32Bit.cpp in Sources */, 3477C2881CBF086A0028B84B /* Widgets.mm in Sources */, 340833172698F8FF0028012F /* TypeFace.cpp in Sources */, 3477C2891CBF086A0028B84B /* Window.mm in Sources */, 3477C3431CC0A6B90028B84B /* DocView.cpp in Sources */, 3477C2841CBF086A0028B84B /* Menu.mm in Sources */, 3477C2B41CBF96F20028B84B /* LMsg.cpp in Sources */, 3477C3311CC04E120028B84B /* 8Bit.cpp in Sources */, 3477C2DD1CC048F90028B84B /* FontSystem.cpp in Sources */, 3477C3101CC04C790028B84B /* Css.cpp in Sources */, 3477C2C91CC046310028B84B /* Unicode.cpp in Sources */, 3477C3221CC04DA00028B84B /* Colour.cpp in Sources */, 3477C2D31CC048100028B84B /* Containers.cpp in Sources */, 3477C2F01CC04BBB0028B84B /* List.cpp in Sources */, 3477C3331CC04E120028B84B /* 16Bit.cpp in Sources */, 3477C2751CBF07DD0028B84B /* App.mm in Sources */, 348CD46A25C63E56001AA32B /* AppCommon.cpp in Sources */, 3477C3411CC0A68E0028B84B /* DateTime.cpp in Sources */, 34C81A5A25DA0AA10053F93A /* Base64.cpp in Sources */, 3477C2C11CBF9A040028B84B /* String.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin XCBuildConfiguration section */ 3477C26E1CBF020F0028B84B /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_CXX_LANGUAGE_STANDARD = "c++14"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = NO; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_IDENTITY = "-"; CODE_SIGN_INJECT_BASE_ENTITLEMENTS = NO; CODE_SIGN_STYLE = Manual; COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = 1; DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; MACOSX_DEPLOYMENT_TARGET = 10.15; MODULEMAP_FILE = module.modulemap; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; name = Debug; }; 3477C26F1CBF020F0028B84B /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_CXX_LANGUAGE_STANDARD = "c++14"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = NO; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_IDENTITY = "-"; CODE_SIGN_INJECT_BASE_ENTITLEMENTS = NO; CODE_SIGN_STYLE = Manual; COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = 1; DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; MACOSX_DEPLOYMENT_TARGET = 10.15; MODULEMAP_FILE = module.modulemap; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = macosx; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; name = Release; }; 3477C2711CBF020F0028B84B /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { CLANG_ANALYZER_NONNULL = YES_NONAGGRESSIVE; CLANG_ENABLE_OBJC_WEAK = YES; CODE_SIGN_IDENTITY = "-"; COMBINE_HIDPI_IMAGES = YES; DEAD_CODE_STRIPPING = YES; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 2AV9WN2LD8; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; FRAMEWORK_VERSION = A; GCC_C_LANGUAGE_STANDARD = c11; HEADER_SEARCH_PATHS = ( ../../../include, ../../../include/lgi/mac/cocoa, ../../../private/mac, ../../../private/common, ); INFOPLIST_FILE = Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/../Frameworks", "@loader_path/Frameworks", ); OTHER_CFLAGS = ( "-DMAC", "-DLGI_COCOA=1", "-DLGI_LIBRARY", "-D_DEBUG", + "-DLGI_UNIT_TESTS", ); OTHER_CODE_SIGN_FLAGS = "--timestamp --deep"; OTHER_CPLUSPLUSFLAGS = ( "-DMAC", "-DLGI_COCOA=1", "-DLGI_LIBRARY", "-D_DEBUG", + "-DLGI_UNIT_TESTS", ); PRODUCT_BUNDLE_IDENTIFIER = com.memecode.Lgi; PRODUCT_NAME = LgiCocoa; SDKROOT = macosx; SKIP_INSTALL = YES; STRIP_INSTALLED_PRODUCT = NO; WARNING_CFLAGS = "-Wno-nullability-completeness"; }; name = Debug; }; 3477C2721CBF020F0028B84B /* Release */ = { isa = XCBuildConfiguration; buildSettings = { CLANG_ANALYZER_NONNULL = YES_NONAGGRESSIVE; CLANG_ENABLE_OBJC_WEAK = YES; CODE_SIGN_IDENTITY = "-"; COMBINE_HIDPI_IMAGES = YES; DEAD_CODE_STRIPPING = YES; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 2AV9WN2LD8; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; FRAMEWORK_VERSION = A; GCC_C_LANGUAGE_STANDARD = c11; HEADER_SEARCH_PATHS = ( ../../../include, ../../../include/lgi/mac/cocoa, ../../../private/mac, ../../../private/common, ); INFOPLIST_FILE = Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/../Frameworks", "@loader_path/Frameworks", ); OTHER_CFLAGS = ( "-DMAC", "-DLGI_COCOA=1", "-DLGI_LIBRARY", ); OTHER_CODE_SIGN_FLAGS = "--timestamp --deep"; OTHER_CPLUSPLUSFLAGS = ( "-DMAC", "-DLGI_COCOA=1", "-DLGI_LIBRARY", ); PRODUCT_BUNDLE_IDENTIFIER = com.memecode.Lgi; PRODUCT_NAME = LgiCocoa; SDKROOT = macosx; SKIP_INSTALL = YES; STRIP_INSTALLED_PRODUCT = NO; WARNING_CFLAGS = "-Wno-nullability-completeness"; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ 3477C2621CBF020F0028B84B /* Build configuration list for PBXProject "LgiCocoa" */ = { isa = XCConfigurationList; buildConfigurations = ( 3477C26E1CBF020F0028B84B /* Debug */, 3477C26F1CBF020F0028B84B /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 3477C2701CBF020F0028B84B /* Build configuration list for PBXNativeTarget "LgiCocoa" */ = { isa = XCConfigurationList; buildConfigurations = ( 3477C2711CBF020F0028B84B /* Debug */, 3477C2721CBF020F0028B84B /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; rootObject = 3477C25F1CBF020F0028B84B /* Project object */; } diff --git a/src/mac/cocoa/Window.mm b/src/mac/cocoa/Window.mm --- a/src/mac/cocoa/Window.mm +++ b/src/mac/cocoa/Window.mm @@ -1,1476 +1,1476 @@ #include #include "lgi/common/Lgi.h" #include "lgi/common/DragAndDrop.h" #include "lgi/common/Popup.h" #include "lgi/common/DisplayString.h" #include "lgi/common/Menu.h" #include "LCocoaView.h" extern void NextTabStop(LViewI *v, int dir); extern void SetDefaultFocus(LViewI *v); extern void BuildTabStops(LArray &Stops, LViewI *v); #define DEBUG_KEYS 0 #define DEBUG_SETFOCUS 0 #define DEBUG_LOGGING 0 #if DEBUG_LOGGING #define LOG(...) printf(__VA_ARGS__) #else #define LOG(...) #endif /* Deleting a LWindow senarios: Users clicks close: NSWindowDelegate::windowWillClose GWindowPrivate::OnClose(CloseUser) LNsWindow::onDelete Something deletes the LWindow programmatically: LWindow::~LWindow GWindowPriv::OnClose(CloseDestructor) LNsWindow::onDelete self.close windowWillClose -> block Something calls LWindow::Quit() LNsWindow::onQuit (async) self.close NSWindowDelegate::windowWillClose GWindowPrivate::OnClose(CloseUser) LNsWindow::onDelete */ #if DEBUG_SETFOCUS || DEBUG_KEYS static LString DescribeView(LViewI *v) { if (!v) return GString(); char s[512]; int ch = 0; LArray p; for (LViewI *i = v; i; i = i->GetParent()) { p.Add(i); } for (int n=MIN(3, (int)p.Length()-1); n>=0; n--) { char Buf[256] = ""; if (!stricmp(v->GetClass(), "LMdiChild")) sprintf(Buf, "'%s'", v->Name()); v = p[n]; ch += sprintf_s(s + ch, sizeof(s) - ch, "%s>%s", Buf, v->GetClass()); } return s; } #endif LRect LScreenFlip(LRect r) { LRect screen(0, 0, -1, -1); for (NSScreen *s in [NSScreen screens]) { LRect pos = s.frame; if (r.Overlap(&pos)) { screen = pos; break; } } if (screen.Valid()) { LRect rc = r; rc.Offset(0, (screen.Y() - r.y1 - r.Y()) - r.y1); // printf("%s:%i - Flip %s -> %s (%s)\n", _FL, r.GetStr(), rc.GetStr(), screen.GetStr()); return rc; } else { // printf("%s:%i - No Screen?\n", _FL); r.ZOff(-1, -1); } return r; } /////////////////////////////////////////////////////////////////////// class HookInfo { public: int Flags; LView *Target; }; @interface LWindowDelegate : NSObject { } - (id)init; - (void)dealloc; - (void)windowDidResize:(NSNotification*)aNotification; - (void)windowDidMove:(NSNotification*)aNotification; - (void)windowWillClose:(NSNotification*)aNotification; - (BOOL)windowShouldClose:(id)sender; - (void)windowDidBecomeMain:(NSNotification*)notification; - (void)windowDidResignMain:(NSNotification*)notification; @end LWindowDelegate *Delegate = nil; class LWindowPrivate { public: LWindow *Wnd = NULL; LWindow *ChildDlg = NULL; LMenu *EmptyMenu = NULL; LViewI *Focus = NULL; NSView *ContentCache = NULL; int Sx = -1, Sy = -1; LKey LastKey; LArray Hooks; uint64 LastMinimize = 0; uint64 LastDragDrop = 0; bool DeleteOnClose = true; bool SnapToEdge = false; bool InitVisible = false; LWindowPrivate(LWindow *wnd) { Wnd = wnd; } ~LWindowPrivate() { DeleteObj(EmptyMenu); } void OnClose(LCloseContext Ctx) { LOG("LWindowPrivate::OnClose %p/%s\n", Wnd, Wnd?Wnd->GetClass():NULL); auto &osw = Wnd->Wnd; if (!osw) return; LCocoaView *cv = objc_dynamic_cast(LCocoaView, osw.p.contentView); if (cv) cv.w = NULL; LNsWindow *w = objc_dynamic_cast(LNsWindow, osw.p); if (w) [w onDelete:Ctx]; osw.p.delegate = nil; [osw.p autorelease]; osw = nil; if (DeleteOnClose) delete Wnd; } ssize_t GetHookIndex(LView *Target, bool Create = false) { for (int i=0; iTarget = Target; n->Flags = 0; return Hooks.Length() - 1; } } return -1; } void OnResize() { NSWindow *wnd = Wnd->WindowHandle().p; Wnd->Pos = wnd.frame; Wnd->OnPosChange(); wnd.contentView.needsLayout = YES; } }; #define DefaultStyleMask NSWindowStyleMaskTitled | \ NSWindowStyleMaskResizable | \ NSWindowStyleMaskClosable | \ NSWindowStyleMaskMiniaturizable @implementation LNsWindow - (id)init:(LWindowPrivate*)priv Frame:(NSRect)rc { NSUInteger windowStyleMask = DefaultStyleMask; if ((self = [super initWithContentRect:rc styleMask:windowStyleMask backing:NSBackingStoreBuffered defer:NO ]) != nil) { self.d = priv; self->ReqClose = CSNone; self.contentView = [[LCocoaView alloc] init:priv->Wnd]; [self makeFirstResponder:self.contentView]; self.acceptsMouseMovedEvents = true; self.ignoresMouseEvents = false; self.canFocus = true; // printf("LNsWindow.init\n"); } return self; } - (void)dealloc { if (self.d) self.d->Wnd->OnDealloc(); LCocoaView *cv = objc_dynamic_cast(LCocoaView, self.contentView); cv.w = NULL; [cv release]; self.contentView = NULL; self.canFocus = true; [super dealloc]; // printf("LNsWindow.dealloc.\n"); } - (LWindow*)getWindow { return self.d ? self.d->Wnd : nil; } - (BOOL)canBecomeKeyWindow { return self.canFocus; } - (void)onQuit { #if DEBUG_LOGGING LWindow *wnd = self.d ? self.d->Wnd : NULL; auto cls = wnd ? wnd->GetClass() : NULL; #endif LOG("LNsWindow::onQuit %p/%s %i\n", wnd, cls, self->ReqClose); if (self->ReqClose == CSNone) { self->ReqClose = CSInRequest; if (!self.d) LOG("%s:%i - No priv pointer?\n", _FL); if (!self.d || !self.d->Wnd || !self.d->Wnd->OnRequestClose(false)) { LOG(" ::onQuit %p/%s no 'd' or OnReqClose failed\n", wnd, cls); self->ReqClose = CSNone; return; } } else return; LOG(" ::onQuit %p/%s self.close\n", wnd, cls); self->ReqClose = CSClosed; self.d->Wnd->SetPulse(); [self close]; } - (void)onDelete:(LCloseContext)ctx { LOG("LNsWindow::onDelete %p/%s\n", self.d->Wnd, self.d->Wnd->GetClass()); if (ctx == CloseDestructor && self->ReqClose != CSClosed) { // This is called during the ~LWindow destructor to make sure we // closed the window self->ReqClose = CSClosed; LOG(" ::onDelete %p self.close\n", self.d->Wnd); [self close]; } self.d = NULL; } @end @implementation LWindowDelegate - (id)init { if ((self = [super init]) != nil) { } return self; } - (void)dealloc { [super dealloc]; } - (void)windowDidResize:(NSNotification*)event { LNsWindow *w = event.object; if (w && w.d) w.d->OnResize(); } - (void)windowDidMove:(NSNotification*)event { // LNsWindow *w = event.object; // GRect r = LScreenFlip(w.frame); // printf("windowDidMove: %s\n", r.GetStr()); } - (BOOL)windowShouldClose:(NSWindow*)sender { LNsWindow *w = objc_dynamic_cast(LNsWindow, sender); if (w && w.d && w.d->Wnd) return w.d->Wnd->OnRequestClose(false); return YES; } - (void)windowWillClose:(NSNotification*)event { LNsWindow *w = event.object; if (w && w.d) w.d->OnClose(CloseUser); } - (void)windowDidBecomeMain:(NSNotification*)event { LNsWindow *w = event.object; if (w && w.d) w.d->Wnd->OnFrontSwitch(true); } - (void)windowDidResignMain:(NSNotification*)event { LNsWindow *w = event.object; if (w && w.d) w.d->Wnd->OnFrontSwitch(false); } @end /////////////////////////////////////////////////////////////////////// #define GWND_CREATE 0x0010000 #if __has_feature(objc_arc) #error "NO ARC!" #endif LWindow::LWindow(OsWindow wnd) : LView(NULL) { d = new LWindowPrivate(this); _QuitOnClose = false; Wnd = NULL; Menu = 0; _Default = 0; _Window = this; WndFlags |= GWND_CREATE; LView::Visible(false); _Lock = new LMutex("LWindow"); LRect pos(200, 200, 200, 200); NSRect frame = pos; if (wnd) Wnd = wnd; else Wnd.p = [[LNsWindow alloc] init:d Frame:frame]; if (Wnd) { [Wnd.p retain]; if (!Delegate) Delegate = [[LWindowDelegate alloc] init]; //[Wnd.p makeKeyAndOrderFront:NSApp]; Wnd.p.delegate = Delegate; d->ContentCache = Wnd.p.contentView; } } LWindow::~LWindow() { LOG("LWindow::~LWindow %p\n", this); if (LAppInst->AppWnd == this) LAppInst->AppWnd = 0; _Delete(); d->DeleteOnClose = false; // We're already in the destructor, don't redelete. d->OnClose(CloseDestructor); DeleteObj(Menu); DeleteObj(d); DeleteObj(_Lock); } int LWindow::WaitThread() { return 0; } NSView *LWindow::Handle() { if (!InThread()) return d->ContentCache; if (Wnd.p != nil) return Wnd.p.contentView; return NULL; } bool LWindow::SetTitleBar(bool ShowTitleBar) { if (!Wnd.p) return false; if (ShowTitleBar) { Wnd.p.titlebarAppearsTransparent = false; Wnd.p.titleVisibility = NSWindowTitleVisible; Wnd.p.styleMask = DefaultStyleMask; } else { Wnd.p.titlebarAppearsTransparent = true; Wnd.p.titleVisibility = NSWindowTitleHidden; Wnd.p.styleMask = 0; } return true; } bool LWindow::SetIcon(const char *FileName) { #warning "Impl LWindow::SetIcon" return false; } bool LWindow::SetWillFocus(bool f) { if (!Wnd.p) return false; LNsWindow *w = objc_dynamic_cast(LNsWindow, Wnd.p); if (!w) return false; w.canFocus = f; return true; } LViewI *LWindow::GetFocus() { return d->Focus; } void LWindow::SetFocus(LViewI *ctrl, FocusType type) { const char *TypeName = NULL; switch (type) { case GainFocus: TypeName = "Gain"; break; case LoseFocus: TypeName = "Lose"; break; case ViewDelete: TypeName = "Delete"; break; } switch (type) { case GainFocus: { // Check if the control already has focus if (d->Focus == ctrl) return; if (d->Focus) { LView *v = d->Focus->GetGView(); if (v) v->WndFlags &= ~GWF_FOCUS; d->Focus->OnFocus(false); d->Focus->Invalidate(); #if DEBUG_SETFOCUS auto _foc = DescribeView(d->Focus); LgiTrace(".....defocus: %s\n", _foc.Get()); #endif } d->Focus = ctrl; if (d->Focus) { LView *v = d->Focus->GetGView(); if (v) v->WndFlags |= GWF_FOCUS; d->Focus->OnFocus(true); d->Focus->Invalidate(); #if DEBUG_SETFOCUS auto _set = DescribeView(d->Focus); LgiTrace("LWindow::SetFocus(%s, %s) focusing\n", _set.Get(), TypeName); #endif } break; } case LoseFocus: { if (ctrl == d->Focus) { LView *v = d->Focus->GetGView(); if (v) { if (v->WndFlags & GWF_FOCUS) { // View thinks it has focus v->WndFlags &= ~GWF_FOCUS; d->Focus->OnFocus(false); // keep d->Focus pointer, as we want to be able to re-focus the child // view when we get focus again #if DEBUG_SETFOCUS auto _ctrl = DescribeView(ctrl); auto _foc = DescribeView(d->Focus); LgiTrace("LWindow::SetFocus(%s, %s) keep_focus: %s\n", _ctrl.Get(), TypeName, _foc.Get()); #endif } // else view doesn't think it has focus anyway... } else { // Non GView handler d->Focus->OnFocus(false); d->Focus->Invalidate(); d->Focus = NULL; } } else { /* LgiTrace("LWindow::SetFocus(%p.%s, %s) error on losefocus: %p(%s)\n", ctrl, ctrl ? ctrl->GetClass() : NULL, TypeName, d->Focus, d->Focus ? d->Focus->GetClass() : NULL); */ } break; } case ViewDelete: { if (ctrl == d->Focus) { #if DEBUG_SETFOCUS LgiTrace("LWindow::SetFocus(%p.%s, %s) delete_focus: %p(%s)\n", ctrl, ctrl ? ctrl->GetClass() : NULL, TypeName, d->Focus, d->Focus ? d->Focus->GetClass() : NULL); #endif d->Focus = NULL; } break; } } } void LWindow::SetDragHandlers(bool On) { #if 0 if (Wnd && _View) SetAutomaticControlDragTrackingEnabledForWindow(Wnd, On); #endif } void LWindow::Quit(bool DontDelete) { // LAutoPool Pool; if (_QuitOnClose) { _QuitOnClose = false; LCloseApp(); } if (Wnd) SetDragHandlers(false); if (d && DontDelete) { // If DontDelete is true, we should be already in the destructor of the LWindow. // Which means we DON'T call onQuit, as it's too late to ask the user if they don't // want to close the window. The window IS closed come what may, and the object is // going away. Futhermore we can't access the window's memory after it's deleted and // that may happen if the onQuit is processed after ~LWindow. d->DeleteOnClose = false; if (Wnd) [Wnd.p close]; } else if (Wnd) { [Wnd.p performSelectorOnMainThread:@selector(onQuit) withObject:nil waitUntilDone:false]; } } LWindow *LWindow::GetModalChild() { return d->ChildDlg; } bool LWindow::SetModalChild(LWindow *Dlg) { d->ChildDlg = Dlg; return true; } bool LWindow::GetSnapToEdge() { return d->SnapToEdge; } void LWindow::SetSnapToEdge(bool s) { d->SnapToEdge = s; } void LWindow::OnFrontSwitch(bool b) { if (b && Menu) { [NSApplication sharedApplication].mainMenu = Menu->Handle().p; } else { auto m = LAppInst->Default.Get(); [NSApplication sharedApplication].mainMenu = m ? m->Handle() : nil; } // printf("%s:%i - menu for %s is %p\n", _FL, Name(), [NSApplication sharedApplication].mainMenu); } bool LWindow::Visible() { // LAutoPool Pool; if (!Wnd) return false; return [Wnd.p isVisible]; } void LWindow::Visible(bool i) { // LAutoPool Pool; if (!Wnd) return; if (i) { d->InitVisible = true; PourAll(); [Wnd.p makeKeyAndOrderFront:NULL]; [NSApp activateIgnoringOtherApps:YES]; SetDefaultFocus(this); OnPosChange(); } else { [Wnd.p orderOut:Wnd.p]; } } bool LWindow::IsActive() { return Wnd ? [Wnd.p isKeyWindow] : false; } bool LWindow::SetActive() { [[NSApplication sharedApplication] activateIgnoringOtherApps : YES]; return false; } void LWindow::SetDeleteOnClose(bool i) { d->DeleteOnClose = i; } void LWindow::SetAlwaysOnTop(bool b) { if (!Wnd) { LgiTrace("%s:%i - No window.\n", _FL); return; } if (b) [Wnd.p setLevel:NSFloatingWindowLevel]; else [Wnd.p setLevel:NSNormalWindowLevel]; } bool LWindow::PostEvent(int Event, LMessage::Param a, LMessage::Param b, int64_t TimeoutMs) { return LAppInst->PostEvent(this, Event, a, b); } bool LWindow::Attach(LViewI *p) { bool Status = false; if (Wnd) { if (LBase::Name()) Name(LBase::Name()); Status = true; // Setup default button... if (!_Default) { _Default = FindControl(IDOK); if (_Default) _Default->Invalidate(); } OnCreate(); OnAttach(); OnPosChange(); // Set the first control as the focus... NextTabStop(this, 0); } return Status; } bool LWindow::OnRequestClose(bool OsShuttingDown) { if (GetQuitOnClose()) { LCloseApp(); } return LView::OnRequestClose(OsShuttingDown); } bool LWindow::HandleViewMouse(LView *v, LMouse &m) { if (m.Down()) { bool ParentPopup = false; LViewI *p = m.Target; while (p && p->GetParent()) { if (dynamic_cast(p)) { ParentPopup = true; break; } p = p->GetParent(); } if (!ParentPopup) { for (int i=0; iVisible()) { // printf("Hiding popup %s\n", pu->GetClass()); pu->Visible(false); } } } if (!m.IsMove() && LAppInst) { auto mh = LAppInst->GetMouseHook(); if (mh) mh->TrackClick(v); } } for (int i=0; iHooks.Length(); i++) { if (d->Hooks[i].Flags & LMouseEvents) { if (!d->Hooks[i].Target->OnViewMouse(v, m)) { return false; } } } return true; } bool LWindow::HandleViewKey(LView *v, LKey &k) { bool Status = false; LViewI *Ctrl = NULL; if (!v && d->Focus) v = d->Focus->GetGView(); if (!v) { #if DEBUG_KEYS k.Trace("No focus view to handle key."); #endif return false; } // Give key to popups if (LAppInst && LAppInst->GetMouseHook() && LAppInst->GetMouseHook()->OnViewKey(v, k)) { goto AllDone; } // Allow any hooks to see the key... for (int i=0; iHooks.Length(); i++) { if (d->Hooks[i].Flags & LKeyEvents) { if (d->Hooks[i].Target->OnViewKey(v, k)) { Status = true; #if DEBUG_KEYS printf("Hook ate '%c'(%i) down=%i alt=%i ctrl=%i sh=%i\n", k.c16, k.c16, k.Down(), k.Alt(), k.Ctrl(), k.Shift()); #endif goto AllDone; } } } // Give the key to the window... if (v->OnKey(k)) { #if DEBUG_KEYS GString vv = DescribeView(v); printf("%s ate '%c'(%i) down=%i alt=%i ctrl=%i sh=%i\n", vv.Get(), k.c16, k.c16, k.Down(), k.Alt(), k.Ctrl(), k.Shift()); #endif Status = true; goto AllDone; } // Window didn't want the key... switch (k.vkey) { case LK_RETURN: case LK_KEYPADENTER: { Ctrl = _Default; break; } case LK_ESCAPE: { Ctrl = FindControl(IDCANCEL); break; } case LK_TAB: { // Go to the next control? if (k.Down()) { LArray Stops; BuildTabStops(Stops, v->GetWindow()); ssize_t Idx = Stops.IndexOf(v); if (Idx >= 0) { if (k.Shift()) { Idx--; if (Idx < 0) Idx = Stops.Length() - 1; } else { Idx++; if (Idx >= Stops.Length()) Idx = 0; } Stops[Idx]->Focus(true); } } return true; } } if (Ctrl && Ctrl->Enabled()) { if (Ctrl->OnKey(k)) { Status = true; #if DEBUG_KEYS printf("Default Button ate '%c'(%i) down=%i alt=%i ctrl=%i sh=%i\n", k.c16, k.c16, k.Down(), k.Alt(), k.Ctrl(), k.Shift()); #endif goto AllDone; } } if (Menu) { Status = Menu->OnKey(v, k); if (Status) { #if DEBUG_KEYS printf("Menu ate '%c' down=%i alt=%i ctrl=%i sh=%i\n", k.c16, k.Down(), k.Alt(), k.Ctrl(), k.Shift()); #endif } } // Command+W closes the window... if it doesn't get nabbed earlier. if (k.Down() && k.System() && tolower(k.c16) == 'w') { // Close Quit(); return true; } AllDone: if (d) d->LastKey = k; else LAssert(!"Window was deleted and we are accessing unallocated mem."); return Status; } void LWindow::Raise() { if (Wnd) { // BringToFront(Wnd); } } LWindowZoom LWindow::GetZoom() { if (Wnd) { #if 0 bool c = IsWindowCollapsed(Wnd); // printf("IsWindowCollapsed=%i\n", c); if (c) return LZoomMin; c = IsWindowInStandardState(Wnd, NULL, NULL); // printf("IsWindowInStandardState=%i\n", c); if (!c) return LZoomMax; #endif } return LZoomNormal; } void LWindow::SetZoom(LWindowZoom i) { #if 0 OSStatus e = 0; switch (i) { case LZoomMin: { e = CollapseWindow(Wnd, true); if (e) printf("%s:%i - CollapseWindow failed with %i\n", _FL, (int)e); // else printf("LZoomMin ok.\n"); break; } default: case LZoomNormal: { e = CollapseWindow(Wnd, false); if (e) printf("%s:%i - [Un]CollapseWindow failed with %i\n", _FL, (int)e); // else printf("LZoomNormal ok.\n"); break; } } #endif } LViewI *LWindow::GetDefault() { return _Default; } void LWindow::SetDefault(LViewI *v) { if (v && v->GetWindow() == (LViewI*)this) { if (_Default != v) { auto Old = _Default; _Default = v; if (Old) Old->Invalidate(); if (_Default) _Default->Invalidate(); } } else { _Default = 0; } } bool LWindow::Name(const char *n) { // LAutoPool Pool; bool Status = LBase::Name(n); if (Wnd) { NSString *ns = [NSString stringWithCString:n encoding:NSUTF8StringEncoding]; Wnd.p.title = ns; //[ns release]; } return Status; } const char *LWindow::Name() { return LBase::Name(); } LRect &LWindow::GetClient(bool ClientSpace) { // LAutoPool Pool; static LRect r; if (Wnd) { r = Wnd.p.contentView.frame; if (ClientSpace) r.Offset(-r.x1, -r.y1); } else { r.ZOff(-1, -1); } return r; } bool LWindow::SerializeState(LDom *Store, const char *FieldName, bool Load) { if (!Store || !FieldName) return false; if (Load) { LVariant v; if (Store->GetValue(FieldName, v) && v.Str()) { LRect Position(0, 0, -1, -1); LWindowZoom State = LZoomNormal; auto t = LString(v.Str()).SplitDelimit(";"); for (auto s: t) { auto v = s.SplitDelimit("=", 1); if (v.Length() == 2) { if (v[0].Equals("State")) State = (LWindowZoom)v[1].Int(); else if (v[0].Equals("Pos")) Position.SetStr(v[1]); } else return false; } if (Position.Valid()) { if (Position.X() < 64) Position.x2 = Position.x1 + 63; if (Position.Y() < 64) Position.y2 = Position.y1 + 63; SetPos(Position); } SetZoom(State); } else return false; } else { char s[256]; LWindowZoom State = GetZoom(); snprintf(s, sizeof(s), "State=%i;Pos=%s", State, GetPos().GetStr()); LVariant v = s; if (!Store->SetValue(FieldName, v)) return false; } return true; } -LPoint LWindow::GetDpi() +LPoint LWindow::GetDpi() const { return LScreenDpi(); } LPointF LWindow::GetDpiScale() { auto Dpi = GetDpi(); return LPointF(Dpi.x / 100.0, Dpi.y / 100.0); } LRect &LWindow::GetPos() { // LAutoPool Pool; if (Wnd) { Pos = LScreenFlip(Wnd.p.frame); // printf("%s::GetPos %s\n", GetClass(), Pos.GetStr()); } return Pos; } bool LWindow::SetPos(LRect &p, bool Repaint) { // LAutoPool Pool; Pos = p; if (Wnd) { LRect r = LScreenFlip(p); [Wnd.p setFrame:r display:YES]; // printf("%s::SetPos %s\n", GetClass(), Pos.GetStr()); } return true; } void LWindow::OnChildrenChanged(LViewI *Wnd, bool Attaching) { if (dynamic_cast(Wnd)) { printf("%s:%i - Ignoring GPopup in OnChildrenChanged handler.\n", _FL); return; } PourAll(); } void LWindow::OnCreate() { } void LWindow::OnPaint(LSurface *pDC) { pDC->Colour(L_MED); pDC->Rectangle(); } void LWindow::OnPosChange() { LView::OnPosChange(); if (d->Sx != X() || d->Sy != Y()) { PourAll(); d->Sx = X(); d->Sy = Y(); } } #define IsTool(v) \ ( \ dynamic_cast(v) \ && \ dynamic_cast(v)->_IsToolBar \ ) void LWindow::PourAll() { LRect r = GetClient(); // printf("::Pour r=%s\n", r.GetStr()); LRegion Client(r); LRegion Update(Client); bool HasTools = false; LViewI *v; List::I Lst = Children.begin(); { LRegion Tools; for (v = *Lst; v; v = *++Lst) { if (IsTool(v)) { LRect OldPos = v->GetPos(); Update.Union(&OldPos); if (HasTools) { // 2nd and later toolbars if (v->Pour(Tools)) { if (!v->Visible()) { v->Visible(true); } if (OldPos != v->GetPos()) { // position has changed update... v->Invalidate(); } Tools.Subtract(&v->GetPos()); Update.Subtract(&v->GetPos()); } } else { // First toolbar if (v->Pour(Client)) { HasTools = true; if (!v->Visible()) { v->Visible(true); } if (OldPos != v->GetPos()) { v->Invalidate(); } LRect Bar(v->GetPos()); Bar.x2 = GetClient().x2; Tools = Bar; Tools.Subtract(&v->GetPos()); Client.Subtract(&Bar); Update.Subtract(&Bar); } } } } } Lst = Children.begin(); for (LViewI *v = *Lst; v; v = *++Lst) { if (!IsTool(v)) { LRect OldPos = v->GetPos(); Update.Union(&OldPos); if (v->Pour(Client)) { if (!v->Visible()) { v->Visible(true); } v->Invalidate(); Client.Subtract(&v->GetPos()); Update.Subtract(&v->GetPos()); } else { // non-pourable } } } for (int i=0; iMsg()) { case M_CLOSE: { if (Wnd) [Wnd.p performSelectorOnMainThread:@selector(onQuit) withObject:nil waitUntilDone:false]; else LAssert(!"No window?"); break; } case M_DESTROY: { delete this; return true; } } return LView::OnEvent(m); } bool LWindow::RegisterHook(LView *Target, LWindowHookType EventType, int Priority) { bool Status = false; if (Target && EventType) { ssize_t i = d->GetHookIndex(Target, true); if (i >= 0) { d->Hooks[i].Flags = EventType; Status = true; } } return Status; } bool LWindow::UnregisterHook(LView *Target) { ssize_t i = d->GetHookIndex(Target); if (i >= 0) { d->Hooks.DeleteAt(i); return true; } return false; } LViewI *LWindow::WindowFromPoint(int x, int y, int DebugDepth) { for (int i=0; iVisible()) { auto r = p->GetPos(); if (r.Overlap(x, y)) { // printf("WindowFromPoint got %s click (%i,%i)\n", p->GetClass(), x, y); return p->WindowFromPoint(x - r.x1, y - r.y1, DebugDepth ? DebugDepth + 1 : 0); } } } return LView::WindowFromPoint(x, y, DebugDepth ? DebugDepth + 1 : 0); } int LWindow::OnCommand(int Cmd, int Event, OsView SrcCtrl) { #if 0 OsView v; switch (Cmd) { case kHICommandCut: { OSErr e = GetKeyboardFocus(Wnd, (ControlRef*) &v); if (!e) LgiPostEvent(v, M_CUT); break; } case kHICommandCopy: { OSErr e = GetKeyboardFocus(Wnd, (ControlRef*) &v); if (!e) LgiPostEvent(v, M_COPY); break; } case kHICommandPaste: { OSErr e = GetKeyboardFocus(Wnd, (ControlRef*) &v); if (!e) LgiPostEvent(v, M_PASTE); break; } case 'dele': { OSErr e = GetKeyboardFocus(Wnd, (ControlRef*) &v); if (!e) LgiPostEvent(v, M_DELETE); break; } } #endif return 0; } void LWindow::OnTrayClick(LMouse &m) { if (m.Down() || m.IsContextMenu()) { LSubMenu RClick; OnTrayMenu(RClick); if (GetMouse(m, true)) { #if WINNATIVE SetForegroundWindow(Handle()); #endif int Result = RClick.Float(this, m.x, m.y); #if WINNATIVE PostMessage(Handle(), WM_NULL, 0, 0); #endif OnTrayMenuResult(Result); } } } bool LWindow::Obscured() { // LAutoPool Pool; if (!Wnd) return false; auto s = [Wnd.p occlusionState]; return !(s & NSWindowOcclusionStateVisible); } diff --git a/test/UnitTests/mac/Assets.xcassets/AppIcon.appiconset/Contents.json b/test/UnitTests/mac/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 --- /dev/null +++ b/test/UnitTests/mac/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,58 @@ +{ + "images" : [ + { + "idiom" : "mac", + "size" : "16x16", + "scale" : "1x" + }, + { + "idiom" : "mac", + "size" : "16x16", + "scale" : "2x" + }, + { + "idiom" : "mac", + "size" : "32x32", + "scale" : "1x" + }, + { + "idiom" : "mac", + "size" : "32x32", + "scale" : "2x" + }, + { + "idiom" : "mac", + "size" : "128x128", + "scale" : "1x" + }, + { + "idiom" : "mac", + "size" : "128x128", + "scale" : "2x" + }, + { + "idiom" : "mac", + "size" : "256x256", + "scale" : "1x" + }, + { + "idiom" : "mac", + "size" : "256x256", + "scale" : "2x" + }, + { + "idiom" : "mac", + "size" : "512x512", + "scale" : "1x" + }, + { + "idiom" : "mac", + "size" : "512x512", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/test/UnitTests/mac/Assets.xcassets/Contents.json b/test/UnitTests/mac/Assets.xcassets/Contents.json new file mode 100644 --- /dev/null +++ b/test/UnitTests/mac/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/test/UnitTests/mac/Base.lproj/MainMenu.xib b/test/UnitTests/mac/Base.lproj/MainMenu.xib new file mode 100644 --- /dev/null +++ b/test/UnitTests/mac/Base.lproj/MainMenu.xib @@ -0,0 +1,692 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Default + + + + + + + Left to Right + + + + + + + Right to Left + + + + + + + + + + + Default + + + + + + + Left to Right + + + + + + + Right to Left + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/UnitTests/mac/Info.plist b/test/UnitTests/mac/Info.plist new file mode 100644 --- /dev/null +++ b/test/UnitTests/mac/Info.plist @@ -0,0 +1,30 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIconFile + mac-icon.icns + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSMinimumSystemVersion + $(MACOSX_DEPLOYMENT_TARGET) + NSHumanReadableCopyright + Copyright © 2019 Memecode. All rights reserved. + NSPrincipalClass + NSApplication + + diff --git a/test/UnitTests/mac/UnitTests.entitlements b/test/UnitTests/mac/UnitTests.entitlements new file mode 100644 --- /dev/null +++ b/test/UnitTests/mac/UnitTests.entitlements @@ -0,0 +1,5 @@ + + + + + diff --git a/test/UnitTests/mac/UnitTests.xcodeproj/project.pbxproj b/test/UnitTests/mac/UnitTests.xcodeproj/project.pbxproj new file mode 100644 --- /dev/null +++ b/test/UnitTests/mac/UnitTests.xcodeproj/project.pbxproj @@ -0,0 +1,478 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 50; + objects = { + +/* Begin PBXBuildFile section */ + 343FB8952386621600797ABC /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 343FB8942386621600797ABC /* Assets.xcassets */; }; + 343FB8C62386638C00797ABC /* LgiCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 343FB8A92386627A00797ABC /* LgiCocoa.framework */; }; + 343FB8C82386639C00797ABC /* LgiCocoa.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 343FB8A92386627A00797ABC /* LgiCocoa.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 349F93902B92C590002410A2 /* XmlTest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 349F93832B92C590002410A2 /* XmlTest.cpp */; }; + 349F93912B92C590002410A2 /* UnitTests.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 349F93842B92C590002410A2 /* UnitTests.cpp */; }; + 349F93922B92C590002410A2 /* AutoPtrTest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 349F93852B92C590002410A2 /* AutoPtrTest.cpp */; }; + 349F93932B92C590002410A2 /* NetworkTests.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 349F93862B92C590002410A2 /* NetworkTests.cpp */; }; + 349F93942B92C590002410A2 /* StringClassTests.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 349F93882B92C590002410A2 /* StringClassTests.cpp */; }; + 349F93952B92C590002410A2 /* StringTests.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 349F93892B92C590002410A2 /* StringTests.cpp */; }; + 349F93962B92C590002410A2 /* RangeTest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 349F938A2B92C590002410A2 /* RangeTest.cpp */; }; + 349F93972B92C590002410A2 /* ContainerTests.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 349F938B2B92C590002410A2 /* ContainerTests.cpp */; }; + 349F93982B92C590002410A2 /* MatrixTest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 349F938C2B92C590002410A2 /* MatrixTest.cpp */; }; + 349F93992B92C590002410A2 /* JsonTest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 349F938D2B92C590002410A2 /* JsonTest.cpp */; }; + 349F939A2B92C590002410A2 /* CssTest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 349F938E2B92C590002410A2 /* CssTest.cpp */; }; + 349F939B2B92C590002410A2 /* BitsTest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 349F938F2B92C590002410A2 /* BitsTest.cpp */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 343FB8A82386627A00797ABC /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 343FB8A42386627A00797ABC /* LgiCocoa.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 3477C2681CBF020F0028B84B; + remoteInfo = LgiCocoa; + }; + 343FB8C32386638800797ABC /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 343FB8A42386627A00797ABC /* LgiCocoa.xcodeproj */; + proxyType = 1; + remoteGlobalIDString = 3477C2671CBF020F0028B84B; + remoteInfo = LgiCocoa; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 343FB8C72386639100797ABC /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + 343FB8C82386639C00797ABC /* LgiCocoa.framework in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 343FB88E2386621400797ABC /* UnitTests.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = UnitTests.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 343FB8942386621600797ABC /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 343FB8992386621600797ABC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 343FB89C2386621600797ABC /* UnitTests.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = UnitTests.entitlements; sourceTree = ""; }; + 343FB8A42386627A00797ABC /* LgiCocoa.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = LgiCocoa.xcodeproj; path = ../../../src/mac/cocoa/LgiCocoa.xcodeproj; sourceTree = ""; }; + 349F93832B92C590002410A2 /* XmlTest.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = XmlTest.cpp; path = ../src/XmlTest.cpp; sourceTree = ""; }; + 349F93842B92C590002410A2 /* UnitTests.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = UnitTests.cpp; path = ../src/UnitTests.cpp; sourceTree = ""; }; + 349F93852B92C590002410A2 /* AutoPtrTest.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = AutoPtrTest.cpp; path = ../src/AutoPtrTest.cpp; sourceTree = ""; }; + 349F93862B92C590002410A2 /* NetworkTests.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = NetworkTests.cpp; path = ../src/NetworkTests.cpp; sourceTree = ""; }; + 349F93872B92C590002410A2 /* UnitTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = UnitTests.h; path = ../src/UnitTests.h; sourceTree = ""; }; + 349F93882B92C590002410A2 /* StringClassTests.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = StringClassTests.cpp; path = ../src/StringClassTests.cpp; sourceTree = ""; }; + 349F93892B92C590002410A2 /* StringTests.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = StringTests.cpp; path = ../src/StringTests.cpp; sourceTree = ""; }; + 349F938A2B92C590002410A2 /* RangeTest.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = RangeTest.cpp; path = ../src/RangeTest.cpp; sourceTree = ""; }; + 349F938B2B92C590002410A2 /* ContainerTests.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ContainerTests.cpp; path = ../src/ContainerTests.cpp; sourceTree = ""; }; + 349F938C2B92C590002410A2 /* MatrixTest.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MatrixTest.cpp; path = ../src/MatrixTest.cpp; sourceTree = ""; }; + 349F938D2B92C590002410A2 /* JsonTest.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = JsonTest.cpp; path = ../src/JsonTest.cpp; sourceTree = ""; }; + 349F938E2B92C590002410A2 /* CssTest.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CssTest.cpp; path = ../src/CssTest.cpp; sourceTree = ""; }; + 349F938F2B92C590002410A2 /* BitsTest.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = BitsTest.cpp; path = ../src/BitsTest.cpp; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 343FB88B2386621400797ABC /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 343FB8C62386638C00797ABC /* LgiCocoa.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 343FB8852386621400797ABC = { + isa = PBXGroup; + children = ( + 343FB8A22386625100797ABC /* Cocoa */, + 343FB8B9238662DB00797ABC /* Code */, + 343FB8C52386638C00797ABC /* Frameworks */, + 343FB8A32386626700797ABC /* Lgi */, + 343FB88F2386621400797ABC /* Products */, + ); + sourceTree = ""; + }; + 343FB88F2386621400797ABC /* Products */ = { + isa = PBXGroup; + children = ( + 343FB88E2386621400797ABC /* UnitTests.app */, + ); + name = Products; + sourceTree = ""; + }; + 343FB8A22386625100797ABC /* Cocoa */ = { + isa = PBXGroup; + children = ( + 343FB8942386621600797ABC /* Assets.xcassets */, + 343FB8992386621600797ABC /* Info.plist */, + 343FB89C2386621600797ABC /* UnitTests.entitlements */, + ); + name = Cocoa; + sourceTree = ""; + }; + 343FB8A32386626700797ABC /* Lgi */ = { + isa = PBXGroup; + children = ( + ); + name = Lgi; + sourceTree = ""; + }; + 343FB8A52386627A00797ABC /* Products */ = { + isa = PBXGroup; + children = ( + 343FB8A92386627A00797ABC /* LgiCocoa.framework */, + ); + name = Products; + sourceTree = ""; + }; + 343FB8B9238662DB00797ABC /* Code */ = { + isa = PBXGroup; + children = ( + 349F93852B92C590002410A2 /* AutoPtrTest.cpp */, + 349F938F2B92C590002410A2 /* BitsTest.cpp */, + 349F938B2B92C590002410A2 /* ContainerTests.cpp */, + 349F938E2B92C590002410A2 /* CssTest.cpp */, + 349F938D2B92C590002410A2 /* JsonTest.cpp */, + 349F938C2B92C590002410A2 /* MatrixTest.cpp */, + 349F93862B92C590002410A2 /* NetworkTests.cpp */, + 349F938A2B92C590002410A2 /* RangeTest.cpp */, + 349F93882B92C590002410A2 /* StringClassTests.cpp */, + 349F93892B92C590002410A2 /* StringTests.cpp */, + 349F93842B92C590002410A2 /* UnitTests.cpp */, + 349F93872B92C590002410A2 /* UnitTests.h */, + 349F93832B92C590002410A2 /* XmlTest.cpp */, + ); + name = Code; + sourceTree = ""; + }; + 343FB8C52386638C00797ABC /* Frameworks */ = { + isa = PBXGroup; + children = ( + 343FB8A42386627A00797ABC /* LgiCocoa.xcodeproj */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 343FB88D2386621400797ABC /* UnitTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 343FB89F2386621600797ABC /* Build configuration list for PBXNativeTarget "UnitTests" */; + buildPhases = ( + 343FB88A2386621400797ABC /* Sources */, + 343FB88B2386621400797ABC /* Frameworks */, + 343FB88C2386621400797ABC /* Resources */, + 343FB8C72386639100797ABC /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + 343FB8C42386638800797ABC /* PBXTargetDependency */, + ); + name = UnitTests; + productName = UnitTests; + productReference = 343FB88E2386621400797ABC /* UnitTests.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 343FB8862386621400797ABC /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 1010; + ORGANIZATIONNAME = Memecode; + TargetAttributes = { + 343FB88D2386621400797ABC = { + CreatedOnToolsVersion = 10.1; + }; + }; + }; + buildConfigurationList = 343FB8892386621400797ABC /* Build configuration list for PBXProject "UnitTests" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 343FB8852386621400797ABC; + productRefGroup = 343FB88F2386621400797ABC /* Products */; + projectDirPath = ""; + projectReferences = ( + { + ProductGroup = 343FB8A52386627A00797ABC /* Products */; + ProjectRef = 343FB8A42386627A00797ABC /* LgiCocoa.xcodeproj */; + }, + ); + projectRoot = ""; + targets = ( + 343FB88D2386621400797ABC /* UnitTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXReferenceProxy section */ + 343FB8A92386627A00797ABC /* LgiCocoa.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = LgiCocoa.framework; + remoteRef = 343FB8A82386627A00797ABC /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; +/* End PBXReferenceProxy section */ + +/* Begin PBXResourcesBuildPhase section */ + 343FB88C2386621400797ABC /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 343FB8952386621600797ABC /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 343FB88A2386621400797ABC /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 349F939B2B92C590002410A2 /* BitsTest.cpp in Sources */, + 349F93922B92C590002410A2 /* AutoPtrTest.cpp in Sources */, + 349F93962B92C590002410A2 /* RangeTest.cpp in Sources */, + 349F93982B92C590002410A2 /* MatrixTest.cpp in Sources */, + 349F93952B92C590002410A2 /* StringTests.cpp in Sources */, + 349F93932B92C590002410A2 /* NetworkTests.cpp in Sources */, + 349F93912B92C590002410A2 /* UnitTests.cpp in Sources */, + 349F93992B92C590002410A2 /* JsonTest.cpp in Sources */, + 349F93942B92C590002410A2 /* StringClassTests.cpp in Sources */, + 349F93972B92C590002410A2 /* ContainerTests.cpp in Sources */, + 349F93902B92C590002410A2 /* XmlTest.cpp in Sources */, + 349F939A2B92C590002410A2 /* CssTest.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 343FB8C42386638800797ABC /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = LgiCocoa; + targetProxy = 343FB8C32386638800797ABC /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 343FB89D2386621600797ABC /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.13; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + }; + name = Debug; + }; + 343FB89E2386621600797ABC /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.13; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = macosx; + }; + name = Release; + }; + 343FB8A02386621600797ABC /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + LGI_UNIT_TESTS, + ); + HEADER_SEARCH_PATHS = ( + ../../../include, + ../../../include/lgi/mac/cocoa, + ../Code, + ../Resources, + ); + INFOPLIST_FILE = Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + MACOSX_DEPLOYMENT_TARGET = 10.15; + OTHER_CFLAGS = ( + "-DMAC", + "-DLGI_COCOA", + "-D_DEBUG", + ); + OTHER_CPLUSPLUSFLAGS = ( + "$(OTHER_CFLAGS)", + "-Wno-nullability-completeness", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.memecode.UnitTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 343FB8A12386621600797ABC /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + GCC_PREPROCESSOR_DEFINITIONS = LGI_UNIT_TESTS; + HEADER_SEARCH_PATHS = ( + ../../../include, + ../../../include/lgi/mac/cocoa, + ../Code, + ../Resources, + ); + INFOPLIST_FILE = Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + MACOSX_DEPLOYMENT_TARGET = 10.15; + OTHER_CFLAGS = ( + "-DMAC", + "-DLGI_COCOA", + ); + OTHER_CPLUSPLUSFLAGS = ( + "$(OTHER_CFLAGS)", + "-Wno-nullability-completeness", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.memecode.UnitTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 343FB8892386621400797ABC /* Build configuration list for PBXProject "UnitTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 343FB89D2386621600797ABC /* Debug */, + 343FB89E2386621600797ABC /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 343FB89F2386621600797ABC /* Build configuration list for PBXNativeTarget "UnitTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 343FB8A02386621600797ABC /* Debug */, + 343FB8A12386621600797ABC /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 343FB8862386621400797ABC /* Project object */; +} diff --git a/test/UnitTests/src/StringTests.cpp b/test/UnitTests/src/StringTests.cpp --- a/test/UnitTests/src/StringTests.cpp +++ b/test/UnitTests/src/StringTests.cpp @@ -1,91 +1,95 @@ -#include "lgi/common/Lgi.h" +#include "lgi/common/Lgi.h" #include "lgi/common/TextConvert.h" #include "UnitTests.h" class PrivLStringTests { public: }; LStringTests::LStringTests() : UnitTest("LStringTests") { d = new PrivLStringTests; } LStringTests::~LStringTests() { DeleteObj(d); } bool LStringTests::Run() { /* Things to test: bool Is8Bit(const char *Text); [[deprecated]] char *DecodeBase64Str(char *Str, ssize_t Len = -1); LString LDecodeBase64Str(LString Str); [[deprecated]] char *DecodeQuotedPrintableStr(char *Str, ssize_t Len = -1); LString LDecodeQuotedPrintableStr(LString Str); [[deprecated]] char *DecodeRfc2047(char *Str); LString LDecodeRfc2047(LString Str); LString LEncodeRfc2047(LString Str, const char *Charset, List *CharsetPrefs, ssize_t LineLength = 0); */ const char *EncodeResult1 = "=?iso-8859-1?Q?Beytullah_Gen=E7?="; const char *EncodeResult2 = "=?iso-8859-9?Q?Beytullah_Gen=E7?="; LString::Array CharsetPrefs; const char *Rfc2047Input = "Beytullah Genç"; const char *UtfInput = "Beytullah Genç"; const char *Charset = "windows-1252"; // No prefered charset testing: LAutoString result1( EncodeRfc2047(NewStr(Rfc2047Input), Charset) ); if (Stricmp(result1.Get(), EncodeResult1)) + { + printf("result1='%s'\n", result1.Get()); + printf("EncodeResult1='%s'\n", EncodeResult1); return FAIL(_FL, "EncodeRfc2047"); + } LAutoString decode1( DecodeRfc2047(NewStr(result1)) ); if (Strcmp(UtfInput, decode1.Get())) return FAIL(_FL, "DecodeRfc2047"); LString result2 = LEncodeRfc2047(Rfc2047Input, Charset); if (Stricmp(result2.Get(), EncodeResult1)) return FAIL(_FL, "LEncodeRfc2047"); LAutoString decode2( DecodeRfc2047(NewStr(result2)) ); if (Strcmp(UtfInput, decode2.Get())) return FAIL(_FL, "DecodeRfc2047"); // Redo tests with a charset preference set: CharsetPrefs.Add("iso-8859-9"); LAutoString result3( EncodeRfc2047(NewStr(Rfc2047Input), Charset, &CharsetPrefs)); if (Stricmp(result3.Get(), EncodeResult2)) return FAIL(_FL, "EncodeRfc2047"); LString result4 = LEncodeRfc2047(Rfc2047Input, Charset, &CharsetPrefs); if (Stricmp(result4.Get(), EncodeResult2)) return FAIL(_FL, "EncodeRfc2047"); // Quoted printable decode test: auto input5 = "=?UTF-8?q?=D0=92=D0=B0=D0=BC_=D0=BF=D1=80=D0=B8=D1=88=D0=BB=D0=BE_=D0=BD?= =?UTF-8?q?=D0=BE=D0=B2=D0=BE=D0=B5_=D1=81=D0=BE=D0=BE=D0=B1=D1=89=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D0=B5?="; auto result5 = LDecodeRfc2047(input5); auto encodeResult5 = L"Вам пришло новое сообщение"; LAutoWString decode5( Utf8ToWide(result5) ); if (Stricmp(encodeResult5, decode5.Get())) return FAIL(_FL, "LDecodeRfc2047"); // Mixing encoded and plain words: auto input6 = "test =?UTF-8?q?=D0=BE=D0=B2=D0=BE=D0=B5?= of words =?UTF-8?q?=D0=BE=D0=B2=D0=BE=D0=B5?="; auto result6 = LDecodeRfc2047(input6); auto encodeResult6 = L"test овое of words овое"; LAutoWString decode6( Utf8ToWide(result6) ); if (Stricmp(encodeResult6, decode6.Get())) return FAIL(_FL, "LDecodeRfc2047"); return true; } diff --git a/test/UnitTests/src/UnitTests.h b/test/UnitTests/src/UnitTests.h --- a/test/UnitTests/src/UnitTests.h +++ b/test/UnitTests/src/UnitTests.h @@ -1,58 +1,58 @@ #pragma once class UnitTest : public LBase { public: UnitTest(const char *name) { Name(name); } virtual ~UnitTest() {} virtual bool Run() = 0; bool FAIL(const char *File, int Line, const char *Msg) { LAssert(0); printf("%s:%i - Error in %s: %s\n", File, Line, Name(), Msg); return false; } }; #define DECL_TEST(name) \ class name : public UnitTest \ { \ class Priv##name *d; \ public: \ name(); \ ~name(); \ bool Run(); \ } DECL_TEST(LAutoPtrTest); DECL_TEST(LCssTest); DECL_TEST(LMatrixTest); DECL_TEST(LContainers); DECL_TEST(LStringClassTest); DECL_TEST(LStringTests); DECL_TEST(LRangeTest); DECL_TEST(JsonTest); DECL_TEST(LBitsTest); DECL_TEST(NetworkTests); DECL_TEST(XmlTest); class LDateTimeTest : public UnitTest { public: LDateTimeTest() : UnitTest("LDateTimeTest") {} bool Run() { #ifdef _DEBUG - return LDateTime_Test(); + return LDateTime::UnitTests(); #else #error "Requires _DEBUG" #endif } }; \ No newline at end of file diff --git a/test/UnitTests/UnitTests.sln b/test/UnitTests/win/UnitTests.sln rename from test/UnitTests/UnitTests.sln rename to test/UnitTests/win/UnitTests.sln --- a/test/UnitTests/UnitTests.sln +++ b/test/UnitTests/win/UnitTests.sln @@ -1,28 +1,28 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2019 VisualStudioVersion = 12.0.40629.0 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UnitTests", "UnitTests.vcxproj", "{18BF30E1-D77B-496E-8761-99A426DD3B41}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Lgi", "..\..\win\Lgi_vs2019.vcxproj", "{95DF9CA4-6D37-4A85-A648-80C2712E0DA1}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Lgi", "..\..\..\win\Lgi_vs2019.vcxproj", "{95DF9CA4-6D37-4A85-A648-80C2712E0DA1}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 Release|x64 = Release|x64 ReleaseNoOptimize|x64 = ReleaseNoOptimize|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {18BF30E1-D77B-496E-8761-99A426DD3B41}.Debug|x64.ActiveCfg = Debug|x64 {18BF30E1-D77B-496E-8761-99A426DD3B41}.Debug|x64.Build.0 = Debug|x64 {18BF30E1-D77B-496E-8761-99A426DD3B41}.Release|x64.ActiveCfg = Release|x64 {18BF30E1-D77B-496E-8761-99A426DD3B41}.ReleaseNoOptimize|x64.ActiveCfg = Release|x64 {95DF9CA4-6D37-4A85-A648-80C2712E0DA1}.Debug|x64.ActiveCfg = Debug|x64 {95DF9CA4-6D37-4A85-A648-80C2712E0DA1}.Debug|x64.Build.0 = Debug|x64 {95DF9CA4-6D37-4A85-A648-80C2712E0DA1}.Release|x64.ActiveCfg = Release|x64 {95DF9CA4-6D37-4A85-A648-80C2712E0DA1}.ReleaseNoOptimize|x64.ActiveCfg = ReleaseNoOptimize|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection EndGlobal diff --git a/test/UnitTests/UnitTests.vcxproj b/test/UnitTests/win/UnitTests.vcxproj rename from test/UnitTests/UnitTests.vcxproj rename to test/UnitTests/win/UnitTests.vcxproj --- a/test/UnitTests/UnitTests.vcxproj +++ b/test/UnitTests/win/UnitTests.vcxproj @@ -1,279 +1,280 @@  Debug Win32 Debug x64 Release Win32 Release x64 {18BF30E1-D77B-496E-8761-99A426DD3B41} 10.0 Application v142 false MultiByte Application v142 false MultiByte Application v142 false MultiByte Application v142 false MultiByte <_ProjectFileVersion>12.0.30501.0 $(Platform)$(Configuration)14\ $(Platform)$(Configuration)14\ true true $(Platform)$(Configuration)14\ $(Platform)$(Configuration)14\ $(Platform)$(Configuration)14\ $(Platform)$(Configuration)14\ false false $(Platform)$(Configuration)14\ $(Platform)$(Configuration)14\ .\Debug/UnitTests.tlb Disabled - ..\..\include;..\..\include\lgi\win;%(AdditionalIncludeDirectories) + ..\..\..\include;..\..\..\include\lgi\win;%(AdditionalIncludeDirectories) WIN32;_DEBUG;_CONSOLE;WINDOWS;LGI_UNIT_TESTS;%(PreprocessorDefinitions) true EnableFastChecks MultiThreadedDebugDLL true $(IntDir)/UnitTests.pch $(IntDir) $(IntDir) $(IntDir)vc$(PlatformToolsetVersion).pdb true ProgramDatabase _DEBUG;%(PreprocessorDefinitions) 0x0c09 $(OutDir)$(TargetName)$(TargetExt) true true $(IntDir)/UnitTests.pdb Console false MachineX86 true .\Debug/UnitTests.bsc .\Debug/UnitTests.tlb Disabled - ..\..\include;..\..\include\lgi\win;%(AdditionalIncludeDirectories) + ..\..\..\include;..\..\..\include\lgi\win;%(AdditionalIncludeDirectories) WIN32;_DEBUG;_CONSOLE;WINDOWS;LGI_UNIT_TESTS;%(PreprocessorDefinitions) EnableFastChecks MultiThreadedDebugDLL true $(IntDir)/UnitTests.pch $(IntDir) $(IntDir) $(IntDir)vc$(PlatformToolsetVersion).pdb true ProgramDatabase Level3 _DEBUG;%(PreprocessorDefinitions) 0x0c09 true true $(IntDir)/UnitTests.pdb Console false true .\Debug/UnitTests.bsc .\Release/UnitTests.tlb MinSpace OnlyExplicitInline ..\..\include;..\..\include\lgi\win;%(AdditionalIncludeDirectories) WIN32;NDEBUG;_CONSOLE;WINDOWS;LGI_UNIT_TESTS;%(PreprocessorDefinitions) true MultiThreadedDLL true true $(IntDir)/UnitTests.pch $(IntDir) $(IntDir) $(IntDir)vc$(PlatformToolsetVersion).pdb true NDEBUG;%(PreprocessorDefinitions) 0x0c09 $(OutDir)$(TargetName)$(TargetExt) true $(IntDir)/UnitTests.pdb Console false MachineX86 true .\Release/UnitTests.bsc .\Release/UnitTests.tlb MinSpace OnlyExplicitInline ..\..\include;..\..\include\lgi\win;%(AdditionalIncludeDirectories) WIN32;NDEBUG;_CONSOLE;WINDOWS;LGI_UNIT_TESTS;%(PreprocessorDefinitions) true MultiThreadedDLL true true $(IntDir)/UnitTests.pch $(IntDir) $(IntDir) $(IntDir)vc$(PlatformToolsetVersion).pdb true NDEBUG;%(PreprocessorDefinitions) 0x0c09 $(OutDir)$(TargetName)$(TargetExt) true $(IntDir)/UnitTests.pdb Console false true .\Release/UnitTests.bsc - - - - - - - - - - - - - - + + + + + + + + + + + + + + + - - - - - + + + + + - + {95df9ca4-6d37-4a85-a648-80c2712e0da1} \ No newline at end of file diff --git a/test/UnitTests/UnitTests.vcxproj.filters b/test/UnitTests/win/UnitTests.vcxproj.filters rename from test/UnitTests/UnitTests.vcxproj.filters rename to test/UnitTests/win/UnitTests.vcxproj.filters --- a/test/UnitTests/UnitTests.vcxproj.filters +++ b/test/UnitTests/win/UnitTests.vcxproj.filters @@ -1,80 +1,83 @@  {1b5d1f69-ef66-4844-baa5-25ec23d2151c} cpp;c;cxx;rc;def;r;odl;idl;hpj;bat {a734023f-ceb2-4882-9efc-5719c6bf4c8d} h;hpp;hxx;hm;inl {134d3911-e459-4919-8af8-82198f95a179} {8b34f4e6-17d2-42af-8c12-1386ba1acae0} Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Lgi Source Files\Testing Source Files Source Files Lgi Source Files + + Source Files + Header Files Header Files Header Files Source Files\Testing Lgi \ No newline at end of file