diff --git a/Ide/linux/Makefile.linux b/Ide/linux/Makefile.linux --- a/Ide/linux/Makefile.linux +++ b/Ide/linux/Makefile.linux @@ -1,423 +1,421 @@ #!/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) Flags = -fPIC -fno-inline -fpermissive -Wno-format-truncation ifeq ($(Build),Debug) Flags += -MMD -MP -g -std=c++14 Tag = d Defs = -D_DEBUG -DLINUX -D_REENTRANT -D_FILE_OFFSET_BITS=64 -DPOSIX Libs = \ -lmagic \ -lpthread \ `pkg-config --libs gtk+-3.0` \ -static-libgcc \ -llgi-gtk3$(Tag) \ -L../$(BuildDir) Inc = \ `pkg-config --cflags gtk+-3.0` \ -I./src \ -I./resources \ -I../include/lgi/linux/Gtk \ -I../include/lgi/linux \ -I../include \ -I../../../../codelib/openssl/include else Flags += -MMD -MP -s -Os -std=c++14 Defs = -DLINUX -D_REENTRANT -D_FILE_OFFSET_BITS=64 -DPOSIX Libs = \ -lmagic \ -lpthread \ `pkg-config --libs gtk+-3.0` \ -static-libgcc \ -llgi-gtk3$(Tag) \ -L../$(BuildDir) Inc = \ `pkg-config --cflags gtk+-3.0` \ -I./src \ -I./resources \ -I../include/lgi/linux/Gtk \ -I../include/lgi/linux \ -I../include \ -I../../../../codelib/openssl/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/Debugger.cpp \ src/DebugContext.cpp \ src/AddFtpFile.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) $(Flags) $(Defs) -c $< -o $@ $(BuildDir)/%.o: %.cpp mkdir -p $(@D) echo $(notdir $<) [$(Build)] $(CPP) $(Inc) $(Flags) $(Defs) -c $< -o $@ # Target # Executable target $(Target) : ../$(BuildDir)/liblgi-gtk3$(Tag).so $(Objects) mkdir -p $(BuildDir) @echo Linking $(Target) [$(Build)]... $(CPP) -Wl,-export-dynamic,-R. -o \ $(Target) $(Objects) $(Libs) @echo Done. ../$(BuildDir)/liblgi-gtk3$(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/LgiNetInc.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/NetTools.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/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/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/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/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/linux/Gtk/LgiOsClasses.h \ ../include/lgi/linux/Gtk/LgiOsDefs.h \ ../include/lgi/linux/Gtk/LgiWidget.h \ ../include/lgi/linux/Gtk/LgiWinManGlue.h \ ../include/lgi/linux/SymLookup.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/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/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/SubProcess.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/NetTools.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/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/linux/General/File.cpp \ ../src/linux/General/Mem.cpp \ ../src/linux/General/ShowFileProp_Linux.cpp \ ../src/linux/Gtk/Gdc2.cpp \ ../src/linux/Gtk/LgiWidget.cpp \ ../src/linux/Gtk/MemDC.cpp \ ../src/linux/Gtk/PrintDC.cpp \ ../src/linux/Gtk/ScreenDC.cpp \ ../src/linux/Lgi/App.cpp \ ../src/linux/Lgi/ClipBoard.cpp \ ../src/linux/Lgi/DragAndDrop.cpp \ ../src/linux/Lgi/General.cpp \ ../src/linux/Lgi/Layout.cpp \ ../src/linux/Lgi/Menu.cpp \ ../src/linux/Lgi/Printer.cpp \ ../src/linux/Lgi/Thread.cpp \ ../src/linux/Lgi/View.cpp \ ../src/linux/Lgi/Widgets.cpp \ ../src/linux/Lgi/Window.cpp export Build=$(Build); \ - $(MAKE) -C ../ -f linux/Makefile.linux + $(MAKE) -C ../ -f ./linux/Makefile.linux -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 "linux/Makefile.linux" clean + +make -C "../" -f "Makefile.linux" 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/Lvc/linux/Makefile.linux b/Lvc/linux/Makefile.linux --- a/Lvc/linux/Makefile.linux +++ b/Lvc/linux/Makefile.linux @@ -1,396 +1,394 @@ #!/usr/bin/make # # This makefile generated by LgiIde # http://www.memecode.com/lgi.php # .SILENT : CC = gcc CPP = g++ Target = ./lvc ifndef Build Build = Debug endif MakeDir := $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST)))) BuildDir = $(Build) -Flags = -fPIC -w -fno-inline -fpermissive +Flags = -fPIC -fno-inline -fpermissive -Wno-format-truncation ifeq ($(Build),Debug) Flags += -MMD -MP -g -std=c++14 Tag = d Defs = -D_DEBUG -DLINUX -D_REENTRANT -D_FILE_OFFSET_BITS=64 -DPOSIX Libs = \ -lssh \ -lmagic \ -static-libgcc \ `pkg-config --libs gtk+-3.0` \ -pthread \ -llgi-gtk3$(Tag) \ -L../$(BuildDir) Inc = \ -I./src \ -I./resources \ `pkg-config --cflags gtk+-3.0` \ -I../../../../../../usr/include/gstreamer-1.0 \ -I../include/lgi/linux/Gtk \ -I../include/lgi/linux \ -I../include else Flags += -MMD -MP -s -Os -std=c++14 Defs = -DLINUX -D_REENTRANT -D_FILE_OFFSET_BITS=64 -DPOSIX Libs = \ -lssh \ -lmagic \ -static-libgcc \ `pkg-config --libs gtk+-3.0` \ -pthread \ -llgi-gtk3$(Tag) \ -L../$(BuildDir) Inc = \ -I./src \ -I./resources \ `pkg-config --cflags gtk+-3.0` \ -I../../../../../../usr/include/gstreamer-1.0 \ -I../include/lgi/linux/Gtk \ -I../include/lgi/linux \ -I../include endif # Dependencies Source = src/VcFolder.cpp \ src/VcFile.cpp \ src/VcCommit.cpp \ src/SshConnection.cpp \ src/PatchViewer.cpp \ src/Main.cpp \ src/DropDownBtn.cpp \ src/BlameUi.cpp \ ../src/common/Widgets/ControlTree.cpp \ ../src/common/Text/XmlTreeUi.cpp \ ../src/common/Text/DeEscape.cpp \ ../src/common/Lgi/StructuredLog.cpp \ ../src/common/Lgi/LgiMain.cpp \ ../src/common/Gdc2/Filters/Png.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) $(Flags) $(Defs) -c $< -o $@ $(BuildDir)/%.o: %.cpp mkdir -p $(@D) echo $(notdir $<) [$(Build)] $(CPP) $(Inc) $(Flags) $(Defs) -c $< -o $@ # Target # Executable target $(Target) : ../$(BuildDir)/liblgi-gtk3$(Tag).so $(Objects) mkdir -p $(BuildDir) @echo Linking $(Target) [$(Build)]... $(CPP) -Wl,-export-dynamic,-R. -o \ $(Target) $(Objects) $(Libs) @echo Done. ../$(BuildDir)/liblgi-gtk3$(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/LgiNetInc.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/NetTools.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/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/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/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/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/linux/Gtk/LgiOsClasses.h \ ../include/lgi/linux/Gtk/LgiOsDefs.h \ ../include/lgi/linux/Gtk/LgiWidget.h \ ../include/lgi/linux/Gtk/LgiWinManGlue.h \ ../include/lgi/linux/SymLookup.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/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/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/SubProcess.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/NetTools.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/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/linux/General/File.cpp \ ../src/linux/General/Mem.cpp \ ../src/linux/General/ShowFileProp_Linux.cpp \ ../src/linux/Gtk/Gdc2.cpp \ ../src/linux/Gtk/LgiWidget.cpp \ ../src/linux/Gtk/MemDC.cpp \ ../src/linux/Gtk/PrintDC.cpp \ ../src/linux/Gtk/ScreenDC.cpp \ ../src/linux/Lgi/App.cpp \ ../src/linux/Lgi/ClipBoard.cpp \ ../src/linux/Lgi/DragAndDrop.cpp \ ../src/linux/Lgi/General.cpp \ ../src/linux/Lgi/Layout.cpp \ ../src/linux/Lgi/Menu.cpp \ ../src/linux/Lgi/Printer.cpp \ ../src/linux/Lgi/Thread.cpp \ ../src/linux/Lgi/View.cpp \ ../src/linux/Lgi/Widgets.cpp \ ../src/linux/Lgi/Window.cpp export Build=$(Build); \ - $(MAKE) -C ../ -f Makefile.linux + $(MAKE) -C ../ -f ./linux/Makefile.linux -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 "Makefile.linux" clean VPATH=$(BuildDir) \ ../src/common/Widgets \ ../src/common/Text \ ../src/common/Lgi \ ../src/common/Gdc2/Filters \ ./src diff --git a/Lvc/src/Main.cpp b/Lvc/src/Main.cpp --- a/Lvc/src/Main.cpp +++ b/Lvc/src/Main.cpp @@ -1,1718 +1,1718 @@ #include "lgi/common/Lgi.h" #include "lgi/common/TableLayout.h" #include "lgi/common/TextLog.h" #include "lgi/common/Button.h" #include "lgi/common/XmlTreeUi.h" #include "lgi/common/Tree.h" #include "lgi/common/FileSelect.h" #include "lgi/common/StructuredLog.h" #include "Lvc.h" #include "resdefs.h" #ifdef WINDOWS #include "resource.h" #endif ////////////////////////////////////////////////////////////////// const char *AppName = "Lvc"; #define DEFAULT_BUILD_FIX_MSG "Build fix." #define OPT_Hosts "Hosts" #define OPT_Host "Host" AppPriv::~AppPriv() { if (CurFolder) CurFolder->Empty(); } SshConnection *AppPriv::GetConnection(const char *Uri, bool Create) { LUri u(Uri); u.sPath.Empty(); auto s = u.ToString(); auto Conn = Connections.Find(s); if (!Conn && Create) Connections.Add(s, Conn = new SshConnection(Log, s, "matthew@*$ ")); return Conn; } VersionCtrl AppPriv::DetectVcs(VcFolder *Fld) { char p[MAX_PATH_LEN]; LUri u = Fld->GetUri(); if (!u.IsFile() || !u.sPath) { auto c = GetConnection(u.ToString()); if (!c) return VcNone; auto type = c->Types.Find(u.sPath); if (type) return type; c->DetectVcs(Fld); Fld->GetCss(true)->Color(LColour::Blue); Fld->Update(); return VcPending; } auto Path = u.sPath.Get(); #ifdef WINDOWS if (*Path == '/') Path++; #endif if (LMakePath(p, sizeof(p), Path, ".git") && LDirExists(p)) return VcGit; if (LMakePath(p, sizeof(p), Path, ".svn") && LDirExists(p)) return VcSvn; if (LMakePath(p, sizeof(p), Path, ".hg") && LDirExists(p)) return VcHg; if (LMakePath(p, sizeof(p), Path, "CVS") && LDirExists(p)) return VcCvs; return VcNone; } class DiffView : public LTextLog { public: DiffView(int id) : LTextLog(id) { } void PourStyle(size_t Start, ssize_t Length) { for (auto ln : LTextView3::Line) { if (!ln->c.IsValid()) { char16 *t = Text + ln->Start; if (*t == '+') { ln->c = LColour::Green; ln->Back.Rgb(245, 255, 245); } else if (*t == '-') { ln->c = LColour::Red; ln->Back.Rgb(255, 245, 245); } else if (*t == '@') { ln->c.Rgb(128, 128, 128); ln->Back.Rgb(235, 235, 235); } else ln->c = LColour(L_TEXT); } } } }; class ToolBar : public LLayout, public LResourceLoad { public: ToolBar() { LAutoString Name; LRect Pos; if (LoadFromResource(IDD_TOOLBAR, this, &Pos, &Name)) { OnPosChange(); } else LAssert(!"Missing toolbar resource"); } void OnCreate() { AttachChildren(); } void OnPosChange() { LRect Cli = GetClient(); LTableLayout *v; if (GetViewById(IDC_TABLE, v)) { v->SetPos(Cli); v->OnPosChange(); auto r = v->GetUsedArea(); if (r.Y() <= 1) r.Set(0, 0, 30, 30); // printf("Used = %s\n", r.GetStr()); LCss::Len NewSz(LCss::LenPx, (float)r.Y()+3); auto OldSz = GetCss(true)->Height(); if (OldSz != NewSz) { GetCss(true)->Height(NewSz); SendNotify(LNotifyTableLayoutRefresh); } } else LAssert(!"Missing table ctrl"); } void OnPaint(LSurface *pDC) { pDC->Colour(LColour(L_MED)); pDC->Rectangle(); } }; class CommitCtrls : public LLayout, public LResourceLoad { public: CommitCtrls() { LAutoString Name; LRect Pos; if (LoadFromResource(IDD_COMMIT, this, &Pos, &Name)) { LTableLayout *v; if (GetViewById(IDC_COMMIT_TABLE, v)) { v->GetCss(true)->PaddingRight("8px"); LRect r = v->GetPos(); r.Offset(-r.x1, -r.y1); r.x2++; v->SetPos(r); v->OnPosChange(); r = v->GetUsedArea(); if (r.Y() <= 1) r.Set(0, 0, 30, 30); GetCss(true)->Height(LCss::Len(LCss::LenPx, (float)r.Y())); } else LAssert(!"Missing table ctrl"); } else LAssert(!"Missing toolbar resource"); } void OnPosChange() { LTableLayout *v; if (GetViewById(IDC_COMMIT_TABLE, v)) v->SetPos(GetClient()); } void OnCreate() { AttachChildren(); } }; LString::Array GetProgramsInPath(const char *Program) { LString::Array Bin; LString Prog = Program; #ifdef WINDOWS Prog += LGI_EXECUTABLE_EXT; #endif LString::Array a = LGetPath(); for (auto p : a) { LFile::Path c(p, Prog); if (c.Exists()) Bin.New() = c.GetFull(); } return Bin; } class OptionsDlg : public LDialog, public LXmlTreeUi { LOptionsFile &Opts; public: OptionsDlg(LViewI *Parent, LOptionsFile &opts) : Opts(opts) { SetParent(Parent); Map("svn-path", IDC_SVN, GV_STRING); Map("svn-limit", IDC_SVN_LIMIT); Map("git-path", IDC_GIT, GV_STRING); Map("git-limit", IDC_GIT_LIMIT); Map("hg-path", IDC_HG, GV_STRING); Map("hg-limit", IDC_HG_LIMIT); Map("cvs-path", IDC_CVS, GV_STRING); Map("cvs-limit", IDC_CVS_LIMIT); if (LoadFromResource(IDD_OPTIONS)) { MoveSameScreen(Parent); Convert(&Opts, this, true); } } void Browse(int EditId) { auto s = new LFileSelect; s->Parent(this); s->Open([this, EditId](auto s, auto status) { if (status) SetCtrlName(EditId, s->Name()); delete s; }); } void BrowseFiles(LViewI *Ctrl, const char *Bin, int EditId) { LRect Pos = Ctrl->GetPos(); LPoint Pt(Pos.x1, Pos.y2 + 1); PointToScreen(Pt); LSubMenu s; LString::Array Bins = GetProgramsInPath(Bin); for (unsigned i=0; i= 1000) { LString Bin = Bins[Cmd - 1000]; if (Bin) SetCtrlName(EditId, Bin); } break; } } } int OnNotify(LViewI *Ctrl, LNotification n) { switch (Ctrl->GetId()) { case IDC_SVN_BROWSE: BrowseFiles(Ctrl, "svn", IDC_SVN); break; case IDC_GIT_BROWSE: BrowseFiles(Ctrl, "git", IDC_GIT); break; case IDC_HG_BROWSE: BrowseFiles(Ctrl, "hg", IDC_HG); break; case IDC_CVS_BROWSE: BrowseFiles(Ctrl, "cvs", IDC_CVS); break; case IDOK: Convert(&Opts, this, false); // fall case IDCANCEL: { EndModal(Ctrl->GetId() == IDOK); break; } } return LDialog::OnNotify(Ctrl, n); } }; int CommitDataCmp(VcCommit **_a, VcCommit **_b) { auto a = *_a; auto b = *_b; return a->GetTs().Compare(&b->GetTs()); } LString::Array AppPriv::GetCommitRange() { LString::Array r; if (Commits) { LArray Sel; Commits->GetSelection(Sel); if (Sel.Length() > 1) { Sel.Sort(CommitDataCmp); r.Add(Sel[0]->GetRev()); r.Add(Sel.Last()->GetRev()); } else { r.Add(Sel[0]->GetRev()); } } else LAssert(!"No commit list ptr"); return r; } LArray AppPriv::GetRevs(LString::Array &Revs) { LArray a; for (auto i = Commits->begin(); i != Commits->end(); i++) { VcCommit *c = dynamic_cast(*i); if (c) { for (auto r: Revs) { if (r.Equals(c->GetRev())) { a.Add(c); break; } } } } return a; } class CommitList : public LList { public: CommitList(int id) : LList(id, 0, 0, 200, 200) { } void SelectRevisions(LString::Array &Revs, const char *BranchHint = NULL) { VcCommit *Scroll = NULL; LArray Matches; for (auto i: *this) { VcCommit *item = dynamic_cast(i); if (!item) continue; bool IsMatch = false; for (auto r: Revs) { if (item->IsRev(r)) { IsMatch = true; break; } } if (IsMatch) Matches.Add(item); else if (i->Select()) i->Select(false); } for (auto item: Matches) { auto b = item->GetBranch(); if (BranchHint) { if (!b || Stricmp(b, BranchHint)) continue; } else if (b) { continue; } if (!Scroll) Scroll = item; item->Select(true); } if (!Scroll && Matches.Length() > 0) { Scroll = Matches[0]; Scroll->Select(true); } if (Scroll) Scroll->ScrollTo(); } bool OnKey(LKey &k) { switch (k.c16) { case 'p': case 'P': { if (k.Down()) { LArray Sel; GetSelection(Sel); if (Sel.Length()) { auto first = Sel[0]; auto branch = first->GetBranch(); auto p = first->GetParents(); if (p->Length() == 0) break; for (auto c:Sel) c->Select(false); SelectRevisions(*p, branch); } } return true; } case 'c': case 'C': { if (k.Down()) { LArray Sel; GetSelection(Sel); if (Sel.Length()) { LHashTbl,VcCommit*> Map; for (auto s:Sel) Map.Add(s->GetRev(), s); LString::Array n; for (auto it = begin(); it != end(); it++) { VcCommit *c = dynamic_cast(*it); if (c) { for (auto r:*c->GetParents()) { if (Map.Find(r)) { n.Add(c->GetRev()); break; } } } } for (auto c:Sel) c->Select(false); SelectRevisions(n, Sel[0]->GetBranch()); } } return true; } } return LList::OnKey(k); } }; int LstCmp(LListItem *a, LListItem *b, int Col) { VcCommit *A = dynamic_cast(a); VcCommit *B = dynamic_cast(b); if (A == NULL || B == NULL) { return (A ? 1 : -1) - (B ? 1 : -1); } auto f = A->GetFolder(); auto flds = f->GetFields(); if (!flds.Length()) { LgiTrace("%s:%i - No fields?\n", _FL); return 0; } auto fld = flds[Col]; switch (fld) { case LGraph: case LIndex: case LParents: case LRevision: default: return (int) (B->GetIndex() - A->GetIndex()); case LBranch: case LAuthor: case LMessageTxt: return Stricmp(A->GetFieldText(fld), B->GetFieldText(fld)); case LTimeStamp: return B->GetTs().Compare(&A->GetTs()); } return 0; } struct TestThread : public LThread { public: TestThread() : LThread("test") { Run(); } int Main() { auto Path = LGetPath(); LSubProcess p("python", "/Users/matthew/CodeLib/test.py"); auto t = LString(LGI_PATH_SEPARATOR).Join(Path); for (auto s: Path) printf("s: %s\n", s.Get()); p.SetEnvironment("PATH", t); if (p.Start()) { LStringPipe s; p.Communicate(&s); printf("Test: %s\n", s.NewLStr().Get()); } return 0; } }; class RemoteFolderDlg : public LDialog { class App *app; LTree *tree; struct SshHost *root, *newhost; LXmlTreeUi Ui; public: LString Uri; RemoteFolderDlg(App *application); ~RemoteFolderDlg(); int OnNotify(LViewI *Ctrl, LNotification n); }; class VcDiffFile : public LTreeItem { AppPriv *d; LString File; public: VcDiffFile(AppPriv *priv, LString file) : d(priv), File(file) { } const char *GetText(int i = 0) override { - return i ? NULL : File; + return i ? NULL : File.Get(); } LString StripFirst(LString s) { return s.Replace("\\","/").SplitDelimit("/", 1).Last(); } void Select(bool s) override { LTreeItem::Select(s); if (s) { d->Files->Empty(); d->Diff->Name(NULL); LFile in(File, O_READ); LString s = in.Read(); if (!s) return; LString NewLine("\n"); LString::Array a = s.Replace("\r").Split("\n"); LArray index; LString oldName, newName; LString::Array Diff; VcFile *f = NULL; bool InPreamble = false; bool InDiff = false; for (unsigned i=0; iSetDiff(NewLine.Join(Diff)); f->Select(false); } Diff.Empty(); oldName.Empty(); newName.Empty(); InDiff = false; InPreamble = true; } else if (!Strnicmp(Ln, "Index", 5)) { if (InPreamble) index = a[i].SplitDelimit(": ", 1).Slice(1); } else if (!strncmp(Ln, "--- ", 4)) { auto p = a[i].SplitDelimit(" \t", 1); if (p.Length() > 1) oldName = p[1]; } else if (!strncmp(Ln, "+++ ", 4)) { auto p = a[i].SplitDelimit(" \t", 1); if (p.Length() > 1) newName = p[1]; if (oldName && newName) { InDiff = true; InPreamble = false; f = d->FindFile(newName); if (!f) - f = new VcFile(d, NULL, NULL, false); + f = new VcFile(d, NULL, LString(), false); const char *nullFn = "dev/null"; if (newName.Find(nullFn) >= 0) { // Delete f->SetText(StripFirst(oldName), COL_FILENAME); f->SetText("D", COL_STATE); } else { f->SetText(StripFirst(newName), COL_FILENAME); if (oldName.Find(nullFn) >= 0) // Add f->SetText("A", COL_STATE); else // Modify f->SetText("M", COL_STATE); } f->GetStatus(); d->Files->Insert(f); } } else if (!_strnicmp(Ln, "------", 6)) { InPreamble = !InPreamble; } else if (!_strnicmp(Ln, "======", 6)) { InPreamble = false; InDiff = true; } else if (InDiff) { Diff.Add(a[i]); } } if (f && Diff.Length()) { f->SetDiff(NewLine.Join(Diff)); Diff.Empty(); } } } }; class App : public LWindow, public AppPriv { LAutoPtr ImgLst; LBox *FoldersBox = NULL; bool CallMethod(const char *MethodName, LVariant *ReturnValue, LArray &Args) { if (!Stricmp(MethodName, METHOD_GetContext)) { *ReturnValue = (AppPriv*)this; return true; } return false; } public: App() { LString AppRev; AppRev.Printf("%s v%s", AppName, APP_VERSION); Name(AppRev); LRect r(0, 0, 1400, 800); SetPos(r); MoveToCenter(); SetQuitOnClose(true); Opts.SerializeFile(false); SerializeState(&Opts, "WndPos", true); #ifdef WINDOWS SetIcon(MAKEINTRESOURCEA(IDI_ICON1)); #else SetIcon("icon32.png"); #endif ImgLst.Reset(LLoadImageList("image-list.png", 16, 16)); if (Attach(0)) { if ((Menu = new LMenu)) { Menu->SetPrefAndAboutItems(IDM_OPTIONS, IDM_ABOUT); Menu->Attach(this); Menu->Load(this, "IDM_MENU"); } LBox *ToolsBox = new LBox(IDC_TOOLS_BOX, true, "ToolsBox"); FoldersBox = new LBox(IDC_FOLDERS_BOX, false, "FoldersBox"); LBox *CommitsBox = new LBox(IDC_COMMITS_BOX, true, "CommitsBox"); ToolBar *Tools = new ToolBar; ToolsBox->Attach(this); Tools->Attach(ToolsBox); FoldersBox->Attach(ToolsBox); auto FolderLayout = new LTableLayout(IDC_FOLDER_TBL); auto c = FolderLayout->GetCell(0, 0, true, 2); Tree = new LTree(IDC_TREE, 0, 0, 320, 200); Tree->SetImageList(ImgLst, false); Tree->ShowColumnHeader(true); Tree->AddColumn("Folder", 250); Tree->AddColumn("Counts", 50); c->Add(Tree); c = FolderLayout->GetCell(0, 1); c->Add(new LEdit(IDC_FILTER_FOLDERS, 0, 0, -1, -1)); c = FolderLayout->GetCell(1, 1); c->Add(new LButton(IDC_CLEAR_FILTER_FOLDERS, 0, 0, -1, -1, "x")); FolderLayout->Attach(FoldersBox); CommitsBox->Attach(FoldersBox); auto CommitsLayout = new LTableLayout(IDC_COMMITS_TBL); c = CommitsLayout->GetCell(0, 0, true, 2); Commits = new CommitList(IDC_LIST); c->Add(Commits); c = CommitsLayout->GetCell(0, 1); c->Add(new LEdit(IDC_FILTER_COMMITS, 0, 0, -1, -1)); c = CommitsLayout->GetCell(1, 1); c->Add(new LButton(IDC_CLEAR_FILTER_COMMITS, 0, 0, -1, -1, "x")); CommitsLayout->Attach(CommitsBox); CommitsLayout->GetCss(true)->Height("40%"); LBox *FilesBox = new LBox(IDC_FILES_BOX, false); FilesBox->Attach(CommitsBox); auto FilesLayout = new LTableLayout(IDC_FILES_TBL); c = FilesLayout->GetCell(0, 0, true, 2); Files = new LList(IDC_FILES, 0, 0, 200, 200); Files->AddColumn("[ ]", 30); Files->AddColumn("State", 100); Files->AddColumn("Name", 400); c->Add(Files); c = FilesLayout->GetCell(0, 1); c->Add(new LEdit(IDC_FILTER_FILES, 0, 0, -1, -1)); c = FilesLayout->GetCell(1, 1); c->Add(new LButton(IDC_CLEAR_FILTER_FILES, 0, 0, -1, -1, "x")); FilesLayout->GetCss(true)->Width("35%"); FilesLayout->Attach(FilesBox); LBox *MsgBox = new LBox(IDC_MSG_BOX, true); MsgBox->Attach(FilesBox); CommitCtrls *Commit = new CommitCtrls; Commit->Attach(MsgBox); Commit->GetCss(true)->Height("25%"); if (Commit->GetViewById(IDC_MSG, Msg)) { LTextView3 *Tv = dynamic_cast(Msg); if (Tv) { Tv->Sunken(true); Tv->SetWrapType(TEXTED_WRAP_NONE); } } else LAssert(!"No ctrl?"); Tabs = new LTabView(IDC_TAB_VIEW); Tabs->Attach(MsgBox); const char *Style = "Padding: 0px 8px 8px 0px"; Tabs->GetCss(true)->Parse(Style); LTabPage *p = Tabs->Append("Diff"); p->Append(Diff = new DiffView(IDC_TXT)); // Diff->Sunken(true); Diff->SetWrapType(TEXTED_WRAP_NONE); p = Tabs->Append("Log"); p->Append(Log = new LTextLog(IDC_LOG)); // Log->Sunken(true); Log->SetWrapType(TEXTED_WRAP_NONE); SetCtrlValue(IDC_UPDATE, true); AttachChildren(); Visible(true); } LXmlTag *f = Opts.LockTag(OPT_Folders, _FL); if (!f) { Opts.CreateTag(OPT_Folders); f = Opts.LockTag(OPT_Folders, _FL); } if (f) { bool Req[VcMax] = {0}; for (auto c: f->Children) { if (c->IsTag(OPT_Folder)) { auto f = new VcFolder(this, c); Tree->Insert(f); if (!Req[f->GetType()]) { Req[f->GetType()] = true; f->GetVersion(); } } } Opts.Unlock(); LRect Large(0, 0, 2000, 200); Tree->SetPos(Large); Tree->ResizeColumnsToContent(); LItemColumn *c; int i = 0, px = 0; while ((c = Tree->ColumnAt(i++))) { px += c->Width(); } FoldersBox->Value(MAX(320, px + 20)); // new TestThread(); } SetPulse(200); DropTarget(true); } ~App() { SerializeState(&Opts, "WndPos", false); SaveFolders(); } void SaveFolders() { LXmlTag *f = Opts.LockTag(OPT_Folders, _FL); if (!f) return; f->EmptyChildren(); for (auto i: *Tree) { VcFolder *vcf = dynamic_cast(i); if (vcf) f->InsertTag(vcf->Save()); } Opts.Unlock(); Opts.SerializeFile(true); } LMessage::Result OnEvent(LMessage *Msg) { switch (Msg->Msg()) { case M_RESPONSE: { SshConnection::HandleMsg(Msg); break; } case M_HANDLE_CALLBACK: { LAutoPtr Pc((ProcessCallback*)Msg->A()); if (Pc) Pc->OnComplete(); break; } } return LWindow::OnEvent(Msg); } void OnReceiveFiles(LArray &Files) { for (auto f : Files) { if (LDirExists(f)) OpenLocalFolder(f); } } int OnCommand(int Cmd, int Event, OsView Wnd) { switch (Cmd) { case IDM_PATCH_VIEWER: { OpenPatchViewer(this, &Opts); break; } case IDM_OPEN_LOCAL: { OpenLocalFolder(); break; } case IDM_OPEN_REMOTE: { OpenRemoteFolder(); break; } case IDM_OPEN_DIFF: { auto s = new LFileSelect; s->Parent(this); s->Open([this](auto dlg, auto status) { if (status) OpenDiff(dlg->Name()); delete dlg; }); break; } case IDM_OPTIONS: { auto Dlg = new OptionsDlg(this, Opts); Dlg->DoModal([](auto dlg, auto ctrlId) { delete dlg; }); break; } case IDM_FIND: { auto i = new LInput(this, "", "Search string:"); i->DoModal([this, i](auto dlg, auto ctrlId) { if (ctrlId == IDOK) { LString::Array Revs; Revs.Add(i->GetStr()); CommitList *cl; if (GetViewById(IDC_LIST, cl)) cl->SelectRevisions(Revs); } delete dlg; }); break; } case IDM_UNTRACKED: { auto mi = GetMenu()->FindItem(IDM_UNTRACKED); if (!mi) break; mi->Checked(!mi->Checked()); LArray Flds; Tree->GetSelection(Flds); for (auto f : Flds) { f->Refresh(); } break; } case IDM_REFRESH: { LArray Flds; Tree->GetSelection(Flds); for (auto f: Flds) f->Refresh(); break; } case IDM_PULL: { LArray Flds; Tree->GetSelection(Flds); for (auto f: Flds) f->Pull(); break; } case IDM_PUSH: { LArray Flds; Tree->GetSelection(Flds); for (auto f: Flds) f->Push(); break; } case IDM_STATUS: { LArray Flds; Tree->GetSelection(Flds); for (auto f: Flds) f->FolderStatus(); break; } case IDM_UPDATE_SUBS: { LArray Flds; Tree->GetSelection(Flds); for (auto f: Flds) f->UpdateSubs(); break; break; } case IDM_EXIT: { LCloseApp(); break; } } return 0; } void OnPulse() { for (auto i:*Tree) { VcFolder *vcf = dynamic_cast(i); if (vcf) vcf->OnPulse(); } } void OpenLocalFolder(const char *Fld = NULL) { auto Load = [this](const char *Fld) { // Check the folder isn't already loaded... bool Has = false; LArray Folders; Tree->GetAll(Folders); for (auto f: Folders) { if (f->GetUri().IsFile() && !Stricmp(f->LocalPath(), Fld)) { Has = true; break; } } if (!Has) { LUri u; u.SetFile(Fld); auto f = new VcFolder(this, u.ToString()); if (f) { Tree->Insert(f); SaveFolders(); } } }; if (!Fld) { auto s = new LFileSelect; s->Parent(this); s->OpenFolder([this, Load](auto s, auto status) { if (status) Load(s->Name()); delete s; }); } else Load(Fld); } void OpenRemoteFolder() { auto Dlg = new RemoteFolderDlg(this); Dlg->DoModal([this, Dlg](auto dlg, auto status) { if (status) { Tree->Insert(new VcFolder(this, Dlg->Uri)); SaveFolders(); } delete dlg; }); } void OpenDiff(const char *File) { Tree->Insert(new VcDiffFile(this, File)); } void OnFilterFolders() { if (!Tree) return; for (auto i = Tree->GetChild(); i; i = i->GetNext()) { auto n = i->GetText(); bool vis = !FolderFilter || Stristr(n, FolderFilter.Get()) != NULL; i->GetCss(true)->Display(vis ? LCss::DispBlock : LCss::DispNone); } Tree->UpdateAllItems(); Tree->Invalidate(); } void OnFilterCommits() { if (!Commits) return; LArray a; if (!Commits->GetAll(a)) return; auto cols = Commits->GetColumns(); for (auto i: a) { bool vis = !CommitFilter; for (int c=1; !vis && cGetText(c); if (Stristr(txt, CommitFilter.Get())) vis = true; } i->GetCss(true)->Display(vis ? LCss::DispBlock : LCss::DispNone); } Commits->UpdateAllItems(); Commits->Invalidate(); } void OnFilterFiles() { VcFolder *f = dynamic_cast(Tree->Selection()); if (f) f->FilterCurrentFiles(); } int OnNotify(LViewI *c, LNotification n) { switch (c->GetId()) { case IDC_CLEAR_FILTER_FOLDERS: { SetCtrlName(IDC_FILTER_FOLDERS, NULL); // Fall through } case IDC_FILTER_FOLDERS: { if (n.Type == LNotifyEscapeKey) SetCtrlName(IDC_FILTER_FOLDERS, NULL); LString n = GetCtrlName(IDC_FILTER_FOLDERS); if (n != FolderFilter) { FolderFilter = n; OnFilterFolders(); } break; } case IDC_CLEAR_FILTER_COMMITS: { SetCtrlName(IDC_FILTER_COMMITS, NULL); // Fall through } case IDC_FILTER_COMMITS: { if (n.Type == LNotifyEscapeKey) SetCtrlName(IDC_FILTER_COMMITS, NULL); LString n = GetCtrlName(IDC_FILTER_COMMITS); if (n != CommitFilter) { CommitFilter = n; OnFilterCommits(); } break; } case IDC_CLEAR_FILTER_FILES: { SetCtrlName(IDC_FILTER_FILES, NULL); // Fall through } case IDC_FILTER_FILES: { if (n.Type == LNotifyEscapeKey) SetCtrlName(IDC_FILTER_FILES, NULL); LString n = GetCtrlName(IDC_FILTER_FILES); if (n != FileFilter) { FileFilter = n; OnFilterFiles(); } break; } case IDC_FILES: { switch (n.Type) { case LNotifyItemColumnClicked: { int Col = -1; LMouse m; if (Files->GetColumnClickInfo(Col, m)) { if (Col == 0) { // Select / deselect all check boxes.. List n; if (Files->GetAll(n)) { bool Checked = false; for (auto f: n) Checked |= f->Checked() > 0; for (auto f: n) f->Checked(Checked ? 0 : 1); } } } break; } default: break; } break; } case IDC_OPEN: { OpenLocalFolder(); break; } case IDC_TREE: { switch (n.Type) { case LNotifyContainerClick: { LMouse m; c->GetMouse(m); if (m.Right()) { LSubMenu s; s.AppendItem("Add Local", IDM_ADD_LOCAL); s.AppendItem("Add Remote", IDM_ADD_REMOTE); int Cmd = s.Float(c->GetGView(), m); switch (Cmd) { case IDM_ADD_LOCAL: { OpenLocalFolder(); break; } case IDM_ADD_REMOTE: { OpenRemoteFolder(); break; } } } break; } case (LNotifyType)LvcCommandStart: { SetCtrlEnabled(IDC_PUSH, false); SetCtrlEnabled(IDC_PULL, false); SetCtrlEnabled(IDC_PULL_ALL, false); break; } case (LNotifyType)LvcCommandEnd: { SetCtrlEnabled(IDC_PUSH, true); SetCtrlEnabled(IDC_PULL, true); SetCtrlEnabled(IDC_PULL_ALL, true); break; } default: break; } break; } case IDC_COMMIT_AND_PUSH: case IDC_COMMIT: { auto BuildFix = GetCtrlValue(IDC_BUILD_FIX); const char *Msg = GetCtrlName(IDC_MSG); if (BuildFix || ValidStr(Msg)) { auto Sel = Tree->Selection(); if (Sel) { VcFolder *f = dynamic_cast(Sel); if (!f) { for (auto p = Sel->GetParent(); p; p = p->GetParent()) { f = dynamic_cast(p); if (f) break; } } if (f) { auto Branch = GetCtrlName(IDC_BRANCH); bool AndPush = c->GetId() == IDC_COMMIT_AND_PUSH; f->Commit(BuildFix ? DEFAULT_BUILD_FIX_MSG : Msg, ValidStr(Branch) ? Branch : NULL, AndPush); } } } else LgiMsg(this, "No message for commit.", AppName); break; } case IDC_PUSH: { VcFolder *f = dynamic_cast(Tree->Selection()); if (f) f->Push(); break; } case IDC_PULL: { VcFolder *f = dynamic_cast(Tree->Selection()); if (f) f->Pull(); break; } case IDC_PULL_ALL: { LArray Folders; Tree->GetAll(Folders); bool AndUpdate = GetCtrlValue(IDC_UPDATE) != 0; for (auto f : Folders) { f->Pull(AndUpdate, LogSilo); } break; } case IDC_STATUS: { LArray Folders; Tree->GetAll(Folders); for (auto f : Folders) { f->FolderStatus(); } break; } case IDC_HEADS: { if (n.Type == LNotifyValueChanged) { auto Revs = LString(c->Name()).SplitDelimit(); CommitList *cl; if (GetViewById(IDC_LIST, cl)) cl->SelectRevisions(Revs); } break; } case IDC_LIST: { switch (n.Type) { case LNotifyItemColumnClicked: { int Col = -1; LMouse Ms; Commits->GetColumnClickInfo(Col, Ms); Commits->Sort(LstCmp, Col); break; } case LNotifyItemDoubleClick: { VcFolder *f = dynamic_cast(Tree->Selection()); if (!f) break; LArray s; if (Commits->GetSelection(s) && s.Length() == 1) f->OnUpdate(s[0]->GetRev()); break; } default: break; } break; } } return 0; } }; struct SshHost : public LTreeItem { LXmlTag *t; LString Host, User, Pass; SshHost(LXmlTag *tag = NULL) { t = tag; if (t) { Serialize(false); SetText(Host); } } void Serialize(bool WriteToTag) { if (WriteToTag) { LUri u; u.sProtocol = "ssh"; u.sHost = Host; u.sUser = User; u.sPass = Pass; t->SetContent(u.ToString()); } else { LUri u(t->GetContent()); if (!Stricmp(u.sProtocol.Get(), "ssh")) { Host = u.sHost; User = u.sUser; Pass = u.sPass; } } } }; RemoteFolderDlg::RemoteFolderDlg(App *application) : app(application), root(NULL), newhost(NULL), tree(NULL) { SetParent(app); LoadFromResource(IDD_REMOTE_FOLDER); if (GetViewById(IDC_HOSTS, tree)) { printf("tree=%p\n", tree); tree->Insert(root = new SshHost()); root->SetText("Ssh Hosts"); } else return; LViewI *v; if (GetViewById(IDC_HOSTNAME, v)) v->Focus(true); Ui.Map("Host", IDC_HOSTNAME); Ui.Map("User", IDC_USER); Ui.Map("Password", IDC_PASS); LXmlTag *hosts = app->Opts.LockTag(OPT_Hosts, _FL); if (hosts) { SshHost *h; for (auto c: hosts->Children) if (c->IsTag(OPT_Host) && (h = new SshHost(c))) root->Insert(h); app->Opts.Unlock(); } root->Insert(newhost = new SshHost()); newhost->SetText("New Host"); root->Expanded(true); newhost->Select(true); } RemoteFolderDlg::~RemoteFolderDlg() { } int RemoteFolderDlg::OnNotify(LViewI *Ctrl, LNotification n) { SshHost *cur = tree ? dynamic_cast(tree->Selection()) : NULL; #define CHECK_SPECIAL() \ if (cur == newhost) \ { \ root->Insert(cur = new SshHost()); \ cur->Select(true); \ } \ if (cur == root) \ break; switch (Ctrl->GetId()) { case IDC_HOSTS: { switch (n.Type) { case LNotifyItemSelect: { bool isRoot = cur == root; SetCtrlEnabled(IDC_HOSTNAME, !isRoot); SetCtrlEnabled(IDC_USER, !isRoot); SetCtrlEnabled(IDC_PASS, !isRoot); SetCtrlEnabled(IDC_DELETE, !isRoot && !(cur == newhost)); - SetCtrlName(IDC_HOSTNAME, cur ? cur->Host : NULL); - SetCtrlName(IDC_USER, cur ? cur->User : NULL); - SetCtrlName(IDC_PASS, cur ? cur->Pass : NULL); + SetCtrlName(IDC_HOSTNAME, cur ? cur->Host.Get() : NULL); + SetCtrlName(IDC_USER, cur ? cur->User.Get() : NULL); + SetCtrlName(IDC_PASS, cur ? cur->Pass.Get() : NULL); break; } default: break; } break; } case IDC_HOSTNAME: { CHECK_SPECIAL() if (cur) { cur->Host = Ctrl->Name(); cur->SetText(cur->Host ? cur->Host : ""); } break; } case IDC_DELETE: { auto sel = tree ? dynamic_cast(tree->Selection()) : NULL; if (!sel) break; LXmlTag *hosts = app->Opts.LockTag(OPT_Hosts, _FL); if (!hosts) { LAssert(!"Couldn't lock tag."); break; } if (hosts->Children.HasItem(sel->t)) { sel->t->RemoveTag(); DeleteObj(sel->t); delete sel; } app->Opts.Unlock(); break; } case IDC_USER: { CHECK_SPECIAL() if (cur) cur->User = Ctrl->Name(); break; } case IDC_PASS: { CHECK_SPECIAL() if (cur) cur->Pass = Ctrl->Name(); break; } case IDOK: { LXmlTag *hosts; if (!(hosts = app->Opts.LockTag(OPT_Hosts, _FL))) { if (!(app->Opts.CreateTag(OPT_Hosts) && (hosts = app->Opts.LockTag(OPT_Hosts, _FL)))) break; } LAssert(hosts != NULL); for (auto i = root->GetChild(); i; i = i->GetNext()) { SshHost *h = dynamic_cast(i); if (!h || h == newhost) continue; if (h->t) ; else if ((h->t = new LXmlTag(OPT_Host))) hosts->InsertTag(cur->t); else return false; h->Serialize(true); } app->Opts.Unlock(); LUri u; u.sProtocol = "ssh"; u.sHost = GetCtrlName(IDC_HOSTNAME); u.sUser = GetCtrlName(IDC_USER); u.sPass = GetCtrlName(IDC_PASS); u.sPath = GetCtrlName(IDC_REMOTE_PATH); Uri = u.ToString(); // Fall through } case IDCANCEL: { EndModal(Ctrl->GetId() == IDOK); break; } } return 0; } ////////////////////////////////////////////////////////////////// int LgiMain(OsAppArguments &AppArgs) { LApp a(AppArgs, AppName); if (a.IsOk()) { // LStructuredLog::UnitTest(); a.AppWnd = new App; a.Run(); DeleteObj(a.AppWnd); } LAssert(VcCommit::Instances == 0); return 0; } diff --git a/Lvc/src/SshConnection.h b/Lvc/src/SshConnection.h --- a/Lvc/src/SshConnection.h +++ b/Lvc/src/SshConnection.h @@ -1,29 +1,30 @@ #pragma once #include "lgi/common/Ssh.h" #include "lgi/common/EventTargetThread.h" +#include "lgi/common/Uri.h" class SshConnection : public LSsh, public LEventTargetThread { int GuiHnd; LUri Host; LAutoPtr c; LString Uri, Prompt; AppPriv *d; LMessage::Result OnEvent(LMessage *Msg); LStream *GetConsole(); bool WaitPrompt(LStream *c, LString *Data = NULL, const char *Debug = NULL); public: LHashTbl,VersionCtrl> Types; LArray TypeNotify; SshConnection(LTextLog *log, const char *uri, const char *prompt); bool DetectVcs(VcFolder *Fld); bool Command(VcFolder *Fld, LString Exe, LString Args, ParseFn Parser, ParseParams *Params); // This is the GUI thread message handler static bool HandleMsg(LMessage *m); }; diff --git a/Lvc/src/VcFolder.cpp b/Lvc/src/VcFolder.cpp --- a/Lvc/src/VcFolder.cpp +++ b/Lvc/src/VcFolder.cpp @@ -1,4542 +1,4544 @@ #include "Lvc.h" + #include "lgi/common/Combo.h" #include "lgi/common/ClipBoard.h" #include "lgi/common/Json.h" #include "lgi/common/ProgressDlg.h" + #include "resdefs.h" #ifndef CALL_MEMBER_FN #define CALL_MEMBER_FN(object,ptrToMember) ((object).*(ptrToMember)) #endif #define MAX_AUTO_RESIZE_ITEMS 2000 #define PROFILE_FN 0 #if PROFILE_FN #define PROF(s) Prof.Add(s) #else #define PROF(s) #endif class TmpFile : public LFile { int Status; LString Hint; public: TmpFile(const char *hint = NULL) { Status = 0; if (hint) Hint = hint; else Hint = "_lvc"; } LFile &Create() { LFile::Path p(LSP_TEMP); p += Hint; do { char s[256]; sprintf_s(s, sizeof(s), "../%s%i.tmp", Hint.Get(), LRand()); p += s; } while (p.Exists()); Status = LFile::Open(p.GetFull(), O_READWRITE); return *this; } }; bool TerminalAt(LString Path) { #if defined(MAC) return LExecute("/Applications/Utilities/Terminal.app/Contents/MacOS/Terminal", Path); #elif defined(WINDOWS) TCHAR w[MAX_PATH_LEN]; auto r = GetWindowsDirectory(w, CountOf(w)); if (r > 0) { LFile::Path p = LString(w); p += "system32\\cmd.exe"; FileDev->SetCurrentFolder(Path); return LExecute(p); } #elif defined(LINUX) LExecute("gnome-terminal", NULL, Path); #endif return false; } int Ver2Int(LString v) { auto p = v.Split("."); int i = 0; for (auto s : p) { auto Int = s.Int(); if (Int < 256) { i <<= 8; i |= (uint8_t)Int; } else { LAssert(0); return 0; } } return i; } int ToolVersion[VcMax] = {0}; #define DEBUG_READER_THREAD 0 #if DEBUG_READER_THREAD #define LOG_READER(...) printf(__VA_ARGS__) #else #define LOG_READER(...) #endif ReaderThread::ReaderThread(VersionCtrl vcs, LAutoPtr p, LStream *out) : LThread("ReaderThread") { Vcs = vcs; Process = p; Out = out; Result = -1; FilterCount = 0; // We don't start this thread immediately... because the number of threads is scaled to the system // resources, particularly CPU cores. } ReaderThread::~ReaderThread() { Out = NULL; while (!IsExited()) LSleep(1); } const char *HgFilter = "We\'re removing Mercurial support"; const char *CvsKill = "No such file or directory"; int ReaderThread::OnLine(char *s, ssize_t len) { switch (Vcs) { case VcHg: { if (strnistr(s, HgFilter, len)) FilterCount = 4; if (FilterCount > 0) { FilterCount--; return 0; } else if (LString(s, len).Strip().Equals("remote:")) { return 0; } break; } case VcCvs: { if (strnistr(s, CvsKill, len)) return -1; break; } default: break; } return 1; } bool ReaderThread::OnData(char *Buf, ssize_t &r) { LOG_READER("OnData %i\n", (int)r); #if 1 char *Start = Buf; for (char *c = Buf; c < Buf + r;) { bool nl = *c == '\n'; c++; if (nl) { int Result = OnLine(Start, c - Start); if (Result < 0) { // Kill process and exit thread. Process->Kill(); return false; } if (Result == 0) { ssize_t LineLen = c - Start; ssize_t NextLine = c - Buf; ssize_t Remain = r - NextLine; if (Remain > 0) memmove(Start, Buf + NextLine, Remain); r -= LineLen; c = Start; } else Start = c; } } #endif Out->Write(Buf, r); return true; } int ReaderThread::Main() { bool b = Process->Start(true, false); if (!b) { LString s("Process->Start failed.\n"); Out->Write(s.Get(), s.Length(), ErrSubProcessFailed); return ErrSubProcessFailed; } char Buf[1024]; ssize_t r; LOG_READER("%s:%i - starting reader loop, pid=%i\n", _FL, Process->Handle()); while (Process->IsRunning()) { if (Out) { LOG_READER("%s:%i - starting read.\n", _FL); r = Process->Read(Buf, sizeof(Buf)); LOG_READER("%s:%i - read=%i.\n", _FL, (int)r); if (r > 0) { if (!OnData(Buf, r)) return -1; } } else { Process->Kill(); return -1; break; } } LOG_READER("%s:%i - process loop done.\n", _FL); if (Out) { while ((r = Process->Read(Buf, sizeof(Buf))) > 0) OnData(Buf, r); } LOG_READER("%s:%i - loop done.\n", _FL); Result = (int) Process->GetExitValue(); #if _DEBUG if (Result) printf("%s:%i - Process err: %i 0x%x\n", _FL, Result, Result); #endif return Result; } ///////////////////////////////////////////////////////////////////////////////////////////// int VcFolder::CmdMaxThreads = 0; int VcFolder::CmdActiveThreads = 0; void VcFolder::Init(AppPriv *priv) { if (!CmdMaxThreads) CmdMaxThreads = LAppInst->GetCpuCount(); d = priv; IsCommit = false; IsLogging = false; IsUpdate = false; IsFilesCmd = false; IsWorkingFld = false; CommitListDirty = false; IsUpdatingCounts = false; IsBranches = StatusNone; IsIdent = StatusNone; Unpushed = Unpulled = -1; Type = VcNone; CmdErrors = 0; CurrentCommitIdx = -1; Expanded(false); Insert(Tmp = new LTreeItem); Tmp->SetText("Loading..."); LAssert(d != NULL); } VcFolder::VcFolder(AppPriv *priv, const char *uri) { Init(priv); Uri.Set(uri); GetType(); } VcFolder::VcFolder(AppPriv *priv, LXmlTag *t) { Init(priv); Serialize(t, false); } VcFolder::~VcFolder() { Log.DeleteObjects(); } VersionCtrl VcFolder::GetType() { if (Type == VcNone) Type = d->DetectVcs(this); return Type; } const char *VcFolder::LocalPath() { if (!Uri.IsProtocol("file") || Uri.sPath.IsEmpty()) { LAssert(!"Shouldn't call this if not a file path."); return NULL; } auto c = Uri.sPath.Get(); #ifdef WINDOWS if (*c == '/') c++; #endif return c; } const char *VcFolder::GetText(int Col) { switch (Col) { case 0: { if (Uri.IsFile()) Cache = LocalPath(); else Cache.Printf("%s%s", Uri.sHost.Get(), Uri.sPath.Get()); if (Cmds.Length()) Cache += " (...)"; return Cache; } case 1: { CountCache.Printf("%i/%i", Unpulled, Unpushed); CountCache = CountCache.Replace("-1", "--"); return CountCache; } } return NULL; } bool VcFolder::Serialize(LXmlTag *t, bool Write) { if (Write) t->SetContent(Uri.ToString()); else { LString s = t->GetContent(); bool isUri = s.Find("://") >= 0; if (isUri) Uri.Set(s); else Uri.SetFile(s); } return true; } LXmlTag *VcFolder::Save() { LXmlTag *t = new LXmlTag(OPT_Folder); if (t) Serialize(t, true); return t; } const char *VcFolder::GetVcName() { const char *Def = NULL; switch (GetType()) { case VcGit: Def = "git"; break; case VcSvn: Def = "svn"; break; case VcHg: Def = "hg"; break; case VcCvs: Def = "cvs"; break; default: break; } if (!VcCmd) { LString Opt; Opt.Printf("%s-path", Def); LVariant v; if (d->Opts.GetValue(Opt, v)) VcCmd = v.Str(); } if (!VcCmd) VcCmd = Def; return VcCmd; } bool VcFolder::RunCmd(const char *Args, LoggingType Logging, std::function Callback) { Result Ret; Ret.Code = -1; const char *Exe = GetVcName(); if (!Exe || CmdErrors > 2) return false; if (Uri.IsFile()) { new ProcessCallback(Exe, Args, LocalPath(), Logging == LogNone ? d->Log : NULL, GetTree()->GetWindow(), Callback); } else { LAssert(!"Impl me."); return false; } return true; } bool VcFolder::StartCmd(const char *Args, ParseFn Parser, ParseParams *Params, LoggingType Logging) { const char *Exe = GetVcName(); if (!Exe) return false; if (CmdErrors > 2) return false; if (Uri.IsFile()) { if (d->Log && Logging != LogSilo) d->Log->Print("%s %s\n", Exe, Args); LAutoPtr Process(new LSubProcess(Exe, Args)); if (!Process) return false; Process->SetInitFolder(Params && Params->AltInitPath ? Params->AltInitPath.Get() : LocalPath()); #ifdef MAC // Mac GUI apps don't share the terminal path, so this overrides that and make it work auto Path = LGetPath(); if (Path.Length()) { LString Tmp = LString(LGI_PATH_SEPARATOR).Join(Path); Process->SetEnvironment("PATH", Tmp); } #endif LString::Array Ctx; Ctx.SetFixedLength(false); Ctx.Add(LocalPath()); Ctx.Add(Exe); Ctx.Add(Args); LAutoPtr c(new Cmd(Ctx, Logging, d->Log)); if (!c) return false; c->PostOp = Parser; c->Params.Reset(Params); c->Rd.Reset(new ReaderThread(GetType(), Process, c)); Cmds.Add(c.Release()); } else { auto c = d->GetConnection(Uri.ToString()); if (!c) return false; if (!c->Command(this, Exe, Args, Parser, Params)) return false; } Update(); return true; } int LogDateCmp(LListItem *a, LListItem *b, NativeInt Data) { VcCommit *A = dynamic_cast(a); VcCommit *B = dynamic_cast(b); if ((A != NULL) ^ (B != NULL)) { // This handles keeping the "working folder" list item at the top return (A != NULL) - (B != NULL); } // Sort the by date from most recent to least return -A->GetTs().Compare(&B->GetTs()); } void VcFolder::AddGitName(LString Hash, LString Name) { LString Existing = GitNames.Find(Hash); if (Existing) GitNames.Add(Hash, Existing + "," + Name); else GitNames.Add(Hash, Name); } LString VcFolder::GetGitNames(LString Hash) { LString Short = Hash(0, 11); return GitNames.Find(Short); } bool VcFolder::ParseBranches(int Result, LString s, ParseParams *Params) { switch (GetType()) { case VcGit: { LString::Array a = s.SplitDelimit("\r\n"); for (auto &l: a) { LString::Array c = l.SplitDelimit(" \t"); if (c[0].Equals("*")) { CurrentBranch = c[1]; AddGitName(c[2], CurrentBranch); Branches.Add(CurrentBranch, new VcBranch(CurrentBranch, c[2])); } else { AddGitName(c[1], c[0]); Branches.Add(c[0], new VcBranch(c[0], c[1])); } } break; } case VcHg: { auto a = s.SplitDelimit("\r\n"); Branches.DeleteObjects(); for (auto b: a) { if (!CurrentBranch) CurrentBranch = b; Branches.Add(b, new VcBranch(b)); } if (Params && Params->Str.Equals("CountToTip")) CountToTip(); break; } default: { break; } } IsBranches = Result ? StatusError : StatusNone; OnBranchesChange(); return false; } void VcFolder::GetRemoteUrl(std::function Callback) { LAutoPtr p(new ParseParams); p->Callback = Callback; switch (GetType()) { case VcGit: { StartCmd("config --get remote.origin.url", NULL, p.Release()); break; } case VcSvn: { StartCmd("info --show-item=url", NULL, p.Release()); break; } case VcHg: { StartCmd("paths default", NULL, p.Release()); break; } default: break; } } void VcFolder::OnBranchesChange() { auto *w = d->Tree->GetWindow(); if (!w || !LTreeItem::Select()) return; if (Branches.Length()) { // Set the colours up LString Default; for (auto b: Branches) { if (!stricmp(b.key, "default") || !stricmp(b.key, "trunk")) Default = b.key; /* else printf("Other=%s\n", b.key); */ } int Idx = 1; for (auto b: Branches) { if (!b.value->Colour.IsValid()) { if (Default && !stricmp(b.key, Default)) b.value->Colour = GetPaletteColour(0); else b.value->Colour = GetPaletteColour(Idx++); } } } DropDownBtn *dd; if (w->GetViewById(IDC_BRANCH_DROPDOWN, dd)) { LString::Array a; for (auto b: Branches) a.Add(b.key); dd->SetList(IDC_BRANCH, a); } LViewI *b; if (Branches.Length() > 0 && w->GetViewById(IDC_BRANCH, b)) { if (CurrentBranch) { b->Name(CurrentBranch); } else { auto it = Branches.begin(); if (it != Branches.end()) b->Name((*it).key); } } } void VcFolder::DefaultFields() { if (Fields.Length() == 0) { switch (GetType()) { case VcHg: { Fields.Add(LGraph); Fields.Add(LIndex); Fields.Add(LRevision); Fields.Add(LBranch); Fields.Add(LAuthor); Fields.Add(LTimeStamp); Fields.Add(LMessageTxt); break; } case VcGit: { Fields.Add(LGraph); Fields.Add(LRevision); Fields.Add(LBranch); Fields.Add(LAuthor); Fields.Add(LTimeStamp); Fields.Add(LMessageTxt); break; } default: { Fields.Add(LGraph); Fields.Add(LRevision); Fields.Add(LAuthor); Fields.Add(LTimeStamp); Fields.Add(LMessageTxt); break; } } } } void VcFolder::UpdateColumns() { d->Commits->EmptyColumns(); for (auto c: Fields) { switch (c) { case LGraph: d->Commits->AddColumn("---", 60); break; case LIndex: d->Commits->AddColumn("Index", 60); break; case LBranch: d->Commits->AddColumn("Branch", 60); break; case LRevision: d->Commits->AddColumn("Revision", 60); break; case LAuthor: d->Commits->AddColumn("Author", 240); break; case LTimeStamp: d->Commits->AddColumn("Date", 130); break; case LMessageTxt: d->Commits->AddColumn("Message", 400); break; default: LAssert(0); break; } } } void VcFolder::FilterCurrentFiles() { LArray All; d->Files->GetAll(All); // Update the display property for (auto i: All) { auto fn = i->GetText(COL_FILENAME); bool vis = !d->FileFilter || Stristr(fn, d->FileFilter.Get()); i->GetCss(true)->Display(vis ? LCss::DispBlock : LCss::DispNone); // LgiTrace("Filter '%s' by '%s' = %i\n", fn, d->FileFilter.Get(), vis); } d->Files->Sort(0); d->Files->UpdateAllItems(); d->Files->ResizeColumnsToContent(); } void VcFolder::Select(bool b) { #if PROFILE_FN LProfile Prof("Select"); #endif if (!b) { auto *w = d->Tree->GetWindow(); w->SetCtrlName(IDC_BRANCH, NULL); } PROF("Parent.Select"); LTreeItem::Select(b); if (b) { if (Uri.IsFile() && !LDirExists(LocalPath())) return; PROF("DefaultFields"); DefaultFields(); PROF("Type Change"); if (GetType() != d->PrevType) { d->PrevType = GetType(); UpdateColumns(); } PROF("UpdateCommitList"); if ((Log.Length() == 0 || CommitListDirty) && !IsLogging) { switch (GetType()) { case VcGit: { LVariant Limit; d->Opts.GetValue("git-limit", Limit); LString cmd = "rev-list --all --header --timestamp --author-date-order", s; if (Limit.CastInt32() > 0) { s.Printf(" -n %i", Limit.CastInt32()); cmd += s; } IsLogging = StartCmd(cmd, &VcFolder::ParseRevList); break; } case VcSvn: { LVariant Limit; d->Opts.GetValue("svn-limit", Limit); if (CommitListDirty) { IsLogging = StartCmd("up", &VcFolder::ParsePull, new ParseParams("log")); break; } LString s; if (Limit.CastInt32() > 0) s.Printf("log --limit %i", Limit.CastInt32()); else s = "log"; IsLogging = StartCmd(s, &VcFolder::ParseLog); break; } case VcHg: { IsLogging = StartCmd("log", &VcFolder::ParseLog); StartCmd("resolve -l", &VcFolder::ParseResolveList); break; } case VcPending: { break; } default: { IsLogging = StartCmd("log", &VcFolder::ParseLog); break; } } CommitListDirty = false; } PROF("GetBranches"); if (GetBranches()) OnBranchesChange(); if (d->CurFolder != this) { PROF("RemoveAll"); d->CurFolder = this; d->Commits->RemoveAll(); } PROF("Uncommit"); if (!Uncommit) Uncommit.Reset(new UncommitedItem(d)); d->Commits->Insert(Uncommit, 0); PROF("Log Loop"); int64 CurRev = Atoi(CurrentCommit.Get()); List Ls; for (auto l: Log) { if (CurrentCommit && l->GetRev()) { switch (GetType()) { case VcSvn: { int64 LogRev = Atoi(l->GetRev()); if (CurRev >= 0 && CurRev >= LogRev) { CurRev = -1; l->SetCurrent(true); } else { l->SetCurrent(false); } break; } default: l->SetCurrent(!_stricmp(CurrentCommit, l->GetRev())); break; } } bool Add = !d->CommitFilter; if (d->CommitFilter) { const char *s = l->GetRev(); if (s && strstr(s, d->CommitFilter) != NULL) Add = true; s = l->GetAuthor(); if (s && stristr(s, d->CommitFilter) != NULL) Add = true; s = l->GetMsg(); if (s && stristr(s, d->CommitFilter) != NULL) Add = true; } LList *CurOwner = l->GetList(); if (Add ^ (CurOwner != NULL)) { if (Add) Ls.Insert(l); else d->Commits->Remove(l); } } PROF("Ls Ins"); d->Commits->Insert(Ls); if (d->Resort >= 0) { PROF("Resort"); d->Commits->Sort(LstCmp, d->Resort); d->Resort = -1; } PROF("ColSizing"); if (d->Commits->Length() > MAX_AUTO_RESIZE_ITEMS) { int i = 0; if (GetType() == VcHg && d->Commits->GetColumns() >= 7) { d->Commits->ColumnAt(i++)->Width(60); // LGraph d->Commits->ColumnAt(i++)->Width(40); // LIndex d->Commits->ColumnAt(i++)->Width(100); // LRevision d->Commits->ColumnAt(i++)->Width(60); // LBranch d->Commits->ColumnAt(i++)->Width(240); // LAuthor d->Commits->ColumnAt(i++)->Width(130); // LTimeStamp d->Commits->ColumnAt(i++)->Width(400); // LMessage } else if (d->Commits->GetColumns() >= 5) { d->Commits->ColumnAt(i++)->Width(40); // LGraph d->Commits->ColumnAt(i++)->Width(270); // LRevision d->Commits->ColumnAt(i++)->Width(240); // LAuthor d->Commits->ColumnAt(i++)->Width(130); // LTimeStamp d->Commits->ColumnAt(i++)->Width(400); // LMessage } } else d->Commits->ResizeColumnsToContent(); PROF("UpdateAll"); d->Commits->UpdateAllItems(); PROF("GetCur"); GetCurrentRevision(); } } int CommitRevCmp(VcCommit **a, VcCommit **b) { int64 arev = Atoi((*a)->GetRev()); int64 brev = Atoi((*b)->GetRev()); int64 diff = (int64)brev - arev; if (diff < 0) return -1; return (diff > 0) ? 1 : 0; } int CommitIndexCmp(VcCommit **a, VcCommit **b) { auto ai = (*a)->GetIndex(); auto bi = (*b)->GetIndex(); auto diff = (int64)bi - ai; if (diff < 0) return -1; return (diff > 0) ? 1 : 0; } int CommitDateCmp(VcCommit **a, VcCommit **b) { uint64 ats, bts; (*a)->GetTs().Get(ats); (*b)->GetTs().Get(bts); int64 diff = (int64)bts - ats; if (diff < 0) return -1; return (diff > 0) ? 1 : 0; } void VcFolder::GetCurrentRevision(ParseParams *Params) { if (CurrentCommit || IsIdent != StatusNone) return; switch (GetType()) { case VcGit: if (StartCmd("rev-parse HEAD", &VcFolder::ParseInfo, Params)) IsIdent = StatusActive; break; case VcSvn: if (StartCmd("info", &VcFolder::ParseInfo, Params)) IsIdent = StatusActive; break; case VcHg: if (StartCmd("id -i -n", &VcFolder::ParseInfo, Params)) IsIdent = StatusActive; break; case VcCvs: break; default: break; } } bool VcFolder::GetBranches(ParseParams *Params) { if (Branches.Length() > 0 || IsBranches != StatusNone) return true; switch (GetType()) { case VcGit: if (StartCmd("-P branch -a -v", &VcFolder::ParseBranches, Params)) IsBranches = StatusActive; break; case VcSvn: Branches.Add("trunk", new VcBranch("trunk")); OnBranchesChange(); break; case VcHg: if (StartCmd("branch", &VcFolder::ParseBranches, Params)) IsBranches = StatusActive; break; case VcCvs: break; default: break; } return false; } bool VcFolder::ParseRevList(int Result, LString s, ParseParams *Params) { Log.DeleteObjects(); int Errors = 0; switch (GetType()) { case VcGit: { LString::Array Commits; Commits.SetFixedLength(false); // Split on the NULL chars... char *c = s.Get(); char *e = c + s.Length(); while (c < e) { char *nul = c; while (nul < e && *nul) nul++; if (nul <= c) break; Commits.New().Set(c, nul-c); if (nul >= e) break; c = nul + 1; } for (auto Commit: Commits) { LAutoPtr Rev(new VcCommit(d, this)); if (Rev->GitParse(Commit, true)) { Log.Add(Rev.Release()); } else { LAssert(!"Parse failed."); LgiTrace("%s:%i - Failed:\n%s\n\n", _FL, Commit.Get()); Errors++; } } LinkParents(); break; } default: LAssert(!"Impl me."); break; } IsLogging = false; return Errors == 0; } LString VcFolder::GetFilePart(const char *uri) { LUri u(uri); LString File = u.IsFile() ? u.DecodeStr(u.LocalPath()) : u.sPath(Uri.sPath.Length(), -1).LStrip("/"); return File; } void VcFolder::LogFile(const char *uri) { LString Args; switch (GetType()) { case VcSvn: case VcHg: case VcGit: { LString File = GetFilePart(uri); ParseParams *Params = new ParseParams(uri); Args.Printf("log \"%s\"", File.Get()); IsLogging = StartCmd(Args, &VcFolder::ParseLog, Params, LogNormal); break; } default: LAssert(!"Impl me."); break; } } VcLeaf *VcFolder::FindLeaf(const char *Path, bool OpenTree) { VcLeaf *r = NULL; if (OpenTree) DoExpand(); for (auto n = GetChild(); !r && n; n = n->GetNext()) { auto l = dynamic_cast(n); if (l) r = l->FindLeaf(Path, OpenTree); } return r; } bool VcFolder::ParseLog(int Result, LString s, ParseParams *Params) { LHashTbl, VcCommit*> Map; for (auto pc: Log) Map.Add(pc->GetRev(), pc); int Skipped = 0, Errors = 0; VcLeaf *File = Params ? FindLeaf(Params->Str, true) : NULL; LArray *Out = File ? &File->Log : &Log; if (File) { for (auto Leaf = File; Leaf; Leaf = dynamic_cast(Leaf->GetParent())) Leaf->OnExpand(true); File->Select(true); File->ScrollTo(); } switch (GetType()) { case VcGit: { LString::Array c; c.SetFixedLength(false); char *prev = s.Get(); for (char *i = s.Get(); *i; ) { if (!strnicmp(i, "commit ", 7)) { if (i > prev) { c.New().Set(prev, i - prev); // LgiTrace("commit=%i\n", (int)(i - prev)); } prev = i; } while (*i) { if (*i++ == '\n') break; } } for (unsigned i=0; i Rev(new VcCommit(d, this)); if (Rev->GitParse(c[i], false)) { if (!Map.Find(Rev->GetRev())) Out->Add(Rev.Release()); else Skipped++; } else { LgiTrace("%s:%i - Failed:\n%s\n\n", _FL, c[i].Get()); Errors++; } } Out->Sort(CommitDateCmp); break; } case VcSvn: { LString::Array c = s.Split("------------------------------------------------------------------------"); for (unsigned i=0; i Rev(new VcCommit(d, this)); LString Raw = c[i].Strip(); if (Rev->SvnParse(Raw)) { if (File || !Map.Find(Rev->GetRev())) Out->Add(Rev.Release()); else Skipped++; } else if (Raw) { OnCmdError(Raw, "ParseLog Failed"); Errors++; } } Out->Sort(CommitRevCmp); break; } case VcHg: { LString::Array c = s.Split("\n\n"); LHashTbl, VcCommit*> Idx; for (auto &Commit: c) { LAutoPtr Rev(new VcCommit(d, this)); if (Rev->HgParse(Commit)) { auto Existing = File ? NULL : Map.Find(Rev->GetRev()); if (!Existing) Out->Add(Existing = Rev.Release()); if (Existing->GetIndex() >= 0) Idx.Add(Existing->GetIndex(), Existing); } } if (!File) { // Patch all the trivial parents... for (auto c: Log) { if (c->GetParents()->Length() > 0) continue; auto CIdx = c->GetIndex(); if (CIdx <= 0) continue; auto Par = Idx.Find(CIdx - 1); if (Par) c->GetParents()->Add(Par->GetRev()); } } Out->Sort(CommitIndexCmp); if (!File) LinkParents(); d->Resort = 1; break; } case VcCvs: { if (Result) { OnCmdError(s, "Cvs command failed."); break; } LHashTbl, VcCommit*> Map; LString::Array c = s.Split("============================================================================="); for (auto &Commit: c) { if (Commit.Strip().Length()) { LString Head, File; LString::Array Versions = Commit.Split("----------------------------"); LString::Array Lines = Versions[0].SplitDelimit("\r\n"); for (auto &Line: Lines) { LString::Array p = Line.Split(":", 1); if (p.Length() == 2) { // LgiTrace("Line: %s\n", Line->Get()); LString Var = p[0].Strip().Lower(); LString Val = p[1].Strip(); if (Var.Equals("branch")) { if (Val.Length()) Branches.Add(Val, new VcBranch(Val)); } else if (Var.Equals("head")) { Head = Val; } else if (Var.Equals("rcs file")) { LString::Array f = Val.SplitDelimit(","); File = f.First(); } } } // LgiTrace("%s\n", Commit->Get()); for (unsigned i=1; i= 3) { LString Ver = Lines[0].Split(" ").Last(); LString::Array a = Lines[1].SplitDelimit(";"); LString Date = a[0].Split(":", 1).Last().Strip(); LString Author = a[1].Split(":", 1).Last().Strip(); LString Id = a[2].Split(":", 1).Last().Strip(); LString Msg = Lines[2]; LDateTime Dt; if (Dt.Parse(Date)) { uint64 Ts; if (Dt.Get(Ts)) { VcCommit *Cc = Map.Find(Ts); if (!Cc) { Map.Add(Ts, Cc = new VcCommit(d, this)); Out->Add(Cc); Cc->CvsParse(Dt, Author, Msg); } Cc->Files.Add(File.Get()); } else LAssert(!"NO ts for date."); } else LAssert(!"Date parsing failed."); } } } } break; } default: LAssert(!"Impl me."); break; } if (File) File->ShowLog(); // LgiTrace("%s:%i - ParseLog: Skip=%i, Error=%i\n", _FL, Skipped, Errors); IsLogging = false; return !Result; } void VcFolder::LinkParents() { #if PROFILE_FN LProfile Prof("LinkParents"); #endif LHashTbl,VcCommit*> Map; // Index all the commits int i = 0; for (auto c:Log) { c->Idx = i++; c->NodeIdx = -1; Map.Add(c->GetRev(), c); } // Create all the edges... PROF("Create edges."); for (auto c:Log) { auto *Par = c->GetParents(); for (auto &pRev : *Par) { auto *p = Map.Find(pRev); if (p) new VcEdge(p, c); #if 0 else return; #endif } } // Map the edges to positions PROF("Map edges."); typedef LArray EdgeArr; LArray Active; for (auto c:Log) { for (unsigned i=0; c->NodeIdx<0 && iParent == c) { c->NodeIdx = i; break; } } } // Add starting edges to active set for (auto e:c->Edges) { if (e->Child == c) { if (c->NodeIdx < 0) c->NodeIdx = (int)Active.Length(); e->Idx = c->NodeIdx; c->Pos.Add(e, e->Idx); Active[e->Idx].Add(e); } } // Now for all active edges... assign positions for (unsigned i=0; iLength(); n++) { LAssert(Active.PtrCheck(Edges)); VcEdge *e = (*Edges)[n]; if (c == e->Child || c == e->Parent) { LAssert(c->NodeIdx >= 0); c->Pos.Add(e, c->NodeIdx); } else { // May need to untangle edges with different parents here bool Diff = false; for (auto edge: *Edges) { if (edge != e && edge->Child != c && edge->Parent != e->Parent) { Diff = true; break; } } if (Diff) { int NewIndex = -1; // Look through existing indexes for a parent match for (unsigned ii=0; iiParent? bool Match = true; for (auto ee: Active[ii]) { if (ee->Parent != e->Parent) { Match = false; break; } } if (Match) NewIndex = ii; } if (NewIndex < 0) // Create new index for this parent NewIndex = (int)Active.Length(); Edges->Delete(e); auto &NewEdges = Active[NewIndex]; NewEdges.Add(e); Edges = &Active[i]; // The 'Add' above can invalidate the object 'Edges' refers to e->Idx = NewIndex; c->Pos.Add(e, NewIndex); n--; } else { LAssert(e->Idx == i); c->Pos.Add(e, i); } } } } // Process terminating edges for (auto e: c->Edges) { if (e->Parent == c) { if (e->Idx < 0) { // This happens with out of order commits..? continue; } int i = e->Idx; if (c->NodeIdx < 0) c->NodeIdx = i; if (Active[i].HasItem(e)) Active[i].Delete(e); else LgiTrace("%s:%i - Warning: Active doesn't have 'e'.\n", _FL); } } // Collapse any empty active columns for (unsigned i=0; iIdx > 0); edge->Idx--; c->Pos.Add(edge, edge->Idx); } } i--; } } } // Find all the "heads", i.e. a commit without any children PROF("Find heads."); LCombo *Heads; if (d->Wnd()->GetViewById(IDC_HEADS, Heads)) { Heads->Empty(); for (auto c:Log) { bool Has = false; for (auto e:c->Edges) { if (e->Parent == c) { Has = true; break; } } if (!Has) Heads->Insert(c->GetRev()); } Heads->SendNotify(LNotifyTableLayoutRefresh); } } VcFile *AppPriv::FindFile(const char *Path) { if (!Path) return NULL; LArray files; if (Files->GetAll(files)) { LString p = Path; p = p.Replace(DIR_STR, "/"); for (auto f : files) { auto Fn = f->GetFileName(); if (p.Equals(Fn)) return f; } } return NULL; } VcFile *VcFolder::FindFile(const char *Path) { return d->FindFile(Path); } void VcFolder::OnCmdError(LString Output, const char *Msg) { if (!CmdErrors) { if (Output.Length()) d->Log->Write(Output, Output.Length()); auto vc_name = GetVcName(); if (vc_name) { LString::Array a = GetProgramsInPath(GetVcName()); d->Log->Print("'%s' executables in the path:\n", GetVcName()); for (auto Bin : a) d->Log->Print(" %s\n", Bin.Get()); } else if (Msg) { d->Log->Print("%s\n", Msg); } } CmdErrors++; d->Tabs->Value(1); GetCss(true)->Color(LColour::Red); } void VcFolder::ClearError() { GetCss(true)->Color(LCss::ColorInherit); } bool VcFolder::ParseInfo(int Result, LString s, ParseParams *Params) { switch (GetType()) { case VcGit: case VcHg: { auto p = s.Strip().SplitDelimit(); CurrentCommit = p[0].Strip(" \t\r\n+"); if (p.Length() > 1) CurrentCommitIdx = p[1].Int(); else CurrentCommitIdx = -1; if (Params && Params->Str.Equals("CountToTip")) CountToTip(); break; } case VcSvn: { if (s.Find("client is too old") >= 0) { OnCmdError(s, "Client too old"); break; } LString::Array c = s.Split("\n"); for (unsigned i=0; iIsWorking = true; ParseStatus(Result, s, Params); break; } case VcCvs: { bool Untracked = d->IsMenuChecked(IDM_UNTRACKED); if (Untracked) { auto Lines = s.SplitDelimit("\n"); for (auto Ln: Lines) { auto p = Ln.SplitDelimit(" \t", 1); if (p.Length() > 1) { - auto f = new VcFile(d, this, NULL, true); + auto f = new VcFile(d, this, LString(), true); f->SetText(p[0], COL_STATE); f->SetText(p[1], COL_FILENAME); f->GetStatus(); d->Files->Insert(f); } } } // else fall thru } default: { - ParseDiffs(s, NULL, true); + ParseDiffs(s, LString(), true); break; } } IsWorkingFld = false; FilterCurrentFiles(); d->Files->ResizeColumnsToContent(); if (GetType() == VcSvn) { Unpushed = d->Files->Length() > 0 ? 1 : 0; Update(); } return false; } void VcFolder::DiffRange(const char *FromRev, const char *ToRev) { if (!FromRev || !ToRev) return; switch (GetType()) { case VcSvn: { ParseParams *p = new ParseParams; p->IsWorking = false; p->Str = LString(FromRev) + ":" + ToRev; LString a; a.Printf("diff -r%s:%s", FromRev, ToRev); StartCmd(a, &VcFolder::ParseDiff, p); break; } case VcGit: { ParseParams *p = new ParseParams; p->IsWorking = false; p->Str = LString(FromRev) + ":" + ToRev; LString a; a.Printf("-P diff %s..%s", FromRev, ToRev); StartCmd(a, &VcFolder::ParseDiff, p); break; } case VcCvs: case VcHg: default: LAssert(!"Impl me."); break; } } bool VcFolder::ParseDiff(int Result, LString s, ParseParams *Params) { if (Params) ParseDiffs(s, Params->Str, Params->IsWorking); else - ParseDiffs(s, NULL, true); + ParseDiffs(s, LString(), true); return false; } void VcFolder::Diff(VcFile *file) { auto Fn = file->GetFileName(); if (!Fn || !Stricmp(Fn, ".") || !Stricmp(Fn, "..")) return; const char *Prefix = ""; switch (GetType()) { case VcGit: Prefix = "-P "; // fall through case VcHg: { LString a; auto rev = file->GetRevision(); if (rev) a.Printf("%sdiff %s \"%s\"", Prefix, rev, Fn); else a.Printf("%sdiff \"%s\"", Prefix, Fn); StartCmd(a, &VcFolder::ParseDiff); break; } case VcSvn: { LString a; if (file->GetRevision()) a.Printf("diff -r %s \"%s\"", file->GetRevision(), Fn); else a.Printf("diff \"%s\"", Fn); StartCmd(a, &VcFolder::ParseDiff); break; } case VcCvs: break; default: LAssert(!"Impl me."); break; } } bool VcFolder::ParseDiffs(LString s, LString Rev, bool IsWorking) { LAssert(IsWorking || Rev.Get() != NULL); switch (GetType()) { case VcGit: { LString::Array a = s.Split("\n"); LString Diff; VcFile *f = NULL; for (unsigned i=0; iSetDiff(Diff); Diff.Empty(); auto Bits = a[i].SplitDelimit(); LString Fn, State = "M"; if (Bits[1].Equals("--cc")) { Fn = Bits.Last(); State = "C"; } else Fn = Bits.Last()(2,-1); // LgiTrace("%s\n", a[i].Get()); f = FindFile(Fn); if (!f) f = new VcFile(d, this, Rev, IsWorking); f->SetText(State, COL_STATE); f->SetText(Fn.Replace("\\","/"), COL_FILENAME); f->GetStatus(); d->Files->Insert(f); } else if (!_strnicmp(Ln, "new file", 8)) { if (f) f->SetText("A", COL_STATE); } else if (!_strnicmp(Ln, "index", 5) || !_strnicmp(Ln, "commit", 6) || !_strnicmp(Ln, "Author:", 7) || !_strnicmp(Ln, "Date:", 5) || !_strnicmp(Ln, "+++", 3) || !_strnicmp(Ln, "---", 3)) { // Ignore } else { if (Diff) Diff += "\n"; Diff += a[i]; } } if (f && Diff) { f->SetDiff(Diff); Diff.Empty(); } break; } case VcHg: { LString Sep("\n"); LString::Array a = s.Split(Sep); LString::Array Diffs; VcFile *f = NULL; List Files; LProgressDlg Prog(GetTree(), 1000); Prog.SetDescription("Reading diff lines..."); Prog.SetRange(a.Length()); // Prog.SetYieldTime(300); for (unsigned i=0; iSetDiff(Sep.Join(Diffs)); Diffs.Empty(); auto MainParts = a[i].Split(" -r "); auto FileParts = MainParts.Last().Split(" ",1); LString Fn = FileParts.Last(); f = FindFile(Fn); if (!f) f = new VcFile(d, this, Rev, IsWorking); f->SetText(Fn.Replace("\\","/"), COL_FILENAME); // f->SetText(Status, COL_STATE); Files.Insert(f); } else if (!_strnicmp(Ln, "index", 5) || !_strnicmp(Ln, "commit", 6) || !_strnicmp(Ln, "Author:", 7) || !_strnicmp(Ln, "Date:", 5) || !_strnicmp(Ln, "+++", 3) || !_strnicmp(Ln, "---", 3)) { // Ignore } else { Diffs.Add(a[i]); } Prog.Value(i); if (Prog.IsCancelled()) break; } if (f && Diffs.Length()) { f->SetDiff(Sep.Join(Diffs)); Diffs.Empty(); } d->Files->Insert(Files); break; } case VcSvn: { LString::Array a = s.Replace("\r").Split("\n"); LString Diff; VcFile *f = NULL; bool InPreamble = false; bool InDiff = false; for (unsigned i=0; iSetDiff(Diff); f->Select(false); } Diff.Empty(); InDiff = false; InPreamble = false; LString Fn = a[i].Split(":", 1).Last().Strip(); f = FindFile(Fn); if (!f) f = new VcFile(d, this, Rev, IsWorking); f->SetText(Fn.Replace("\\","/"), COL_FILENAME); f->SetText("M", COL_STATE); f->GetStatus(); d->Files->Insert(f); } else if (!_strnicmp(Ln, "------", 6)) { InPreamble = !InPreamble; } else if (!_strnicmp(Ln, "======", 6)) { InPreamble = false; InDiff = true; } else if (InDiff) { if (!strncmp(Ln, "--- ", 4) || !strncmp(Ln, "+++ ", 4)) { } else { if (Diff) Diff += "\n"; Diff += a[i]; } } } if (f && Diff) { f->SetDiff(Diff); Diff.Empty(); } break; } case VcCvs: { break; } default: { LAssert(!"Impl me."); break; } } FilterCurrentFiles(); return true; } bool VcFolder::ParseFiles(int Result, LString s, ParseParams *Params) { d->ClearFiles(); ParseDiffs(s, Params->Str, false); IsFilesCmd = false; FilterCurrentFiles(); return false; } void VcFolder::OnSshCmd(SshParams *p) { if (!p || !p->f) { LAssert(!"Param error."); return; } LString s = p->Output; int Result = p->ExitCode; if (Result == ErrSubProcessFailed) { CmdErrors++; } else if (p->Parser) { bool Reselect = CALL_MEMBER_FN(*this, p->Parser)(Result, s, p->Params); if (Reselect) { if (LTreeItem::Select()) Select(true); } } if (p->Params && p->Params->Callback) { p->Params->Callback(s); } } void VcFolder::OnPulse() { bool Reselect = false, CmdsChanged = false; static bool Processing = false; if (!Processing) { Processing = true; // Lock out processing, if it puts up a dialog or something... // bad things happen if we try and re-process something. // printf("Cmds.Len=%i\n", (int)Cmds.Length()); for (unsigned i=0; iRd->GetState()=%i\n", c->Rd->GetState()); if (c->Rd->GetState() == LThread::THREAD_INIT) { if (CmdActiveThreads < CmdMaxThreads) { c->Rd->Run(); CmdActiveThreads++; // LgiTrace("CmdActiveThreads++ = %i\n", CmdActiveThreads); } // else printf("Too many active threads."); } else if (c->Rd->IsExited()) { CmdActiveThreads--; // LgiTrace("CmdActiveThreads-- = %i\n", CmdActiveThreads); LString s = c->GetBuf(); int Result = c->Rd->ExitCode(); if (Result == ErrSubProcessFailed) { if (!CmdErrors) d->Log->Print("Error: Can't run '%s'\n", GetVcName()); CmdErrors++; } else if (c->PostOp) { if (s.Length() == 18 && s.Equals("GSUBPROCESS_ERROR\n")) { OnCmdError(s, "Sub process failed."); } else { Reselect |= CALL_MEMBER_FN(*this, c->PostOp)(Result, s, c->Params); } } if (c->Params && c->Params->Callback) { c->Params->Callback(s); } Cmds.DeleteAt(i--, true); delete c; CmdsChanged = true; } // else printf("Not exited.\n"); } Processing = false; } if (Reselect) { if (LTreeItem::Select()) Select(true); } if (CmdsChanged) { Update(); } if (CmdErrors) { d->Tabs->Value(1); CmdErrors = false; } } void VcFolder::OnRemove() { LXmlTag *t = d->Opts.LockTag(OPT_Folders, _FL); if (t) { Uncommit.Reset(); if (LTreeItem::Select()) { d->Files->Empty(); d->Commits->RemoveAll(); } bool Found = false; auto u = Uri.ToString(); for (auto c: t->Children) { if (!c->IsTag(OPT_Folder)) printf("%s:%i - Wrong tag: %s, %s\n", _FL, c->GetTag(), OPT_Folder); else if (!c->GetContent()) printf("%s:%i - No content.\n", _FL); else { auto Content = c->GetContent(); if (!_stricmp(Content, u)) { c->RemoveTag(); delete c; Found = true; break; } } } LAssert(Found); d->Opts.Unlock(); } } void VcFolder::Empty() { Type = VcNone; IsCommit = false; IsLogging = false; IsUpdate = false; IsFilesCmd = false; IsWorkingFld = false; CommitListDirty = false; IsUpdatingCounts = false; IsBranches = StatusNone; IsIdent = StatusNone; Unpushed = Unpulled = -1; CmdErrors = 0; CurrentCommitIdx = -1; CurrentCommit.Empty(); RepoUrl.Empty(); VcCmd.Empty(); Uncommit.Reset(); Log.DeleteObjects(); d->Commits->Empty(); d->Files->Empty(); if (!Uri.IsFile()) GetCss(true)->Color(LColour::Blue); } void VcFolder::OnMouseClick(LMouse &m) { if (m.IsContextMenu()) { LSubMenu s; s.AppendItem("Browse To", IDM_BROWSE_FOLDER, Uri.IsFile()); s.AppendItem( #ifdef WINDOWS "Command Prompt At", #else "Terminal At", #endif IDM_TERMINAL, Uri.IsFile()); s.AppendItem("Clean", IDM_CLEAN); s.AppendSeparator(); s.AppendItem("Pull", IDM_PULL); s.AppendItem("Status", IDM_STATUS); s.AppendItem("Push", IDM_PUSH); s.AppendItem("Update Subs", IDM_UPDATE_SUBS, GetType() == VcGit); s.AppendSeparator(); s.AppendItem("Remove", IDM_REMOVE); s.AppendItem("Remote URL", IDM_REMOTE_URL); if (!Uri.IsFile()) { s.AppendSeparator(); s.AppendItem("Edit Location", IDM_EDIT); } int Cmd = s.Float(GetTree(), m); switch (Cmd) { case IDM_BROWSE_FOLDER: { LBrowseToFile(LocalPath()); break; } case IDM_TERMINAL: { TerminalAt(LocalPath()); break; } case IDM_CLEAN: { Clean(); break; } case IDM_PULL: { Pull(); break; } case IDM_STATUS: { FolderStatus(); break; } case IDM_PUSH: { Push(); break; } case IDM_UPDATE_SUBS: { UpdateSubs(); break; } case IDM_REMOVE: { OnRemove(); delete this; break; } case IDM_EDIT: { auto Dlg = new LInput(GetTree(), Uri.ToString(), "URI:", "Remote Folder Location"); Dlg->DoModal([this, Dlg](auto dlg, auto ctrlId) { if (ctrlId) { Uri.Set(Dlg->GetStr()); Empty(); Select(true); } delete dlg; }); break; } case IDM_REMOTE_URL: { GetRemoteUrl([this](auto str) { LString Url = str.Strip(); if (Url) { auto a = new LAlert(GetTree(), "Remote Url", Url, "Copy", "Ok"); a->DoModal([this, Url](auto dlg, auto code) { if (code == 1) { LClipBoard c(GetTree()); c.Text(Url); } delete dlg; }); } }); break; } default: break; } } } void VcFolder::OnUpdate(const char *Rev) { if (!Rev) return; if (!IsUpdate) { LString Args; NewRev = Rev; switch (GetType()) { case VcGit: Args.Printf("checkout %s", Rev); IsUpdate = StartCmd(Args, &VcFolder::ParseUpdate, NULL, LogNormal); break; case VcSvn: Args.Printf("up -r %s", Rev); IsUpdate = StartCmd(Args, &VcFolder::ParseUpdate, NULL, LogNormal); break; case VcHg: Args.Printf("update -r %s", Rev); IsUpdate = StartCmd(Args, &VcFolder::ParseUpdate, NULL, LogNormal); break; default: { LAssert(!"Impl me."); break; } } } } /////////////////////////////////////////////////////////////////////////////////////// int FolderCompare(LTreeItem *a, LTreeItem *b, NativeInt UserData) { VcLeaf *A = dynamic_cast(a); VcLeaf *B = dynamic_cast(b); if (!A || !B) return 0; return A->Compare(B); } struct SshFindEntry { LString Flags, Name, User, Group; uint64_t Size; LDateTime Modified, Access; SshFindEntry &operator =(const LString &s) { auto p = s.SplitDelimit("/"); if (p.Length() == 7) { Flags = p[0]; Group = p[1]; User = p[2]; Access.Set((uint64_t) p[3].Int()); Modified.Set((uint64_t) p[4].Int()); Size = p[5].Int(); Name = p[6]; } return *this; } bool IsDir() { return Flags(0) == 'd'; } bool IsHidden() { return Name(0) == '.'; } const char *GetName() { return Name; } static int Compare(SshFindEntry *a, SshFindEntry *b) { return Stricmp(a->Name.Get(), b->Name.Get()); } }; bool VcFolder::ParseRemoteFind(int Result, LString s, ParseParams *Params) { if (!Params || !s) return false; auto Parent = Params->Leaf ? static_cast(Params->Leaf) : static_cast(this); LUri u(Params->Str); auto Lines = s.SplitDelimit("\r\n"); LArray Entries; for (size_t i=1; iStr, Dir.GetName(), true); } } else if (!Dir.IsHidden()) { char *Ext = LGetExtension(Dir.GetName()); if (!Ext) continue; if (!stricmp(Ext, "c") || !stricmp(Ext, "cpp") || !stricmp(Ext, "h")) { LUri Path = u; Path += Dir.GetName(); new VcLeaf(this, Parent, Params->Str, Dir.GetName(), false); } } } return false; } void VcFolder::ReadDir(LTreeItem *Parent, const char *ReadUri) { LUri u(ReadUri); if (u.IsFile()) { // Read child items LDirectory Dir; for (int b = Dir.First(u.LocalPath()); b; b = Dir.Next()) { auto name = Dir.GetName(); if (Dir.IsHidden()) continue; LUri Path = u; Path += name; new VcLeaf(this, Parent, u.ToString(), name, Dir.IsDir()); } } else { auto c = d->GetConnection(ReadUri); if (!c) return; LString Path = u.sPath(Uri.sPath.Length(), -1).LStrip("/"); LString Args; Args.Printf("\"%s\" -maxdepth 1 -printf \"%%M/%%g/%%u/%%A@/%%T@/%%s/%%P\n\"", Path ? Path.Get() : "."); auto *Params = new ParseParams(ReadUri); Params->Leaf = dynamic_cast(Parent); c->Command(this, "find", Args, &VcFolder::ParseRemoteFind, Params); return; } Parent->Sort(FolderCompare); } void VcFolder::OnVcsType(LString errorMsg) { if (!d) { LAssert(!"No priv instance"); return; } auto c = d->GetConnection(Uri.ToString(), false); if (c) { auto NewType = c->Types.Find(Uri.sPath); if (NewType && NewType != Type) { if (NewType == VcError) { - OnCmdError(NULL, errorMsg); + OnCmdError(LString(), errorMsg); } else { Type = NewType; ClearError(); Update(); if (LTreeItem::Select()) Select(true); } } } } void VcFolder::DoExpand() { if (Tmp) { Tmp->Remove(); DeleteObj(Tmp); ReadDir(this, Uri.ToString()); } } void VcFolder::OnExpand(bool b) { if (b) DoExpand(); } void VcFolder::ListCommit(VcCommit *c) { if (!IsFilesCmd) { LString Args; switch (GetType()) { case VcGit: Args.Printf("-P show %s", c->GetRev()); IsFilesCmd = StartCmd(Args, &VcFolder::ParseFiles, new ParseParams(c->GetRev())); break; case VcSvn: Args.Printf("log --verbose --diff -r %s", c->GetRev()); IsFilesCmd = StartCmd(Args, &VcFolder::ParseFiles, new ParseParams(c->GetRev())); break; case VcCvs: { d->ClearFiles(); for (unsigned i=0; iFiles.Length(); i++) { VcFile *f = new VcFile(d, this, c->GetRev(), false); if (f) { f->SetText(c->Files[i], COL_FILENAME); d->Files->Insert(f); } } FilterCurrentFiles(); break; } case VcHg: { Args.Printf("diff --change %s", c->GetRev()); IsFilesCmd = StartCmd(Args, &VcFolder::ParseFiles, new ParseParams(c->GetRev())); break; } default: LAssert(!"Impl me."); break; } if (IsFilesCmd) d->ClearFiles(); } } LString ConvertUPlus(LString s) { LArray c; LUtf8Ptr p(s); int32 ch; while ((ch = p)) { if (ch == '{') { auto n = p.GetPtr(); if (n[1] == 'U' && n[2] == '+') { // Convert unicode code point p += 3; ch = (int32)htoi(p.GetPtr()); c.Add(ch); while ((ch = p) != '}') p++; } else c.Add(ch); } else c.Add(ch); p++; } c.Add(0); #ifdef LINUX return LString((char16*)c.AddressOf()); #else return LString(c.AddressOf()); #endif } bool VcFolder::ParseStatus(int Result, LString s, ParseParams *Params) { bool ShowUntracked = d->Wnd()->GetCtrlValue(IDC_UNTRACKED) != 0; bool IsWorking = Params ? Params->IsWorking : false; List Ins; switch (GetType()) { case VcCvs: { LHashTbl,VcFile*> Map; for (auto i: *d->Files) { VcFile *f = dynamic_cast(i); if (f) Map.Add(f->GetText(COL_FILENAME), f); } #if 0 LFile Tmp("C:\\tmp\\output.txt", O_WRITE); Tmp.Write(s); Tmp.Close(); #endif LString::Array a = s.Split("==================================================================="); for (auto i : a) { LString::Array Lines = i.SplitDelimit("\r\n"); if (Lines.Length() == 0) continue; LString f = Lines[0].Strip(); if (f.Find("File:") == 0) { LString::Array Parts = f.SplitDelimit("\t"); LString File = Parts[0].Split(": ").Last().Strip(); LString Status = Parts[1].Split(": ").Last(); LString WorkingRev; for (auto l : Lines) { LString::Array p = l.Strip().Split(":", 1); if (p.Length() > 1 && p[0].Strip().Equals("Working revision")) { WorkingRev = p[1].Strip(); } } VcFile *f = Map.Find(File); if (!f) { if ((f = new VcFile(d, this, WorkingRev, IsWorking))) Ins.Insert(f); } if (f) { f->SetText(Status, COL_STATE); f->SetText(File, COL_FILENAME); f->Update(); } } else if (f(0) == '?' && ShowUntracked) { LString File = f(2, -1); VcFile *f = Map.Find(File); if (!f) { - if ((f = new VcFile(d, this, NULL, IsWorking))) + if ((f = new VcFile(d, this, LString(), IsWorking))) Ins.Insert(f); } if (f) { f->SetText("?", COL_STATE); f->SetText(File, COL_FILENAME); f->Update(); } } } for (auto i: *d->Files) { VcFile *f = dynamic_cast(i); if (f) { if (f->GetStatus() == VcFile::SUnknown) f->SetStatus(VcFile::SUntracked); } } break; } case VcGit: { LString::Array Lines = s.SplitDelimit("\r\n"); int Fmt = ToolVersion[VcGit] >= Ver2Int("2.8.0") ? 2 : 1; for (auto Ln : Lines) { char Type = Ln(0); if (Ln.Lower().Find("error:") >= 0) { } else if (Ln.Find("usage: git") >= 0) { // It's probably complaining about the --porcelain=2 parameter OnCmdError(s, "Args error"); } else if (Type != '?') { VcFile *f = NULL; if (Fmt == 2) { LString::Array p = Ln.SplitDelimit(" ", 8); if (p.Length() < 7) d->Log->Print("%s:%i - Error: not enough tokens: '%s'\n", _FL, Ln.Get()); else { f = new VcFile(d, this, p[6], IsWorking); f->SetText(p[1].Strip("."), COL_STATE); f->SetText(p.Last(), COL_FILENAME); } } else if (Fmt == 1) { LString::Array p = Ln.SplitDelimit(" "); - f = new VcFile(d, this, NULL, IsWorking); + f = new VcFile(d, this, LString(), IsWorking); f->SetText(p[0], COL_STATE); f->SetText(p.Last(), COL_FILENAME); } if (f) Ins.Insert(f); } else if (ShowUntracked) { - VcFile *f = new VcFile(d, this, NULL, IsWorking); + VcFile *f = new VcFile(d, this, LString(), IsWorking); f->SetText("?", COL_STATE); f->SetText(Ln(2,-1), COL_FILENAME); Ins.Insert(f); } } break; } case VcHg: case VcSvn: { if (s.Find("failed to import") >= 0) { OnCmdError(s, "Tool error."); return false; } LString::Array Lines = s.SplitDelimit("\r\n"); for (auto Ln : Lines) { char Type = Ln(0); if (Ln.Lower().Find("error:") >= 0) { } else if (Ln.Find("client is too old") >= 0) { OnCmdError(s, "Client too old."); return false; } else if (Strchr(" \t", Type) || Ln.Find("Summary of conflicts") >= 0) { // Ignore } else if (Type != '?') { LString::Array p = Ln.SplitDelimit(" ", 1); if (p.Length() == 2) { LString File; if (GetType() == VcSvn) File = ConvertUPlus(p.Last()); else File = p.Last(); if (GetType() == VcSvn && File.Find("+ ") == 0) { File = File(5, -1); } - VcFile *f = new VcFile(d, this, NULL, IsWorking); + VcFile *f = new VcFile(d, this, LString(), IsWorking); f->SetText(p[0], COL_STATE); f->SetText(File.Replace("\\","/"), COL_FILENAME); f->GetStatus(); Ins.Insert(f); } else LAssert(!"What happen?"); } else if (ShowUntracked) { - VcFile *f = new VcFile(d, this, NULL, IsWorking); + VcFile *f = new VcFile(d, this, LString(), IsWorking); f->SetText("?", COL_STATE); f->SetText(Ln(2,-1), COL_FILENAME); Ins.Insert(f); } } break; } default: { LAssert(!"Impl me."); break; } } if ((Unpushed = Ins.Length() > 0)) { if (CmdErrors == 0) GetCss(true)->Color(LColour(255, 128, 0)); } else if (Unpulled == 0) { GetCss(true)->Color(LCss::ColorInherit); } Update(); if (LTreeItem::Select()) { d->Files->Insert(Ins); FilterCurrentFiles(); } else { Ins.DeleteObjects(); } if (Params && Params->Leaf) Params->Leaf->AfterBrowse(); return false; // Don't refresh list } // Clone/checkout any sub-repositries. bool VcFolder::UpdateSubs() { LString Arg; switch (GetType()) { default: case VcSvn: case VcHg: case VcCvs: return false; case VcGit: Arg = "submodule update --init"; break; } return StartCmd(Arg, &VcFolder::ParseUpdateSubs, NULL, LogNormal); } bool VcFolder::ParseUpdateSubs(int Result, LString s, ParseParams *Params) { switch (GetType()) { default: case VcSvn: case VcHg: case VcCvs: return false; case VcGit: break; } return false; } void VcFolder::FolderStatus(const char *uri, VcLeaf *Notify) { LUri Uri(uri); if (Uri.IsFile() && Uri.sPath) { LFile::Path p(Uri.sPath(1,-1)); if (!p.IsFolder()) { LAssert(!"Needs to be a folder."); return; } } if (LTreeItem::Select()) d->ClearFiles(); LString Arg; switch (GetType()) { case VcSvn: case VcHg: Arg = "status"; break; case VcCvs: Arg = "status -l"; break; case VcGit: if (!ToolVersion[VcGit]) LAssert(!"Where is the version?"); // What version did =2 become available? It's definitely not in v2.5.4 // Not in v2.7.4 either... if (ToolVersion[VcGit] >= Ver2Int("2.8.0")) Arg = "-P status --porcelain=2"; else Arg = "-P status --porcelain"; break; default: return; } ParseParams *p = new ParseParams; if (uri && Notify) { p->AltInitPath = uri; p->Leaf = Notify; } else { p->IsWorking = true; } StartCmd(Arg, &VcFolder::ParseStatus, p); switch (GetType()) { case VcHg: CountToTip(); break; default: break; } } void VcFolder::CountToTip() { // if (Path.Equals("C:\\Users\\matthew\\Code\\Lgi\\trunk")) { // LgiTrace("%s: CountToTip, br=%s, idx=%i\n", Path.Get(), CurrentBranch.Get(), (int)CurrentCommitIdx); if (!CurrentBranch) GetBranches(new ParseParams("CountToTip")); else if (CurrentCommitIdx < 0) GetCurrentRevision(new ParseParams("CountToTip")); else { LString Arg; Arg.Printf("id -n -r %s", CurrentBranch.Get()); StartCmd(Arg, &VcFolder::ParseCountToTip); } } } bool VcFolder::ParseCountToTip(int Result, LString s, ParseParams *Params) { switch (GetType()) { case VcHg: if (CurrentCommitIdx >= 0) { auto p = s.Strip(); auto idx = p.Int(); if (idx >= CurrentCommitIdx) { Unpulled = (int) (idx - CurrentCommitIdx); Update(); } } break; default: break; } return false; } void VcFolder::ListWorkingFolder() { if (IsWorkingFld) return; d->ClearFiles(); bool Untracked = d->IsMenuChecked(IDM_UNTRACKED); LString Arg; switch (GetType()) { case VcCvs: if (Untracked) Arg = "-qn update"; else Arg = "-q diff --brief"; break; case VcSvn: Arg = "status"; break; case VcGit: Arg = "-P diff --diff-filter=ACDMRTU"; break; case VcHg: Arg = "status -mard"; break; default: return; } IsWorkingFld = StartCmd(Arg, &VcFolder::ParseWorking); } void VcFolder::GitAdd() { if (!PostAdd) return; LString Args; if (PostAdd->Files.Length() == 0) { LString m(PostAdd->Msg); m = m.Replace("\"", "\\\""); Args.Printf("commit -m \"%s\"", m.Get()); IsCommit = StartCmd(Args, &VcFolder::ParseCommit, PostAdd->Param, LogNormal); PostAdd.Reset(); } else { LString Last = PostAdd->Files.Last(); Args.Printf("add \"%s\"", Last.Replace("\"", "\\\"").Replace("/", DIR_STR).Get()); PostAdd->Files.PopLast(); StartCmd(Args, &VcFolder::ParseGitAdd, NULL, LogNormal); } } bool VcFolder::ParseGitAdd(int Result, LString s, ParseParams *Params) { GitAdd(); return false; } bool VcFolder::ParseCommit(int Result, LString s, ParseParams *Params) { if (LTreeItem::Select()) Select(true); CommitListDirty = Result == 0; CurrentCommit.Empty(); IsCommit = false; if (Result) { switch (GetType()) { case VcGit: { if (s.Find("Please tell me who you are") >= 0) { auto i = new LInput(GetTree(), "", "Git user name:", AppName); i->DoModal([this, i](auto dlg, auto ctrlId) { if (ctrlId) { LString Args; Args.Printf("config --global user.name \"%s\"", i->GetStr().Get()); StartCmd(Args); auto inp = new LInput(GetTree(), "", "Git user email:", AppName); i->DoModal([this, inp](auto dlg, auto ctrlId) { if (ctrlId) { LString Args; Args.Printf("config --global user.email \"%s\"", inp->GetStr().Get()); StartCmd(Args); } delete dlg; }); } delete dlg; }); } break; } default: break; } return false; } if (Result == 0 && LTreeItem::Select()) { d->ClearFiles(); auto *w = d->Diff ? d->Diff->GetWindow() : NULL; if (w) w->SetCtrlName(IDC_MSG, NULL); } switch (GetType()) { case VcGit: { Unpushed++; CommitListDirty = true; Update(); if (Params && Params->Str.Find("Push") >= 0) Push(); break; } case VcSvn: { CurrentCommit.Empty(); CommitListDirty = true; GetTree()->SendNotify((LNotifyType)LvcCommandEnd); if (!Result) { Unpushed = 0; Update(); GetCss(true)->Color(LColour::Green); } break; } case VcHg: { CurrentCommit.Empty(); CommitListDirty = true; GetTree()->SendNotify((LNotifyType)LvcCommandEnd); if (!Result) { Unpushed = 0; Update(); if (Params && Params->Str.Find("Push") >= 0) Push(); else GetCss(true)->Color(LColour::Green); } break; } case VcCvs: { CurrentCommit.Empty(); CommitListDirty = true; GetTree()->SendNotify((LNotifyType)LvcCommandEnd); if (!Result) { Unpushed = 0; Update(); GetCss(true)->Color(LColour::Green); } break; } default: { LAssert(!"Impl me."); break; } } return true; } void VcFolder::Commit(const char *Msg, const char *Branch, bool AndPush) { LArray Add; bool Partial = false; for (auto fp: *d->Files) { VcFile *f = dynamic_cast(fp); if (f) { int c = f->Checked(); if (c > 0) Add.Add(f); else Partial = true; } } if (CurrentBranch && Branch && !CurrentBranch.Equals(Branch)) { int Response = LgiMsg(GetTree(), "Do you want to start a new branch?", AppName, MB_YESNO); if (Response != IDYES) return; LJson j; j.Set("Command", "commit"); j.Set("Msg", Msg); j.Set("AndPush", (int64_t)AndPush); StartBranch(Branch, j.GetJson()); return; } if (!IsCommit) { LString Args; ParseParams *Param = AndPush ? new ParseParams("Push") : NULL; switch (GetType()) { case VcGit: { if (Add.Length() == 0) { break; } else if (Partial) { if (PostAdd.Reset(new GitCommit)) { PostAdd->Files.SetFixedLength(false); for (auto f : Add) PostAdd->Files.Add(f->GetFileName()); PostAdd->Msg = Msg; PostAdd->Branch = Branch; PostAdd->Param = Param; GitAdd(); } } else { LString m(Msg); m = m.Replace("\"", "\\\""); Args.Printf("commit -am \"%s\"", m.Get()); IsCommit = StartCmd(Args, &VcFolder::ParseCommit, Param, LogNormal); } break; } case VcSvn: { LString::Array a; a.New().Printf("commit -m \"%s\"", Msg); for (auto pf: Add) { LString s = pf->GetFileName(); if (s.Find(" ") >= 0) a.New().Printf("\"%s\"", s.Get()); else a.New() = s; } Args = LString(" ").Join(a); IsCommit = StartCmd(Args, &VcFolder::ParseCommit, Param, LogNormal); if (d->Tabs && IsCommit) { d->Tabs->Value(1); GetTree()->SendNotify((LNotifyType)LvcCommandStart); } break; } case VcHg: { LString::Array a; LString CommitMsg = Msg; TmpFile Tmp; if (CommitMsg.Find("\n") >= 0) { Tmp.Create().Write(Msg); a.New().Printf("commit -l \"%s\"", Tmp.GetName()); } else { a.New().Printf("commit -m \"%s\"", Msg); } if (Partial) { for (auto pf: Add) { LString s = pf->GetFileName(); if (s.Find(" ") >= 0) a.New().Printf("\"%s\"", s.Get()); else a.New() = s; } } Args = LString(" ").Join(a); IsCommit = StartCmd(Args, &VcFolder::ParseCommit, Param, LogNormal); if (d->Tabs && IsCommit) { d->Tabs->Value(1); GetTree()->SendNotify((LNotifyType)LvcCommandStart); } break; } case VcCvs: { LString a; a.Printf("commit -m \"%s\"", Msg); IsCommit = StartCmd(a, &VcFolder::ParseCommit, NULL, LogNormal); break; } default: { - OnCmdError(NULL, "No commit impl for type."); + OnCmdError(LString(), "No commit impl for type."); break; } } } } bool VcFolder::ParseStartBranch(int Result, LString s, ParseParams *Params) { switch (GetType()) { case VcHg: { if (Result == 0 && Params && Params->Str) { LJson j(Params->Str); auto cmd = j.Get("Command"); if (cmd.Equals("commit")) { auto Msg = j.Get("Msg"); auto AndPush = j.Get("AndPush").Int(); if (Msg) { Commit(Msg, NULL, AndPush > 0); } } } break; } default: { - OnCmdError(NULL, "No commit impl for type."); + OnCmdError(LString(), "No commit impl for type."); break; } } return true; } void VcFolder::StartBranch(const char *BranchName, const char *OnCreated) { if (!BranchName) return; switch (GetType()) { case VcHg: { LString a; a.Printf("branch \"%s\"", BranchName); StartCmd(a, &VcFolder::ParseStartBranch, OnCreated ? new ParseParams(OnCreated) : NULL); break; } default: { - OnCmdError(NULL, "No commit impl for type."); + OnCmdError(LString(), "No commit impl for type."); break; } } } void VcFolder::Push(bool NewBranchOk) { LString Args; bool Working = false; switch (GetType()) { case VcHg: { auto args = NewBranchOk ? "push --new-branch" : "push"; Working = StartCmd(args, &VcFolder::ParsePush, NULL, LogNormal); break; } case VcGit: { Working = StartCmd("push", &VcFolder::ParsePush, NULL, LogNormal); break; } case VcSvn: { // Nothing to do here.. the commit pushed the data already break; } default: { - OnCmdError(NULL, "No push impl for type."); + OnCmdError(LString(), "No push impl for type."); break; } } if (d->Tabs && Working) { d->Tabs->Value(1); GetTree()->SendNotify((LNotifyType)LvcCommandStart); } } bool VcFolder::ParsePush(int Result, LString s, ParseParams *Params) { bool Status = false; if (Result) { if (GetType() == VcHg) { if (s.Find("push creates new remote branches") > 0) { if (LgiMsg(GetTree(), "Push will create a new remote branch. Is that ok?", AppName, MB_YESNO) == IDYES) { Push(true); return false; } } } OnCmdError(s, "Push failed."); } else { switch (GetType()) { case VcGit: break; case VcSvn: break; default: break; } Unpushed = 0; GetCss(true)->Color(LColour::Green); Update(); Status = true; } GetTree()->SendNotify((LNotifyType)LvcCommandEnd); return Status; // no reselect } void VcFolder::Pull(int AndUpdate, LoggingType Logging) { bool Status = false; if (AndUpdate < 0) AndUpdate = GetTree()->GetWindow()->GetCtrlValue(IDC_UPDATE) != 0; switch (GetType()) { case VcNone: return; case VcHg: Status = StartCmd(AndUpdate ? "pull -u" : "pull", &VcFolder::ParsePull, NULL, Logging); break; case VcGit: Status = StartCmd(AndUpdate ? "pull" : "fetch", &VcFolder::ParsePull, NULL, Logging); break; case VcSvn: Status = StartCmd("up", &VcFolder::ParsePull, NULL, Logging); break; default: - OnCmdError(NULL, "No pull impl for type."); + OnCmdError(LString(), "No pull impl for type."); break; } if (d->Tabs && Status) { d->Tabs->Value(1); GetTree()->SendNotify((LNotifyType)LvcCommandStart); } } bool VcFolder::ParsePull(int Result, LString s, ParseParams *Params) { GetTree()->SendNotify((LNotifyType)LvcCommandEnd); if (Result) { OnCmdError(s, "Pull failed."); return false; } else ClearError(); switch (GetType()) { case VcGit: { // Git does a merge by default, so the current commit changes... CurrentCommit.Empty(); break; } case VcHg: { CurrentCommit.Empty(); auto Lines = s.SplitDelimit("\n"); bool HasUpdates = false; for (auto Ln: Lines) { if (Ln.Find("files updated") < 0) continue; auto Parts = Ln.Split(","); for (auto p: Parts) { auto n = p.Strip().Split(" ", 1); if (n.Length() == 2) { if (n[0].Int() > 0) HasUpdates = true; } } } if (HasUpdates) GetCss(true)->Color(LColour::Green); else GetCss(true)->Color(LCss::ColorInherit); break; } case VcSvn: { // Svn also does a merge by default and can update our current position... CurrentCommit.Empty(); LString::Array a = s.SplitDelimit("\r\n"); for (auto &Ln: a) { if (Ln.Find("At revision") >= 0) { LString::Array p = Ln.SplitDelimit(" ."); CurrentCommit = p.Last(); break; } else if (Ln.Find("svn cleanup") >= 0) { OnCmdError(s, "Needs cleanup"); break; } } if (Params && Params->Str.Equals("log")) { LVariant Limit; d->Opts.GetValue("svn-limit", Limit); LString Args; if (Limit.CastInt32() > 0) Args.Printf("log --limit %i", Limit.CastInt32()); else Args = "log"; IsLogging = StartCmd(Args, &VcFolder::ParseLog); return false; } break; } default: break; } CommitListDirty = true; return true; // Yes - reselect and update } void VcFolder::MergeToLocal(LString Rev) { switch (GetType()) { case VcGit: { LString Args; Args.Printf("merge -m \"Merge with %s\" %s", Rev.Get(), Rev.Get()); StartCmd(Args, &VcFolder::ParseMerge, NULL, LogNormal); break; } case VcHg: { LString Args; Args.Printf("merge -r %s", Rev.Get()); StartCmd(Args, &VcFolder::ParseMerge, NULL, LogNormal); break; } default: LgiMsg(GetTree(), "Not implemented.", AppName); break; } } bool VcFolder::ParseMerge(int Result, LString s, ParseParams *Params) { switch (GetType()) { case VcGit: case VcHg: if (Result == 0) CommitListDirty = true; else OnCmdError(s, "Merge failed."); break; default: LAssert(!"Impl me."); break; } return true; } void VcFolder::Refresh() { CommitListDirty = true; CurrentCommit.Empty(); GitNames.Empty(); Branches.DeleteObjects(); if (Uncommit && Uncommit->LListItem::Select()) Uncommit->Select(true); Select(true); } void VcFolder::Clean() { switch (GetType()) { case VcSvn: StartCmd("cleanup", &VcFolder::ParseClean, NULL, LogNormal); break; default: LgiMsg(GetTree(), "Not implemented.", AppName); break; } } bool VcFolder::ParseClean(int Result, LString s, ParseParams *Params) { switch (GetType()) { case VcSvn: if (Result == 0) GetCss(true)->Color(LCss::ColorInherit); break; default: LAssert(!"Impl me."); break; } return false; } LColour VcFolder::BranchColour(const char *Name) { if (!Name) return GetPaletteColour(0); auto b = Branches.Find(Name); if (!b) // Must be a new one? { int i = 1; for (auto b: Branches) { auto &v = b.value; if (!v->Colour.IsValid()) { if (v->Default) v->Colour = GetPaletteColour(0); else v->Colour = GetPaletteColour(i++); } } Branches.Add(Name, b = new VcBranch(Name)); b->Colour = GetPaletteColour((int)Branches.Length()); } return b ? b->Colour : GetPaletteColour(0); } void VcFolder::CurrentRev(std::function Callback) { LString Cmd; Cmd.Printf("id -i"); RunCmd(Cmd, LogNormal, [Callback](auto r) { if (r.Code == 0) Callback(r.Out.Strip()); }); } bool VcFolder::RenameBranch(LString NewName, LArray &Revs) { switch (GetType()) { case VcHg: { // Update to the ancestor of the commits LHashTbl,int> Refs(0, -1); for (auto c: Revs) { for (auto p:*c->GetParents()) if (Refs.Find(p) < 0) Refs.Add(p, 0); if (Refs.Find(c->GetRev()) >= 0) Refs.Add(c->GetRev(), 1); } LString::Array Ans; for (auto i:Refs) { if (i.value == 0) Ans.Add(i.key); } LArray Ancestors = d->GetRevs(Ans); if (Ans.Length() != 1) { // We should only have one ancestor LString s, m; s.Printf("Wrong number of ancestors: " LPrintfInt64 ".\n", Ans.Length()); for (auto i: Ancestors) { m.Printf("\t%s\n", i->GetRev()); s += m; } LgiMsg(GetTree(), s, AppName, MB_OK); break; } LArray Top; for (auto c:Revs) { for (auto p:*c->GetParents()) if (Refs.Find(p) == 0) Top.Add(c); } if (Top.Length() != 1) { d->Log->Print("Error: Can't find top most commit. (%s:%i)\n", _FL); return false; } // Create the new branch... auto First = Ancestors.First(); LString Cmd; Cmd.Printf("update -r " LPrintfInt64, First->GetIndex()); RunCmd(Cmd, LogNormal, [this, &Cmd, NewName, &Top](auto r) { if (r.Code) { d->Log->Print("Error: Cmd '%s' failed. (%s:%i)\n", Cmd.Get(), _FL); return; } Cmd.Printf("branch \"%s\"", NewName.Get()); RunCmd(Cmd, LogNormal, [this, &Cmd, NewName, &Top](auto r) { if (r.Code) { d->Log->Print("Error: Cmd '%s' failed. (%s:%i)\n", Cmd.Get(), _FL); return; } // Commit it to get a revision point to rebase to Cmd.Printf("commit -m \"Branch: %s\"", NewName.Get()); RunCmd(Cmd, LogNormal, [this, &Cmd, NewName, &Top](auto r) { if (r.Code) { d->Log->Print("Error: Cmd '%s' failed. (%s:%i)\n", Cmd.Get(), _FL); return; } CurrentRev([this, &Cmd, NewName, &Top](auto BranchNode) { // Rebase the old tree to this point Cmd.Printf("rebase -s %s -d %s", Top.First()->GetRev(), BranchNode.Get()); RunCmd(Cmd, LogNormal, [this, &Cmd, NewName, Top](auto r) { if (r.Code) { d->Log->Print("Error: Cmd '%s' failed. (%s:%i)\n", Cmd.Get(), _FL); return; } CommitListDirty = true; d->Log->Print("Finished rename.\n", _FL); }); }); }); }); }); break; } default: { LgiMsg(GetTree(), "Not impl for this VCS.", AppName); break; } } return true; } void VcFolder::GetVersion() { auto t = GetType(); switch (t) { case VcGit: case VcSvn: case VcHg: case VcCvs: StartCmd("--version", &VcFolder::ParseVersion, NULL, LogNormal); break; case VcPending: break; default: - OnCmdError(NULL, "No version control found."); + OnCmdError(LString(), "No version control found."); break; } } bool VcFolder::ParseVersion(int Result, LString s, ParseParams *Params) { if (Result) return false; auto p = s.SplitDelimit(); switch (GetType()) { case VcGit: { if (p.Length() > 2) { ToolVersion[GetType()] = Ver2Int(p[2]); printf("Git version: %s\n", p[2].Get()); } else LAssert(0); break; } case VcSvn: { if (p.Length() > 2) { ToolVersion[GetType()] = Ver2Int(p[2]); printf("Svn version: %s\n", p[2].Get()); } else LAssert(0); break; } case VcHg: { if (p.Length() >= 5) { auto Ver = p[4].Strip("()"); ToolVersion[GetType()] = Ver2Int(Ver); printf("Hg version: %s\n", Ver.Get()); } break; } case VcCvs: { #ifdef _DEBUG for (auto i : p) printf("i='%s'\n", i.Get()); #endif if (p.Length() > 1) { auto Ver = p[2]; ToolVersion[GetType()] = Ver2Int(Ver); printf("Cvs version: %s\n", Ver.Get()); } break; } default: break; } return false; } bool VcFolder::ParseAddFile(int Result, LString s, ParseParams *Params) { switch (GetType()) { case VcCvs: { if (Result) { d->Tabs->Value(1); OnCmdError(s, "Add file failed"); } else ClearError(); break; } default: break; } return false; } bool VcFolder::AddFile(const char *Path, bool AsBinary) { if (!Path) return false; switch (GetType()) { case VcCvs: { auto p = LString(Path).RSplit(DIR_STR, 1); ParseParams *params = NULL; if (p.Length() >= 2) { if ((params = new ParseParams)) params->AltInitPath = p[0]; } LString a; a.Printf("add%s \"%s\"", AsBinary ? " -kb" : "", p.Length() > 1 ? p.Last().Get() : Path); return StartCmd(a, &VcFolder::ParseAddFile, params); break; } default: { LAssert(!"Impl me."); break; } } return false; } bool VcFolder::ParseRevert(int Result, LString s, ParseParams *Params) { if (GetType() == VcSvn) { if (s.Find("Skipped ") >= 0) Result = 1; // Stupid svn... *sigh* } if (Result) { OnCmdError(s, "Error reverting changes."); } ListWorkingFolder(); return false; } bool VcFolder::Revert(LString::Array &Uris, const char *Revision) { if (Uris.Length() == 0) return false; switch (GetType()) { case VcGit: { LStringPipe p; p.Print("checkout"); for (auto u: Uris) { auto Path = GetFilePart(u); p.Print(" \"%s\"", Path.Get()); } auto a = p.NewLStr(); return StartCmd(a, &VcFolder::ParseRevert); break; } case VcHg: case VcSvn: { LStringPipe p; if (Revision) p.Print("up -r %s", Revision); else p.Print("revert"); for (auto u: Uris) { auto Path = GetFilePart(u); p.Print(" \"%s\"", Path.Get()); } auto a = p.NewLStr(); return StartCmd(a, &VcFolder::ParseRevert); break; } default: { LAssert(!"Impl me."); break; } } return false; } bool VcFolder::ParseResolveList(int Result, LString s, ParseParams *Params) { switch (GetType()) { case VcHg: { auto lines = s.Replace("\r").Split("\n"); for (auto &ln: lines) { auto p = ln.Split(" ", 1); if (p.Length() == 2) { if (p[0].Equals("U")) { - auto f = new VcFile(d, this, NULL, true); + auto f = new VcFile(d, this, LString(), true); f->SetText(p[0], COL_STATE); f->SetText(p[1], COL_FILENAME); f->GetStatus(); d->Files->Insert(f); } } } break; } default: { LAssert(!"Impl me."); break; } } return true; } bool VcFolder::ParseResolve(int Result, LString s, ParseParams *Params) { switch (GetType()) { case VcGit: { break; } case VcHg: { d->Log->Print("Resolve: %s\n", s.Get()); break; } default: { LAssert(!"Impl me."); break; } } return true; } bool VcFolder::Resolve(const char *Path, LvcResolve Type) { if (!Path) return false; switch (GetType()) { case VcGit: { LString a; a.Printf("add \"%s\"", Path); return StartCmd(a, &VcFolder::ParseResolve, new ParseParams(Path)); } case VcHg: { LString a; auto local = GetFilePart(Path); switch (Type) { case ResolveMark: a.Printf("resolve -m \"%s\"", local.Get()); break; case ResolveUnmark: a.Printf("resolve -u \"%s\"", local.Get()); break; case ResolveLocal: a.Printf("resolve -t internal:local \"%s\"", local.Get()); break; case ResolveIncoming: a.Printf("resolve -t internal:other \"%s\"", local.Get()); break; default: break; } if (a) return StartCmd(a, &VcFolder::ParseResolve, new ParseParams(Path)); break; } case VcSvn: case VcCvs: default: { LAssert(!"Impl me."); break; } } return false; } bool VcFolder::ParseBlame(int Result, LString s, ParseParams *Params) { new BlameUi(d, GetType(), s); return false; } bool VcFolder::Blame(const char *Path) { if (!Path) return false; LUri u(Path); switch (GetType()) { case VcGit: { LString a; a.Printf("-P blame \"%s\"", u.sPath.Get()); return StartCmd(a, &VcFolder::ParseBlame); break; } case VcHg: { LString a; a.Printf("annotate -un \"%s\"", u.sPath.Get()); return StartCmd(a, &VcFolder::ParseBlame); break; } case VcSvn: { LString a; a.Printf("blame \"%s\"", u.sPath.Get()); return StartCmd(a, &VcFolder::ParseBlame); break; } default: { LAssert(!"Impl me."); break; } } return true; } bool VcFolder::SaveFileAs(const char *Path, const char *Revision) { if (!Path || !Revision) return false; return true; } bool VcFolder::ParseSaveAs(int Result, LString s, ParseParams *Params) { return false; } bool VcFolder::ParseCounts(int Result, LString s, ParseParams *Params) { switch (GetType()) { case VcGit: { Unpushed = (int) s.Strip().Split("\n").Length(); break; } case VcSvn: { int64 ServerRev = 0; bool HasUpdate = false; LString::Array c = s.Split("\n"); for (unsigned i=0; i 1 && a[0].Equals("Status")) ServerRev = a.Last().Int(); else if (a[0].Equals("*")) HasUpdate = true; } if (ServerRev > 0 && HasUpdate) { int64 CurRev = CurrentCommit.Int(); Unpulled = (int) (ServerRev - CurRev); } else Unpulled = 0; Update(); break; } default: { LAssert(!"Impl me."); break; } } IsUpdatingCounts = false; Update(); return false; // No re-select } void VcFolder::SetEol(const char *Path, int Type) { if (!Path) return; switch (Type) { case IDM_EOL_LF: { ConvertEol(Path, false); break; } case IDM_EOL_CRLF: { ConvertEol(Path, true); break; } case IDM_EOL_AUTO: { #ifdef WINDOWS ConvertEol(Path, true); #else ConvertEol(Path, false); #endif break; } } } void VcFolder::UncommitedItem::Select(bool b) { LListItem::Select(b); if (b) { LTreeItem *i = d->Tree->Selection(); VcFolder *f = dynamic_cast(i); if (f) f->ListWorkingFolder(); if (d->Msg) { d->Msg->Name(NULL); auto *w = d->Msg->GetWindow(); if (w) { w->SetCtrlEnabled(IDC_COMMIT, true); w->SetCtrlEnabled(IDC_COMMIT_AND_PUSH, true); } } } } void VcFolder::UncommitedItem::OnPaint(LItem::ItemPaintCtx &Ctx) { LFont *f = GetList()->GetFont(); f->Transparent(false); f->Colour(Ctx.Fore, Ctx.Back); LDisplayString ds(f, "(working folder)"); ds.Draw(Ctx.pDC, Ctx.x1 + ((Ctx.X() - ds.X()) / 2), Ctx.y1 + ((Ctx.Y() - ds.Y()) / 2), &Ctx); } ////////////////////////////////////////////////////////////////////////////////////////// VcLeaf::VcLeaf(VcFolder *parent, LTreeItem *Item, LString uri, LString leaf, bool folder) { Parent = parent; d = Parent->GetPriv(); LAssert(uri.Find("://") >= 0); // Is URI Uri.Set(uri); LAssert(Uri); Leaf = leaf; Folder = folder; Tmp = NULL; Item->Insert(this); if (Folder) { Insert(Tmp = new LTreeItem); Tmp->SetText("Loading..."); } } VcLeaf::~VcLeaf() { Log.DeleteObjects(); } LString VcLeaf::Full() { LUri u = Uri; u += Leaf; return u.ToString(); } void VcLeaf::OnBrowse() { auto full = Full(); LList *Files = d->Files; Files->Empty(); LDirectory Dir; for (int b = Dir.First(full); b; b = Dir.Next()) { if (Dir.IsDir()) continue; - VcFile *f = new VcFile(d, Parent, NULL, true); + VcFile *f = new VcFile(d, Parent, LString(), true); if (f) { f->SetUri(LString("file://") + full); f->SetText(Dir.GetName(), COL_FILENAME); Files->Insert(f); } } Files->ResizeColumnsToContent(); if (Folder) Parent->FolderStatus(full, this); } void VcLeaf::AfterBrowse() { } VcLeaf *VcLeaf::FindLeaf(const char *Path, bool OpenTree) { if (!Stricmp(Path, Full().Get())) return this; if (OpenTree) DoExpand(); VcLeaf *r = NULL; for (auto n = GetChild(); !r && n; n = n->GetNext()) { auto l = dynamic_cast(n); if (l) r = l->FindLeaf(Path, OpenTree); } return r; } void VcLeaf::DoExpand() { if (Tmp) { Tmp->Remove(); DeleteObj(Tmp); Parent->ReadDir(this, Full()); } } void VcLeaf::OnExpand(bool b) { if (b) DoExpand(); } const char *VcLeaf::GetText(int Col) { if (Col == 0) return Leaf; return NULL; } int VcLeaf::GetImage(int Flags) { return Folder ? IcoFolder : IcoFile; } int VcLeaf::Compare(VcLeaf *b) { // Sort folders to the top... if (Folder ^ b->Folder) return (int)b->Folder - (int)Folder; // Then alphabetical return Stricmp(Leaf.Get(), b->Leaf.Get()); } bool VcLeaf::Select() { return LTreeItem::Select(); } void VcLeaf::Select(bool b) { LTreeItem::Select(b); if (b) { d->Commits->RemoveAll(); OnBrowse(); ShowLog(); } } void VcLeaf::ShowLog() { if (Log.Length()) { d->Commits->RemoveAll(); Parent->DefaultFields(); Parent->UpdateColumns(); for (auto i: Log) d->Commits->Insert(i); } } void VcLeaf::OnMouseClick(LMouse &m) { if (m.IsContextMenu()) { LSubMenu s; s.AppendItem("Log", IDM_LOG); s.AppendItem("Blame", IDM_BLAME, !Folder); s.AppendSeparator(); s.AppendItem("Browse To", IDM_BROWSE_FOLDER); s.AppendItem("Terminal At", IDM_TERMINAL); int Cmd = s.Float(GetTree(), m - _ScrollPos()); switch (Cmd) { case IDM_LOG: { Parent->LogFile(Full()); break; } case IDM_BLAME: { Parent->Blame(Full()); break; } case IDM_BROWSE_FOLDER: { LBrowseToFile(Full()); break; } case IDM_TERMINAL: { TerminalAt(Full()); break; } } } } ///////////////////////////////////////////////////////////////////////////////////////// ProcessCallback::ProcessCallback(LString exe, LString args, LString localPath, LTextLog *log, LView *view, std::function callback) : Log(log), View(view), Callback(callback), LThread("ProcessCallback.Thread"), LSubProcess(exe, args) { SetInitFolder(localPath); if (Log) Log->Print("%s %s\n", exe.Get(), args.Get()); Run(); } int ProcessCallback::Main() { if (!Start()) { Ret.Out.Printf("Process failed with %i", GetErrorCode()); Callback(Ret); } else { while (IsRunning()) { auto Rd = Read(); if (Rd.Length()) { Ret.Out += Rd; if (Log) Log->Write(Rd.Get(), Rd.Length()); } } auto Rd = Read(); if (Rd.Length()) { Ret.Out += Rd; if (Log) Log->Write(Rd.Get(), Rd.Length()); } Ret.Code = GetExitValue(); } View->PostEvent(M_HANDLE_CALLBACK, (LMessage::Param)this); return 0; } void ProcessCallback::OnComplete() // Called in the GUI thread... { Callback(Ret); } diff --git a/Lvc/src/VcFolder.h b/Lvc/src/VcFolder.h --- a/Lvc/src/VcFolder.h +++ b/Lvc/src/VcFolder.h @@ -1,374 +1,376 @@ #ifndef _VcFolder_h_ #define _VcFolder_h_ #include "lgi/common/SubProcess.h" +#include "lgi/common/Uri.h" + #include class VcLeaf; enum LoggingType { LogNone, // No output from cmd LogNormal, // Output appears as it's available LogSilo, // Output appears after cmd finished (keeps it non-interleaved with other log msgs) }; enum LvcError { ErrNone, ErrSubProcessFailed = GSUBPROCESS_ERROR, }; enum LvcStatus { StatusNone, StatusActive, StatusError, }; enum LvcResolve { ResolveNone, //--- ResolveMark, ResolveUnmark, //--- ResolveLocal, ResolveIncoming, ResolveTool, }; class ReaderThread : public LThread { VersionCtrl Vcs; LStream *Out; LAutoPtr Process; int FilterCount; int OnLine(char *s, ssize_t len); bool OnData(char *Buf, ssize_t &r); public: int Result; ReaderThread(VersionCtrl vcs, LAutoPtr p, LStream *out); ~ReaderThread(); int Main(); }; extern int Ver2Int(LString v); extern int ToolVersion[VcMax]; extern int LstCmp(LListItem *a, LListItem *b, int Col); struct Result { int Code; LString Out; }; struct ProcessCallback : public LThread, public LSubProcess { Result Ret; LView *View = NULL; LTextLog *Log = NULL; std::function Callback; public: ProcessCallback(LString exe, LString args, LString localPath, LTextLog *log, LView *view, std::function callback); int Main(); void OnComplete(); }; struct VcBranch : public LString { bool Default; LColour Colour; LString Hash; - VcBranch(LString name, LString hash = NULL) + VcBranch(LString name, LString hash = LString()) { Default = name.Equals("default") || name.Equals("trunk") || name.Equals("main"); Set(name); if (hash) Hash = hash; } }; struct SshParams { SshConnection *c; VcFolder *f; LString Exe, Args, Path, Output; VersionCtrl Vcs; ParseFn Parser; ParseParams *Params; int ExitCode; SshParams(SshConnection *con) : c(con) { f = NULL; Parser = NULL; Params = NULL; Vcs = VcNone; ExitCode = -1; } }; class VcFolder : public LTreeItem { friend class VcCommit; class Cmd : public LStream { LString::Array Context; LStringPipe Buf; public: LoggingType Logging; LStream *Log; LAutoPtr Rd; ParseFn PostOp; LAutoPtr Params; LvcError Err; Cmd(LString::Array &context, LoggingType logging, LStream *log) { Context = context; Logging = logging; Log = log; Err = ErrNone; } ~Cmd() { } LString GetBuf() { LString s = Buf.NewLStr(); if (Log && Logging == LogSilo) { LString m; m.Printf("=== %s ===\n\t%s %s\n", Context[0].Get(), Context[1].Get(), Context[2].Get()); Log->Write(m.Get(), m.Length()); auto Lines = s.Split("\n"); for (auto Ln : Lines) Log->Print("\t%s\n", Ln.Get()); } return s; } ssize_t Write(const void *Ptr, ssize_t Size, int Flags = 0) { ssize_t Wr = Buf.Write(Ptr, Size, Flags); if (Log && Logging == LogNormal) Log->Write(Ptr, Size, Flags); if (Flags) Err = (LvcError) Flags; return Wr; } }; class UncommitedItem : public LListItem { AppPriv *d; public: UncommitedItem(AppPriv *priv) { d = priv; } void OnPaint(LItem::ItemPaintCtx &Ctx); void Select(bool b); }; AppPriv *d; VersionCtrl Type; LUri Uri; LString CurrentCommit, RepoUrl, VcCmd; int64 CurrentCommitIdx; LArray Log; LString CurrentBranch; LHashTbl,VcBranch*> Branches; LAutoPtr Uncommit; LString Cache, NewRev; bool CommitListDirty = false; int Unpushed = 0, Unpulled = 0; LString CountCache; LTreeItem *Tmp = NULL; int CmdErrors = 0; LArray Fields; // Git specific LHashTbl,LString> GitNames; void AddGitName(LString Hash, LString Name); LString GetGitNames(LString Hash); static int CmdMaxThreads; static int CmdActiveThreads; struct GitCommit { LString::Array Files; LString Msg, Branch; ParseParams *Param; GitCommit() { Param = NULL; } }; LAutoPtr PostAdd; void GitAdd(); LArray Cmds; bool IsLogging, IsUpdate, IsFilesCmd, IsWorkingFld, IsCommit, IsUpdatingCounts; LvcStatus IsBranches, IsIdent; void Init(AppPriv *priv); const char *GetVcName(); bool StartCmd(const char *Args, ParseFn Parser = NULL, ParseParams *Params = NULL, LoggingType Logging = LogNone); bool RunCmd(const char *Args, LoggingType Logging, std::function Callback); void OnBranchesChange(); void OnCmdError(LString Output, const char *Msg); void ClearError(); VcFile *FindFile(const char *Path); void LinkParents(); void CurrentRev(std::function Callback); LColour BranchColour(const char *Name); bool ParseDiffs(LString s, LString Rev, bool IsWorking); bool ParseRevList(int Result, LString s, ParseParams *Params); bool ParseLog(int Result, LString s, ParseParams *Params); bool ParseInfo(int Result, LString s, ParseParams *Params); bool ParseFiles(int Result, LString s, ParseParams *Params); bool ParseWorking(int Result, LString s, ParseParams *Params); bool ParseUpdate(int Result, LString s, ParseParams *Params); bool ParseCommit(int Result, LString s, ParseParams *Params); bool ParseGitAdd(int Result, LString s, ParseParams *Params); bool ParsePush(int Result, LString s, ParseParams *Params); bool ParsePull(int Result, LString s, ParseParams *Params); bool ParseCounts(int Result, LString s, ParseParams *Params); bool ParseRevert(int Result, LString s, ParseParams *Params); bool ParseResolveList(int Result, LString s, ParseParams *Params); bool ParseResolve(int Result, LString s, ParseParams *Params); bool ParseBlame(int Result, LString s, ParseParams *Params); bool ParseSaveAs(int Result, LString s, ParseParams *Params); bool ParseBranches(int Result, LString s, ParseParams *Params); bool ParseStatus(int Result, LString s, ParseParams *Params); bool ParseAddFile(int Result, LString s, ParseParams *Params); bool ParseVersion(int Result, LString s, ParseParams *Params); bool ParseClean(int Result, LString s, ParseParams *Params); bool ParseDiff(int Result, LString s, ParseParams *Params); bool ParseMerge(int Result, LString s, ParseParams *Params); bool ParseCountToTip(int Result, LString s, ParseParams *Params); bool ParseUpdateSubs(int Result, LString s, ParseParams *Params); bool ParseRemoteFind(int Result, LString s, ParseParams *Params); bool ParseStartBranch(int Result, LString s, ParseParams *Params); void DoExpand(); public: VcFolder(AppPriv *priv, const char *uri); VcFolder(AppPriv *priv, LXmlTag *t); ~VcFolder(); VersionCtrl GetType(); AppPriv *GetPriv() { return d; } const char *LocalPath(); LUri GetUri() { return Uri; } VcLeaf *FindLeaf(const char *Path, bool OpenTree); void DefaultFields(); void UpdateColumns(); const char *GetText(int Col); LArray &GetFields() { return Fields; } bool Serialize(LXmlTag *t, bool Write); LXmlTag *Save(); void Empty(); void Select(bool b); void ListCommit(VcCommit *c); void ListWorkingFolder(); void FolderStatus(const char *Path = NULL, VcLeaf *Notify = NULL); void Commit(const char *Msg, const char *Branch, bool AndPush); void StartBranch(const char *BranchName, const char *OnCreated = NULL); void Push(bool NewBranchOk = false); void Pull(int AndUpdate = -1, LoggingType Logging = LogNormal); void Clean(); bool Revert(LString::Array &uris, const char *Revision = NULL); bool Resolve(const char *Path, LvcResolve Type); bool AddFile(const char *Path, bool AsBinary = true); bool Blame(const char *Path); bool SaveFileAs(const char *Path, const char *Revision); void ReadDir(LTreeItem *Parent, const char *Uri); void SetEol(const char *Path, int Type); void GetVersion(); void Diff(VcFile *file); void DiffRange(const char *FromRev, const char *ToRev); void MergeToLocal(LString Rev); bool RenameBranch(LString NewName, LArray &Revs); void Refresh(); bool GetBranches(ParseParams *Params = NULL); void GetCurrentRevision(ParseParams *Params = NULL); void CountToTip(); bool UpdateSubs(); // Clone/checkout any sub-repositries. void LogFile(const char *Path); LString GetFilePart(const char *uri); void FilterCurrentFiles(); void GetRemoteUrl(std::function Callback); void OnPulse(); void OnUpdate(const char *Rev); void OnMouseClick(LMouse &m); void OnRemove(); void OnExpand(bool b); void OnVcsType(LString errorMsg); void OnSshCmd(SshParams *p); }; class VcLeaf : public LTreeItem { AppPriv *d; VcFolder *Parent; bool Folder; LUri Uri; LString Leaf; LTreeItem *Tmp; void DoExpand(); public: LArray Log; VcLeaf(VcFolder *parent, LTreeItem *Item, LString uri, LString leaf, bool folder); ~VcLeaf(); LString Full(); VcLeaf *FindLeaf(const char *Path, bool OpenTree); void OnBrowse(); void AfterBrowse(); void OnExpand(bool b); const char *GetText(int Col); int GetImage(int Flags); int Compare(VcLeaf *b); bool Select(); void Select(bool b); void OnMouseClick(LMouse &m); void ShowLog(); }; #endif diff --git a/linux/Makefile.linux b/linux/Makefile.linux --- a/linux/Makefile.linux +++ b/linux/Makefile.linux @@ -1,230 +1,229 @@ #!/usr/bin/make # # This makefile generated by LgiIde # http://www.memecode.com/lgi.php # .SILENT : CC = gcc CPP = g++ Target = lgi-gtk3 ifndef Build Build = Debug endif MakeDir := $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST)))) BuildDir = $(Build) -Flags = -fPIC -fno-inline -fpermissive +Flags = -fPIC -fno-inline -fpermissive -Wno-format-truncation ifeq ($(Build),Debug) Flags += -MMD -MP -g -std=c++14 Tag = d Defs = -D_DEBUG -DLINUX -D_REENTRANT -D_FILE_OFFSET_BITS=64 -DPOSIX -DLGI_LIBRARY Libs = \ -lmagic \ -lappindicator3 \ -lcrypt \ -static-libgcc \ `pkg-config --libs gtk+-3.0` Inc = \ `pkg-config --cflags gtk+-3.0` \ `pkg-config --cflags gstreamer-1.0` \ -I../../../../../usr/include/libappindicator3-0.1 \ -I./private/linux \ -I./private/common \ -I./include/lgi/linux/Gtk \ -I./include/lgi/linux \ -I./include else Flags += -MMD -MP -s -Os -std=c++14 Defs = -DLINUX -D_REENTRANT -D_FILE_OFFSET_BITS=64 -DPOSIX -DLGI_LIBRARY Libs = \ -lmagic \ -lappindicator3 \ -lcrypt \ -static-libgcc \ `pkg-config --libs gtk+-3.0` Inc = \ `pkg-config --cflags gtk+-3.0` \ `pkg-config --cflags gstreamer-1.0` \ -I../../../../../usr/include/libappindicator3-0.1 \ -I./private/linux \ -I./private/common \ -I./include/lgi/linux/Gtk \ -I./include/lgi/linux \ -I./include endif # Dependencies Source = src/linux/Lgi/Window.cpp \ src/linux/Lgi/Widgets.cpp \ src/linux/Lgi/View.cpp \ src/linux/Lgi/Thread.cpp \ src/linux/Lgi/Printer.cpp \ src/linux/Lgi/Menu.cpp \ src/linux/Lgi/Layout.cpp \ src/linux/Lgi/General.cpp \ src/linux/Lgi/DragAndDrop.cpp \ src/linux/Lgi/ClipBoard.cpp \ src/linux/Lgi/App.cpp \ src/linux/Gtk/ScreenDC.cpp \ src/linux/Gtk/PrintDC.cpp \ src/linux/Gtk/MemDC.cpp \ src/linux/Gtk/LgiWidget.cpp \ src/linux/Gtk/Gdc2.cpp \ src/linux/General/ShowFileProp_Linux.cpp \ src/linux/General/Mem.cpp \ src/linux/General/File.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/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/NetTools.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/SubProcess.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/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) $(Flags) $(Defs) -c $< -o $@ $(BuildDir)/%.o: %.cpp mkdir -p $(@D) echo $(notdir $<) [$(Build)] $(CPP) $(Inc) $(Flags) $(Defs) -c $< -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/linux/Lgi \ ./src/linux/Gtk \ ./src/linux/General \ ./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/src/common/Lgi/FileSelect.cpp b/src/common/Lgi/FileSelect.cpp --- a/src/common/Lgi/FileSelect.cpp +++ b/src/common/Lgi/FileSelect.cpp @@ -1,2256 +1,2257 @@ /*hdr ** FILE: LFileSelect.cpp ** AUTHOR: Matthew Allen ** DATE: 20/5/2002 ** DESCRIPTION: Common file/directory selection dialog ** ** Copyright (C) 1998-2002, Matthew Allen ** fret@memecode.com */ #include #include #include "lgi/common/Lgi.h" #include "lgi/common/Popup.h" #include "lgi/common/List.h" #include "lgi/common/TextLabel.h" #include "lgi/common/Edit.h" #include "lgi/common/Button.h" #include "lgi/common/CheckBox.h" #include "lgi/common/Combo.h" #include "lgi/common/Tree.h" #include "lgi/common/TableLayout.h" #include "lgi/common/Box.h" #include "lgi/common/FileSelect.h" #include "lgi/common/Menu.h" +#include "lgi/common/Uri.h" #define FSI_FILE 0 #define FSI_DIRECTORY 1 #define FSI_BACK 2 #define FSI_UPDIR 3 #define FSI_NEWDIR 4 #define FSI_DESKTOP 5 #define FSI_HARDDISK 6 #define FSI_CDROM 7 #define FSI_FLOPPY 8 #define FSI_NETWORK 9 enum DlgType { TypeNone, TypeOpenFile, TypeOpenFolder, TypeSaveFile }; class LFileSelectDlg; char ModuleName[] = "File Select"; uint32_t IconBits[] = { 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x0000F81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xC980FA8A, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x738E738E, 0xF81F738E, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x8430F81F, 0x84308430, 0x84308430, 0xF81F8430, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x00000000, 0x00000000, 0x00000000, 0xF81F0000, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x9CE09CE0, 0x9CE09CE0, 0x00009CE0, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xCCE0F81F, 0x9800FCF9, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x738E738E, 0xCE6C9E73, 0x738EC638, 0xF81F738E, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x9FFF738E, 0x9FFF9FFF, 0x9FFF9FFF, 0x00009FFF, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xFFFF0000, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x9CE0F81F, 0xFFF9F7BE, 0xFFF3FFF9, 0x9CE0FFF3, 0xF81F0000, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x9CE09CE0, 0x9CE09CE0, 0x00009CE0, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x0000F81F, 0xF81FF81F, 0xF81FF81F, 0x04F904F9, 0x04F904F9, 0xAD720313, 0xAD72AD72, 0xAD72AD72, 0xFE60CCE0, 0x00009B00, 0x0000AD72, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x738EF81F, 0x667334F3, 0xCE6C9E73, 0xC638B5B6, 0x3186C638, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xFFFF738E, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0xF81FF81F, 0x738E738E, 0x738E738E, 0x738E738E, 0x738E738E, 0x738E738E, 0x738E738E, 0xF81F738E, 0xFFFF0000, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFF0000, 0xF81F0000, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xCE6C9CE0, 0xCE6CCE6C, 0xCE6CCE6C, 0xCE6CCE6C, 0x9CE09CE0, 0x9CE09CE0, 0x9CE09CE0, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x0000F81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x9CE0F81F, 0xFFF9F7BE, 0xFFF3FFF9, 0x9CE0FFF3, 0xF81F0000, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81F0000, 0xF81FF81F, 0xF81FF81F, 0xF81F0000, 0x667F04F9, 0x031304F9, 0xFFFFCE73, 0xFFF9FFFF, 0xCCE0FFFF, 0x9B00FFF3, 0x04F90000, 0x00000313, 0xF81FF81F, 0x738E738E, 0x738E738E, 0x738E738E, 0x738E738E, 0x738E738E, 0x738E738E, 0xF81F738E, 0xF81FF81F, 0xF81FF81F, 0x6673738E, 0x34F36673, 0xCE736673, 0xB5B6C638, 0xB5B6DEFB, 0xF81F3186, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xFFFF738E, 0xFFFFFFFF, 0xCFFFCFFF, 0x0000CFFF, 0x738EF81F, 0xB5B6B5B6, 0xB5B6B5B6, 0xB5B6B5B6, 0xB5B6B5B6, 0xB5B6B5B6, 0xB5B6B5B6, 0x0000738E, 0xFFFF0000, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFF0000, 0x0000FFFF, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xFFF99CE0, 0xFFF9FFF9, 0xFFF9FFF9, 0xFFF9FFF9, 0xFFF9FFF9, 0xFFF9FFF9, 0xCE6CFFF3, 0xF81F0000, 0xF81FF81F, 0xF81FF81F, 0x00000000, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xCE6C9CE0, 0xCE6CCE6C, 0xCE6CCE6C, 0xCE6CCE6C, 0x9CE09CE0, 0x9CE09CE0, 0x9CE09CE0, 0xF81FF81F, 0xF81FF81F, 0x9CE09CE0, 0x9CE09CE0, 0xF81F0000, 0x0000F81F, 0x0000F81F, 0x0000F81F, 0xF81FF81F, 0x04F904F9, 0xCE730313, 0xFFFFFFFF, 0xFFF9FFF9, 0xFE60CCE0, 0x00009B00, 0x667F3313, 0x00000313, 0x738EF81F, 0xB5B6B5B6, 0xB5B6B5B6, 0xB5B6B5B6, 0xB5B6B5B6, 0xB5B6B5B6, 0xB5B6B5B6, 0x0000738E, 0xF81FF81F, 0xF81FF81F, 0x34F98430, 0x667364F9, 0x84306679, 0xD6BAB5B6, 0xB5B6C638, 0xF81F3186, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xCFFF738E, 0xCFFFCFFF, 0xCFFFCFFF, 0x0000CFFF, 0xFFFF738E, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x9CF3FFFF, 0x0000738E, 0xFFFF0000, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, 0xF81F0000, 0xF81FF81F, 0xF81FF81F, 0xFFF99CE0, 0xFFF3FFF3, 0xFFF3FFF3, 0xFFF3FFF3, 0xFFF3FFF3, 0xFFF3FFF3, 0xCE6CFE73, 0xF81F0000, 0xF81FF81F, 0x0000F81F, 0x000007FF, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xFFF99CE0, 0xFFF9FFF9, 0xFFF9FFF9, 0xFFF9FFF9, 0xFFF9FFF9, 0xFFF9FFF9, 0xCE6CFFF3, 0xF81F0000, 0x9CE0F81F, 0xFFF9F7BE, 0xFFF3FFF3, 0x00009CE0, 0xF81FF81F, 0xF81F0000, 0xF81F0000, 0xF81FF81F, 0x031304F9, 0xFFFFCE73, 0x94B294B2, 0xCCE094B2, 0x9B00FFF3, 0xAD720000, 0x3313FFF9, 0x00000313, 0xFFFF738E, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x9CF3FFFF, 0x0000738E, 0xF81FF81F, 0x738EF81F, 0xA53494B2, 0x667964F3, 0x00008430, 0xA5348430, 0xCE79B5B6, 0x3186CE79, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xCE79738E, 0xCE79C638, 0xC638B5B6, 0x0000B5B6, 0xD6BA738E, 0xC638C638, 0xC638C638, 0xC638C638, 0xB5B6C638, 0x04200660, 0x94B2B5B6, 0x0000738E, 0xFFFF0000, 0xFFFFFFFF, 0xFFFFFFFF, 0xD699FFFF, 0xD699D699, 0xF81F0000, 0xF81FF81F, 0xF81FF81F, 0xFFF99CE0, 0xFFF3FFF3, 0xFFF3FFF3, 0xFFF3FFF3, 0xFE73FFF3, 0xFE73FFF3, 0xCE6CFFF3, 0xF81F0000, 0xF81FF81F, 0x07FF0000, 0x000007FF, 0x00000000, 0x00000000, 0x00000000, 0xF81F0000, 0xF81FF81F, 0xFFF99CE0, 0xFFF3FFF3, 0x0000FFF3, 0xFFF3FFF3, 0xFFF3FFF3, 0xFFF3FFF3, 0xCE6CFE73, 0xF81F0000, 0xCE6C9CE0, 0xCE6CCE6C, 0xCE6CCE6C, 0x9CE0CE6C, 0x9CE09CE0, 0xF81F9CE0, 0x0000F81F, 0x0000F81F, 0xCE730313, 0xFFFFFFFF, 0xFFFF94B2, 0xFE60CCE0, 0x00009B00, 0xFFF9AD72, 0xCE73CE73, 0x00003313, 0xD6BA738E, 0xC638C638, 0xC638C638, 0xC638C638, 0xB5B6C638, 0x04200660, 0x94B2B5B6, 0x0000738E, 0xF81FF81F, 0x738EF81F, 0xB5B6B5B6, 0x8430CE79, 0xF81F0000, 0x84300000, 0xCE79CE79, 0x3186CE79, 0xF81FF81F, 0x84308430, 0x84308430, 0x84308430, 0xC638738E, 0x84308430, 0x84308430, 0x0000C638, 0xDEFB738E, 0xC638B5B6, 0xC638C638, 0xC638C638, 0xB5B6B5B6, 0xB5B6B5B6, 0x94B2B5B6, 0x0000738E, 0xFFFF0000, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xD699FFFF, 0xF81F0000, 0xF81FF81F, 0xF81FF81F, 0xFFF99CE0, 0xFFF3FFF3, 0xFFF3FFF3, 0xFFF3FFF3, 0xFFF3FFF3, 0xFFF3FE73, 0xCE6CFE73, 0xF81F0000, 0x0000F81F, 0x07FF07FF, 0x07FF07FF, 0x07FF07FF, 0x07FF07FF, 0x07FF07FF, 0xF81F0000, 0xF81FF81F, 0xFFF99CE0, 0xFFF3FFF3, 0x00000000, 0xFFF30000, 0xFE73FFF3, 0xFE73FFF3, 0xCE6CFFF3, 0xF81F0000, 0xFFF99CE0, 0xFFF9FFF9, 0xFFF9FFF9, 0xFFF9FFF9, 0xFFF3FFF9, 0x0000CE6C, 0xF81F0000, 0xF81FF81F, 0xFFFFAD72, 0xFFF9FFFF, 0xFFFF94B2, 0x9B009CEC, 0x00000000, 0xCE73FFF9, 0xAD72FFF9, 0x000094B2, 0xDEFB738E, 0xC638B5B6, 0xC638C638, 0xC638C638, 0xB5B6B5B6, 0xB5B6B5B6, 0x94B2B5B6, 0x0000738E, 0xF81FF81F, 0x738EF81F, 0xF7BEE73C, 0xB5B6E73C, 0x00008430, 0xB5B68430, 0xF7BEEF7D, 0x3186CE79, 0x8430F81F, 0xC638C638, 0xC638C638, 0xC638C638, 0xCE79738E, 0x0000738E, 0xFFFF738E, 0x0000B5B6, 0xDEFB738E, 0x738E738E, 0x738E738E, 0x738E738E, 0x738E738E, 0x738E738E, 0x94B2B5B6, 0x0000738E, 0xFFFF0000, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xD699FFFF, 0xF81F0000, 0xF81FF81F, 0xF81FF81F, 0xFFF99CE0, 0xFFF3FFF3, 0xFFF3FFF3, 0xFE73FFF3, 0xFE73FFF3, 0xFE73FFF3, 0xCE6CFFF3, 0xF81F0000, 0x0000F81F, 0x07FF07FF, 0x07FF07FF, 0x07FF07FF, 0x07FF07FF, 0x07FF07FF, 0xF81F0000, 0xF81FF81F, 0xFFF99CE0, 0x0000FFF3, 0x00000000, 0x00000000, 0xFFF3FFF3, 0xFFF3FE73, 0xCE6CFE73, 0xF81F0000, 0xFFF99CE0, 0xFFF3FFF3, 0xFFF3FFF3, 0xFFF3FFF3, 0xFE73FFF3, 0x0000CE6C, 0x0000F81F, 0xF81FF81F, 0xFFFFAD72, 0xFFF9FFF9, 0xFFFF94B2, 0xFFFF0000, 0x0000FFF9, 0xFFF9CE73, 0xCE73AD72, 0x000094B2, 0xDEFB738E, 0x738E738E, 0x738E738E, 0x738E738E, 0x738E738E, 0x738E738E, 0x94B2B5B6, 0x0000738E, 0x8430F81F, 0x84308430, 0xA5348430, 0xA534B5B6, 0x8430A534, 0xDEFB34F3, 0xFFFFE73C, 0xF81F3186, 0xFFFF8430, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x0000FFFF, 0x00000000, 0x00000000, 0xF81F0000, 0xD6BA738E, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x94B2B5B6, 0x0000738E, 0xFFFF0000, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xD699FFFF, 0xF81F0000, 0xF81FF81F, 0xF81FF81F, 0xFFF99CE0, 0xFFF3FFF3, 0xFFF3FFF3, 0xFFF3FFF3, 0xFFF3FE73, 0xFFF3FE73, 0xCE6CFE73, 0xF81F0000, 0xF81FF81F, 0x07FF0000, 0x000007FF, 0x00000000, 0x00000000, 0x00000000, 0xF81F0000, 0xF81FF81F, 0xFFF99CE0, 0xFFF3FFF3, 0x0000FFF3, 0xFE73FFF3, 0xFE73FFF3, 0xFE73FFF3, 0xCE6CFFF3, 0xF81F0000, 0xFFF99CE0, 0xFFF3FFF3, 0xFFF3FFF3, 0xFFF3FFF3, 0xFFF3FE73, 0x0000CE6C, 0xF81FF81F, 0xF81F0000, 0xFFF9AD72, 0xFFF9FFF9, 0xFFFF94B2, 0xFFFFFFFF, 0x0000FFF9, 0xCE73FFF9, 0xAD72CE73, 0x000094B2, 0xD6BA738E, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x94B2B5B6, 0x0000738E, 0xFFFF8430, 0xFFFFFFFF, 0xA534738E, 0xB5B6A534, 0xCE73D6BA, 0x34F96673, 0xB5B6CFFF, 0xF81F3186, 0xC6388430, 0xC638C638, 0xC638C638, 0xC638C638, 0xC638C638, 0xC638F800, 0x738E8430, 0xF81F0000, 0x738E738E, 0x738E738E, 0x738E738E, 0x738E738E, 0x738E738E, 0x738E738E, 0x738E738E, 0xF81F0000, 0xFFFF0000, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xD699FFFF, 0xF81F0000, 0xF81FF81F, 0xF81FF81F, 0xFFF99CE0, 0xFFF3FFF3, 0xFE73FFF3, 0xFE73FFF3, 0xFE73FFF3, 0xFE73FFF3, 0xCE6CFE73, 0xF81F0000, 0xF81FF81F, 0x0000F81F, 0x000007FF, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xFFF99CE0, 0xFFF3FFF3, 0x0000FFF3, 0xFFF3FFF3, 0xFFF3FE73, 0xFFF3FE73, 0xCE6CFE73, 0xF81F0000, 0xFFF99CE0, 0xFFF3FFF3, 0xFFF3FFF3, 0xFE73FFF3, 0xFE73FFF3, 0x0000CE6C, 0xF81FF81F, 0xF81FF81F, 0xFFF9AD72, 0xFFF9FFF9, 0xFFFF94B2, 0xFFFFFFFF, 0x0000FFF9, 0xCE73CE73, 0xCE73AD72, 0x000094B2, 0x738E738E, 0x738E738E, 0x738E738E, 0x738E738E, 0x738E738E, 0x738E738E, 0x738E738E, 0xF81F0000, 0xFFFF8430, 0xC638C638, 0x738EC638, 0xB5B6A534, 0xCE73CE79, 0x04F99E73, 0x000004F9, 0xF81FF81F, 0xC6388430, 0xC638C638, 0x84308430, 0x84308430, 0xC638C638, 0xC638C638, 0x738E8430, 0xF81F0000, 0x0000F81F, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xF81FF81F, 0xFFFF0000, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xD699FFFF, 0xF81F0000, 0xF81FF81F, 0xF81FF81F, 0xFFF99CE0, 0xFFF3FE73, 0xFFF3FE73, 0xFFF3FE73, 0xFFF3FE73, 0xFE73FE73, 0xCE6CFE73, 0xF81F0000, 0xF81FF81F, 0xF81FF81F, 0x00000000, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xFFF99CE0, 0xFFF3FFF3, 0x0000FFF3, 0x00000000, 0x00000000, 0xFE730000, 0xCE6CFE73, 0xF81F0000, 0xFFF99CE0, 0xFFF3FFF3, 0xFFF3FE73, 0xFFF3FE73, 0xFFF3FE73, 0x0000CE6C, 0xF81FF81F, 0xF81FF81F, 0xFFF904F9, 0xFFF9FFF9, 0xFFF994B2, 0xFFF9FFF9, 0x0000FFF9, 0xAD72CE73, 0xAD72CE73, 0x00003313, 0x0000F81F, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xF81FF81F, 0xFFFF8430, 0x31863186, 0x31863186, 0x84308430, 0xCE73CE79, 0x31869E73, 0x00003186, 0xF81FF81F, 0xC6388430, 0x84308430, 0x00000000, 0x00000000, 0x84308430, 0xC6388430, 0x738E8430, 0xF81F0000, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xB5B6F81F, 0xF81F0000, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xFFFF0000, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xD699FFFF, 0xF81F0000, 0xF81FF81F, 0xF81FF81F, 0xCE6C9CE0, 0xCE6CCE6C, 0xCE6CCE6C, 0xCE6CCE6C, 0xCE6CCE6C, 0xCE6CCE6C, 0xCE6CCE6C, 0xF81F0000, 0xF81FF81F, 0xF81FF81F, 0x0000F81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xFFF99CE0, 0xFFF3FE73, 0xFFF3FE73, 0xFFF3FE73, 0xFFF3FE73, 0xFE73FE73, 0xCE6CFE73, 0xF81F0000, 0xFFF99CE0, 0xFFF3FE73, 0xFE73FFF3, 0xFE73FFF3, 0xFE73FFF3, 0x0000CE6C, 0xF81FF81F, 0xF81FF81F, 0x04F904F9, 0xFFF9FFF9, 0x00000000, 0x00000000, 0x00000000, 0xCE73AD72, 0x3313AD72, 0x00003313, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xFFFF8430, 0x042007E0, 0xFFFFFFFF, 0xC638C638, 0x31863186, 0xC6383186, 0x0000738E, 0xF81FF81F, 0xC6388430, 0xC638C638, 0xFFFFFFFF, 0xFFFFFFFF, 0xC638C638, 0xC638C638, 0x738E8430, 0xF81F0000, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xB5B6F81F, 0xF81F0000, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xFFFF0000, 0xD699D699, 0xD699D699, 0xD699D699, 0xD699D699, 0xF81F0000, 0xF81FF81F, 0xF81FF81F, 0x0000F81F, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xF81F0000, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xCE6C9CE0, 0xCE6CCE6C, 0xCE6CCE6C, 0xCE6CCE6C, 0xCE6CCE6C, 0xCE6CCE6C, 0xCE6CCE6C, 0xF81F0000, 0xCE6C9CE0, 0xCE6CCE6C, 0xCE6CCE6C, 0xCE6CCE6C, 0xCE6CCE6C, 0x0000CE6C, 0xF81FF81F, 0xF81FF81F, 0x667F04F9, 0xFFF904F9, 0xCE73FFF9, 0xCE73FFF9, 0xAD72CE73, 0xAD72CE73, 0x667F3313, 0x00003313, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x8430738E, 0x84308430, 0x84308430, 0x84308430, 0x84308430, 0x84308430, 0x00008430, 0xF81FF81F, 0x84308430, 0x84308430, 0x84308430, 0x84308430, 0x84308430, 0x84308430, 0x00008430, 0xF81FF81F, 0xB5B6F81F, 0xB5B6F81F, 0xB5B6B5B6, 0xB5B6B5B6, 0xB5B6B5B6, 0xB5B6B5B6, 0xF81FB5B6, 0xF81FB5B6, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xF81F0000, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x0000F81F, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xF81F0000, 0x0000F81F, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xF81FF81F, 0xF81FF81F, 0x03130313, 0x04F90313, 0x94B294B2, 0x94B294B2, 0x94B294B2, 0x331394B2, 0x33133313, 0x00003313, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x0000F81F, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xF81F0000, 0xF81FF81F, 0x0000F81F, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xF81F0000, 0xF81FF81F, 0x0000F81F, 0x0000F81F, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xF81F0000, 0xF81F0000, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F}; LInlineBmp FileSelectIcons = { 160, 16, 16, IconBits }; enum Icons2Idx { IcoApps, IcoHome, IcoDesktop, IcoDocuments, IcoDownloads, IcoMovies, IcoMusic, IcoPhotos, IcoFolder, IcoComputer, IcoCDROM, IcoDrive, }; uint32_t Icons2[] = { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xF7FFFFFF, 0xFFF56264, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x0000FFFF, 0x0E000000, 0xFFFFFF93, 0xFFFFFFFF, 0xFFFFFFFF, 0xC3FFFFFF, 0x04042368, 0xFFB86421, 0xFFFFFFFF, 0xFFFFFFFF, 0x000000FF, 0x00000000, 0x00000000, 0x00000000, 0xFFFFFF00, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x5487BCEF, 0xFFFF1821, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xBFFFFFFF, 0x06062568, 0xFFBF6825, 0xFFFFFFFF, 0xFFFFFFFF, 0x43B0FFFF, 0x31313131, 0x31313131, 0xB0413131, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x62A4F8FF, 0xA8664D45, 0xFFFFFFF8, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x180089FF, 0xFFFFFFF5, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x31DEFFFF, 0xDD2F0000, 0xFF0000FF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x9800FFFF, 0x02319498, 0xFFFF8029, 0xFFFFFFFF, 0xFFFFFFFF, 0x0445DFFF, 0x1616120C, 0x3D040C14, 0xFFFFFFD5, 0xFFFFFFFF, 0x00FF00FF, 0xCFCFCFCF, 0xCFCFCFCF, 0xFF00CFCF, 0xFFFFFF00, 0xFFFFFFFF, 0xFFFFFFFF, 0x23568CCF, 0x00000000, 0xFFFF0000, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x0060FFFF, 0x88000000, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x4141D9FF, 0xB2B2A483, 0x414180A4, 0xFFFFFFD7, 0xFFFFFFFF, 0xBA35F6FF, 0xD9D9D9D9, 0xD9D9D9D9, 0x35BAD9D9, 0xFFFFFFF3, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x5E1F0E92, 0x2C4D4D76, 0xFFFF900F, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x0000D4FF, 0xFFFFFF7C, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x0010B5FF, 0x0E000000, 0xFF0000B4, 0xFFFFFFFF, 0x1DC3FFFF, 0x00000000, 0x00000000, 0x00000000, 0xC11A0000, 0xFFFFFFFF, 0xFEFEFFFF, 0x9800FFFF, 0x00969898, 0xFF7E1AE3, 0xFFFFFFFF, 0xFFFFFFFF, 0x160C10C9, 0x16161616, 0x0C161616, 0xFFFFBC09, 0xFFFFFFFF, 0x000000FF, 0xCFCFCFCF, 0xCFCFCFCF, 0x0000CFCF, 0xFFFFFF00, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000004, 0x00000000, 0xFFFF0000, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xE500FFFF, 0x00CBE5E5, 0xFFFFFF98, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xC9900CC5, 0xB4B4B4C5, 0x78B4B4B4, 0xFFFFC30B, 0xFFFFFFFF, 0xD937D3FF, 0x1212127C, 0xD5701212, 0x37D9BAB2, 0xFFFFFFD3, 0x087EFFFF, 0x00000000, 0x00000000, 0x00000000, 0x7E080000, 0xFFFFFFFF, 0x3FF3FFFF, 0x94C15A16, 0x73C17070, 0xF442285B, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x006899A9, 0xFFFFE006, 0xFFFFFFFF, 0xFFFFFFFF, 0xFDFFFFFF, 0x0E000080, 0x000EABAB, 0xFF000000, 0xFFFFFFFF, 0xC321FFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x1FC1FFFF, 0xFFFFFFFF, 0x000000FF, 0x47000000, 0x00989898, 0x7E18D6FF, 0xFFFFFFFF, 0xDFFFFFFF, 0x16160E12, 0x00000008, 0x16160800, 0xFFD7090E, 0xFFFFFFFF, 0x00FF00FF, 0xCFCFCFCF, 0xCFCFCFCF, 0xFF00CFCF, 0xFFFFFF00, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, 0xFFFF0000, 0xFFFFFFFF, 0x0200F3FF, 0x0014BCFF, 0x14000000, 0xFFFFFFBE, 0xFFFFFFFF, 0xE500FFFF, 0xBAE5E5E5, 0x00000060, 0x00000000, 0x64000000, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xD9FFFFFF, 0xE7FFAC0E, 0xB4B4B6C3, 0xB4B4B4B4, 0xFFD70D81, 0xFFFFFFFF, 0xD943C3FF, 0xD9D9D9D9, 0xA2CBD9D9, 0x43D9B2C7, 0xFFFFFFBF, 0xCD0AFFFF, 0xCDCDCDCD, 0xCDCDCDCD, 0xCDCDCDCD, 0x0ACDCDCD, 0xFFFFFFFF, 0x2339FCFF, 0x6A92AA68, 0x6BBA6B6B, 0x3C249298, 0xFFFFFFFE, 0xFFFFFFFF, 0xDCFFFFFF, 0x0E9D0004, 0xFFFF5800, 0xFFFFFFFF, 0xFFFFFFFF, 0x49ECFFFF, 0xD22D0000, 0x2BD1F2F2, 0xEB000000, 0xFFFFFFFF, 0x0800FFFF, 0x08080808, 0x08080808, 0x06060606, 0x00060606, 0xFFFFFFFF, 0xE3E300FF, 0x04025CD9, 0x0098983D, 0x25DDFFFF, 0xFFFFFF8C, 0x45FFFFFF, 0x1616160A, 0xFFFFFF00, 0x161600FF, 0xFF340B16, 0xFFFFFFFF, 0x000000FF, 0xCFCFCFCF, 0xCFCFCFCF, 0x0000CFCF, 0xFFFFFF00, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x62270000, 0xFFFF007E, 0xFFFFFFFF, 0xFFFFFFFF, 0x66431FCF, 0x43666666, 0xFFFFD723, 0xFFFFFFFF, 0xE500FFFF, 0xE5E5E5E5, 0xE5E5E5E5, 0xE5E5E5E5, 0x00E5E5E5, 0xFFFFFFFF, 0x020274FF, 0x02020202, 0x02020202, 0x02020202, 0xFFFFFF74, 0x39FFFFFF, 0xFFFFFF8A, 0x76769CD5, 0xB2B4B494, 0xFF3877B2, 0xFFFFFFFF, 0xD954A8FF, 0xAAC3DBD9, 0xC7989E9E, 0x54D9AAF3, 0xFFFFFFAA, 0xCD00FFFF, 0xCDCDCDCD, 0xCDCDCDCD, 0xCDCDCDCD, 0x00CDCDCD, 0xFFFFFFFF, 0x641691FF, 0x6666B47A, 0x9CB66C66, 0x176365A6, 0xFFFFFF94, 0xFFFFFFFF, 0xA5FFFFFF, 0x8DEE1F7E, 0xFFC80000, 0xFFFFFFFF, 0xFFFFFFFF, 0x001FCCFF, 0x91EC5C00, 0xEB854343, 0x1D00005A, 0xFFFFFFCB, 0x9800FFFF, 0x97979798, 0x97979797, 0x96969696, 0x00969696, 0xFFFFFFFF, 0xE3E300FF, 0x8700DDE3, 0x00982502, 0x02000000, 0xFFFFFF0C, 0x02CDFFFF, 0x16161616, 0xFFFFFF00, 0x161600FF, 0xB8031616, 0xFFFFFFFF, 0x00FF00FF, 0xCFCFCFCF, 0xCFCFCFCF, 0xFF00CFCF, 0xFFFFFF00, 0xFFFFFFFF, 0xFFFFFFFF, 0x763B0400, 0xFFFFEBB0, 0xFFFF00FF, 0xFFFFFFFF, 0x00000035, 0x2D583D12, 0x582D1212, 0x00000E39, 0xFFFF3D00, 0x1000FFFF, 0x00000000, 0x00000000, 0x00000000, 0x00100000, 0xFFFFFFFF, 0xD1D102FF, 0xD1D1D1D1, 0xD1D1D1D1, 0xCDD1D1D1, 0xFFFFFF04, 0x41BCFFFF, 0xF6FCDBC9, 0x8A8A6074, 0xB2AE5A5E, 0xBA40B2B2, 0xFFFFFFFF, 0xD96698FF, 0xDBB2AAD5, 0xF3E5EDED, 0x66D9A6F3, 0xFFFFFF94, 0xCD00FFFF, 0xCDCDCDCD, 0xCDCDCDCD, 0xCDCDCDCD, 0x00CDCDCD, 0xFFFFFFFF, 0x604F10FB, 0x606068B4, 0x96FCD160, 0x92A66E60, 0xFFFFF611, 0xFFFFFFFF, 0x0AF7FFFF, 0xFDFFDE1F, 0xFF6A002F, 0xFFFFFFFF, 0xFFFFFFFF, 0x0400069B, 0x43F2F291, 0xF133DEDE, 0x00028EF1, 0xFFFF9C04, 0xA000FFFF, 0x97979798, 0x97979797, 0x96969696, 0x009E9696, 0xFFFFFFFF, 0xE3E300FF, 0xFE00E3E3, 0x98250AC5, 0x2D949898, 0xFFFFFF00, 0x0C74FFFF, 0x16161616, 0xFFFFFF00, 0x161600FF, 0x620C1616, 0xFFFFFFFF, 0x000000FF, 0xCFCFCFCF, 0xCFCFCFCF, 0x0000CFCF, 0xFFFFFF00, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFE700, 0xFFFFFFFF, 0xFFFF00FF, 0xFFFFFFFF, 0x66666600, 0x8C143966, 0x148CCFCD, 0x122F5239, 0xFFFF005E, 0x9C00FFFF, 0xB2B2B2B2, 0xB2B2B2B2, 0xB2B2B2B2, 0x009CB2B2, 0xFFFFFFFF, 0xD1D102FF, 0xD1D1D1D1, 0xD1D1D1D1, 0xD1D1D1D1, 0xFFFFFF02, 0x8366FFFF, 0x70C9BABA, 0xE7E7E794, 0xB25990E7, 0x6581B2B2, 0xFFFFFFFF, 0xD9767EFF, 0xCDF3BFB6, 0xF3CD8585, 0x76D99AF1, 0xFFFFFF7C, 0xCD00FFFF, 0xCDCDCDCD, 0xCDCDCDCD, 0xCDCDCDCD, 0x00CDCDCD, 0xFFFFFFFF, 0xC9B125AC, 0x8C6060E5, 0x60DBC5B8, 0x5868A4AC, 0xFFFFA61B, 0xFFFFFFFF, 0x0082FFFF, 0xFFFFFF31, 0xE30000E1, 0xFFFFFFFF, 0xFFFFFFFF, 0xC20E0068, 0x43F2F2F2, 0xF133FFFF, 0x0AC1F1F1, 0xFFFF6600, 0xA000FFFF, 0x97979798, 0x97979797, 0x96969696, 0x009E9696, 0xFFFFFFFF, 0xE3E300FF, 0xFE00E3E3, 0x2B0EC9FE, 0x92969898, 0xFFFFFF00, 0x1231FFFF, 0x16161616, 0xFFFFFF00, 0x161600FF, 0x1F141616, 0xFFFFFFFF, 0x00FF00FF, 0xCFCFCFCF, 0xCFCFCFCF, 0xFF00CFCF, 0xFFFFFF00, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFF00, 0xFFFFFFFF, 0xFFFF00FF, 0xFFFFFFFF, 0x66666600, 0x62DB1F49, 0xD7620A0A, 0x12314721, 0xFFFF005E, 0xB200FFFF, 0xB2B2B2B2, 0xB2B2B2B2, 0xB2B2B2B2, 0x00B2B2B2, 0xFFFFFFFF, 0xD1D102FF, 0xD1D1D1D1, 0xD1D1D1D1, 0xD1D1D1D1, 0xFFFFFF02, 0xA823FFFF, 0x5E94B4B4, 0x5E5EBFE7, 0x925FE7BF, 0x22A6B2B2, 0xFFFFFFFF, 0xD98C68FF, 0x6CF3EDA0, 0xF370D1D5, 0x8CD9A0ED, 0xFFFFFF68, 0xCD00FFFF, 0xCDCDCDCD, 0xCDCDCDCD, 0xCDCDCDCD, 0x00CDCDCD, 0xFFFFFFFF, 0xFF622B66, 0x7EA2B6FF, 0xBCC35254, 0x5252526C, 0xFFFF642B, 0x0818FFFF, 0x0000E231, 0x0A0A23D8, 0x35006A7E, 0x166A78B0, 0xFFFFFFFF, 0xF2007AFE, 0x43F2F2F2, 0xF1334747, 0x00F1F1F1, 0xFFFFFE78, 0xA000FFFF, 0x97979798, 0x97979797, 0x96969696, 0x009E9696, 0xFFFFFFFF, 0xE3E300FF, 0x0000E3E3, 0x00060000, 0x96969898, 0xFFFFFF00, 0x1414FFFF, 0x00081216, 0xFFFFFF00, 0x080000FF, 0x04161612, 0xFFFFFFFF, 0x000000FF, 0x00000000, 0x00000000, 0x00000000, 0xFFFFFF00, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFF00, 0xFFFFFFFF, 0xFFFF00FF, 0xFFFFFFFF, 0x66666600, 0x394BAE1F, 0x4B396060, 0x5E521DB0, 0xFFFF0066, 0xB200FFFF, 0xB2B2B2B2, 0xB2B2B2B2, 0xB2B2B2B2, 0x00B2B2B2, 0xFFFFFFFF, 0xD1D102FF, 0xD1D1D1D1, 0xD1D1D1D1, 0xD1D1D1D1, 0xFFFFFF02, 0xB206FFFF, 0x8A76B4B4, 0xF6F65EE7, 0x7588E75E, 0x05B0B2B2, 0xFFFFFFFF, 0xD99E52FF, 0xAAF3EDA0, 0xF3A86E6E, 0x9CD9A0ED, 0xFFFFFF50, 0xCD00FFFF, 0xCDCDCDCD, 0xCDCDCDCD, 0xCDCDCDCD, 0x00CDCDCD, 0xFFFFFFFF, 0xB8492F4D, 0x49494994, 0xA8FF7E49, 0x49494949, 0xFFFF4531, 0x0000FFFF, 0x640043AB, 0x00000064, 0x0002CD00, 0x0008A8C1, 0xFFFFFFFF, 0xF200FFFF, 0xE2F2F2F2, 0xF1DFD2D2, 0x00F1F1F1, 0xFFFFFFFF, 0xA000FFFF, 0x97979798, 0x97979797, 0x96969696, 0x009E9696, 0xFFFFFFFF, 0xE3E300FF, 0xE3E3E3E3, 0x005ADDE3, 0x96969898, 0xFFFFFF00, 0x141AFFFF, 0xFF980816, 0xFFFFFFFF, 0x98FFFFFF, 0x04161608, 0xFFFFFFFF, 0x00FF00FF, 0xCFCFCFCF, 0xCFCFCFCF, 0xFF00CFCF, 0xFFFFFF00, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFF00, 0xFFFFFFFF, 0xFFFF00FF, 0xFFFFFFFF, 0x64666600, 0x660ADF00, 0x0A666666, 0x666402DF, 0xFFFF0066, 0xB200FFFF, 0xB2B2B2B2, 0xB2B2B2B2, 0xB2B2B2B2, 0x00B2B2B2, 0xFFFFFFFF, 0xD1D102FF, 0xD1D1D1D1, 0xD1D1D1D1, 0xD1D1D1D1, 0xFFFFFF02, 0xAC0CFFFF, 0x8A76B4B4, 0xF6F65EE7, 0x7588E55E, 0x05B2B2B2, 0xFFFFFFFF, 0xD9AC39FF, 0xF3F6C3B6, 0xF6F3EDED, 0xAED9B6C3, 0xFFFFFF39, 0xCD00FFFF, 0xCDCDCDCD, 0xCDCDCDCD, 0xCDCDCDCD, 0x00CDCDCD, 0xFFFFFFFF, 0xA8413F49, 0x41414141, 0x83C98841, 0x41414141, 0xFFFF452B, 0x4308FFFF, 0xD20200C6, 0x0A0A0A0A, 0x8AB25A00, 0x1808C383, 0xFFFFFFFF, 0xF200FFFF, 0xF2F2F2F2, 0xF1F1F2F2, 0x00F1F1F1, 0xFFFFFFFF, 0xA000FFFF, 0x97979798, 0x97979797, 0x96969696, 0x009E9696, 0xFFFFFFFF, 0xE3E300FF, 0xE3E3E3E3, 0x00D9E3E3, 0x96969898, 0xFFFFFF00, 0x1235FFFF, 0xBC001216, 0xFFFFFFFF, 0x02BCFFFF, 0x21141612, 0xFFFFFFFF, 0x000000FF, 0xCFCFCFCF, 0xCFCFCFCF, 0x0000CFCF, 0xFFFFFF00, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFF00, 0xFFFFFFFF, 0xFFFF00FF, 0xFFFFFFFF, 0x64666600, 0x660CDD04, 0x0C666666, 0x666204DD, 0xFFFF0066, 0xB200FFFF, 0xB2B2B2B2, 0xB2B2B2B2, 0xB2B2B2B2, 0x00B2B2B2, 0xFFFFFFFF, 0xD1D102FF, 0xD1D1D1D1, 0xD1D1D1D1, 0xD1D1D1D1, 0xFFFFFF02, 0xA227FFFF, 0x6094B4B4, 0x605EBFE7, 0x925FE5BF, 0x21A8B2B2, 0xFFFFFFFF, 0xD9C123FF, 0xE5BAAAD5, 0xBAE3F8FA, 0xC1D9D5AA, 0xFFFFFF23, 0xCD00FFFF, 0xCDCDCDCD, 0xCDCDCDCD, 0xCDCDCDCD, 0x00CDCDCD, 0xFFFFFFFF, 0xA8435A5E, 0x3B3B3B3B, 0xB43FA83B, 0x3B3B3B3D, 0xFFFF702B, 0xF9FFFFFF, 0xFF9F0012, 0xFFFFFFFF, 0x47FFFFFF, 0xFFFF7E00, 0xFFFFFFFF, 0xF200FFFF, 0x00F2F2F2, 0xF1000000, 0x00F1F1F1, 0xFFFFFFFF, 0xA000FFFF, 0x97979798, 0x97979797, 0x96969696, 0x009E9696, 0xFFFFFFFF, 0xE3E300FF, 0xE3E3E3E3, 0x00DDE3E3, 0x9696989A, 0xFFFFFF00, 0x0C6CFFFF, 0x0C0E1616, 0xFFFFFFDB, 0x0E0EDBFF, 0x640C1616, 0xFFFFFFFF, 0x00FF00FF, 0xCFCFCFCF, 0xCFCFCFCF, 0xFF00CFCF, 0xFFFFFF00, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFF00, 0xFFFFFFFF, 0xFFFF00F5, 0xFFFFFFFF, 0x66666600, 0x392DC716, 0x2D396666, 0x666614C9, 0xFFFF0066, 0xB200FFFF, 0xB2B2B2B2, 0xB2B2B2B2, 0xB2B2B2B2, 0x00B2B2B2, 0xFFFFFFFF, 0xD1D102FF, 0xD1D1D1D1, 0xD1D1D1D1, 0xD1D1D1D1, 0xFFFFFF02, 0x885EFFFF, 0x5AB4B4B4, 0xE7E7E790, 0xC76F92E5, 0x5C85B8B8, 0xFFFFFFFF, 0xD9D30AFF, 0xAAC3DBD9, 0xC3AA9E9E, 0xD5D9D9DB, 0xFFFFFF08, 0xCD0AFFFF, 0xCDCDCDCD, 0xCDCDCDCD, 0xCDCDCDCD, 0x0ACDCDCD, 0xFFFFFFFF, 0xE1A814AC, 0x33333388, 0x56337C54, 0x70416EA2, 0xFFFFA62B, 0x51FFFFFF, 0xFFFB1200, 0xFFFFFFFF, 0xCBFFFFFF, 0xFFDBB03F, 0xFFFFFFFF, 0xF200FFFF, 0x00F2F2F2, 0xF1000000, 0x00F1F1F1, 0xFFFFFFFF, 0xA000FFFF, 0x4B4B9798, 0x4B4B4B4B, 0x49494949, 0x009E9696, 0xFFFFFFFF, 0xE3E300FF, 0xE3E3E3E3, 0x00DDE3E3, 0x9696989A, 0xFFFFFF00, 0x02C7FFFF, 0x0C161616, 0xFFFFF323, 0x160C23F3, 0xB9041616, 0xFFFFFFFF, 0x000000FF, 0xCFCFCFCF, 0xCFCFCFCF, 0x0000CFCF, 0xFFFFFF00, 0xFFFFFFFF, 0xFAFFFFFF, 0xFFFFFF00, 0x8EDDFFFF, 0xFFFF0035, 0xFFFFFFFF, 0x66666600, 0x2FD93741, 0xD92F0E0E, 0x66663F39, 0xFFFF0066, 0xB002FFFF, 0xB2B2B2B2, 0xB2B2B2B2, 0xB2B2B2B2, 0x00B0B2B2, 0xFFFFFFFF, 0xD1D102FF, 0xD1D1D1D1, 0xD1D1D1D1, 0xD1D1D1D1, 0xFFFFFF02, 0x43BAFFFF, 0xAEB4B4B4, 0x888A5E5A, 0xFAF4735F, 0xB841C7D9, 0xFFFFFFFF, 0xD18C2FFF, 0xD9D9D9D9, 0xD9D9D9D9, 0x8ED1D9D9, 0xFFFFFF2F, 0x087EFFFF, 0x00000000, 0x00000000, 0x00000000, 0x7C080000, 0xFFFFFFFF, 0xFF720CFC, 0x2B2B49ED, 0x2B2B458E, 0x62EDFF9A, 0xFFFFF80C, 0x76F0FFFF, 0xFFFF9702, 0xFFFFFFFF, 0xFFFFFFFF, 0xE50C0094, 0xFFFFFFFF, 0xF200FFFF, 0x00F2F2F2, 0xF1006A00, 0x00F1F1F1, 0xFFFFFFFF, 0xA000FFFF, 0xFF4B9798, 0xFFFF4BFF, 0x49FFFF4B, 0x009E9696, 0xFFFFFFFF, 0xE3E300FF, 0xE3E3E3E3, 0x00E3E3E3, 0x00000000, 0xFFFFFF00, 0x45FFFFFF, 0x1616160A, 0xFFFF410A, 0x16160A41, 0xFF350C16, 0xFFFFFFFF, 0x00FF00FF, 0xCFCFCFCF, 0xCFCFCFCF, 0xFF00CFCF, 0xFFFFFF00, 0xFFFFFFFF, 0x398CDBFF, 0xFFFFFF00, 0x000085FF, 0xFFFF0000, 0xFFFFFFFF, 0x66666600, 0xBC353966, 0x35BCDBDB, 0x66666639, 0xFFFF0066, 0xB302FFFF, 0xB2B2B2B2, 0xB2B2B2B2, 0xB2B2B2B2, 0x00B2B2B2, 0xFFFFFFFF, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0xFFFF0202, 0x39FFFFFF, 0xB4B4B478, 0x757594B4, 0xFFFFD39A, 0xFF378CFF, 0xFFFFFFFF, 0x165000FF, 0x16161616, 0x16161616, 0x50161616, 0xFFFFFF00, 0xFFFFFFFF, 0xFFFFFFFF, 0x787878FF, 0xFFFFFF78, 0xFFFFFFFF, 0xFFFFFFFF, 0xA40A90FF, 0xAA947892, 0x9E98C9FF, 0x21BFFAAC, 0xFFFFFF96, 0x376EFFFF, 0xFFFFFEB7, 0xFFFFFFFF, 0xFFFFFFFF, 0x7C000083, 0xFFFFFFFF, 0xF300FFFF, 0x00F2F2F2, 0xF1005800, 0x00F1F1F1, 0xFFFFFFFF, 0x7421FFFF, 0xFF4B9798, 0xFFFF4BFF, 0x49FFFF4B, 0x1F749696, 0xFFFFFFFF, 0xE3E300FF, 0xE3E3E3E3, 0x00E3E3E3, 0xFEFEFFFE, 0xFFFFFFFF, 0xDFFFFFFF, 0x16160E12, 0x6A680816, 0x16161608, 0xFFD9080E, 0xFFFFFFFF, 0x000000FF, 0xCFCFCFCF, 0xCFCFCFCF, 0x0000CFCF, 0xFFFFFF00, 0xFFFFFFFF, 0x0000008A, 0xFFFFFF00, 0x00000CFF, 0xFFFF0C00, 0xFFFFFFFF, 0x66666600, 0x18436666, 0x43180000, 0x66666666, 0xFFFF0066, 0x9902FFFF, 0x98989899, 0x98989898, 0x98989898, 0x00989898, 0xFFFFFFFF, 0x02020280, 0x02020202, 0x02020202, 0x02020202, 0xFFFF7F02, 0xD9FFFFFF, 0xB4B4830E, 0xB2B2B2B4, 0xFFE5C1B4, 0xFFD90CAA, 0xFFFFFFFF, 0x1D9800FF, 0x98989898, 0x984B984B, 0x9898984B, 0xFFFFFF00, 0xFFFFFFFF, 0xFFFFFFFF, 0x9E9E9ED3, 0xFFFFD39E, 0xFFFFFFFF, 0xFFFFFFFF, 0x0A3BFFFF, 0x2F1D1DA4, 0x1D1D50D1, 0x3F081D1D, 0xFFFFFFFF, 0x2129FFFF, 0xFFFFFFE2, 0xFFFFFFFF, 0xFFFFFFFF, 0x450039F1, 0xFFFFFFFF, 0xF300FFFF, 0x00F2F2F2, 0xF1000000, 0x00F1F1F1, 0xFFFFFFFF, 0x10C3FFFF, 0x00000000, 0x00000000, 0x00000000, 0xBE100000, 0xFFFFFFFF, 0xE3E300FF, 0xE3E3E3E3, 0x00E3E3E3, 0xFFFFFFFE, 0xFFFFFFFF, 0xFFFFFFFF, 0x160C0EC9, 0x0C0C1616, 0x0C161616, 0xFFFFBE0A, 0xFFFFFFFF, 0x00FF00FF, 0xCFCFCFCF, 0xCFCFCFCF, 0xFF00CFCF, 0xFFFFFF00, 0xFFFFFFFF, 0x0000000C, 0xFFFFFF04, 0x00001AFF, 0xFFFF4D00, 0xFFFFFFFF, 0x00000045, 0x00000000, 0x00000000, 0x00000000, 0xFFFF4300, 0x0235FFFF, 0x00020202, 0x00000000, 0x00000000, 0x31000000, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xB4780CC5, 0xB2B2B2B2, 0x8CC7C3B2, 0xFFFFC30A, 0xFFFFFFFF, 0x1D9800FF, 0x98989898, 0x984B984B, 0x9800984B, 0xFFFFFF00, 0xFFFFFFFF, 0xFFFFFFFF, 0x64646464, 0xFFFF6464, 0xFFFFFFFF, 0xFFFFFFFF, 0x39F6FFFF, 0x1616391D, 0x16161694, 0xF63D0610, 0xFFFFFFFF, 0xE82BFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x665CFEFF, 0xFFFFFFFF, 0x0000FFFF, 0x00000000, 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x000000FF, 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x043DDDFF, 0x1616120C, 0x35040C14, 0xFFFFFFD5, 0xFFFFFFFF, 0x000000FF, 0x00000000, 0x00000000, 0x00000000, 0xFFFFFF00, 0xFFFFFFFF, 0x0000001F, 0xFFFFFF43, 0x181DB8FF, 0xFFFFED5C, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x4239D9FF, 0xACB0A481, 0x374180A0, 0xFFFFFFD7, 0xFFFFFFFF, 0x1D7810FF, 0x98989898, 0x984B984B, 0x7898984B, 0xFFFFFF10, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x080C1088, 0x08080C62, 0xFFFE880A, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xBEFFFFFF, 0x020A2766, 0xFFB6621F, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x6E2521B8, 0xFFFFFFF1, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xB8FFFFFF, 0x08022164, 0xFFB86427, 0xFFFFFFFF, 0xFFFFFFFF, 0x00028CFF, 0x00000000, 0x00000000, 0x02000000, 0xFFFFFF8C, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x62A8FAFF, 0xAC624349, 0xFFFFFFFA, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, }; LInlineBmp TreeIconsImg = { 308, 22, 8, Icons2 }; ////////////////////////////////////////////////////////////////////////// char *LFileType::DefaultExtension() { char *Status = 0; auto T = LString(Extension()).SplitDelimit(";"); if (T.Length()) { char s[256]; strcpy(s, T[0]); char *Dir = strchr(s, '.'); if (Dir) { Status = NewStr(Dir+1); if (Status) strlwr(Status); } } return Status; } ////////////////////////////////////////////////////////////////////////// class LFolderItem : public LListItem { LFileSelectDlg *Dlg; LString Path; public: char *File; bool IsDir; LFolderItem(LFileSelectDlg *dlg, char *FullPath, LDirectory *Dir); ~LFolderItem(); void OnActivate(); const char *GetText(int i); int GetImage(int Flags); void OnSelect(); void OnDelete(bool Ask = true); void OnRename(); void OnMouseClick(LMouse &m); }; ////////////////////////////////////////////////////////////////////////// // This is just a private data container to make it easier to change the // implementation of this class without effecting headers and applications. class LFileSelectPrivate { friend class LFileSelect; friend class LFileSelectDlg; friend class LFolderList; LView *Parent = NULL; LFileSelect *Select = NULL; DlgType Type = TypeNone; LString Title; LString DefExt; bool MultiSelect = false; List Files; int CurrentType = -1; List Types; List History; bool ShowReadOnly = false; bool ReadOnly = false; bool EatClose = false; public: static LImageList *BtnIcons, *TreeIcons; static LString InitPath; static bool InitShowHiddenFiles; static LRect InitSize; LFileSelectPrivate(LFileSelect *select) { Select = select; if (!BtnIcons) BtnIcons = new LImageList(16, 16, FileSelectIcons.Create(0xF81F)); if (!TreeIcons) { LAutoPtr a(TreeIconsImg.Create(0xF81F)); LAutoPtr m(new LMemDC(a->X(), a->Y(), System32BitColourSpace)); if (a && m) { LColour fore(128, 128, 128); for (int y=0; yY(); y++) { uint8_t *i = (uint8_t*)(*a)[y]; auto *e = i + a->X(); System32BitPixel *o = (System32BitPixel*)(*m)[y]; while (i < e) { o->r = (int)fore.r() * *i / 255; o->g = (int)fore.g() * *i / 255; o->b = (int)fore.b() * *i / 255; o->a = *i; i++; o++; } } TreeIcons = new LImageList(22, 22, m.Release()); } } } virtual ~LFileSelectPrivate() { Types.DeleteObjects(); Files.DeleteArrays(); History.DeleteArrays(); } }; LImageList *LFileSelectPrivate::BtnIcons = NULL; LImageList *LFileSelectPrivate::TreeIcons = NULL; LString LFileSelectPrivate::InitPath; bool LFileSelectPrivate::InitShowHiddenFiles = false; LRect LFileSelectPrivate::InitSize(0, 0, -1, -1); ////////////////////////////////////////////////////////////////////////// // This class implements the UI for the selector. class LFileSelectDlg; class LFolderView { protected: LFileSelectDlg *Dlg; public: LFolderView(LFileSelectDlg *dlg) { Dlg = dlg; } virtual void OnFolder() {} }; class LFolderDrop : public LDropDown, public LFolderView { public: LFolderDrop(LFileSelectDlg *dlg, int Id, int x, int y, int cx, int cy); void OnFolder(); bool OnLayout(LViewLayoutInfo &Inf) { Inf.Width.Min = Inf.Width.Max = 18; return true; } }; class LIconButton : public LLayout { LImageList *Icons; int Icon; bool Down; public: LIconButton(int Id, int x, int y, int cx, int cy, LImageList *icons, int icon) { Icons = icons; Icon = icon; SetId(Id); LRect r(x, y, x+cx, y+cy); SetPos(r); Down = false; SetTabStop(true); } void OnPaint(LSurface *pDC) { LRect c = GetClient(); LColour Background(L_MED); c.Offset(-c.x1, -c.y1); LWideBorder(pDC, c, Down ? DefaultSunkenEdge : DefaultRaisedEdge); pDC->Colour(Background); pDC->Rectangle(&c); int x = (c.X()-Icons->TileX()) / 2; int y = (c.Y()-Icons->TileY()) / 2; if (Focus()) { #if WINNATIVE RECT r = c; DrawFocusRect(pDC->Handle(), &r); #endif } Icons->Draw(pDC, c.x1+x+Down, c.y1+y+Down, Icon, Background, !Enabled()); } void OnFocus(bool f) { Invalidate(); } void OnMouseClick(LMouse &m) { if (Enabled()) { bool Trigger = Down && !m.Down(); Capture(Down = m.Down()); if (Down) Focus(true); Invalidate(); if (Trigger) { LViewI *n=GetNotify()?GetNotify():GetParent(); if (n) n->OnNotify(this, LNotification(m)); } } } void OnMouseEnter(LMouse &m) { if (IsCapturing()) { Down = true; Invalidate(); } } void OnMouseExit(LMouse &m) { if (IsCapturing()) { Down = false; Invalidate(); } } bool OnKey(LKey &k) { if (k.c16 == ' ' || k.c16 == LK_RETURN) { if (Enabled() && Down ^ k.Down()) { Down = k.Down(); Invalidate(); if (!Down) { LViewI *n=GetNotify()?GetNotify():GetParent(); if (n) n->OnNotify(this, LNotifyActivate); } } return true; } return false; } bool OnLayout(LViewLayoutInfo &Inf) { Inf.Width.Min = Inf.Width.Max = Icons->TileX() + 4; Inf.Width.Max += 4; Inf.Height.Min = Inf.Height.Max = Icons->TileY() + 4; Inf.Height.Max += 4; return true; } }; class LFolderList : public LList, public LFolderView { LString FilterKey; public: LFolderList(LFileSelectDlg *dlg, int Id, int x, int y, int cx, int cy); void OnFolder(); bool OnKey(LKey &k); void SetFilterKey(LString s) { FilterKey = s; OnFolder(); } }; enum Ctrls { IDC_STATIC = -1, IDD_FILE_SELECT = 1000, IDC_PATH, IDC_DROP, IDC_BACK, IDC_UP, IDC_NEW, IDC_VIEW, IDC_FILE, IDC_TYPE, IDC_SHOWHIDDEN, IDC_SUB_TBL, IDC_BOOKMARKS, IDC_FILTER, IDC_FILTER_CLEAR, }; #if 1 #define USE_FOLDER_CTRL 1 enum FolderCtrlMessages { M_DELETE_EDIT = M_USER + 100, M_NOTIFY_VALUE_CHANGED, M_LOST_FOCUS, }; class FolderCtrlEdit : public LEdit { public: FolderCtrlEdit(int id, LRect c) : LEdit(id, c.x1, c.y1, c.X()-1, c.Y()-1) { } void OnFocus(bool f) { printf("OnFocus(%i)\n", f); if (!f && GetParent()) GetParent()->PostEvent(M_LOST_FOCUS); } }; class FolderCtrl : public LView { struct Part { LAutoPtr ds; LRect Arrow; LRect Text; }; LEdit *e; LArray p; Part *Over; ssize_t Cursor; Part *HitPart(int x, int y, int *Sub = NULL) { for (unsigned i=0; iGetHeight() + 4; Inf.Height.Max = Inf.Height.Min; } return true; } LString NameAt(ssize_t Level) { LString n; #ifndef WINDOWS n += "/"; #endif for (unsigned i=0; i<=Level && iTransparent(false); LDisplayString Arrow(f, ">"); for (unsigned i=0; iArrow.ZOff(Arrow.X()+1, c.Y()-1); n->Arrow.Offset(c.x1, c.y1); f->Colour(LColour(192,192,192), Bk); Arrow.DrawCenter(pDC, &n->Arrow); c.x1 = n->Arrow.x2 + 1; if (n->ds) { // Layout and draw text n->Text.ZOff(n->ds->X() + 4, c.Y()-1); n->Text.Offset(c.x1, c.y1); f->Colour(Fore, Bk); n->ds->DrawCenter(pDC, &n->Text); c.x1 = n->Text.x2 + 1; } } if (p.Length() == 0) { // Layout and draw arrow for the "root folder" f->Colour(LColour(192,192,192), L_WORKSPACE); LRect a; a.ZOff(Arrow.X()+1, c.Y()-1); a.Offset(c.x1, c.y1); Arrow.DrawCenter(pDC, &a); c.x1 = a.x2 + 1; } pDC->Colour(L_WORKSPACE); pDC->Rectangle(&c); } void ExitEditMode() { if (e) { Name(e->Name()); DeleteObj(e); PostEvent(M_DELETE_EDIT); PostEvent(M_NOTIFY_VALUE_CHANGED); Invalidate(); } } void OnMouseClick(LMouse &m) { if (m.IsContextMenu()) { } else if (m.Left()) { if (m.Down()) { Over = HitPart(m.x, m.y); if (p.PtrCheck(Over)) { // Over a path node... Cursor = Over - p.AddressOf(0); Part &o = p[Cursor]; Invalidate(); SendNotify(LNotifyValueChanged); if (o.Arrow.Overlap(m.x, m.y)) { // Show sub-menu at this level ShowMenu(Cursor); } } else if (!e) { // In empty space LRect c = GetClient(); e = new FolderCtrlEdit(GetId()+1, c); if (e) { e->Attach(this); LString s = Name(); e->Name(s); e->SetCaret(s.Length()); e->Focus(true); } } } } } void OnMouseMove(LMouse &m) { Part *o = Over; Over = HitPart(m.x, m.y); if (o != Over) Invalidate(); } void OnMouseExit(LMouse &m) { if (Over) { Over = NULL; Invalidate(); } } int OnNotify(LViewI *c, LNotification n) { if (e != NULL && c->GetId() == e->GetId()) { if (n.Type == LNotifyReturnKey) { ExitEditMode(); } } return 0; } LMessage::Result OnEvent(LMessage *m) { switch (m->Msg()) { case M_LOST_FOCUS: { ExitEditMode(); break; } case M_DELETE_EDIT: { DeleteObj(e); break; } case M_NOTIFY_VALUE_CHANGED: { SendNotify(LNotifyValueChanged); break; } } return LView::OnEvent(m); } virtual bool ShowMenu(ssize_t Level) { if (Level <= 0) return false; LString dir = NameAt(Level-1); LSubMenu s; LDirectory d; LString::Array Opts; for (int b = d.First(dir); b; b = d.Next()) { if (d.IsDir()) { Opts.New() = d.GetName(); s.AppendItem(d.GetName(), (int)Opts.Length()); } } Part &i = p[Level]; LPoint pt(i.Arrow.x1, i.Arrow.y2+1); PointToScreen(pt); int Cmd = s.Float(this, pt.x, pt.y, true); if (Cmd) { LString np; np = dir + DIR_STR + Opts[Cmd-1]; Name(np); PostEvent(M_NOTIFY_VALUE_CHANGED); } else return false; return true; } }; #else #define USE_FOLDER_CTRL 0 #endif class LFileSelectDlg : public LDialog { LRect OldPos; LRect MinSize; LArray Links; LArray Hidden; public: LFileSelectPrivate *d = NULL; LTableLayout *Tbl = NULL; LBox *Sub = NULL; LTree *Bookmarks = NULL; LTextLabel *Ctrl1 = NULL; #if USE_FOLDER_CTRL FolderCtrl *Ctrl2 = NULL; #else LEdit *Ctrl2 = NULL; #endif LFolderDrop *Ctrl3 = NULL; LIconButton *BackBtn = NULL; LIconButton *UpBtn = NULL; LIconButton *NewDirBtn = NULL; LFolderList *FileLst = NULL; LTextLabel *Ctrl8 = NULL; LTextLabel *Ctrl9 = NULL; LEdit *FileNameEdit = NULL; LCombo *FileTypeCbo = NULL; LButton *SaveBtn = NULL; LButton *CancelBtn = NULL; LCheckBox *ShowHidden = NULL; LEdit *FilterEdit = NULL; LFileSelectDlg(LFileSelectPrivate *Select); ~LFileSelectDlg(); const char *GetClass() override { return "LFileSelectDlg"; } int OnNotify(LViewI *Ctrl, LNotification n) override; void OnUpFolder(); void SetFolder(char *f); void OnFolder(); void OnFile(char *f); void OnFilter(const char *Key); bool OnViewKey(LView *v, LKey &k) override { if (k.vkey == LK_UP && k.Alt()) { if (k.Down()) { UpBtn->SendNotify(); } return true; } return false; } void Add(LTreeItem *i, LVolume *v) { if (!i || !v) return; auto Path = v->Path(); i->SetText(v->Name()); i->SetText(Path, 1); for (unsigned n=0; nFirst(); cv; cv = cv->Next()) { LTreeItem *ci = new LTreeItem; if (ci) { i->Insert(ci); Add(ci, cv); } } i->Expanded(true); } }; LFileSelectDlg::LFileSelectDlg(LFileSelectPrivate *select) { d = select; SetParent(d->Parent); MinSize.ZOff(450, 300); if (!d->InitSize.Valid()) { auto Dpi = LScreenDpi(); auto Scale = (float)Dpi.x / 96.0; d->InitSize.Set(0, 0, (int)(650*Scale), (int)(450*Scale) + LAppInst->GetMetric(LGI_MET_DECOR_Y) ); } SetPos(d->InitSize); int x = 0, y = 0; AddView(Tbl = new LTableLayout); // Top Row auto *c = Tbl->GetCell(x++, y); c->Add(Ctrl1 = new LTextLabel(IDC_STATIC, 0, 0, -1, -1, "Look in:")); c->VerticalAlign(LCss::Len(LCss::VerticalMiddle)); c = Tbl->GetCell(x++, y); #if USE_FOLDER_CTRL c->Add(Ctrl2 = new FolderCtrl(IDC_PATH)); #else c->Add(Ctrl2 = new LEdit(IDC_PATH, 0, 0, 245, 21, "")); #endif c = Tbl->GetCell(x++, y); c->Add(Ctrl3 = new LFolderDrop(this, IDC_DROP, 336, 7, 16, 21)); c = Tbl->GetCell(x++, y); c->Add(BackBtn = new LIconButton(IDC_BACK, 378, 7, 27, 21, d->BtnIcons, FSI_BACK)); c = Tbl->GetCell(x++, y); c->Add(UpBtn = new LIconButton(IDC_UP, 406, 7, 27, 21, d->BtnIcons, FSI_UPDIR)); c = Tbl->GetCell(x++, y); c->Add(NewDirBtn = new LIconButton(IDC_NEW, 434, 7, 27, 21, d->BtnIcons, FSI_NEWDIR)); // Folders/items row x = 0; y++; c = Tbl->GetCell(x, y, true, 6, 1); c->Add(Sub = new LBox(IDC_SUB_TBL)); Sub->AddView(Bookmarks = new LTree(IDC_BOOKMARKS, 0, 0, -1, -1)); Bookmarks->GetCss(true)->Width(LCss::Len(LCss::LenPx, 150.0f)); Bookmarks->SetImageList(d->TreeIcons, false); LTableLayout *t; Sub->AddView(t = new LTableLayout(11)); // Filter / search row c = t->GetCell(0, 0); c->Add(new LCheckBox(IDC_FILTER_CLEAR, 0, 0, -1, -1, "Filter items:")); c->VerticalAlign(LCss::Len(LCss::VerticalMiddle)); c = t->GetCell(1, 0); c->Add(FilterEdit = new LEdit(IDC_FILTER, 0, 0, 60, 20)); c = t->GetCell(0, 1, true, 2); c->Add(FileLst = new LFolderList(this, IDC_VIEW, 14, 35, 448, 226)); // File name row x = 0; y++; c = Tbl->GetCell(x++, y); c->Add(Ctrl8 = new LTextLabel(IDC_STATIC, 14, 275, -1, -1, "File name:")); c = Tbl->GetCell(x, y, true, 2); x += 2; c->Add(FileNameEdit = new LEdit(IDC_FILE, 100, 268, 266, 21, "")); c = Tbl->GetCell(x, y, true, 3); c->Add(SaveBtn = new LButton(IDOK, 392, 268, 70, 21, "Ok")); // 4th row x = 0; y++; c = Tbl->GetCell(x++, y); c->Add(Ctrl9 = new LTextLabel(IDC_STATIC, 14, 303, -1, -1, "Files of type:")); c = Tbl->GetCell(x, y, true, 2); x += 2; c->Add(FileTypeCbo = new LCombo(IDC_TYPE, 100, 296, 266, 21, "")); c = Tbl->GetCell(x++, y, true, 3); c->Add(CancelBtn = new LButton(IDCANCEL, 392, 296, 70, 21, "Cancel")); // 5th row x = 0; y++; c = Tbl->GetCell(x++, y, true, 6); c->Add(ShowHidden = new LCheckBox(IDC_SHOWHIDDEN, 14, 326, -1, -1, "Show hidden files.")); // Init if (BackBtn) BackBtn->Enabled(false); if (SaveBtn) SaveBtn->Enabled(false); if (FileLst) FileLst->MultiSelect(d->MultiSelect); if (ShowHidden) ShowHidden->Value(d->InitShowHiddenFiles); // Load types if (!d->Types.Length()) { LFileType *t = new LFileType; if (t) { t->Description("All Files"); t->Extension(LGI_ALL_FILES); d->Types.Insert(t); } } for (auto t: d->Types) { char s[256]; snprintf(s, sizeof(s), "%s (%s)", t->Description(), t->Extension()); if (FileTypeCbo) FileTypeCbo->Insert(s); } d->CurrentType = 0; // File + Path char *File = d->Files[0]; if (File) { char *Dir = strrchr(File, DIR_CHAR); if (Dir) { OnFile(Dir + 1); } else { OnFile(File); } } if (d->InitPath) { SetFolder(d->InitPath); } else { SetFolder(LGetExePath()); } OnFolder(); // Size/layout SetPos(d->InitSize); MoveToCenter(); RegisterHook(this, LKeyEvents); FileLst->Focus(true); LgiGetUsersLinks(Links); auto v = FileDev->GetRootVolume(); if (v) { for (auto vol = v; vol; vol = vol->Next()) { if (auto *ti = new LTreeItem) { Bookmarks->Insert(ti); Add(ti, vol); ti->Expanded(true); } } } if (Links.Length()) { if (auto *ti = new LTreeItem) { ti->SetText("Bookmarks"); Bookmarks->Insert(ti); for (unsigned n=0; nSetText(leaf?leaf+1:p, 0); ci->SetText(p, 1); ti->Insert(ci); } } ti->Expanded(true); } } } LFileSelectDlg::~LFileSelectDlg() { UnregisterHook(this); d->InitShowHiddenFiles = ShowHidden ? ShowHidden->Value() : false; d->InitSize = GetPos(); auto CurPath = GetCtrlName(IDC_PATH); if (ValidStr(CurPath)) d->InitPath = CurPath; } void LFileSelectDlg::OnFile(char *f) { if (d->Type != TypeOpenFolder) { FileNameEdit->Name(f ? f : (char*)""); SaveBtn->Enabled(ValidStr(f)); } } void LFileSelectDlg::SetFolder(char *f) { auto CurPath = GetCtrlName(IDC_PATH); if (CurPath) { d->History.Insert(NewStr(CurPath)); SetCtrlEnabled(IDC_BACK, true); } SetCtrlName(IDC_PATH, f); } void LFileSelectDlg::OnFolder() { if (Ctrl3) Ctrl3->OnFolder(); if (FileLst) FileLst->OnFolder(); auto CurPath = GetCtrlName(IDC_PATH); if (CurPath && UpBtn) UpBtn->Enabled(strlen(CurPath)>3); } void LFileSelectDlg::OnUpFolder() { auto Cur = GetCtrlName(IDC_PATH); if (Cur) { char Dir[MAX_PATH_LEN]; strcpy(Dir, Cur); if (strlen(Dir) > 3) { LTrimDir(Dir); if (!strchr(Dir, DIR_CHAR)) strcat(Dir, DIR_STR); SetFolder(Dir); OnFolder(); } } } void LFileSelectDlg::OnFilter(const char *Key) { if (FileLst) FileLst->SetFilterKey(Key); } int LFileSelectDlg::OnNotify(LViewI *Ctrl, LNotification n) { switch (Ctrl->GetId()) { case IDC_BOOKMARKS: { if (n.Type == LNotifyItemSelect && Bookmarks) { LTreeItem *s = Bookmarks->Selection(); if (s) { const char *p = s->GetText(1); if (LDirExists(p)) { SetCtrlName(IDC_PATH, p); OnFolder(); } } } break; } case IDC_PATH: { if (n.Type == LNotifyValueChanged) OnFolder(); break; } case IDC_VIEW: { if (FileLst) { /* These functions are handled by the list control's OnKey implementation if (Flags == GLIST_NOTIFY_RETURN) { List s; if (FileLst->GetSelection(s)) { LFolderItem *i = dynamic_cast(s.First()); if (i) { i->OnActivate(); } } } else if (Flags == GLIST_NOTIFY_BACKSPACE) { OnUpFolder(); } */ } break; } case IDC_FILE: { auto f = Ctrl->Name(); if (!f) break; if (n.Type == LNotifyReturnKey) { // allow user to insert new type by typing the pattern into the file name edit box and // hitting enter if (strchr(f, '?') || strchr(f, '*')) { // it's a mask, push the new type on the the type stack and // refilter the content int TypeIndex = -1; int n = 0; for (auto t: d->Types) { if (t->Extension() && stricmp(t->Extension(), f) == 0) { TypeIndex = n; break; } n++; } // insert the new type if not already there if (TypeIndex < 0) { LFileType *n = new LFileType; if (n) { n->Description(f); n->Extension(f); TypeIndex = (int)d->Types.Length(); d->Types.Insert(n); FileTypeCbo->Insert(f); } } // select the new type if (TypeIndex >= 0) { FileTypeCbo->Value(d->CurrentType = TypeIndex); } // clear the edit box Ctrl->Name(""); // Update and don't do normal save btn processing. OnFolder(); // Skip the IDOK message generated by the default button d->EatClose = true; printf("%s:%i - eat close true\n", _FL); break; } if (LDirExists(f)) { // Switch to the folder... SetCtrlName(IDC_PATH, f); OnFolder(); Ctrl->Name(NULL); d->EatClose = true; } else if (LFileExists(f)) { // Select the file... d->Files.Insert(NewStr(f)); EndModal(IDOK); break; } } bool HasFile = ValidStr(f); bool BtnEnabled = SaveBtn->Enabled(); if (HasFile ^ BtnEnabled) { SaveBtn->Enabled(HasFile); } break; } case IDC_BACK: { auto It = d->History.rbegin(); char *Dir = *It; if (Dir) { d->History.Delete(Dir); SetCtrlName(IDC_PATH, Dir); OnFolder(); DeleteArray(Dir); if (!d->History[0]) { SetCtrlEnabled(IDC_BACK, false); } } break; } case IDC_SHOWHIDDEN: { FileLst->OnFolder(); break; } case IDC_TYPE: { d->CurrentType = (int)FileTypeCbo->Value(); FileLst->OnFolder(); if (d->Type == TypeSaveFile) { // change extension of current file LFileType *Type = d->Types.ItemAt(d->CurrentType); auto File = FileNameEdit->Name(); if (Type && File) { char *Ext = strchr(File, '.'); if (Ext) { char *DefExt = Type->DefaultExtension(); if (DefExt) { Ext++; char s[256]; ZeroObj(s); memcpy(s, File, Ext-File); strcat(s, DefExt); OnFile(s); DeleteArray(DefExt); } } } } break; } case IDC_UP: { OnUpFolder(); break; } case IDC_FILTER: { const char *n = Ctrl->Name(); SetCtrlValue(IDC_FILTER_CLEAR, ValidStr(n)); OnFilter(n); break; } case IDC_FILTER_CLEAR: { if (!Ctrl->Value()) { SetCtrlName(IDC_FILTER, NULL); OnFilter(NULL); } break; } case IDC_NEW: { LInput Dlg(this, "", "Create new folder:", "New Folder"); Dlg.DoModal([&](auto d, auto code) { char New[MAX_PATH_LEN]; strcpy(New, GetCtrlName(IDC_PATH)); if (New[strlen(New)-1] != DIR_CHAR) strcat(New, DIR_STR); strcat(New, Dlg.GetStr()); FileDev->CreateFolder(New); OnFolder(); }); break; } case IDOK: { if (d->EatClose) { printf("%s:%i - SKIPPING eat close false\n", _FL); d->EatClose = false; break; } auto Path = GetCtrlName(IDC_PATH); auto File = GetCtrlName(IDC_FILE); if (Path) { char f[MAX_PATH_LEN]; d->Files.DeleteArrays(); if (d->Type == TypeOpenFolder) { d->Files.Insert(NewStr(Path)); } else { List Sel; if (d->Type != TypeSaveFile && FileLst && FileLst->GetSelection(Sel) && Sel.Length() > 1) { for (auto i: Sel) { LMakePath(f, sizeof(f), Path, i->GetText(0)); d->Files.Insert(NewStr(f)); } } else if (ValidStr(File)) { if (strchr(File, DIR_CHAR)) strcpy_s(f, sizeof(f), File); else LMakePath(f, sizeof(f), Path, File); d->Files.Insert(NewStr(f)); } } } // fall thru } case IDCANCEL: { EndModal(Ctrl->GetId()); break; } } return 0; } ////////////////////////////////////////////////////////////////////////// class LFileSystemItem : public LTreeItem { class LFileSystemPopup *Popup; LString Path; public: LFileSystemItem(LFileSystemPopup *popup, LVolume *vol, char *path = 0); char *GetPath() { return Path; } void OnPath(const char *p); void OnMouseClick(LMouse &m); bool OnKey(LKey &k); }; #define IDC_TREE 100 class LFileSystemPopup : public LPopup { friend class LFileSystemItem; LFileSelectDlg *Dlg; LTree *Tree; LFileSystemItem *Root; public: LFileSystemPopup(LView *owner, LFileSelectDlg *dlg, int x) : LPopup(owner) { Dlg = dlg; LRect r(0, 0, x, 150); SetPos(r); Children.Insert(Tree = new LTree(IDC_TREE, 1, 1, X()-3, Y()-3)); if (Tree) { Tree->Sunken(false); LVolume *v = FileDev->GetRootVolume(); if (v) { Tree->SetImageList(Dlg->d->BtnIcons, false); Tree->Insert(Root = new LFileSystemItem(this, v)); for (auto next = v->Next(); next; next = next->Next()) { Tree->SetImageList(Dlg->d->BtnIcons, false); Tree->Insert(Root = new LFileSystemItem(this, next)); } } } } ~LFileSystemPopup() { } void Visible(bool i) { if (i && Root) { Root->OnPath(Dlg->GetCtrlName(IDC_PATH)); } LPopup::Visible(i); } void OnPaint(LSurface *pDC) { // Draw border LRect c = GetClient(); c.Offset(-c.x1, -c.y1); pDC->Colour(L_BLACK); pDC->Box(&c); c.Inset(1, 1); } void OnActivate(LFileSystemItem *i) { if (i) { Dlg->SetFolder(i->GetPath()); Dlg->OnFolder(); Visible(false); } } }; LFileSystemItem::LFileSystemItem(LFileSystemPopup *popup, LVolume *Vol, char *path) { Popup = popup; Expanded(true); if (Vol) { Path = Vol->Path(); SetText(Vol->Name()); switch (Vol->Type()) { case VT_FLOPPY: SetImage(FSI_FLOPPY); break; case VT_HARDDISK: case VT_RAMDISK: SetImage(FSI_HARDDISK); break; case VT_CDROM: SetImage(FSI_CDROM); break; case VT_NETWORK_SHARE: SetImage(FSI_NETWORK); break; case VT_DESKTOP: SetImage(FSI_DESKTOP); break; default: SetImage(FSI_DIRECTORY); break; } for (LVolume *v=Vol->First(); v; v=v->Next()) { Insert(new LFileSystemItem(Popup, v)); } } else { Path = NewStr(path); SetText(strrchr(Path, DIR_CHAR)+1); SetImage(FSI_DIRECTORY); } } void LFileSystemItem::OnPath(const char *p) { switch (GetImage()) { case FSI_DESKTOP: { if (p && Path && stricmp(Path, p) == 0) { Select(true); p = 0; } break; } case FSI_DIRECTORY: { return; } default: { LTreeItem *Old = Items[0]; if (Old) { Old->Remove(); DeleteObj(Old); } break; } } if (p) { auto PathLen = strlen(Path); if (Path && strnicmp(Path, p, PathLen) == 0 && (p[PathLen] == DIR_CHAR || p[PathLen] == 0) #ifdef LINUX && strcmp(Path, "/") != 0 #endif ) { LTreeItem *Item = this; if (GetImage() != FSI_DESKTOP && strlen(p) > 3) { auto Start = p + strlen(Path); if (Start) { char s[256]; strcpy(s, Path); auto T = LString(Start).SplitDelimit(DIR_STR); for (int i=0; iInsert(New); Item = New; } } } } if (Item) { Item->Select(true); } } } for (auto item: Items) { LFileSystemItem *i = dynamic_cast(item); if (i) i->OnPath(p); } } void LFileSystemItem::OnMouseClick(LMouse &m) { if (m.Left() && m.Down()) { Popup->OnActivate(this); } } bool LFileSystemItem::OnKey(LKey &k) { if ((k.c16 == ' ' || k.c16 == LK_RETURN)) { if (k.Down() && k.IsChar) { Popup->OnActivate(this); } return true; } return false; } LFolderDrop::LFolderDrop(LFileSelectDlg *dlg, int Id, int x, int y, int cx, int cy) : LDropDown(Id, x, y, cx, cy, 0), LFolderView(dlg) { SetPopup(new LFileSystemPopup(this, dlg, cx + (dlg->Ctrl2 ? dlg->Ctrl2->X() : 0) )); } void LFolderDrop::OnFolder() { } ////////////////////////////////////////////////////////////////////////// #define IDM_OPEN 1000 #define IDM_CUT 1001 #define IDM_COPY 1002 #define IDM_RENAME 1003 #define IDM_PROPERTIES 1004 #define IDM_CREATE_SHORTCUT 1005 #define IDM_DELETE 1006 LFolderItem::LFolderItem(LFileSelectDlg *dlg, char *FullPath, LDirectory *Dir) { Dlg = dlg; Path = FullPath; File = strrchr(Path, DIR_CHAR); if (File) File++; IsDir = Dir->IsDir(); } LFolderItem::~LFolderItem() { } const char *LFolderItem::GetText(int i) { return File; } int LFolderItem::GetImage(int Flags) { return IsDir ? 1 : 0; } void LFolderItem::OnSelect() { if (!IsDir && File) { Dlg->OnFile(Select() ? File : 0); } } void LFolderItem::OnDelete(bool Ask) { if (!Ask || LgiMsg(Parent, "Do you want to delete '%s'?", ModuleName, MB_YESNO, Path.Get()) == IDYES) { bool Status = false; if (IsDir) { Status = FileDev->RemoveFolder(Path, true); } else { Status = FileDev->Delete(Path); } if (Status) { Parent->Remove(this); delete this; } } } void LFolderItem::OnRename() { LInput *Inp = new LInput(Dlg, File, "New name:", Dlg->Name()); Inp->DoModal([&](auto d, auto code) { if (!code) return; char Old[MAX_PATH_LEN]; strcpy_s(Old, sizeof(Old), Path); char New[MAX_PATH_LEN]; File[0] = 0; LMakePath(New, sizeof(New), Path, Inp->GetStr()); if (FileDev->Move(Old, New)) { DeleteArray(Path); Path = NewStr(New); File = strrchr(Path, DIR_CHAR); if (File) File++; Update(); } else { LgiMsg(Dlg, "Renaming '%s' failed.", Dlg->Name(), MB_OK); } delete Inp; }); } void LFolderItem::OnActivate() { if (File) { if (IsDir) { char Dir[256]; strcpy(Dir, Dlg->GetCtrlName(IDC_PATH)); if (Dir[strlen(Dir)-1] != DIR_CHAR) strcat(Dir, DIR_STR); strcat(Dir, File); Dlg->SetFolder(Dir); Dlg->OnFolder(); } else // Is file { Dlg->OnNotify(Dlg->SaveBtn, LNotifyActivate); } } } void LFolderItem::OnMouseClick(LMouse &m) { if (m.Down()) { if (m.Left()) { if (m.Double()) { OnActivate(); } } else if (m.Right()) { LSubMenu *RClick = new LSubMenu; if (RClick) { RClick->AppendItem("Select", IDM_OPEN, true); RClick->AppendSeparator(); RClick->AppendItem("Cut", IDM_CUT, false); RClick->AppendItem("Copy", IDM_COPY, false); RClick->AppendSeparator(); RClick->AppendItem("Create Shortcut", IDM_CREATE_SHORTCUT, false); RClick->AppendItem("Delete", IDM_DELETE, true); RClick->AppendItem("Rename", IDM_RENAME, true); RClick->AppendSeparator(); RClick->AppendItem("Properties", IDM_PROPERTIES, false); if (Parent->GetMouse(m, true)) { switch (RClick->Float(Parent, m.x, m.y)) { case IDM_OPEN: { break; } case IDM_DELETE: { OnDelete(); break; } case IDM_RENAME: { OnRename(); break; } } } DeleteObj(RClick); } } } } int LFolderItemCompare(LListItem *A, LListItem *B, NativeInt Data) { LFolderItem *a = dynamic_cast(A); LFolderItem *b = dynamic_cast(B); if (a && b) { if (a->IsDir ^ b->IsDir) { if (a->IsDir) return -1; else return 1; } else if (a->File && b->File) { return stricmp(a->File, b->File); } } return 0; } LFolderList::LFolderList(LFileSelectDlg *dlg, int Id, int x, int y, int cx, int cy) : LList(Id, x, y, cx, cy), LFolderView(dlg) { SetImageList(Dlg->d->BtnIcons, false); ShowColumnHeader(false); AddColumn("Name", cx-20); SetMode(LListColumns); } bool LFolderList::OnKey(LKey &k) { bool Status = LList::OnKey(k); switch (k.vkey) { case LK_BACKSPACE: { if (k.Down() && GetWindow()) { // Go up a directory LViewI *v = GetWindow()->FindControl(IDC_UP); if (v) { GetWindow()->OnNotify(v, LNotifyBackspaceKey); } } Status = true; break; } case LK_RETURN: #ifdef LK_KP_ENTER case LK_KP_ENTER: #endif { if (k.Down() && GetWindow()) { LFolderItem *Sel = dynamic_cast(GetSelected()); if (Sel) { if (Sel->IsDir) { auto Cur = GetWindow()->GetCtrlName(IDC_PATH); if (Cur) { char Path[256]; LMakePath(Path, sizeof(Path), Cur, Sel->GetText(0)); if (LDirExists(Path)) { GetWindow()->SetCtrlName(IDC_PATH, Path); Dlg->OnFolder(); } } } else { LViewI *Ok = GetWindow()->FindControl(IDOK); if (Ok) { GetWindow()->SetCtrlName(IDC_FILE, Sel->GetText(0)); GetWindow()->OnNotify(Ok, LNotification(k)); } } } } Status = true; break; } case LK_DELETE: { if (k.Down() && !k.IsChar && GetWindow()) { List Sel; if (GetSelection(Sel)) { LStringPipe Msg; Msg.Push("Do you want to delete:\n\n"); List Delete; for (auto i: Sel) { LFolderItem *s = dynamic_cast(i); if (s) { Delete.Insert(s); Msg.Push("\t"); Msg.Push(s->GetText(0)); Msg.Push("\n"); } } char *Mem = Msg.NewStr(); if (Mem) { if (LgiMsg(this, Mem, ModuleName, MB_YESNO) == IDYES) { for (auto d: Delete) { d->OnDelete(false); } } DeleteArray(Mem); } } } Status = true; break; } } // LgiTrace("%s:%i LFolderList::OnKey, key=%i down=%i status=%i\n", _FL, k.vkey, k.Down(), Status); return Status; } void LFolderList::OnFolder() { Empty(); LDirectory Dir; List New; // Get current type LFileType *Type = Dlg->d->Types.ItemAt(Dlg->d->CurrentType); List Ext; if (Type) { auto T = LString(Type->Extension()).SplitDelimit(";"); for (size_t i=0; iCtrl2) return; bool ShowHiddenFiles = Dlg->ShowHidden ? Dlg->ShowHidden->Value() : false; for (auto Found = Dir.First(Dlg->Ctrl2->Name()); Found; Found = Dir.Next()) { char Name[LDirectory::MaxPathLen]; Dir.Path(Name, sizeof(Name)); bool Match = true; if (!ShowHiddenFiles && Dir.IsHidden()) { Match = false; } else if (!Dir.IsDir() && Ext.Length() > 0) { Match = false; for (auto e: Ext) { bool m = MatchStr(e, Name); if (m) { Match = true; break; } } } if (FilterKey && Match) Match = stristr(Dir.GetName(), FilterKey) != NULL; if (Match) New.Insert(new LFolderItem(Dlg, Name, &Dir)); } // Sort items... New.Sort(LFolderItemCompare); // Display items... Insert(New); } ////////////////////////////////////////////////////////////////////////// LFileSelect::LFileSelect(LViewI *Window) { d = new LFileSelectPrivate(this); if (Window) Parent(Window); } LFileSelect::~LFileSelect() { DeleteObj(d); } void LFileSelect::ShowReadOnly(bool b) { d->ShowReadOnly = b;; } bool LFileSelect::ReadOnly() { return d->ReadOnly; } const char *LFileSelect::Name() { return d->Files[0]; } bool LFileSelect::Name(const char *n) { d->Files.DeleteArrays(); if (n) { d->Files.Insert(NewStr(n)); } return true; } const char *LFileSelect::operator [](size_t i) { return d->Files.ItemAt(i); } size_t LFileSelect::Length() { return d->Files.Length(); } size_t LFileSelect::Types() { return d->Types.Length(); } void LFileSelect::ClearTypes() { d->Types.DeleteObjects(); } LFileType *LFileSelect::TypeAt(ssize_t n) { return d->Types.ItemAt(n); } bool LFileSelect::Type(const char *Description, const char *Extension, int Data) { LFileType *Type = new LFileType; if (Type) { Type->Description(Description); Type->Extension(Extension); d->Types.Insert(Type); } return Type != 0; } ssize_t LFileSelect::SelectedType() { return d->CurrentType; } LViewI *LFileSelect::Parent() { return d->Parent; } void LFileSelect::Parent(LViewI *Window) { d->Parent = dynamic_cast(Window); } bool LFileSelect::MultiSelect() { return d->MultiSelect; } void LFileSelect::MultiSelect(bool Multi) { d->MultiSelect = Multi; } #define CharPropImpl(Func, Var) \ const char *LFileSelect::Func() \ { \ return Var; \ } \ void LFileSelect::Func(const char *i) \ { \ Var = i; \ } CharPropImpl(InitialDir, d->InitPath); CharPropImpl(Title, d->Title); CharPropImpl(DefaultExtension, d->DefExt); void LFileSelect::Open(SelectCb Cb) { LFileSelectDlg *Dlg = new LFileSelectDlg(d); d->Type = TypeOpenFile; Dlg->Name("Open"); if (Dlg->SaveBtn) Dlg->SaveBtn->Name("Open"); // printf("LFileSelect domodal.. thread=%p\n", LGetCurrentThread()); Dlg->DoModal([select=this, cb=Cb](auto dlg, auto code) { // printf("LFileSelect cb.. thread=%u lock=%u\n", GetCurrentThreadId(), dlg->WindowHandle()->LockingThread()); if (cb) cb(select, code == IDOK); // printf("LFileSelect deleting.. lock=%u\n", dlg->WindowHandle()->LockingThread()); delete dlg; // printf("LFileSelect deleted..\n"); }); } void LFileSelect::OpenFolder(SelectCb Cb) { auto Dlg = new LFileSelectDlg(d); d->Type = TypeOpenFolder; Dlg->SaveBtn->Enabled(true); Dlg->FileNameEdit->Enabled(false); Dlg->Name("Open Folder"); Dlg->SaveBtn->Name("Open"); printf("LFileSelect::OpenFolder domodal...\n"); Dlg->DoModal([this,Cb](auto d, auto code) { printf("LFileSelect::OpenFolder cb, code=%i\n", code); if (Cb) Cb(this, code == IDOK); delete d; }); } void LFileSelect::Save(SelectCb Cb) { auto *Dlg = new LFileSelectDlg(d); d->Type = TypeSaveFile; Dlg->Name("Save As"); Dlg->SaveBtn->Name("Save As"); // printf("LFileSelect domodal.. thread=%u\n", GetCurrentThreadId()); Dlg->DoModal([this, Cb](auto dlg, auto code) { // printf("LFileSelect cb.. thread=%u, code=%i\n", GetCurrentThreadId(), code); // dlg->WindowHandle()->LockingThread()); if (Cb) Cb(this, code == IDOK); // printf("LFileSelect deleting.. lock=%u\n", dlg->WindowHandle()->LockingThread()); delete dlg; // printf("LFileSelect deleted..\n"); }); } /////////////////////////////////////////////////////////////////////////////////// #if defined(LINUX) #include "lgi/common/Net.h" #endif bool LgiGetUsersLinks(LArray &Links) { LString Folder = LGetSystemPath(LSP_USER_LINKS); if (!Folder) return false; #if defined(WINDOWS) LDirectory d; for (int b = d.First(Folder); b; b = d.Next()) { char *s = d.GetName(); if (s && stristr(s, ".lnk")) { char lnk[MAX_PATH_LEN]; if (d.Path(lnk, sizeof(lnk)) && LResolveShortcut(lnk, lnk, sizeof(lnk))) { Links.New() = lnk; } } } #elif defined(LINUX) char p[MAX_PATH_LEN]; if (!LMakePath(p, sizeof(p), Folder, "bookmarks")) { LgiTrace("%s:%i - Failed to make path '%s'\n", _FL, Folder.Get()); return false; } if (!LFileExists(p)) return false; LAutoString Txt(LReadTextFile(p)); if (!Txt) { LgiTrace("%s:%i - failed to read '%s'\n", _FL, p); return false; } LString s = Txt.Get(); LString::Array a = s.Split("\n"); for (unsigned i=0; i