diff --git a/Ide/LgiIdeProj.xml b/Ide/LgiIdeProj.xml --- a/Ide/LgiIdeProj.xml +++ b/Ide/LgiIdeProj.xml @@ -1,284 +1,280 @@ - - - - - - + + + + + + - + .\win\Makefile.windows ./linux/Makefile.linux ./MacCocoa/LgiIde.xcodeproj Makefile.haiku ./lgiide ./lgiide LgiIde.exe LgiIde.exe WINDOWS WINNATIVE WINDOWS WINNATIVE POSIX POSIX LIBPNG_VERSION=\"1.2\" LIBPNG_VERSION=\"1.2\" MingW /home/matthew/Code/Lgi/trunk/Ide/Code/IdeProjectSettings.cpp ./src ./resources ../include ./src ./resources ../include ..\include\lgi\win ..\include\lgi\win ../include/lgi/linux ../include/lgi/linux/Gtk ../../../../codelib/openssl/include ../include/lgi/linux ../include/lgi/linux/Gtk ../../../../codelib/openssl/include ../include/lgi/haiku ../include/lgi/haiku ../include/lgi/mac/cocoa ../include/lgi/mac/cocoa imm32 imm32 magic pthread `pkg-config --libs gtk+-3.0` -static-libgcc magic pthread `pkg-config --libs gtk+-3.0` -static-libgcc -static-libgcc gnu network be -static-libgcc gnu network be - - ..\x64Debug19 - ..\x64Release19 - - + + Executable lgiide lgiide LgiIde.exe LgiIde.exe lgiide lgiide - `pkg-config --cflags gtk+-3.0` `pkg-config --cflags gtk+-3.0` POSIX _GNU_SOURCE POSIX _GNU_SOURCE - + 4 4 0 1 SOME_TEST=testing - + diff --git a/Ide/linux/Makefile.linux b/Ide/linux/Makefile.linux --- a/Ide/linux/Makefile.linux +++ b/Ide/linux/Makefile.linux @@ -1,422 +1,403 @@ #!/usr/bin/make # # This makefile generated by LgiIde # http://www.memecode.com/lgi.php # -.SILENT : +# .SILENT : CC = gcc CPP = g++ -Target = ./lgiide +Target = ../lgiide ifndef Build Build = Debug endif BuildDir = $(Build) +MakeDir := $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST)))) Flags = -fPIC -w -fno-inline -fpermissive ifeq ($(Build),Debug) Flags += -g -std=c++14 Tag = d Defs = -D_DEBUG -DLINUX -D_REENTRANT -D_FILE_OFFSET_BITS=64 -DPOSIX Libs = \ - -L../Debug \ -lmagic \ -lpthread \ `pkg-config --libs gtk+-3.0` \ -static-libgcc \ -llgi-gtk3$(Tag) \ - -L../$(BuildDir) + -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 + -I$(MakeDir)/../src \ + -I$(MakeDir)/../resources \ + -I$(MakeDir)/../../include/lgi/linux/Gtk \ + -I$(MakeDir)/../../include/lgi/linux \ + -I$(MakeDir)/../../include \ + -I$(MakeDir)/../../../../../codelib/openssl/include else Flags += -s -Os -std=c++14 Defs = -DLINUX -D_REENTRANT -D_FILE_OFFSET_BITS=64 -DPOSIX Libs = \ - -L../Release \ -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/levenshtein.c \ - src/JavascriptParser.cpp \ - src/IdeProjectSettings.cpp \ - src/IdeProject.cpp \ - src/IdeDoc.cpp \ - src/IdeCommon.cpp \ - src/History.cpp \ - src/FtpThread.cpp \ - src/FindSymbol.cpp \ - src/FindInFiles.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 +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/levenshtein.c \ + ../src/JavascriptParser.cpp \ + ../src/IdeProjectSettings.cpp \ + ../src/IdeProject.cpp \ + ../src/IdeDoc.cpp \ + ../src/IdeCommon.cpp \ + ../src/History.cpp \ + ../src/FtpThread.cpp \ + ../src/FindSymbol.cpp \ + ../src/FindInFiles.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 SourceLst := $(patsubst %.c,%.o,$(patsubst %.cpp,%.o,$(Source))) Objects := $(addprefix $(BuildDir)/,$(SourceLst)) Deps := $(patsubst %.o,%.d,$(Objects)) # Target # Executable target -$(Target) : ../$(BuildDir)/liblgi-gtk3$(Tag).so $(Objects) +$(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/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/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 +../../$(BuildDir)/liblgi-gtk3$(Tag).so : ../../include/lgi/common/App.h \ + ../../include/lgi/common/Array.h \ + ../../include/lgi/common/AutoPtr.h \ + ../../include/lgi/common/Box.h \ + ../../include/lgi/common/Button.h \ + ../../include/lgi/common/Cancel.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/Css.h \ + ../../include/lgi/common/CssTools.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/DragAndDrop.h \ + ../../include/lgi/common/DropFiles.h \ + ../../include/lgi/common/Edit.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/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/List.h \ + ../../include/lgi/common/ListItemCheckBox.h \ + ../../include/lgi/common/ListItemRadioBtn.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/OptionsFile.h \ + ../../include/lgi/common/Password.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/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/Rect.h \ + ../../include/lgi/common/RefCount.h \ + ../../include/lgi/common/Res.h \ + ../../include/lgi/common/ScrollBar.h \ + ../../include/lgi/common/Slider.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/Tree.h \ + ../../include/lgi/common/Unicode.h \ + ../../include/lgi/common/UnrolledList.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 Makefile.linux .SECONDEXPANSION: -$(Objects): $(BuildDir)/%.o: $$(wildcard %.c*) +$(Objects): $(BuildDir)/%.o: $$(Source) mkdir -p $(@D) - @echo $( #else #include #endif #include #include "lgi/common/Lgi.h" #include "lgi/common/DragAndDrop.h" #include "lgi/common/Token.h" #include "lgi/common/Combo.h" #include "lgi/common/Net.h" #include "lgi/common/ListItemCheckBox.h" #include "lgi/common/ClipBoard.h" #include "lgi/common/DropFiles.h" #include "lgi/common/SubProcess.h" #include "lgi/common/Css.h" #include "lgi/common/TableLayout.h" #include "lgi/common/TextLabel.h" #include "lgi/common/Button.h" #include "lgi/common/RegKey.h" #include "lgi/common/FileSelect.h" #include "lgi/common/Menu.h" #include "LgiIde.h" #include "resdefs.h" #include "FtpThread.h" #include "ProjectNode.h" #include "WebFldDlg.h" extern const char *Untitled; const char SourcePatterns[] = "*.c;*.h;*.cpp;*.cc;*.java;*.d;*.php;*.html;*.css;*.js"; const char *AddFilesProgress::DefaultExt = "c,cpp,cc,cxx,h,hpp,hxx,html,css,json,js,jsx,txt,png,jpg,jpeg,rc,xml,mk,paths,makefile,py,java,php"; const char *VsBinaries[] = {"devenv.com", "WDExpress.exe"}; #define USE_OPEN_PROGRESS 1 #define STOP_BUILD_TIMEOUT 2000 #ifdef WINDOWS #define LGI_STATIC_LIBRARY_EXT "lib" #else #define LGI_STATIC_LIBRARY_EXT "a" #endif const char *PlatformNames[] = { "Windows", "Linux", "Mac", "Haiku", 0 }; int PlatformCtrlId[] = { IDC_WIN32, IDC_LINUX, IDC_MAC, IDC_HAIKU, 0 }; const char *PlatformDynamicLibraryExt(IdePlatform Platform) { if (Platform == PlatformWin) return "dll"; if (Platform == PlatformMac) return "dylib"; return "so"; } const char *PlatformSharedLibraryExt(IdePlatform Platform) { if (Platform == PlatformWin) return "lib"; return "a"; } const char *PlatformExecutableExt(IdePlatform Platform) { if (Platform == PlatformWin) return ".exe"; return ""; } char *ToUnixPath(char *s) { if (s) { char *c; while ((c = strchr(s, '\\'))) *c = '/'; } return s; } const char *CastEmpty(char *s) { return s ? s : ""; } bool FindInPath(LString &Exe) { LString::Array Path = LString(getenv("PATH")).Split(LGI_PATH_SEPARATOR); for (unsigned i=0; i SubProc; LString::Array BuildConfigs; LString::Array PostBuild; int AppHnd; enum CompilerType { DefaultCompiler, VisualStudio, MingW, Gcc, CrossCompiler, PythonScript, IAR, Nmake, Cygwin, Xcode, } Compiler; enum ArchType { DefaultArch, ArchX32, ArchX64, ArchArm6, ArchArm7, } Arch; public: BuildThread(IdeProject *proj, char *makefile, bool clean, BuildConfig config, bool all, int wordsize); ~BuildThread(); ssize_t Write(const void *Buffer, ssize_t Size, int Flags = 0) override; LString FindExe(); LAutoString WinToMingWPath(const char *path); int Main() override; }; class IdeProjectPrivate { public: AppWnd *App; IdeProject *Project; bool Dirty, UserFileDirty; LString FileName; IdeProject *ParentProject; IdeProjectSettings Settings; LAutoPtr Thread; LHashTbl, ProjectNode*> Nodes; int NextNodeId; // Threads LAutoPtr CreateMakefile; // User info file LString UserFile; LHashTbl,int> UserNodeFlags; IdeProjectPrivate(AppWnd *a, IdeProject *project) : Project(project), Settings(project) { App = a; Dirty = false; UserFileDirty = false; ParentProject = 0; NextNodeId = 1; } void CollectAllFiles(LTreeNode *Base, LArray &Files, bool SubProjects, int Platform); }; LString ToPlatformPath(const char *s, IdePlatform platform) { LString p = s; if (platform == PlatformWin) return p.Replace("/", "\\"); else return p.Replace("\\", "/"); } class MakefileThread : public LThread, public LCancel { IdeProjectPrivate *d; IdeProject *Proj; IdePlatform Platform; LStream *Log; bool BuildAfterwards; bool HasError; public: static int Instances; MakefileThread(IdeProjectPrivate *priv, IdePlatform platform, bool Build) : LThread("MakefileThread") { Instances++; d = priv; Proj = d->Project; Platform = platform; BuildAfterwards = Build; HasError = false; Log = d->App->GetBuildLog(); Run(); } ~MakefileThread() { Cancel(); while (!IsExited()) LSleep(1); Instances--; } void OnError(const char *Fmt, ...) { va_list Arg; va_start(Arg, Fmt); LStreamPrintf(Log, 0, Fmt, Arg); va_end(Arg); HasError = true; } int Main() { const char *PlatformName = PlatformNames[Platform]; const char *PlatformLibraryExt = NULL; const char *PlatformStaticLibExt = NULL; const char *PlatformExeExt = ""; LString LinkerFlags; const char *TargetType = d->Settings.GetStr(ProjTargetType, NULL, Platform); const char *CompilerName = d->Settings.GetStr(ProjCompiler); LString CCompilerBinary = "gcc"; LString CppCompilerBinary = "g++"; LStream *Log = d->App->GetBuildLog(); bool IsExecutableTarget = TargetType && !stricmp(TargetType, "Executable"); bool IsDynamicLibrary = TargetType && !stricmp(TargetType, "DynamicLibrary"); LAssert(Log); if (!Log) return false; Log->Print("CreateMakefile for '%s'...\n", PlatformName); if (Platform == PlatformWin) { LinkerFlags = ",--enable-auto-import"; } else { if (IsDynamicLibrary) { LinkerFlags = ",-soname,$(TargetFile)"; } LinkerFlags += ",-export-dynamic,-R."; } + Log->Print("Log %s:%i\n", _FL); + auto Base = Proj->GetBasePath(); auto MakeFile = Proj->GetMakefile(Platform); LString MakeFilePath; Proj->CheckExists(MakeFile); if (!MakeFile) { MakeFilePath = Base.Get(); LFile::Path p(MakeFilePath, "makefile"); MakeFile = p.GetFull(); } else if (LIsRelativePath(MakeFile)) { LFile::Path p(Base); p += MakeFile; MakeFile = p.GetFull(); p--; MakeFilePath = p.GetFull(); } else { LFile::Path p(MakeFile); p--; MakeFilePath = p.GetFull(); } + Log->Print("Log %s:%i\n", _FL); + // LGI_LIBRARY_EXT switch (Platform) { case PlatformWin: PlatformLibraryExt = "dll"; PlatformStaticLibExt = "lib"; PlatformExeExt = ".exe"; break; case PlatformLinux: case PlatformHaiku: PlatformLibraryExt = "so"; PlatformStaticLibExt = "a"; break; case PlatformMac: PlatformLibraryExt = "dylib"; PlatformStaticLibExt = "a"; break; default: LAssert(0); break; } + Log->Print("Log %s:%i\n", _FL); + if (CompilerName) { if (!stricmp(CompilerName, "cross")) { LString CBin = d->Settings.GetStr(ProjCCrossCompiler, NULL, Platform); if (CBin && !LFileExists(CBin)) FindInPath(CBin); if (CBin && LFileExists(CBin)) CCompilerBinary = CBin; else Log->Print("%s:%i - Error: C cross compiler '%s' not found.\n", _FL, CBin.Get()); LString CppBin = d->Settings.GetStr(ProjCppCrossCompiler, NULL, Platform); if (CppBin && !LFileExists(CppBin)) FindInPath(CppBin); if (CppBin && LFileExists(CppBin)) CppCompilerBinary = CppBin; else Log->Print("%s:%i - Error: C++ cross compiler '%s' not found.\n", _FL, CppBin.Get()); } } LFile m; if (!m.Open(MakeFile, O_WRITE)) { Log->Print("Error: Failed to open '%s' for writing.\n", MakeFile.Get()); return false; } + Log->Print("Log %s:%i\n", _FL); + m.SetSize(0); m.Print("#!/usr/bin/make\n" "#\n" "# This makefile generated by LgiIde\n" "# http://www.memecode.com/lgi.php\n" "#\n" "\n" ".SILENT :\n" "\n" "CC = %s\n" "CPP = %s\n", CCompilerBinary.Get(), CppCompilerBinary.Get()); // Collect all files that require building LArray Files; d->CollectAllFiles ( Proj, Files, false, 1 << Platform ); + Log->Print("Log %s:%i\n", _FL); + if (IsExecutableTarget) { LString Exe = Proj->GetExecutable(Platform); if (Exe) { if (LIsRelativePath(Exe)) m.Print("Target = %s\n", ToPlatformPath(Exe, Platform).Get()); else { - auto RelExe = LMakeRelativePath(Base, Exe); + auto RelExe = LMakeRelativePath(MakeFilePath, Exe); if (Base && RelExe) { m.Print("Target = %s\n", ToPlatformPath(RelExe, Platform).Get()); } else { Log->Print("%s:%i - Error: Missing path (%s, %s).\n", _FL, Base.Get(), RelExe.Get()); return false; } } } else { Log->Print("%s:%i - Error: No executable name specified (%s, %s).\n", _FL, TargetType, d->FileName.Get()); return false; } } else { LString Target = Proj->GetTargetName(Platform); if (Target) m.Print("Target = %s\n", ToPlatformPath(Target, Platform).Get()); else { Log->Print("%s:%i - Error: No target name specified.\n", _FL); return false; } } + Log->Print("Log %s:%i\n", _FL); + // Output the build mode, flags and some paths auto BuildMode = d->App->GetBuildMode(); auto BuildModeName = toString(BuildMode); m.Print("ifndef Build\n" " Build = %s\n" "endif\n", BuildModeName); + m.Print("MakeDir := $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))\n"); LString sDefines[BuildMax]; LString sLibs[BuildMax]; LString sIncludes[BuildMax]; const char *ExtraLinkFlags = NULL; const char *ExeFlags = NULL; if (Platform == PlatformWin) { ExtraLinkFlags = ""; ExeFlags = " -mwindows"; m.Print("BuildDir = $(Build)\n" "\n" "Flags = -fPIC -w -fno-inline -fpermissive\n"); const char *DefDefs = "-DWIN32 -D_REENTRANT"; sDefines[BuildDebug] = DefDefs; sDefines[BuildRelease] = DefDefs; } else { LString PlatformCap = PlatformName; ExtraLinkFlags = ""; ExeFlags = ""; m.Print("BuildDir = $(Build)\n" "\n" "Flags = -fPIC -w -fno-inline -fpermissive\n" // -fexceptions ); sDefines[0].Printf("-D%s -D_REENTRANT", PlatformCap.Upper().Get()); #ifdef LINUX sDefines[BuildDebug] += " -D_FILE_OFFSET_BITS=64"; // >:-( sDefines[BuildDebug] += " -DPOSIX"; #endif sDefines[BuildRelease] = sDefines[BuildDebug]; } + Log->Print("Log %s:%i\n", _FL); + List Deps; Proj->GetChildProjects(Deps); + Log->Print("Log %s:%i\n", _FL); for (int Cfg = BuildDebug; Cfg < BuildMax; Cfg++) { // Set the config auto cfgName = toString((BuildConfig)Cfg); d->Settings.SetCurrentConfig(cfgName); // Get the defines setup auto PDefs = d->Settings.GetStr(ProjDefines, NULL, Platform); if (ValidStr(PDefs)) { LToken Defs(PDefs, " ;,\r\n"); for (int i=0; iSettings.GetStr(ProjLibraryPaths, NULL, Platform); if (ValidStr(PLibPaths)) { LString::Array LibPaths = PLibPaths.Split("\n"); for (auto i: LibPaths) { LString s, in = i.Strip(); if (!in.Length()) continue; if (strchr("`-", in(0))) { s.Printf(" \\\n\t\t%s", in.Get()); } else { LString Rel; if (!LIsRelativePath(in)) - Rel = LMakeRelativePath(Base, in); + Rel = LMakeRelativePath(MakeFilePath, in); + + printf("Rel='%s' ('%s' + '%s')\n", Rel.Get(), MakeFilePath.Get(), in.Get()); LString Final = Rel ? Rel.Get() : in.Get(); if (!Proj->CheckExists(Final)) OnError("%s:%i - Library path '%s' doesn't exist (from %s).\n", _FL, Final.Get(), Proj->GetFileName()); s.Printf(" \\\n\t\t-L%s", ToUnixPath(Final)); } sLibs[Cfg] += s; } } const char *PLibs = d->Settings.GetStr(ProjLibraries, NULL, Platform); if (ValidStr(PLibs)) { LToken Libs(PLibs, "\r\n"); for (int i=0; iCheckExists(l); s.Printf(" \\\n\t\t-l%s", ToUnixPath(l)); } sLibs[Cfg] += s; } } for (auto dep: Deps) { LString Target = dep->GetTargetName(Platform); if (Target) { char t[MAX_PATH_LEN]; strcpy_s(t, sizeof(t), Target); if (!strnicmp(t, "lib", 3)) memmove(t, t + 3, strlen(t + 3) + 1); char *dot = strrchr(t, '.'); if (dot) *dot = 0; LString s, sTarget = t; Proj->CheckExists(sTarget); s.Printf(" \\\n\t\t-l%s$(Tag)", ToUnixPath(sTarget)); sLibs[Cfg] += s; auto DepBase = dep->GetBasePath(); if (DepBase) { LString DepPath = DepBase.Get(); - auto Rel = LMakeRelativePath(Base, DepPath); + auto Rel = LMakeRelativePath(MakeFilePath, DepPath); + printf("Rel=%s (%s + %s)\n", Rel.Get(), MakeFilePath.Get(), DepPath.Get()); LString Final = Rel ? Rel.Get() : DepPath.Get(); Proj->CheckExists(Final); s.Printf(" \\\n\t\t-L%s/$(BuildDir)", ToUnixPath(Final.RStrip("/\\"))); sLibs[Cfg] += s; } } } // Includes // Do include paths LHashTbl,bool> Inc; auto ProjIncludes = d->Settings.GetStr(ProjIncludePaths, NULL, Platform); if (ValidStr(ProjIncludes)) { // Add settings include paths. LToken Paths(ProjIncludes, "\r\n"); for (int i=0; iCheckExists(pn)) OnError("%s:%i - Include path '%s' doesn't exist.\n", _FL, pn.Get()); else if (!Inc.Find(pn)) Inc.Add(pn, true); } } const char *SysIncludes = d->Settings.GetStr(ProjSystemIncludes, NULL, Platform); if (ValidStr(SysIncludes)) { // Add settings include paths. LToken Paths(SysIncludes, "\r\n"); for (int i=0; iCheckExists(pn)) OnError("%s:%i - System include path '%s' doesn't exist (from %s).\n", _FL, pn.Get(), Proj->GetFileName()); else if (!Inc.Find(pn)) Inc.Add(pn, true); } } #if 0 /* Currently this code is adding extra paths that are covered by the official 'IncludePaths' in addition to relative paths in the actual #include parameter. e.g. #include "lgi/common/SomeHeader.h" Hence disabling it for the time being. */ // Add paths of headers for (int i=0; iGetFileName()) { char *e = LGetExtension(n->GetFileName()); if (e && stricmp(e, "h") == 0) { LString Fn = n->GetFileName(); for (char *Dir = Fn; *Dir; Dir++) { if (*Dir == '/' || *Dir == '\\') { *Dir = DIR_CHAR; } } char Path[MAX_PATH_LEN]; strcpy_s(Path, sizeof(Path), Fn); LTrimDir(Path); - LString Rel; - if (!Proj->RelativePath(Rel, Path)) + LString Rel = LMakeRelativePath(MakeFilePath, Path); + if (!Rel) Rel = Path; if (stricmp(Rel, ".") != 0) { LAutoString RelN = ToNativePath(Rel); if (!Proj->CheckExists(RelN)) OnError("Header include path '%s' doesn't exist.\n", RelN.Get()); else if (!Inc.Find(RelN)) Inc.Add(RelN, true); } } } } #endif LString::Array Incs; for (auto i: Inc) Incs.New() = i.key; Incs.Sort(); for (auto i: Incs) { LString s; if (*i == '`') s.Printf(" \\\n\t\t%s", i.Get()); else s.Printf(" \\\n\t\t-I%s", ToUnixPath(i)); sIncludes[Cfg] += s; } } // Output the defs section for Debug and Release // Debug specific m.Print("\n" "ifeq ($(Build),Debug)\n" " Flags += -g -std=c++14\n" " Tag = d\n" " Defs = -D_DEBUG %s\n" " Libs = %s\n" " Inc = %s\n", CastEmpty(sDefines[0].Get()), CastEmpty(sLibs[0].Get()), CastEmpty(sIncludes[0].Get())); // Release specific m.Print("else\n" " Flags += -s -Os -std=c++14\n" " Defs = %s\n" " Libs = %s\n" " Inc = %s\n" "endif\n" "\n", CastEmpty(sDefines[1].Get()), CastEmpty(sLibs[1].Get()), CastEmpty(sIncludes[1].Get())); if (Files.Length()) { LArray IncPaths; if (Proj->BuildIncludePaths(IncPaths, false, false, Platform)) { // Do source code list... m.Print("# Dependencies\n" "Source =\t"); LString::Array SourceFiles; for (auto &n: Files) { if (n->GetType() == NodeSrc) { auto f = n->GetFileName(); auto path = ToPlatformPath(f, Platform); if (path.Find("./") == 0) path = path(2,-1); SourceFiles.Add(path); } } SourceFiles.Sort(); int c = 0; for (auto &src: SourceFiles) { if (c++) m.Print(" \\\n\t\t\t"); m.Print("%s", src.Get()); } m.Print("\n" "\n" "SourceLst := $(patsubst %%.c,%%.o,$(patsubst %%.cpp,%%.o,$(Source)))\n" "Objects := $(addprefix $(BuildDir)/,$(SourceLst))\n" "Deps := $(patsubst %%.o,%%.d,$(Objects))\n" "\n"); // Write out the target stuff m.Print("# Target\n"); LHashTbl,bool> DepFiles; if (TargetType) { if (IsExecutableTarget) { m.Print("# Executable target\n" "$(Target) :"); LStringPipe Rules; IdeProject *Dep; uint64 Last = LCurrentTime(); int Count = 0; auto It = Deps.begin(); for (Dep=*It; Dep && !IsCancelled(); Dep=*(++It), Count++) { // Get dependency to create it's own makefile... Dep->CreateMakefile(Platform, false); // Build a rule to make the dependency if any of the source changes... auto DepBase = Dep->GetBasePath(); - auto Base = MakeFilePath; auto TargetFile = Dep->GetTargetFile(Platform); if (DepBase && Base && TargetFile) { - LString Rel = LMakeRelativePath(Base, DepBase); + LString Rel = LMakeRelativePath(MakeFilePath, DepBase); ToNativePath(Rel); // Add tag to target name auto Parts = TargetFile.SplitDelimit("."); if (Parts.Length() == 2) TargetFile.Printf("lib%s$(Tag).%s", Parts[0].Get(), Parts[1].Get()); LFile::Path Buf(Rel); Buf += "$(BuildDir)"; Buf += TargetFile; m.Print(" %s", Buf.GetFull().Get()); LArray AllDeps; Dep->GetAllDependencies(AllDeps, Platform); LAssert(AllDeps.Length() > 0); AllDeps.Sort(StrSort); Rules.Print("%s : ", Buf.GetFull().Get()); for (int i=0; iGetMakefile(Platform); // RenameMakefileForPlatform(Mk, Platform); if (Mk) { char *DepMakefile = strrchr(Mk, DIR_CHAR); if (DepMakefile) Rules.Print(" -f %s", DepMakefile + 1); } else { Mk = Dep->GetMakefile(Platform); OnError("%s:%i - No makefile for '%s'\n", _FL, Dep->GetFullPath().Get()); } Rules.Print("\n\n"); } uint64 Now = LCurrentTime(); if (Now - Last > 1000) { Last = Now; Log->Print("Building deps %i%%...\n", (int) (((int64)Count+1)*100/Deps.Length())); } } m.Print(" $(Objects)\n" " mkdir -p $(BuildDir)\n" " @echo Linking $(Target) [$(Build)]...\n" " $(CPP)%s%s %s%s -o \\\n" " $(Target) $(Objects) $(Libs)\n", ExtraLinkFlags, ExeFlags, ValidStr(LinkerFlags) ? "-Wl" : "", LinkerFlags.Get()); if (Platform == PlatformHaiku) { // Is there an application icon configured? const char *AppIcon = d->Settings.GetStr(ProjApplicationIcon, NULL, Platform); if (AppIcon) { m.Print(" addattr -f %s -t \"'VICN'\" \"BEOS:ICON\" $(Target)\n", AppIcon); } } LString PostBuildCmds = d->Settings.GetStr(ProjPostBuildCommands, NULL, Platform); if (ValidStr(PostBuildCmds)) { LString::Array a = PostBuildCmds.Split("\n"); for (unsigned i=0; iGetMakefile(Platform); if (mk) { - LAutoString my_base = Proj->GetBasePath(); LAutoString dep_base = d->GetBasePath(); d->CheckExists(dep_base); - auto rel_dir = LMakeRelativePath(my_base, dep_base); + auto rel_dir = LMakeRelativePath(MakeFilePath, dep_base); d->CheckExists(rel_dir); char *mk_leaf = strrchr(mk, DIR_CHAR); m.Print(" +make -C \"%s\" -f \"%s\" clean\n", ToUnixPath(rel_dir ? rel_dir.Get() : dep_base.Get()), ToUnixPath(mk_leaf ? mk_leaf + 1 : mk.Get())); } } m.Print("\n"); } // Shared library else if (!stricmp(TargetType, "DynamicLibrary")) { m.Print("TargetFile = lib$(Target)$(Tag).%s\n" "$(TargetFile) : $(Objects)\n" " mkdir -p $(BuildDir)\n" " @echo Linking $(TargetFile) [$(Build)]...\n" " $(CPP)$s -shared \\\n" " %s%s \\\n" " -o $(BuildDir)/$(TargetFile) \\\n" " $(Objects) \\\n" " $(Libs)\n", PlatformLibraryExt, ValidStr(ExtraLinkFlags) ? "-Wl" : "", ExtraLinkFlags, LinkerFlags.Get()); LString PostBuildCmds = d->Settings.GetStr(ProjPostBuildCommands, NULL, Platform); if (ValidStr(PostBuildCmds)) { LString::Array a = PostBuildCmds.Split("\n"); for (unsigned i=0; iSettings.GetStr(ProjPostBuildCommands, NULL, Platform); if (ValidStr(PostBuildCmds)) { LString::Array a = PostBuildCmds.Split("\n"); for (unsigned i=0; iCheckExists(p); if (p && !strchr(p, '`')) { if (!LIsRelativePath(p)) { - auto a = LMakeRelativePath(Base, p); + auto a = LMakeRelativePath(MakeFilePath, p); m.Print("\t%s \\\n", ToPlatformPath(a ? a.Get() : p.Get(), Platform).Get()); } else { m.Print("\t%s \\\n", ToPlatformPath(p, Platform).Get()); } } } m.Print("\t$(BuildDir)\n\n"); const char *OtherMakefileRules = d->Settings.GetStr(ProjMakefileRules, NULL, Platform); if (ValidStr(OtherMakefileRules)) { m.Print("\n%s\n", OtherMakefileRules); } } } else { m.Print("# No files require building.\n"); } Log->Print("...Done: '%s'\n", MakeFile.Get()); if (BuildAfterwards) { if (!Proj->GetApp()->PostEvent(M_START_BUILD)) printf("%s:%i - PostEvent(M_START_BUILD) failed.\n", _FL); } return HasError; } void OnAfterMain() { Proj->GetApp()->PostEvent(M_MAKEFILES_CREATED, (LMessage::Param)Proj); } }; ///////////////////////////////////////////////////////////////////////////////////// NodeSource::~NodeSource() { if (nView) { nView->nSrc = 0; } } NodeView::~NodeView() { if (nSrc) { nSrc->nView = 0; } } ////////////////////////////////////////////////////////////////////////////////// int NodeSort(LTreeItem *a, LTreeItem *b, NativeInt d) { ProjectNode *A = dynamic_cast(a); ProjectNode *B = dynamic_cast(b); if (A && B) { if ( (A->GetType() == NodeDir) ^ (B->GetType() == NodeDir) ) { return A->GetType() == NodeDir ? -1 : 1; } else { return Stricmp(a->GetText(0), b->GetText(0)); } } return 0; } //////////////////////////////////////////////////////////////////////////////////////////////////// bool ReadVsProjFile(LString File, LString &Ver, LString::Array &Configs) { const char *Ext = LGetExtension(File); if (!Ext || _stricmp(Ext, "vcxproj")) return false; LFile f; if (!f.Open(File, O_READ)) return false; LXmlTree Io; LXmlTag r; if (Io.Read(&r, &f) && r.IsTag("Project")) { Ver = r.GetAttr("ToolsVersion"); LXmlTag *ItemGroup = r.GetChildTag("ItemGroup"); if (ItemGroup) for (auto c: ItemGroup->Children) { if (c->IsTag("ProjectConfiguration")) { char *s = c->GetAttr("Include"); if (s) Configs.New() = s; } } } else return false; return true; } //////////////////////////////////////////////////////////////////////////////////////////////////// BuildThread::BuildThread(IdeProject *proj, char *makefile, bool clean, BuildConfig config, bool all, int wordsize) : LThread("BuildThread") { Proj = proj; Makefile = makefile; Clean = clean; Config = config; All = all; WordSize = wordsize; Arch = DefaultArch; Compiler = DefaultCompiler; AppHnd = Proj->GetApp()->AddDispatch(); LString Cmds = proj->d->Settings.GetStr(ProjPostBuildCommands, NULL); if (ValidStr(Cmds)) PostBuild = Cmds.SplitDelimit("\r\n"); auto Ext = LGetExtension(Makefile); if (Ext && !_stricmp(Ext, "py")) Compiler = PythonScript; else if (Ext && !_stricmp(Ext, "sln")) Compiler = VisualStudio; else if (Ext && !Stricmp(Ext, "xcodeproj")) Compiler = Xcode; else { auto Comp = Proj->GetSettings()->GetStr(ProjCompiler); if (Comp) { // Use the specified compiler... if (!stricmp(Comp, "VisualStudio")) Compiler = VisualStudio; else if (!stricmp(Comp, "MingW")) Compiler = MingW; else if (!stricmp(Comp, "gcc")) Compiler = Gcc; else if (!stricmp(Comp, "cross")) Compiler = CrossCompiler; else if (!stricmp(Comp, "IAR")) Compiler = IAR; else if (!stricmp(Comp, "Cygwin")) Compiler = Cygwin; else LAssert(!"Unknown compiler."); } } if (Compiler == DefaultCompiler) { // Use default compiler for platform... #ifdef WIN32 Compiler = VisualStudio; #else Compiler = Gcc; #endif } Run(); } BuildThread::~BuildThread() { if (SubProc) { bool b = SubProc->Interrupt(); LgiTrace("%s:%i - Sub process interrupt = %i.\n", _FL, b); } else LgiTrace("%s:%i - No sub process to interrupt.\n", _FL); uint64 Start = LCurrentTime(); bool Killed = false; while (!IsExited()) { LSleep(10); if (LCurrentTime() - Start > STOP_BUILD_TIMEOUT && SubProc) { if (Killed) { // Thread is stuck as well... ok kill that too!!! Argh - kill all the things!!!! Terminate(); LgiTrace("%s:%i - Thread killed.\n", _FL); Proj->GetApp()->PostEvent(M_BUILD_DONE); break; } else { // Kill the sub-process... bool b = SubProc->Kill(); Killed = true; LgiTrace("%s:%i - Sub process killed.\n", _FL, b); Start = LCurrentTime(); } } } } ssize_t BuildThread::Write(const void *Buffer, ssize_t Size, int Flags) { if (Proj->GetApp()) { Proj->GetApp()->PostEvent(M_APPEND_TEXT, (LMessage::Param)NewStr((char*)Buffer, Size), AppWnd::BuildTab); } return Size; } #pragma comment(lib, "version.lib") struct ProjInfo { LString Guid, Name, File; LHashTbl,ssize_t> Configs; }; LString BuildThread::FindExe() { LString::Array p = LGetPath(); if (Compiler == PythonScript) { #if defined(WINDOWS) uint32_t BestVer = 0; #else LString BestVer; #endif static LString Best; if (!Best) { const char *binName[] = {"python3", "python"}; for (int i=0; idwProductVersionMS > BestVer) { BestVer = v->dwProductVersionMS; Best = Path; } } } else if (!Best) { Best = Path; } free(Buf); #else LSubProcess p(Path, "--version"); LStringPipe o; if (p.Start()) p.Communicate(&o); auto Out = o.NewLStr(); auto Ver = Out.SplitDelimit().Last(); printf("Ver=%s\n", Ver.Get()); if (!BestVer || Stricmp(Ver.Get(), BestVer.Get()) > 0) { Best = Path; BestVer = Ver; } break; #endif } } } } return Best; } else if (Compiler == VisualStudio) { // Find the version we need: double fVer = 0.0; ProjInfo *First = NULL; LHashTbl, ProjInfo*> Projects; const char *Ext = LGetExtension(Makefile); if (Ext && !_stricmp(Ext, "sln")) { LFile f; if (f.Open(Makefile, O_READ)) { LString ProjKey = "Project("; LString StartSection = "GlobalSection("; LString EndSection = "EndGlobalSection"; LString Section; ssize_t Pos; LString::Array Ln = f.Read().SplitDelimit("\r\n"); for (size_t i = 0; i < Ln.Length(); i++) { LString s = Ln[i].Strip(); if ((Pos = s.Find(ProjKey)) >= 0) { LString::Array p = s.SplitDelimit("(),="); if (p.Length() > 5) { ProjInfo *i = new ProjInfo; i->Name = p[3].Strip(" \t\""); i->File = p[4].Strip(" \t\'\""); i->Guid = p[5].Strip(" \t\""); if (LIsRelativePath(i->File)) { char f[MAX_PATH_LEN]; LMakePath(f, sizeof(f), Makefile, ".."); LMakePath(f, sizeof(f), f, i->File); if (LFileExists(f)) i->File = f; /* else LAssert(0); */ } if (!First) First = i; Projects.Add(i->Guid, i); } } else if (s.Find(StartSection) >= 0) { auto p = s.SplitDelimit("() \t"); Section = p[1]; } else if (s.Find(EndSection) >= 0) { Section.Empty(); } else if (Section == "ProjectConfigurationPlatforms") { auto p = s.SplitDelimit(". \t"); auto i = Projects.Find(p[0]); if (i) { if (!i->Configs.Find(p[1])) { auto Idx = i->Configs.Length() + 1; i->Configs.Add(p[1], Idx); } } } else if (Section == "SolutionConfigurationPlatforms") { auto p = s.SplitDelimit(); auto config = p[0]; for (auto &it: Projects) { auto proj = it.value; if (!proj->Configs.Find(config)) proj->Configs.Add(config, proj->Configs.Length()+1); } } } } } else if (Ext && !_stricmp(Ext, "vcxproj")) { // ProjFile = Makefile; } else { if (Arch == DefaultArch) { if (sizeof(size_t) == 4) Arch = ArchX32; else Arch = ArchX64; } #ifdef _MSC_VER // Nmake file.. LString NmakePath; switch (_MSC_VER) { #ifdef _MSC_VER_VS2013 case _MSC_VER_VS2013: { if (Arch == ArchX32) NmakePath = "c:\\Program Files (x86)\\Microsoft Visual Studio 12.0\\VC\\bin\\nmake.exe"; else NmakePath = "c:\\Program Files (x86)\\Microsoft Visual Studio 12.0\\VC\\bin\\amd64\\nmake.exe"; break; } #endif #ifdef _MSC_VER_VS2015 case _MSC_VER_VS2015: { if (Arch == ArchX32) NmakePath = "c:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\bin\\nmake.exe"; else NmakePath = "c:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\bin\\amd64\\nmake.exe"; break; } #endif default: { LAssert(!"Impl me."); break; } } if (LFileExists(NmakePath)) { Compiler = Nmake; return NmakePath; } #endif } /* if (ProjFile && LFileExists(ProjFile)) { LString sVer; if (ReadVsProjFile(ProjFile, sVer, BuildConfigs)) { fVer = sVer.Float(); } } */ if (First) { for (auto i: First->Configs) { BuildConfigs[i.value - 1] = i.key; LFile f(First->File, O_READ); LXmlTree t; LXmlTag r; if (t.Read(&r, &f)) { if (r.IsTag("Project")) { auto ToolsVersion = r.GetAttr("ToolsVersion"); if (ToolsVersion) { fVer = atof(ToolsVersion); } } } } } if (fVer > 0.0) { for (int i=0; i LatestVer) { LatestVer = p[2].Float(); Latest = n; } } } if (Latest && LMakePath(p, sizeof(p), p, Latest) && LMakePath(p, sizeof(p), p, "common\\bin\\IarBuild.exe")) { if (LFileExists(p)) return p; } } else if (Compiler == Xcode) { return "/usr/bin/xcodebuild"; } else if (Compiler == Cygwin) { #ifdef WINDOWS LRegKey k(false, "HKEY_CURRENT_USER\\Software\\Cygwin\\Installations"); List n; k.GetValueNames(n); LString s; for (auto i:n) { s = k.GetStr(i); if (s.Find("\\??\\") == 0) s = s(4,-1); if (LDirExists(s)) { CygwinPath = s; break; } } n.DeleteArrays(); LFile::Path p(s, "bin\\make.exe"); if (p.Exists()) return p.GetFull(); #endif } else { if (Compiler == MingW) { // Have a look in the default spot first... const char *Def = "C:\\MinGW\\msys\\1.0\\bin\\make.exe"; if (LFileExists(Def)) { return Def; } } for (int i=0; iGetApp()->GetOptions()->GetValue(OPT_Jobs, Jobs) || Jobs.CastInt32() < 1) Jobs = 2; auto Pos = InitDir.RFind(DIR_STR); if (Pos) InitDir.Length(Pos); LString TmpArgs, Include, Lib, LibPath, Path; if (Compiler == VisualStudio) { // TmpArgs.Printf("\"%s\" /make \"All - Win32 Debug\"", Makefile.Get()); LString BuildConf = "All - Win32 Debug"; if (BuildConfigs.Length()) { auto Key = toString(Config); for (size_t i=0; i= 0) { if (!BuildConf || (c.Find("x64") >= 0 && BuildConf.Find("x64") < 0)) BuildConf = c; } } } TmpArgs.Printf("\"%s\" %s \"%s\"", Makefile.Get(), Clean ? "/Clean" : "/Build", BuildConf.Get()); } else if (Compiler == Nmake) { const char *DefInc[] = { "C:\\Program Files (x86)\\Microsoft Visual Studio 12.0\\VC\\INCLUDE", "C:\\Program Files (x86)\\Microsoft Visual Studio 12.0\\VC\\ATLMFC\\INCLUDE", "C:\\Program Files (x86)\\Windows Kits\\8.1\\include\\shared", "C:\\Program Files (x86)\\Windows Kits\\8.1\\include\\um", "C:\\Program Files (x86)\\Windows Kits\\8.1\\include\\winrt" }; LString f; #define ADD_PATHS(out, in) \ for (unsigned i=0; iGetChildTag("name") : NULL; if (c) Conf = c->GetContent(); } } TmpArgs.Printf("\"%s\" %s %s -log warnings", Makefile.Get(), Clean ? "-clean" : "-make", Conf.Get()); } else if (Compiler == Xcode) { LString::Array Configs; Configs.SetFixedLength(false); LString a; a.Printf("-list -project \"%s\"", Makefile.Get()); LSubProcess Ls(Exe, a); if (Ls.Start()) { LStringPipe o; Ls.Communicate(&o); auto key = "Build Configurations:"; auto lines = o.NewLStr().SplitDelimit("\n"); int inKey = -1; for (auto l: lines) { int ws = 0; for (int i=0; i=0) inKey = ws; else if (inKey>=0) { if (ws>inKey) { Configs.New() = l.Strip(); } else { inKey = -1; } } // printf("%s\n", l.Get()); } } auto Config = Configs.Length() > 0 ? Configs[0] : LString("Debug"); TmpArgs.Printf("-project \"%s\" -configuration %s", Makefile.Get(), Config.Get()); } else { if (Compiler == Cygwin) { LFile::Path p(CygwinPath, "bin"); Path = p.GetFull(); } if (Compiler == MingW) { LString a; char *Dir = strrchr(MakePath, DIR_CHAR); #if 1 TmpArgs.Printf("/C \"%s\"", Exe.Get()); /* As of MSYS v1.0.18 the support for multiple jobs causes make to hang: http://sourceforge.net/p/mingw/bugs/1950/ http://mingw-users.1079350.n2.nabble.com/MSYS-make-freezes-td7579038.html Apparently it'll be "fixed" in v1.0.19. We'll see. >:-( if (Jobs.CastInt32() > 1) a.Print(" -j %i", Jobs.CastInt32()); */ a.Printf(" -f \"%s\"", Dir ? Dir + 1 : MakePath.Get()); TmpArgs += a; #else TmpArgs.Printf("/C set"); #endif Exe = "C:\\Windows\\System32\\cmd.exe"; } else { if (Jobs.CastInt32()) TmpArgs.Printf("-j %i -f \"%s\"", Jobs.CastInt32(), MakePath.Get()); else TmpArgs.Printf("-f \"%s\"", MakePath.Get()); } if (Clean) { if (All) TmpArgs += " cleanall"; else TmpArgs += " clean"; } if (Config == BuildRelease) TmpArgs += " Build=Release"; } PostThreadEvent(AppHnd, M_SELECT_TAB, AppWnd::BuildTab); LString Msg; Msg.Printf("Making: %s\n", MakePath.Get()); Proj->GetApp()->PostEvent(M_APPEND_TEXT, (LMessage::Param)NewStr(Msg), AppWnd::BuildTab); LgiTrace("%s %s\n", Exe.Get(), TmpArgs.Get()); if (SubProc.Reset(new LSubProcess(Exe, TmpArgs))) { SubProc->SetNewGroup(false); SubProc->SetInitFolder(InitDir); if (Include) SubProc->SetEnvironment("INCLUDE", Include); if (Lib) SubProc->SetEnvironment("LIB", Lib); if (LibPath) SubProc->SetEnvironment("LIBPATHS", LibPath); if (Path) { LString Cur = getenv("PATH"); LString New = Cur + LGI_PATH_SEPARATOR + Path; SubProc->SetEnvironment("PATH", New); } // SubProc->SetEnvironment("DLL", "1"); if (Compiler == MingW) SubProc->SetEnvironment("PATH", "c:\\MingW\\bin;C:\\MinGW\\msys\\1.0\\bin;%PATH%"); if ((Status = SubProc->Start(true, false))) { // Read all the output char Buf[256]; ssize_t rd; while ( (rd = SubProc->Read(Buf, sizeof(Buf))) > 0 ) { Write(Buf, rd); } uint32_t ex = SubProc->Wait(); Print("Make exited with %i (0x%x)\n", ex, ex); if (Compiler == IAR && ex == 0 && PostBuild.Length()) { for (auto Cmd : PostBuild) { auto p = Cmd.Split(" ", 1); if (p[0].Equals("cd")) { if (p.Length() > 1) FileDev->SetCurrentFolder(p[1]); else LAssert(!"No folder for cd?"); } else { LSubProcess PostCmd(p[0], p.Length() > 1 ? p[1] : LString()); if (PostCmd.Start(true, false)) { char Buf[256]; ssize_t rd; while ( (rd = PostCmd.Read(Buf, sizeof(Buf))) > 0 ) { Write(Buf, rd); } } } } } } else { // Create a nice error message. LString ErrStr = LErrorCodeToString(SubProc->GetErrorCode()); if (ErrStr) { char *e = ErrStr.Get() + ErrStr.Length(); while (e > ErrStr && strchr(" \t\r\n.", e[-1])) *(--e) = 0; } sprintf_s(ErrBuf, sizeof(ErrBuf), "Running make failed with %i (%s)\n", SubProc->GetErrorCode(), ErrStr.Get()); Err = ErrBuf; } } } else { Err = "Couldn't find program to build makefile."; LgiTrace("%s,%i - %s.\n", _FL, Err); } AppWnd *w = Proj->GetApp(); if (w) { w->PostEvent(M_BUILD_DONE); if (Err) Proj->GetApp()->PostEvent(M_BUILD_ERR, 0, (LMessage::Param)NewStr(Err)); } else LAssert(0); return 0; } ////////////////////////////////////////////////////////////////////////////////// IdeProject::IdeProject(AppWnd *App) : IdeCommon(NULL) { Project = this; d = new IdeProjectPrivate(App, this); Tag = NewStr("Project"); } IdeProject::~IdeProject() { d->App->OnProjectDestroy(this); LXmlTag::Empty(true); DeleteObj(d); } bool IdeProject::OnNode(const char *Path, ProjectNode *Node, bool Add) { if (!Path || !Node) { LAssert(0); return false; } char Full[MAX_PATH_LEN]; if (LIsRelativePath(Path)) { LAutoString Base = GetBasePath(); if (LMakePath(Full, sizeof(Full), Base, Path)) { Path = Full; } } bool Status = false; if (Add) Status = d->Nodes.Add(Path, Node); else Status = d->Nodes.Delete(Path); LString p = Path; if (Status && CheckExists(p)) d->App->OnNode(p, Node, Add ? FindSymbolSystem::FileAdd : FindSymbolSystem::FileRemove); return Status; } void IdeProject::ShowFileProperties(const char *File) { ProjectNode *Node = NULL; // char *fp = FindFullPath(File, &Node); if (Node) { Node->OnProperties(); } } const char *IdeProject::GetFileComment() { return d->Settings.GetStr(ProjCommentFile); } const char *IdeProject::GetFunctionComment() { return d->Settings.GetStr(ProjCommentFunction); } IdeProject *IdeProject::GetParentProject() { return d->ParentProject; } void IdeProject::SetParentProject(IdeProject *p) { d->ParentProject = p; } bool IdeProject::GetChildProjects(List &c) { CollectAllSubProjects(c); return c.Length() > 0; } bool IdeProject::RelativePath(LString &Out, const char *In, bool Debug) { if (!In) return false; auto Base = GetBasePath(); if (!Base) return false; CheckExists(Base); if (Debug) LgiTrace("XmlBase='%s'\n In='%s'\n", Base.Get(), In); LToken b(Base, DIR_STR); LToken i(In, DIR_STR); char out[MAX_PATH_LEN] = ""; if (Debug) LgiTrace("Len %i-%i\n", b.Length(), i.Length()); auto ILen = i.Length() + (LDirExists(In) ? 0 : 1); auto Max = MIN(b.Length(), ILen); int Common = 0; for (; Common < Max; Common++) { #ifdef WIN32 #define StrCompare stricmp #else #define StrCompare strcmp #endif if (Debug) LgiTrace("Cmd '%s'-'%s'\n", b[Common], i[Common]); if (StrCompare(b[Common], i[Common]) != 0) { break; } } if (Debug) LgiTrace("Common=%i\n", Common); if (Common > 0) { if (Common < b.Length()) { out[0] = 0; auto Back = b.Length() - Common; if (Debug) LgiTrace("Back=%i\n", (int)Back); for (int n=0; nSettings.GetStr(ProjExe); LString Exe; if (!PExe) { // Use the default exe name? Exe = GetExecutable(GetCurrentPlatform()); if (Exe) { printf("Exe='%s'\n", Exe.Get()); PExe = Exe; } } if (!PExe) return false; if (LIsRelativePath(PExe)) { auto Base = GetBasePath(); if (Base) LMakePath(Path, Len, Base, PExe); else return false; } else { strcpy_s(Path, Len, PExe); } return true; } LString IdeProject::GetMakefile(IdePlatform Platform) { const char *PMakefile = d->Settings.GetStr(ProjMakefile, NULL, Platform); if (!PMakefile) return LString(); LString Path; if (LIsRelativePath(PMakefile)) { auto Base = GetBasePath(); if (Base) { char p[MAX_PATH_LEN]; LMakePath(p, sizeof(p), Base, PMakefile); Path = p; } } else { Path = PMakefile; } return Path; } void IdeProject::Clean(bool All, BuildConfig Config) { if (!d->Thread && d->Settings.GetStr(ProjMakefile)) { auto m = GetMakefile(PlatformCurrent); if (m) { CheckExists(m); d->Thread.Reset(new BuildThread(this, m, true, Config, All, sizeof(ssize_t)*8)); } } } char *QuoteStr(char *s) { LStringPipe p(256); while (s && *s) { if (*s == ' ') { p.Push("\\ ", 2); } else p.Push(s, 1); s++; } return p.NewStr(); } class ExecuteThread : public LThread, public LStream, public LCancel { IdeProject *Proj; LString Exe, Args, Path; int Len; ExeAction Act; int AppHnd; public: ExecuteThread(IdeProject *proj, const char *exe, const char *args, char *path, ExeAction act) : LThread("ExecuteThread") { Len = 32 << 10; Proj = proj; Act = act; Exe = exe; Args = args; Path = path; DeleteOnExit = true; AppHnd = proj->GetApp()->AddDispatch(); Run(); } ~ExecuteThread() { Cancel(); while (!IsExited()) LSleep(1); } int Main() override { PostThreadEvent(AppHnd, M_SELECT_TAB, AppWnd::OutputTab); PostThreadEvent(AppHnd, M_APPEND_TEXT, 0, AppWnd::OutputTab); if (Exe) { if (Act == ExeDebug) { LSubProcess sub("kdbg", Exe); if (Path) sub.SetInitFolder(Path); if (sub.Start()) sub.Communicate(this, NULL, this); } else if (Act == ExeValgrind) { #ifdef LINUX LString ExePath = Proj->GetExecutable(GetCurrentPlatform()); if (ExePath) { char Path[MAX_PATH_LEN]; char *ExeLeaf = LGetLeaf(Exe); strcpy_s(Path, sizeof(Path), ExeLeaf ? ExeLeaf : Exe.Get()); LTrimDir(Path); char *Term = 0; char *WorkDir = 0; char *Execute = 0; switch (LGetWindowManager()) { case WM_Kde: Term = "konsole"; WorkDir = "--workdir "; Execute = "-e"; break; case WM_Gnome: Term = "gnome-terminal"; WorkDir = "--working-directory="; Execute = "-x"; break; } if (Term && WorkDir && Execute) { char *e = QuoteStr(ExePath); char *p = QuoteStr(Path); char *a = Proj->GetExeArgs() ? Proj->GetExeArgs() : (char*)""; char Args[512]; sprintf(Args, "%s%s " "--noclose " "%s valgrind --tool=memcheck --num-callers=20 %s %s", WorkDir, p, Execute, e, a); LExecute(Term, Args); } } #endif } else { LSubProcess sub(Exe, Args); if (Path) sub.SetInitFolder(Path); if (sub.Start()) sub.Communicate(this, NULL, this); } } return 0; } ssize_t Write(const void *Buffer, ssize_t Size, int Flags = 0) override { if (Len <= 0) return 0; PostThreadEvent(AppHnd, M_APPEND_TEXT, (LMessage::Param)NewStr((char*)Buffer, Size), AppWnd::OutputTab); Len -= Size; return Size; } }; LDebugContext *IdeProject::Execute(ExeAction Act, LString *ErrMsg) { auto Base = GetBasePath(); if (!Base) { if (ErrMsg) *ErrMsg = "No base path for project."; return NULL; } char e[MAX_PATH_LEN]; if (!GetExePath(e, sizeof(e))) { if (ErrMsg) *ErrMsg = "GetExePath failed."; return NULL; } if (!LFileExists(e)) { if (ErrMsg) ErrMsg->Printf("Executable '%s' doesn't exist.\n", e); return NULL; } const char *Args = d->Settings.GetStr(ProjArgs); const char *Env = d->Settings.GetStr(ProjEnv); LString InitDir = d->Settings.GetStr(ProjInitDir); int RunAsAdmin = d->Settings.GetInt(ProjDebugAdmin); #ifndef HAIKU if (Act == ExeDebug) { if (InitDir && LIsRelativePath(InitDir)) { LFile::Path p(Base); p += InitDir; InitDir = p.GetFull(); } return new LDebugContext(d->App, this, e, Args, RunAsAdmin != 0, Env, InitDir); } #endif new ExecuteThread( this, e, Args, Base, #if defined(HAIKU) || defined(WINDOWS) ExeRun // No gdb or valgrind #else Act #endif ); return NULL; } bool IdeProject::IsMakefileAScript() { auto m = GetMakefile(PlatformCurrent); if (m) { auto Ext = LGetExtension(m); if (!Stricmp(Ext, "py")) { // Not a makefile but a build script... can't update. return true; } } return false; } bool IdeProject::IsMakefileUpToDate() { if (IsMakefileAScript()) return true; // We can't know if it's up to date... List Proj; GetChildProjects(Proj); Proj.Insert(this); for (auto p: Proj) { // Is the project file modified after the makefile? auto Proj = p->GetFullPath(); uint64 ProjModTime = 0, MakeModTime = 0; LDirectory dir; if (dir.First(Proj)) { ProjModTime = dir.GetLastWriteTime(); dir.Close(); } auto m = p->GetMakefile(PlatformCurrent); if (!m) { d->App->GetBuildLog()->Print("Error: no makefile? (%s:%i)\n", _FL); break; } auto Ext = LGetExtension(m); if (!Stricmp(Ext, "py")) { // Not a makefile but a build script... can't update. return true; } if (dir.First(m)) { MakeModTime = dir.GetLastWriteTime(); dir.Close(); } // printf("Proj=%s - Timestamps " LGI_PrintfInt64 " - " LGI_PrintfInt64 "\n", Proj.Get(), ProjModTime, MakeModTime); if (ProjModTime != 0 && MakeModTime != 0 && ProjModTime > MakeModTime) { // Need to rebuild the makefile... return false; } } return true; } bool IdeProject::FindDuplicateSymbols() { LStream *Log = d->App->GetBuildLog(); Log->Print("FindDuplicateSymbols starting...\n"); List Proj; CollectAllSubProjects(Proj); Proj.Insert(this); int Lines = 0; LHashTbl,int64> Map(200000); int Found = 0; for (auto p: Proj) { LString s = p->GetExecutable(GetCurrentPlatform()); if (s) { LString Args; Args.Printf("--print-size --defined-only -C %s", s.Get()); LSubProcess Nm("nm", Args); if (Nm.Start(true, false)) { char Buf[256]; LStringPipe q; for (ssize_t Rd = 0; (Rd = Nm.Read(Buf, sizeof(Buf))); ) q.Write(Buf, Rd); LString::Array a = q.NewLStr().SplitDelimit("\r\n"); LHashTbl,bool> Local(200000); for (auto &Ln: a) { LString::Array p = Ln.SplitDelimit(" \t", 3); if (!Local.Find(p.Last())) { Local.Add(p.Last(), true); // const char *Sz = p[1]; int64 Ours = p[1].Int(16); int64 Theirs = Map.Find(p.Last()); if (Theirs >= 0) { if (Ours != Theirs) { if (Found++ < 100) Log->Print(" %s (" LPrintfInt64 " -> " LPrintfInt64 ")\n", p.Last().Get(), Ours, Theirs); } } else if (Ours >= 0) { Map.Add(p.Last(), Ours); } else { printf("Bad line: %s\n", Ln.Get()); } } Lines++; } } } else printf("%s:%i - GetExecutable failed.\n", _FL); } /* char *Sym; for (int Count = Map.First(&Sym); Count; Count = Map.Next(&Sym)) { if (Count > 1) Log->Print(" %i: %s\n", Count, Sym); } */ Log->Print("FindDuplicateSymbols finished (%i lines)\n", Lines); return false; } bool IdeProject::FixMissingFiles() { FixMissingFilesDlg(this); return true; } void IdeProject::Build(bool All, BuildConfig Config) { if (d->Thread) { d->App->GetBuildLog()->Print("Error: Already building (%s:%i)\n", _FL); return; } auto m = GetMakefile(PlatformCurrent); CheckExists(m); if (!m) { d->App->GetBuildLog()->Print("Error: no makefile? (%s:%i)\n", _FL); return; } if (GetApp()) GetApp()->PostEvent(M_APPEND_TEXT, 0, 0); SetClean([&](bool ok) { if (!ok) return; if (!IsMakefileUpToDate()) CreateMakefile(GetCurrentPlatform(), true); else // Start the build thread... d->Thread.Reset ( new BuildThread ( this, m, false, Config, All, sizeof(size_t)*8 ) ); }); } void IdeProject::StopBuild() { d->Thread.Reset(); } bool IdeProject::Serialize(bool Write) { return true; } AppWnd *IdeProject::GetApp() { return d->App; } const char *IdeProject::GetIncludePaths() { return d->Settings.GetStr(ProjIncludePaths); } const char *IdeProject::GetPreDefinedValues() { return d->Settings.GetStr(ProjDefines); } const char *IdeProject::GetExeArgs() { return d->Settings.GetStr(ProjArgs); } LString IdeProject::GetExecutable(IdePlatform Platform) { LString Bin = d->Settings.GetStr(ProjExe, NULL, Platform); auto TargetType = d->Settings.GetStr(ProjTargetType, NULL, Platform); auto Base = GetBasePath(); if (Bin) { if (LIsRelativePath(Bin) && Base) { char p[MAX_PATH_LEN]; if (LMakePath(p, sizeof(p), Base, Bin)) Bin = p; } return Bin; } // Create binary name from target: auto Target = GetTargetName(Platform); if (Target) { bool IsLibrary = Stristr(TargetType, "library"); int BuildMode = d->App->GetBuildMode(); const char *Name = BuildMode ? "Release" : "Debug"; const char *Postfix = BuildMode ? "" : "d"; switch (Platform) { case PlatformWin: { if (IsLibrary) Bin.Printf("%s%s.dll", Target.Get(), Postfix); else Bin = Target; break; } case PlatformMac: { if (IsLibrary) Bin.Printf("lib%s%s.dylib", Target.Get(), Postfix); else Bin = Target; break; } case PlatformLinux: case PlatformHaiku: { if (IsLibrary) Bin.Printf("lib%s%s.so", Target.Get(), Postfix); else Bin = Target; break; } default: { LAssert(0); printf("%s:%i - Unknown platform.\n", _FL); return LString(); } } // Find the actual file... if (!Base) { printf("%s:%i - GetBasePath failed.\n", _FL); return LString(); } char Path[MAX_PATH_LEN]; LMakePath(Path, sizeof(Path), Base, Name); LMakePath(Path, sizeof(Path), Path, Bin); if (LFileExists(Path)) Bin = Path; else printf("%s:%i - '%s' doesn't exist.\n", _FL, Path); return Bin; } return LString(); } const char *IdeProject::GetFileName() { return d->FileName; } int IdeProject::GetPlatforms() { return PLATFORM_ALL; } LAutoString IdeProject::GetFullPath() { LAutoString Status; if (!d->FileName) { // LAssert(!"No path."); return Status; } LArray sections; IdeProject *proj = this; while ( proj && proj->GetFileName() && LIsRelativePath(proj->GetFileName())) { sections.AddAt(0, proj->GetFileName()); proj = proj->GetParentProject(); } if (!proj) { // LAssert(!"All projects have a relative path?"); return Status; // No absolute path in the parent projects? } char p[MAX_PATH_LEN]; strcpy_s(p, sizeof(p), proj->GetFileName()); // Copy the base path if (sections.Length() > 0) LTrimDir(p); // Trim off the filename for (int i=0; iFileName.Empty(); d->UserFile.Empty(); d->App->GetTree()->Insert(this); ProjectNode *f = new ProjectNode(this); if (f) { f->SetName("Source"); f->SetType(NodeDir); InsertTag(f); } f = new ProjectNode(this); if (f) { f->SetName("Headers"); f->SetType(NodeDir); InsertTag(f); } d->Settings.Set(ProjEditorTabSize, 4); d->Settings.Set(ProjEditorIndentSize, 4); d->Settings.Set(ProjEditorUseHardTabs, true); d->Dirty = true; Expanded(true); } ProjectStatus IdeProject::OpenFile(const char *FileName) { auto Log = d->App->GetBuildLog(); LProfile Prof("IdeProject::OpenFile"); Prof.HideResultsIfBelow(1000); Empty(); Prof.Add("Init"); d->UserNodeFlags.Empty(); if (LIsRelativePath(FileName)) { char p[MAX_PATH_LEN]; getcwd(p, sizeof(p)); LMakePath(p, sizeof(p), p, FileName); d->FileName = p; } else { d->FileName = FileName; } d->UserFile = d->FileName + "." + LCurrentUserName(); if (!d->FileName) { Log->Print("%s:%i - No filename.\n", _FL); return OpenError; } Prof.Add("FileOpen"); LFile f; LString FullPath = d->FileName.Get(); if (!CheckExists(FullPath) || !f.Open(FullPath, O_READWRITE)) { Log->Print("%s:%i - Error: Can't open '%s'.\n", _FL, FullPath.Get()); return OpenError; } Prof.Add("Xml"); LXmlTree x; LXmlTag r; if (!x.Read(&r, &f)) { Log->Print("%s:%i - Error: Can't read XML: %s\n", _FL, x.GetErrorMsg()); return OpenError; } Prof.Add("Progress Setup"); #if DEBUG_OPEN_PROGRESS int64 Nodes = r.CountTags(); LProgressDlg Prog(d->App, 1000); Prog.SetDescription("Loading project..."); Prog.SetLimits(0, Nodes); Prog.SetYieldTime(1000); Prog.SetAlwaysOnTop(true); #endif Prof.Add("UserFile"); if (LFileExists(d->UserFile)) { LFile Uf; if (Uf.Open(d->UserFile, O_READ)) { LString::Array Ln = Uf.Read().SplitDelimit("\n"); for (unsigned i=0; iUserNodeFlags.Add((int)p[0].Int(), (int)p[1].Int(16)); } } } if (!r.IsTag("Project")) { Log->Print("%s:%i - No 'Project' tag.\n", _FL); return OpenError; } Prof.Add("OnOpen"); bool Ok = OnOpen( #if DEBUG_OPEN_PROGRESS &Prog, #else NULL, #endif &r); #if DEBUG_OPEN_PROGRESS if (Prog.IsCancelled()) return OpenCancel; else #endif if (!Ok) return OpenError; Prof.Add("Insert"); d->App->GetTree()->Insert(this); Expanded(true); Prof.Add("Serialize"); d->Settings.Serialize(&r, false /* read */); return OpenOk; } bool IdeProject::SaveFile() { auto Full = GetFullPath(); if (ValidStr(Full) && d->Dirty) { LFile f; if (f.Open(Full, O_WRITE)) { f.SetSize(0); LXmlTree x; d->Settings.Serialize(this, true /* write */); if (x.Write(this, &f)) d->Dirty = false; else LgiTrace("%s:%i - Failed to write XML.\n", _FL); } else LgiTrace("%s:%i - Couldn't open '%s' for writing.\n", _FL, Full.Get()); } if (d->UserFileDirty) { LFile f; LAssert(d->UserFile.Get()); if (f.Open(d->UserFile, O_WRITE)) { f.SetSize(0); // Save user file details.. // int Id; // for (int Flags = d->UserNodeFlags.First(&Id); Flags >= 0; Flags = d->UserNodeFlags.Next(&Id)) for (auto i : d->UserNodeFlags) { f.Print("%i,%x\n", i.key, i.value); } d->UserFileDirty = false; } } printf("\tIdeProject::SaveFile %i %i\n", d->Dirty, d->UserFileDirty); return !d->Dirty && !d->UserFileDirty; } void IdeProject::SetDirty() { d->Dirty = true; d->App->OnProjectChange(); } void IdeProject::SetUserFileDirty() { d->UserFileDirty = true; } bool IdeProject::GetExpanded(int Id) { int f = d->UserNodeFlags.Find(Id); return f >= 0 ? f : false; } void IdeProject::SetExpanded(int Id, bool Exp) { if (d->UserNodeFlags.Find(Id) != (int)Exp) { d->UserNodeFlags.Add(Id, Exp); SetUserFileDirty(); } } int IdeProject::AllocateId() { return d->NextNodeId++; } template bool CheckExists(LAutoString Base, T &p, Fn Setter, bool Debug) { LFile::Path Full; bool WasRel = LIsRelativePath(p); if (WasRel) { Full = Base.Get(); Full += p.Get(); } else Full = p.Get(); bool Ret = Full.Exists(); if (!Ret) { // Is the case wrong? for (int i=1; i%s\n", i, Leaf.Get(), dir.GetName()); Leaf = dir.GetName(); Matched = true; break; } } if (!Matched) break; } } if ((Ret = Full.Exists())) { LString Old = p.Get(); if (WasRel) { auto r = LMakeRelativePath(Base, Full); Setter(p, r); } else { Setter(p, Full.GetFull()); } if (Debug) printf("%s -> %s\n", Old.Get(), p.Get()); } } if (Debug) printf("CheckExists '%s' = %i\n", Full.GetFull().Get(), Ret); return Ret; } bool IdeProject::CheckExists(LString &p, bool Debug) { return ::CheckExists(GetBasePath(), p, [](LString &o, const char *i) { o = i; }, Debug); } bool IdeProject::CheckExists(LAutoString &p, bool Debug) { return ::CheckExists(GetBasePath(), p, [](LAutoString &o, const char *i) { o.Reset(NewStr(i)); }, Debug); } bool IdeProject::GetClean() { for (auto i: *this) { ProjectNode *p = dynamic_cast(i); if (p && !p->GetClean()) return false; } return !d->Dirty && !d->UserFileDirty; } void IdeProject::SetClean(std::function OnDone) { auto CleanNodes = [this, OnDone]() { printf("IdeProject.SetClean.CleanNodes\n"); for (auto i: *this) { ProjectNode *p = dynamic_cast(i); if (!p) break; p->SetClean(); } if (OnDone) OnDone(true); }; printf("IdeProject.SetClean dirty=%i,%i validfile=%i\n", d->Dirty, d->UserFileDirty, ValidStr(d->FileName)); if (d->Dirty || d->UserFileDirty) { if (ValidStr(d->FileName)) SaveFile(); else { auto s = new LFileSelect; s->Parent(Tree); s->Name("Project.xml"); s->Save([this, OnDone, CleanNodes](auto s, auto ok) { printf("IdeProject.SetClean.FileSelect ok=%i\n", ok); if (ok) { d->FileName = s->Name(); d->UserFile = d->FileName + "." + LCurrentUserName(); d->App->OnFile(d->FileName, true); Update(); CleanNodes(); } else { if (OnDone) OnDone(false); } delete s; }); return; } } CleanNodes(); } const char *IdeProject::GetText(int Col) { if (d->FileName) { char *s = strrchr(d->FileName, DIR_CHAR); return s ? s + 1 : d->FileName.Get(); } return Untitled; } int IdeProject::GetImage(int Flags) { return 0; } void IdeProject::Empty() { LXmlTag *t; while (Children.Length() > 0 && (t = Children[0])) { ProjectNode *n = dynamic_cast(t); if (n) { n->Remove(); } DeleteObj(t); } } LXmlTag *IdeProject::Create(char *Tag) { if (!stricmp(Tag, TagSettings)) return NULL; return new ProjectNode(this); } void IdeProject::OnMouseClick(LMouse &m) { if (m.IsContextMenu()) { LSubMenu Sub; Sub.AppendItem("New Folder", IDM_NEW_FOLDER); Sub.AppendItem("New Web Folder", IDM_WEB_FOLDER); Sub.AppendSeparator(); Sub.AppendItem("Build", IDM_BUILD); Sub.AppendItem("Clean Project", IDM_CLEAN_PROJECT); Sub.AppendItem("Clean All", IDM_CLEAN_ALL); Sub.AppendItem("Rebuild Project", IDM_REBUILD_PROJECT); Sub.AppendItem("Rebuild All", IDM_REBUILD_ALL); Sub.AppendSeparator(); Sub.AppendItem("Sort Children", IDM_SORT_CHILDREN); Sub.AppendSeparator(); Sub.AppendItem("Settings", IDM_SETTINGS, true); Sub.AppendItem("Insert Dependency", IDM_INSERT_DEP); m.ToScreen(); auto c = _ScrollPos(); m.x -= c.x; m.y -= c.y; switch (Sub.Float(Tree, m.x, m.y)) { case IDM_NEW_FOLDER: { auto Name = new LInput(Tree, "", "Name:", AppName); Name->DoModal([this, Name](auto d, auto code) { if (code) GetSubFolder(this, Name->GetStr(), true); delete d; }); break; } case IDM_WEB_FOLDER: { WebFldDlg *Dlg = new WebFldDlg(Tree, 0, 0, 0); Dlg->DoModal([&](auto d, auto code) { if (Dlg->Ftp && Dlg->Www) { IdeCommon *f = GetSubFolder(this, Dlg->Name, true); if (f) { f->SetAttr(OPT_Ftp, Dlg->Ftp); f->SetAttr(OPT_Www, Dlg->Www); } } delete Dlg; }); break; } case IDM_BUILD: { StopBuild(); Build(true, d->App->GetBuildMode()); break; } case IDM_CLEAN_PROJECT: { Clean(false, d->App->GetBuildMode()); break; } case IDM_CLEAN_ALL: { Clean(true, d->App->GetBuildMode()); break; } case IDM_REBUILD_PROJECT: { StopBuild(); Clean(false, d->App->GetBuildMode()); Build(false, d->App->GetBuildMode()); break; } case IDM_REBUILD_ALL: { StopBuild(); Clean(true, d->App->GetBuildMode()); Build(true, d->App->GetBuildMode()); break; } case IDM_SORT_CHILDREN: { SortChildren(); Project->SetDirty(); break; } case IDM_SETTINGS: { d->Settings.Edit(Tree, [&]() { SetDirty(); }); break; } case IDM_INSERT_DEP: { LFileSelect *s = new LFileSelect; s->Parent(Tree); s->Type("Project", "*.xml"); s->Open([&](auto s, bool ok) { if (!ok) return; ProjectNode *New = new ProjectNode(this); if (New) { New->SetFileName(s->Name()); New->SetType(NodeDependancy); InsertTag(New); SetDirty(); } delete s; }); break; } } } } char *IdeProject::FindFullPath(const char *File, ProjectNode **Node) { char *Full = 0; for (auto i:*this) { ProjectNode *c = dynamic_cast(i); if (!c) break; ProjectNode *n = c->FindFile(File, &Full); if (n) { if (Node) *Node = n; break; } } return Full; } bool IdeProject::HasNode(ProjectNode *Node) { for (auto i:*this) { ProjectNode *c = dynamic_cast(i); if (!c) break; if (c->HasNode(Node)) return true; } return false; } bool IdeProject::GetAllNodes(LArray &Nodes) { for (auto i:*this) { ProjectNode *c = dynamic_cast(i); if (!c) break; c->AddNodes(Nodes); } return true; } bool IdeProject::InProject(bool FuzzyMatch, const char *Path, bool Open, IdeDoc **Doc) { if (!Path) return false; // Search complete path first... ProjectNode *n = d->Nodes.Find(Path); if (!n && FuzzyMatch) { // No match, do partial matching. const char *Leaf = LGetLeaf(Path); auto PathLen = strlen(Path); auto LeafLen = strlen(Leaf); uint32_t MatchingScore = 0; // Traverse all nodes and try and find the best fit. // const char *p; // for (ProjectNode *Cur = d->Nodes.First(&p); Cur; Cur = d->Nodes.Next(&p)) for (auto Cur : d->Nodes) { int CurPlatform = Cur.value->GetPlatforms(); uint32_t Score = 0; if (stristr(Cur.key, Path)) { Score += PathLen; } else if (stristr(Cur.key, Leaf)) { Score += LeafLen; } const char *pLeaf = LGetLeaf(Cur.key); if (pLeaf && !stricmp(pLeaf, Leaf)) { Score |= 0x40000000; } if (Score && (CurPlatform & PLATFORM_CURRENT) != 0) { Score |= 0x80000000; } if (Score > MatchingScore) { MatchingScore = Score; n = Cur.value; } } } if (n && Doc) { *Doc = n->Open(); } return n != 0; } char *GetQuotedStr(char *Str) { char *s = strchr(Str, '\"'); if (s) { s++; char *e = strchr(s, '\"'); if (e) { return NewStr(s, e - s); } } return 0; } void IdeProject::ImportDsp(const char *File) { if (File && LFileExists(File)) { char Base[256]; strcpy(Base, File); LTrimDir(Base); char *Dsp = LReadTextFile(File); if (Dsp) { LToken Lines(Dsp, "\r\n"); IdeCommon *Current = this; bool IsSource = false; for (int i=0; iGetSubFolder(this, Folder, true); if (Sub) { Current = Sub; } DeleteArray(Folder); } } else if (strnicmp(L, "# End Group", 11) == 0) { IdeCommon *Parent = dynamic_cast(Current->GetParent()); if (Parent) { Current = Parent; } } else if (strnicmp(L, "# Begin Source", 14) == 0) { IsSource = true; } else if (strnicmp(L, "# End Source", 12) == 0) { IsSource = false; } else if (Current && IsSource && strncmp(L, "SOURCE=", 7) == 0) { ProjectNode *New = new ProjectNode(this); if (New) { char *Src = 0; if (strchr(L, '\"')) { Src = GetQuotedStr(L); } else { Src = NewStr(L + 7); } if (Src) { // Make absolute path char Abs[256]; LMakePath(Abs, sizeof(Abs), Base, ToUnixPath(Src)); // Make relitive path New->SetFileName(Src); DeleteArray(Src); } Current->InsertTag(New); SetDirty(); } } } DeleteArray(Dsp); } } } IdeProjectSettings *IdeProject::GetSettings() { return &d->Settings; } bool IdeProject::BuildIncludePaths(LArray &Paths, bool Recurse, bool IncludeSystem, IdePlatform Platform) { List Projects; if (Recurse) GetChildProjects(Projects); Projects.Insert(this, 0); LHashTbl, bool> Map; for (auto p: Projects) { LString ProjInclude = d->Settings.GetStr(ProjIncludePaths, NULL, Platform); LAutoString Base = p->GetBasePath(); const char *Delim = ",;\r\n"; LString::Array In, Out; LString::Array a = ProjInclude.SplitDelimit(Delim); In = a; if (IncludeSystem) { LString SysInclude = d->Settings.GetStr(ProjSystemIncludes, NULL, Platform); a = SysInclude.SplitDelimit(Delim); In.SetFixedLength(false); In.Add(a); } for (unsigned i=0; i 1 ? a[1].Get() : NULL); LStringPipe Buf; if (Proc.Start()) { Proc.Communicate(&Buf); LString result = Buf.NewLStr(); a = result.Split(" \t\r\n"); for (int i=0; i Nodes; if (p->GetAllNodes(Nodes)) { auto Base = p->GetFullPath(); if (Base) { LTrimDir(Base); for (auto &n: Nodes) { if (n->GetType() == NodeHeader && // Only look at headers. (n->GetPlatforms() & (1 << Platform)) != 0) // Exclude files not on this platform. { auto f = n->GetFileName(); char p[MAX_PATH_LEN]; if (f && LMakePath(p, sizeof(p), Base, f)) { char *l = strrchr(p, DIR_CHAR); if (l) *l = 0; if (!Map.Find(p)) { Map.Add(p, true); } } } } } } } // char *p; // for (bool b = Map.First(&p); b; b = Map.Next(&p)) for (auto p : Map) Paths.Add(p.key); return true; } void IdeProjectPrivate::CollectAllFiles(LTreeNode *Base, LArray &Files, bool SubProjects, int Platform) { for (auto i:*Base) { IdeProject *Proj = dynamic_cast(i); if (Proj) { if (Proj->GetParentProject() && !SubProjects) { continue; } } else { ProjectNode *p = dynamic_cast(i); if (p) { if (p->GetType() == NodeSrc || p->GetType() == NodeHeader) { if (p->GetPlatforms() & Platform) { Files.Add(p); } } } } CollectAllFiles(i, Files, SubProjects, Platform); } } LString IdeProject::GetTargetName(IdePlatform Platform) { LString Status; const char *t = d->Settings.GetStr(ProjTargetName, NULL, Platform); if (ValidStr(t)) { // Take target name from the settings Status = t; } else { char *s = strrchr(d->FileName, DIR_CHAR); if (s) { // Generate the target executable name char Target[MAX_PATH_LEN]; strcpy_s(Target, sizeof(Target), s + 1); s = strrchr(Target, '.'); if (s) *s = 0; strlwr(Target); s = Target; for (char *i = Target; *i; i++) { if (*i != '.' && *i != ' ') { *s++ = *i; } } *s = 0; Status = Target; } } return Status; } LString IdeProject::GetTargetFile(IdePlatform Platform) { LString Target = GetTargetName(PlatformCurrent); if (!Target) { LAssert(!"No target?"); return LString(); } const char *TargetType = d->Settings.GetStr(ProjTargetType); if (!TargetType) { LAssert(!"Needs a target type."); return LString(); } if (!stricmp(TargetType, "Executable")) { return Target; } else if (!stricmp(TargetType, "DynamicLibrary")) { char t[MAX_PATH_LEN]; auto DefExt = PlatformDynamicLibraryExt(Platform); strcpy_s(t, sizeof(t), Target); char *ext = LGetExtension(t); if (!ext) sprintf(t + strlen(t), ".%s", DefExt); else if (stricmp(ext, DefExt)) strcpy(ext, DefExt); return t; } else if (!stricmp(TargetType, "StaticLibrary")) { LString Ret; Ret.Printf("%s.%s", Target.Get(), PlatformSharedLibraryExt(Platform)); return Ret; } return LString(); } struct ProjDependency { bool Scanned; LAutoString File; ProjDependency(const char *f) { Scanned = false; File.Reset(NewStr(f)); } }; bool IdeProject::GetAllDependencies(LArray &Files, IdePlatform Platform) { if (!GetTree()->Lock(_FL)) return false; LHashTbl, ProjDependency*> Deps; LAutoString Base = GetBasePath(); // Build list of all the source files... LArray Src; CollectAllSource(Src, Platform); // Get all include paths LArray IncPaths; BuildIncludePaths(IncPaths, false, false, Platform); // Add all source to dependencies for (int i=0; i Unscanned; do { // Find all the unscanned dependencies Unscanned.Length(0); // for (ProjDependency *d = Deps.First(); d; d = Deps.Next()) for (auto d : Deps) { if (!d.value->Scanned) Unscanned.Add(d.value); } for (int i=0; iScanned = true; char *Src = d->File; char Full[MAX_PATH_LEN]; if (LIsRelativePath(d->File)) { LMakePath(Full, sizeof(Full), Base, d->File); Src = Full; } LArray SrcDeps; if (GetDependencies(Src, IncPaths, SrcDeps, Platform)) { for (int n=0; n 0); // for (ProjDependency *d = Deps.First(); d; d = Deps.Next()) for (auto d : Deps) { Files.Add(d.value->File.Release()); } Deps.DeleteObjects(); GetTree()->Unlock(); return true; } bool IdeProject::GetDependencies(const char *InSourceFile, LArray &IncPaths, LArray &Files, IdePlatform Platform) { LString SourceFile = InSourceFile; if (!CheckExists(SourceFile)) { LgiTrace("%s:%i - can't read '%s'\n", _FL, SourceFile.Get()); return false; } LAutoString c8(LReadTextFile(SourceFile)); if (!c8) return false; LArray Headers; if (!BuildHeaderList(c8, Headers, IncPaths, false)) return false; for (int n=0; nCreateMakefile) { if (d->CreateMakefile->IsExited()) d->CreateMakefile.Reset(); else { d->App->GetBuildLog()->Print("%s:%i - Makefile thread still running.\n", _FL); return false; } } if (Platform == PlatformCurrent) Platform = GetCurrentPlatform(); return d->CreateMakefile.Reset(new MakefileThread(d, Platform, BuildAfterwards)); } void IdeProject::OnMakefileCreated() { if (d->CreateMakefile) { d->CreateMakefile.Reset(); if (MakefileThread::Instances == 0) GetApp()->PostEvent(M_LAST_MAKEFILE_CREATED); } } //////////////////////////////////////////////////////////////////////////////////////////// IdeTree::IdeTree() : LTree(IDC_PROJECT_TREE, 0, 0, 100, 100) { Hit = 0; MultiSelect(true); } void IdeTree::OnCreate() { SetWindow(this); } void IdeTree::OnDragExit() { SelectDropTarget(0); } int IdeTree::WillAccept(LDragFormats &Formats, LPoint p, int KeyState) { static bool First = true; Formats.SupportsFileDrops(); Formats.Supports(NODE_DROP_FORMAT); First = false; if (Formats.Length() == 0) { LgiTrace("%s:%i - No valid drop formats.\n", _FL); return DROPEFFECT_NONE; } Hit = ItemAtPoint(p.x, p.y); if (!Hit) { SelectDropTarget(NULL); return DROPEFFECT_NONE; } if (Formats.HasFormat(LGI_FileDropFormat)) { SelectDropTarget(Hit); return DROPEFFECT_LINK; } IdeCommon *Src = dynamic_cast(Selection()); IdeCommon *Dst = dynamic_cast(Hit); if (Src && Dst) { // Check this folder is not a child of the src for (IdeCommon *n=Dst; n; n=dynamic_cast(n->GetParent())) { if (n == Src) return DROPEFFECT_NONE; } } // Valid target SelectDropTarget(Hit); return DROPEFFECT_MOVE; } int IdeTree::OnDrop(LArray &Data, LPoint p, int KeyState) { int Ret = DROPEFFECT_NONE; SelectDropTarget(NULL); if (!(Hit = ItemAtPoint(p.x, p.y))) return Ret; for (unsigned n=0; nType == GV_BINARY && Data->Value.Binary.Length == sizeof(ProjectNode*)) { ProjectNode *Src = ((ProjectNode**)Data->Value.Binary.Data)[0]; if (Src) { ProjectNode *Folder = dynamic_cast(Hit); while (Folder && Folder->GetType() != NodeDir) Folder = dynamic_cast(Folder->GetParent()); IdeCommon *Dst = dynamic_cast(Folder?Folder:Hit); if (Dst) { // Check this folder is not a child of the src for (IdeCommon *n=Dst; n; n=dynamic_cast(n->GetParent())) { if (n == Src) return DROPEFFECT_NONE; } // Detach LTreeItem *i = dynamic_cast(Src); i->Detach(); if (Src->LXmlTag::Parent) { LAssert(Src->LXmlTag::Parent->Children.HasItem(Src)); Src->LXmlTag::Parent->Children.Delete(Src); } // Attach Src->LXmlTag::Parent = Dst; Dst->Children.SetFixedLength(false); Dst->Children.Add(Src); Dst->Children.SetFixedLength(true); Dst->Insert(Src); // Dirty Src->GetProject()->SetDirty(); } Ret = DROPEFFECT_MOVE; } } } else if (dd.IsFileDrop()) { ProjectNode *Folder = dynamic_cast(Hit); while (Folder && Folder->GetType() > NodeDir) Folder = dynamic_cast(Folder->GetParent()); IdeCommon *Dst = dynamic_cast(Folder?Folder:Hit); if (Dst) { AddFilesProgress Prog(this); LDropFiles Df(dd); for (int i=0; iAddFiles(&Prog, Df[i])) Ret = DROPEFFECT_LINK; } } } else LgiTrace("%s:%i - Unknown drop format: %s.\n", _FL, dd.Format.Get()); } return Ret; } ///////////////////////////////////////////////////////////////////////////////////////////////// AddFilesProgress::AddFilesProgress(LViewI *par) { v = 0; Cancel = false; Msg = NULL; SetParent(par); Ts = LCurrentTime(); LRect r(0, 0, 140, 100); SetPos(r); MoveSameScreen(par); Name("Importing files..."); LString::Array a = LString(DefaultExt).SplitDelimit(","); for (unsigned i=0; iGetCell(0, 0); c->Add(new LTextLabel(-1, 0, 0, -1, -1, "Loaded:")); c = t->GetCell(1, 0); c->Add(Msg = new LTextLabel(-1, 0, 0, -1, -1, "...")); c = t->GetCell(0, 1, true, 2); c->TextAlign(LCss::Len(LCss::AlignRight)); c->Add(new LButton(IDCANCEL, 0, 0, -1, -1, "Cancel")); } int64 AddFilesProgress::Value() { return v; } void AddFilesProgress::Value(int64 val) { v = val; uint64 Now = LCurrentTime(); if (Visible()) { if (Now - Ts > 200) { if (Msg) { Msg->Value(v); Msg->SendNotify(LNotifyTableLayoutRefresh); } Ts = Now; } } else if (Now - Ts > 1000) { DoModeless(); SetAlwaysOnTop(true); } } int AddFilesProgress::OnNotify(LViewI *c, LNotification n) { if (c->GetId() == IDCANCEL) Cancel = true; return 0; } diff --git a/src/linux/Lgi/Widgets.cpp b/src/linux/Lgi/Widgets.cpp --- a/src/linux/Lgi/Widgets.cpp +++ b/src/linux/Lgi/Widgets.cpp @@ -1,270 +1,270 @@ /*hdr ** FILE: GuiDlg.cpp ** AUTHOR: Matthew Allen ** DATE: 8/9/1998 ** DESCRIPTION: Dialog components ** ** Copyright (C) 1998 Matthew Allen ** fret@memecode.com */ #include #include #include "lgi/common/Lgi.h" #include "lgi/common/Slider.h" #include "lgi/common/Bitmap.h" #include "lgi/common/TableLayout.h" #include "lgi/common/DisplayString.h" #include "lgi/common/Button.h" using namespace Gtk; #include "LgiWidget.h" /////////////////////////////////////////////////////////////////////////////////////////// #define GreyBackground() struct LDialogPriv { int ModalStatus; int BtnId; bool IsModal, IsModeless; bool Resizable; LDialogPriv() { IsModal = false; IsModeless = false; Resizable = true; ModalStatus = 0; BtnId = -1; } }; /////////////////////////////////////////////////////////////////////////////////////////// LDialog::LDialog(LViewI *parent) : #ifdef __GTK_H__ // , LWindow(gtk_dialog_new()) LWindow(gtk_window_new(GTK_WINDOW_TOPLEVEL)), #endif ResObject(Res_Dialog) { d = new LDialogPriv(); Name("Dialog"); _SetDynamic(false); if (parent) SetParent(parent); } LDialog::~LDialog() { DeleteObj(d); } bool LDialog::IsModal() { return d->IsModal; } int LDialog::GetButtonId() { return d->BtnId; } int LDialog::OnNotify(LViewI *Ctrl, LNotification n) { LButton *b = dynamic_cast(Ctrl); if (b) { d->BtnId = b->GetId(); if (d->IsModal) EndModal(); else if (d->IsModeless) EndModeless(); } return 0; } void LDialog::Quit(bool DontDelete) { if (d->IsModal) EndModal(0); else LView::Quit(DontDelete); } void LDialog::OnPosChange() { LWindow::OnPosChange(); if (Children.Length() == 1) { List::I it = Children.begin(); LTableLayout *t = dynamic_cast((LViewI*)it); if (t) { LRect r = GetClient(); r.Inset(LTableLayout::CellSpacing, LTableLayout::CellSpacing); t->SetPos(r); // _Dump(); } } } bool LDialog::LoadFromResource(int Resource, char *TagList) { LAutoString n; LRect p; - LProfile Prof("LDialog::LoadFromResource"); + // LProfile Prof("LDialog::LoadFromResource"); bool Status = LResourceLoad::LoadFromResource(Resource, this, &p, &n, TagList); if (Status) { - Prof.Add("Name."); + // Prof.Add("Name."); Name(n); SetPos(p); } return Status; } bool LDialog::OnRequestClose(bool OsClose) { if (d->IsModal) { EndModal(0); return false; } return true; } bool LDialog::IsResizeable() { return d->Resizable; } void LDialog::IsResizeable(bool r) { d->Resizable = r; } bool LDialog::SetupDialog(bool Modal) { if (IsResizeable()) { gtk_window_set_default_size(Wnd, Pos.X(), Pos.Y()); } else { gtk_widget_set_size_request(GTK_WIDGET(Wnd), Pos.X(), Pos.Y()); gtk_window_set_resizable(Wnd, FALSE); } auto p = GetParent(); if (!Attach(p)) return false; LWindow::Visible(true); return true; } void LDialog::DoModal(OnClose Callback, OsView OverrideParent) { d->ModalStatus = -1; auto Parent = GetParent(); if (Parent) { gtk_window_set_transient_for(GTK_WINDOW(Wnd), Parent->WindowHandle()); MoveSameScreen(Parent); } d->IsModal = true; d->IsModeless = false; SetupDialog(true); LAppInst->Run(); if (Callback) Callback(this, d->ModalStatus); else delete this; } void LDialog::EndModal(int Code) { if (d->IsModal) { d->IsModal = false; d->ModalStatus = Code; LAppInst->Exit(); } else { // LAssert(0); } } int LDialog::DoModeless() { d->IsModal = false; d->IsModeless = true; SetupDialog(false); return 0; } void LDialog::EndModeless(int Code) { Quit(Code); } extern LButton *FindDefault(LView *w); LMessage::Param LDialog::OnEvent(LMessage *Msg) { return LView::OnEvent(Msg); } /////////////////////////////////////////////////////////////////////////////////////////// LControl::LControl(OsView view) : LView(view) { Pos.ZOff(10, 10); } LControl::~LControl() { } LMessage::Param LControl::OnEvent(LMessage *Msg) { return 0; } LPoint LControl::SizeOfStr(const char *Str) { int y = LSysFont->GetHeight(); LPoint Pt(0, 0); if (Str) { const char *e = 0; for (const char *s = Str; s && *s; s = e?e+1:0) { e = strchr(s, '\n'); auto Len = e ? e - s : strlen(s); LDisplayString ds(LSysFont, s, Len); Pt.x = MAX(Pt.x, ds.X()); Pt.y += y; } } return Pt; } diff --git a/src/linux/Lgi/Window.cpp b/src/linux/Lgi/Window.cpp --- a/src/linux/Lgi/Window.cpp +++ b/src/linux/Lgi/Window.cpp @@ -1,1919 +1,1918 @@ #include #include "lgi/common/Lgi.h" #include "lgi/common/DragAndDrop.h" #include "lgi/common/Token.h" #include "lgi/common/Popup.h" #include "lgi/common/Panel.h" #include "lgi/common/Notifications.h" #include "lgi/common/Menu.h" #include "ViewPriv.h" using namespace Gtk; #undef Status #include "LgiWidget.h" #define DEBUG_SETFOCUS 0 #define DEBUG_HANDLEVIEWKEY 0 extern Gtk::GdkDragAction EffectToDragAction(int Effect); /////////////////////////////////////////////////////////////////////// class HookInfo { public: LWindowHookType Flags; LView *Target; }; enum LAttachState { LUnattached, LAttaching, LAttached, LDetaching, }; class LWindowPrivate { public: int Sx, Sy; bool Dynamic; LKey LastKey; ::LArray Hooks; bool SnapToEdge; ::LString Icon; LRect Decor; gulong DestroySig; LAutoPtr IconImg; LAttachState AttachState; bool ShowTitleBar = true; bool WillFocus = true; // State GdkWindowState State; bool HadCreateEvent; // Focus stuff OsView FirstFocus; LViewI *Focus; bool Active; LWindowPrivate() { AttachState = LUnattached; DestroySig = 0; Decor.ZOff(-1, -1); FirstFocus = NULL; Focus = NULL; Active = false; State = (Gtk::GdkWindowState)0; HadCreateEvent = false; Sx = Sy = 0; Dynamic = true; SnapToEdge = false; LastKey.vkey = 0; LastKey.c16 = 0; LastKey.Data = 0; LastKey.IsChar = 0; } int GetHookIndex(LView *Target, bool Create = false) { for (int i=0; iTarget = Target; n->Flags = LNoEvents; return Hooks.Length() - 1; } } return -1; } }; /////////////////////////////////////////////////////////////////////// #define GWND_CREATE 0x0010000 LWindow::LWindow(GtkWidget *w) : LView(0) { d = new LWindowPrivate; _QuitOnClose = false; Menu = NULL; Wnd = GTK_WINDOW(w); if (Wnd) { - printf("gtk_window_set_decorated %i\n", d->ShowTitleBar); gtk_window_set_decorated(Wnd, d->ShowTitleBar); g_object_set_data(G_OBJECT(Wnd), "LViewI", (LViewI*)this); } _Root = NULL; _MenuBar = NULL; _VBox = NULL; _Default = 0; _Window = this; WndFlags |= GWND_CREATE; ClearFlag(WndFlags, GWF_VISIBLE); _Lock = new ::LMutex("LWindow"); } LWindow::~LWindow() { d->AttachState = LDetaching; if (Wnd && d->DestroySig > 0) { // As we are already in the destructor, we don't want // GtkWindowDestroy to try and delete the object again. g_signal_handler_disconnect(Wnd, d->DestroySig); } if (LAppInst && LAppInst->AppWnd == this) LAppInst->AppWnd = NULL; if (_Root) { lgi_widget_detach(_Root); _Root = NULL; } if (Wnd) { gtk_widget_destroy(GTK_WIDGET(Wnd)); Wnd = NULL; } d->AttachState = LUnattached; DeleteObj(Menu); DeleteObj(d); DeleteObj(_Lock); } int LWindow::WaitThread() { return 0; // Nop for linux } bool LWindow::SetIcon(const char *FileName) { LString a; if (Wnd) { if (!LFileExists(FileName)) { if (a = LFindFile(FileName)) FileName = a; } if (!LFileExists(FileName)) { LgiTrace("%s:%i - SetIcon failed to find '%s'\n", _FL, FileName); return false; } else { #if defined(LINUX) LAppInst->SetApplicationIcon(FileName); #endif #if _MSC_VER GError *error = NULL; if (gtk_window_set_icon_from_file(Wnd, FileName, &error)) return true; #else // On windows this is giving a red for blue channel swap error... if (d->IconImg.Reset(GdcD->Load(a))) gtk_window_set_icon(Wnd, d->IconImg->CreatePixBuf()); #endif } } if (FileName != d->Icon.Get()) d->Icon = FileName; return d->Icon != NULL; } bool LWindow::GetSnapToEdge() { return d->SnapToEdge; } void LWindow::SetSnapToEdge(bool s) { d->SnapToEdge = s; } bool LWindow::IsActive() { return d->Active; } bool LWindow::SetActive() { if (!Wnd) return false; gtk_window_present(Wnd); return true; } bool LWindow::Visible() { return LView::Visible(); } void LWindow::Visible(bool i) { ThreadCheck(); auto w = GTK_WIDGET(Wnd); if (i) gtk_widget_show(w); else gtk_widget_hide(w); } bool LWindow::Obscured() { return d->State == GDK_WINDOW_STATE_WITHDRAWN || d->State == GDK_WINDOW_STATE_ICONIFIED; } void LWindow::_SetDynamic(bool i) { d->Dynamic = i; } void LWindow::_OnViewDelete() { if (d->Dynamic) { delete this; } } void LWindow::OnGtkRealize() { d->AttachState = LAttached; LView::OnGtkRealize(); } void LWindow::OnGtkDelete() { // Delete everything we own... // DeleteObj(Menu); #if 0 while (Children.Length()) { LViewI *c = Children.First(); c->Detach(); } #else for (unsigned i=0; iGetGView(); if (v) v->OnGtkDelete(); } #endif // These will be destroyed by GTK after returning from LWindowCallback Wnd = NULL; #ifndef __GTK_H__ _View = NULL; #endif } LRect *LWindow::GetDecorSize() { return d->Decor.x2 >= 0 ? &d->Decor : NULL; } void LWindow::SetDecor(bool Visible) { if (Wnd) gtk_window_set_decorated (Wnd, Visible); else LgiTrace("%s:%i - No window to set decor.\n", _FL); } LViewI *LWindow::WindowFromPoint(int x, int y, bool Debug) { if (!_Root) return NULL; auto rpos = GtkGetPos(_Root).ZeroTranslate(); if (!rpos.Overlap(x, y)) return NULL; return LView::WindowFromPoint(x - rpos.x1, y - rpos.y1, Debug); } bool LWindow::TranslateMouse(LMouse &m) { m.Target = WindowFromPoint(m.x, m.y, false); if (!m.Target) return false; LViewI *w = this; for (auto p = m.Target; p; p = p->GetParent()) { if (p == w) { auto ppos = GtkGetPos(GTK_WIDGET(WindowHandle())); m.x -= ppos.x1; m.y -= ppos.y1; break; } else { auto pos = p->GetPos(); m.x -= pos.x1; m.y -= pos.y1; } } return true; } gboolean LWindow::OnGtkEvent(GtkWidget *widget, GdkEvent *event) { if (!event) { printf("%s:%i - No event.\n", _FL); return FALSE; } #if 0 if (event->type != 28) LgiTrace("%s::OnGtkEvent(%i) name=%s\n", GetClass(), event->type, Name()); #endif switch (event->type) { case GDK_DELETE: { bool Close = OnRequestClose(false); if (Close) OnGtkDelete(); return !Close; } case GDK_DESTROY: { delete this; return true; } case GDK_KEY_PRESS: case GDK_KEY_RELEASE: { auto ModFlags = LAppInst->GetKeyModFlags(); auto e = &event->key; #define KEY(name) GDK_KEY_##name LKey k; k.Down(e->type == GDK_KEY_PRESS); k.c16 = k.vkey = e->keyval; k.Shift((e->state & ModFlags->Shift) != 0); k.Ctrl((e->state & ModFlags->Ctrl) != 0); k.Alt((e->state & ModFlags->Alt) != 0); k.System((e->state & ModFlags->System) != 0); #if 0//def _DEBUG if (k.vkey == GDK_KEY_Meta_L || k.vkey == GDK_KEY_Meta_R) break; #endif k.IsChar = !k.Ctrl() && !k.Alt() && !k.System() && (k.c16 >= ' ') && (k.c16 >> 8 != 0xff); if (e->keyval > 0xff && e->string != NULL) { // Convert string to unicode char auto *i = e->string; ptrdiff_t len = strlen(i); k.c16 = LgiUtf8To32((uint8_t *&) i, len); } switch (k.vkey) { case GDK_KEY_ISO_Left_Tab: case KEY(Tab): k.IsChar = true; k.c16 = k.vkey = LK_TAB; break; case KEY(Return): case KEY(KP_Enter): k.IsChar = true; k.c16 = k.vkey = LK_RETURN; break; case GDK_KEY_BackSpace: k.c16 = k.vkey = LK_BACKSPACE; k.IsChar = !k.Ctrl() && !k.Alt() && !k.System(); break; case KEY(Left): k.vkey = k.c16 = LK_LEFT; break; case KEY(Right): k.vkey = k.c16 = LK_RIGHT; break; case KEY(Up): k.vkey = k.c16 = LK_UP; break; case KEY(Down): k.vkey = k.c16 = LK_DOWN; break; case KEY(Page_Up): k.vkey = k.c16 = LK_PAGEUP; break; case KEY(Page_Down): k.vkey = k.c16 = LK_PAGEDOWN; break; case KEY(Home): k.vkey = k.c16 = LK_HOME; break; case KEY(End): k.vkey = k.c16 = LK_END; break; case KEY(Delete): k.vkey = k.c16 = LK_DELETE; break; #define KeyPadMap(gdksym, ch, is) \ case gdksym: k.c16 = ch; k.IsChar = is; break; KeyPadMap(KEY(KP_0), '0', true) KeyPadMap(KEY(KP_1), '1', true) KeyPadMap(KEY(KP_2), '2', true) KeyPadMap(KEY(KP_3), '3', true) KeyPadMap(KEY(KP_4), '4', true) KeyPadMap(KEY(KP_5), '5', true) KeyPadMap(KEY(KP_6), '6', true) KeyPadMap(KEY(KP_7), '7', true) KeyPadMap(KEY(KP_8), '8', true) KeyPadMap(KEY(KP_9), '9', true) KeyPadMap(KEY(KP_Space), ' ', true) KeyPadMap(KEY(KP_Tab), '\t', true) KeyPadMap(KEY(KP_F1), LK_F1, false) KeyPadMap(KEY(KP_F2), LK_F2, false) KeyPadMap(KEY(KP_F3), LK_F3, false) KeyPadMap(KEY(KP_F4), LK_F4, false) KeyPadMap(KEY(KP_Home), LK_HOME, false) KeyPadMap(KEY(KP_Left), LK_LEFT, false) KeyPadMap(KEY(KP_Up), LK_UP, false) KeyPadMap(KEY(KP_Right), LK_RIGHT, false) KeyPadMap(KEY(KP_Down), LK_DOWN, false) KeyPadMap(KEY(KP_Page_Up), LK_PAGEUP, false) KeyPadMap(KEY(KP_Page_Down), LK_PAGEDOWN, false) KeyPadMap(KEY(KP_End), LK_END, false) KeyPadMap(KEY(KP_Begin), LK_HOME, false) KeyPadMap(KEY(KP_Insert), LK_INSERT, false) KeyPadMap(KEY(KP_Delete), LK_DELETE, false) KeyPadMap(KEY(KP_Equal), '=', true) KeyPadMap(KEY(KP_Multiply), '*', true) KeyPadMap(KEY(KP_Add), '+', true) KeyPadMap(KEY(KP_Separator), '|', true) // is this right? KeyPadMap(KEY(KP_Subtract), '-', true) KeyPadMap(KEY(KP_Decimal), '.', true) KeyPadMap(KEY(KP_Divide), '/', true) } if (ModFlags->Debug) { :: LString Msg; Msg.Printf("e->state=%x %s", e->state, ModFlags->FlagsToString(e->state).Get()); k.Trace(Msg); } auto v = d->Focus ? d->Focus : this; if (!HandleViewKey(v->GetGView(), k)) { if (!k.Down()) return false; if (k.vkey == LK_TAB || k.vkey == KEY(ISO_Left_Tab)) { // Do tab between controls ::LArray a; BuildTabStops(this, a); int idx = a.IndexOf((LViewI*)v); if (idx >= 0) { idx += k.Shift() ? -1 : 1; int next_idx = idx == 0 ? a.Length() -1 : idx % a.Length(); LViewI *next = a[next_idx]; if (next) { // LgiTrace("Setting focus to %i of %i: %s, %s, %i\n", next_idx, a.Length(), next->GetClass(), next->GetPos().GetStr(), next->GetId()); next->Focus(true); } } } else if (k.System()) { if (ToLower(k.c16) == 'q') { auto AppWnd = LAppInst->AppWnd; auto Wnd = AppWnd ? AppWnd : this; if (Wnd->OnRequestClose(false)) { Wnd->Quit(); return true; } } } else return false; } break; } case GDK_CONFIGURE: { GdkEventConfigure *c = &event->configure; Pos.Set(c->x, c->y, c->x+c->width-1, c->y+c->height-1); // printf("%s::GDK_CONFIGURE %s\n", GetClass(), Pos.GetStr()); OnPosChange(); return FALSE; break; } case GDK_FOCUS_CHANGE: { d->Active = event->focus_change.in; #if 0 printf("%s/%s::GDK_FOCUS_CHANGE(%i)\n", GetClass(), Name(), event->focus_change.in); #endif break; } case GDK_WINDOW_STATE: { d->State = event->window_state.new_window_state; break; } case GDK_PROPERTY_NOTIFY: { gchar *Name = gdk_atom_name (event->property.atom); if (!Name) break; if (!_stricmp(Name, "_NET_FRAME_EXTENTS")) { // printf("PropChange: %i - %s\n", event->property.atom, Name); unsigned long *extents = NULL; if (gdk_property_get(event->property.window, gdk_atom_intern ("_NET_FRAME_EXTENTS", FALSE), gdk_atom_intern ("CARDINAL", FALSE), 0, sizeof (unsigned long) * 4, FALSE, NULL, NULL, NULL, (guchar **)&extents)) { d->Decor.Set(extents[0], extents[2], extents[1], extents[3]); g_free(extents); } else printf("%s:%i - Error: gdk_property_get failed.\n", _FL); } g_free(Name); break; } case GDK_UNMAP: { // LgiTrace("%s:%i - Unmap %s\n", _FL, GetClass()); break; } case GDK_VISIBILITY_NOTIFY: { // LgiTrace("%s:%i - Visible %s\n", _FL, GetClass()); break; } case GDK_DRAG_ENTER: { LgiTrace("%s:%i - GDK_DRAG_ENTER\n", _FL); break; } case GDK_DRAG_LEAVE: { LgiTrace("%s:%i - GDK_DRAG_LEAVE\n", _FL); break; } case GDK_DRAG_MOTION: { LgiTrace("%s:%i - GDK_DRAG_MOTION\n", _FL); break; } case GDK_DRAG_STATUS: { LgiTrace("%s:%i - GDK_DRAG_STATUS\n", _FL); break; } case GDK_DROP_START: { LgiTrace("%s:%i - GDK_DROP_START\n", _FL); break; } case GDK_DROP_FINISHED: { LgiTrace("%s:%i - GDK_DROP_FINISHED\n", _FL); break; } default: { printf("%s:%i - Unknown event %i\n", _FL, event->type); return false; } } return true; } static gboolean GtkWindowDestroy(GtkWidget *widget, LWindow *This) { delete This; return true; } static void GtkWindowRealize(GtkWidget *widget, LWindow *This) { #if 0 LgiTrace("GtkWindowRealize, This=%p(%s\"%s\")\n", This, (NativeInt)This > 0x1000 ? This->GetClass() : 0, (NativeInt)This > 0x1000 ? This->Name() : 0); #endif This->OnGtkRealize(); } static void GtkRootResize(GtkWidget *widget, GdkRectangle *alloc, LView *This) { LWindow *w = This->GetWindow(); if (w) w->PourAll(); } void LWindowUnrealize(GtkWidget *widget, LWindow *wnd) { // printf("%s:%i - LWindowUnrealize %s\n", _FL, wnd->GetClass()); } bool DndPointMap(LViewI *&v, LPoint &p, LDragDropTarget *&t, LWindow *Wnd, int x, int y) { LRect cli = Wnd->GetClient(); t = NULL; v = Wnd->WindowFromPoint(x - cli.x1, y - cli.y1, false); if (!v) { LgiTrace("%s:%i - @ %i,%i\n", _FL, x, y); return false; } v->WindowVirtualOffset(&p); p.x = x - p.x; p.y = y - p.y; for (LViewI *view = v; !t && view; view = view->GetParent()) t = view->DropTarget(); if (t) return true; LgiTrace("%s:%i - No target for %s\n", _FL, v->GetClass()); return false; } void LWindowDragBegin(GtkWidget *widget, GdkDragContext *context, LWindow *Wnd) { LgiTrace("%s:%i - %s %s\n", _FL, Wnd->GetClass(), __func__); } void LWindowDragDataDelete(GtkWidget *widget, GdkDragContext *context, LWindow *Wnd) { LgiTrace("%s:%i - %s %s\n", _FL, Wnd->GetClass(), __func__); } void LWindowDragDataGet(GtkWidget *widget, GdkDragContext *context, GtkSelectionData *data, guint info, guint time, LWindow *Wnd) { LgiTrace("%s:%i - %s %s\n", _FL, Wnd->GetClass(), __func__); } void LWindowDragDataReceived(GtkWidget *widget, GdkDragContext *context, gint x, gint y, GtkSelectionData *data, guint info, guint time, LWindow *Wnd) { LPoint p; LViewI *v; LDragDropTarget *t; if (!DndPointMap(v, p, t, Wnd, x, y)) return; for (auto &d: t->Data) { auto type = gdk_atom_name(gtk_selection_data_get_data_type(data)); if (d.Format.Equals(type)) { gint length = 0; auto ptr = gtk_selection_data_get_data_with_length(data, &length); if (ptr) { d.Data[0].SetBinary(length, (void*)ptr, false); } break; } } } int GetAcceptFmts(::LString::Array &Formats, GdkDragContext *context, LDragDropTarget *t, LPoint &p) { int KeyState = 0; LDragFormats Fmts(true); int Flags = DROPEFFECT_NONE; GList *targets = gdk_drag_context_list_targets(context); Gtk::GList *i = targets; while (i) { auto a = gdk_atom_name((GdkAtom)i->data); if (a) Fmts.Supports(a); i = i->next; } Fmts.SetSource(false); Flags = t->WillAccept(Fmts, p, KeyState); auto Sup = Fmts.GetSupported(); for (auto &s: Sup) Formats.New() = s; return Flags; } gboolean LWindowDragDataDrop(GtkWidget *widget, GdkDragContext *context, gint x, gint y, guint time, LWindow *Wnd) { // Map the point to a view... LPoint p; LViewI *v; LDragDropTarget *t; if (!DndPointMap(v, p, t, Wnd, x, y)) return false; t->Data.Length(0); // Request the data... ::LArray Data; ::LString::Array Formats; int KeyState = 0; int Flags = GetAcceptFmts(Formats, context, t, p); for (auto f: Formats) { t->Data.New().Format = f; gtk_drag_get_data(widget, context, gdk_atom_intern(f, true), time); } // Wait for the data to arrive... uint64_t Start = LCurrentTime(); while (LCurrentTime()-Start < 2000) { int HasData = 0; for (auto d: t->Data) if (d.Data.Length() > 0) HasData++; if (HasData >= Formats.Length()) break; LYield(); } auto Result = t->OnDrop(t->Data, p, KeyState); if (Flags != DROPEFFECT_NONE) gdk_drag_status(context, EffectToDragAction(Flags), time); return Result != DROPEFFECT_NONE; } void LWindowDragEnd(GtkWidget *widget, GdkDragContext *context, LWindow *Wnd) { LgiTrace("%s:%i - %s %s\n", _FL, Wnd->GetClass(), __func__); } gboolean LWindowDragFailed(GtkWidget *widget, GdkDragContext *context, GtkDragResult result, LWindow *Wnd) { LgiTrace("%s:%i - %s %s\n", _FL, Wnd->GetClass(), __func__); return false; } void LWindowDragLeave(GtkWidget *widget, GdkDragContext *context, guint time, LWindow *Wnd) { LgiTrace("%s:%i - %s %s\n", _FL, Wnd->GetClass(), __func__); } gboolean LWindowDragMotion(GtkWidget *widget, GdkDragContext *context, gint x, gint y, guint time, LWindow *Wnd) { LPoint p; LViewI *v; LDragDropTarget *t; if (!DndPointMap(v, p, t, Wnd, x, y)) return false; ::LString::Array Formats; int Flags = GetAcceptFmts(Formats, context, t, p); if (Flags != DROPEFFECT_NONE) gdk_drag_status(context, EffectToDragAction(Flags), time); return Flags != DROPEFFECT_NONE; } bool LWindow::Attach(LViewI *p) { bool Status = false; ThreadCheck(); // Setup default button... if (!_Default) _Default = FindControl(IDOK); // Create a window if needed.. if (!Wnd) Wnd = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL)); if (Wnd) { SetTitleBar(d->ShowTitleBar); SetWillFocus(d->WillFocus); auto Widget = GTK_WIDGET(Wnd); LView *i = this; if (Pos.X() > 0 && Pos.Y() > 0) gtk_window_resize(Wnd, Pos.X(), Pos.Y()); gtk_window_move(Wnd, Pos.x1, Pos.y1); auto Obj = G_OBJECT(Wnd); g_object_set_data(Obj, "LViewI", (LViewI*)this); d->DestroySig = g_signal_connect(Obj, "destroy", G_CALLBACK(GtkWindowDestroy), this); g_signal_connect(Obj, "delete_event", G_CALLBACK(GtkViewCallback), i); g_signal_connect(Obj, "key-press-event", G_CALLBACK(GtkViewCallback), i); g_signal_connect(Obj, "key-release-event", G_CALLBACK(GtkViewCallback), i); g_signal_connect(Obj, "focus-in-event", G_CALLBACK(GtkViewCallback), i); g_signal_connect(Obj, "focus-out-event", G_CALLBACK(GtkViewCallback), i); g_signal_connect(Obj, "window-state-event", G_CALLBACK(GtkViewCallback), i); g_signal_connect(Obj, "property-notify-event", G_CALLBACK(GtkViewCallback), i); g_signal_connect(Obj, "configure-event", G_CALLBACK(GtkViewCallback), i); g_signal_connect(Obj, "unmap-event", G_CALLBACK(GtkViewCallback), i); g_signal_connect(Obj, "visibility-notify-event",G_CALLBACK(GtkViewCallback), i); g_signal_connect(Obj, "realize", G_CALLBACK(GtkWindowRealize), i); g_signal_connect(Obj, "unrealize", G_CALLBACK(LWindowUnrealize), i); g_signal_connect(Obj, "drag-begin", G_CALLBACK(LWindowDragBegin), i); g_signal_connect(Obj, "drag-data-delete", G_CALLBACK(LWindowDragDataDelete), i); g_signal_connect(Obj, "drag-data-get", G_CALLBACK(LWindowDragDataGet), i); g_signal_connect(Obj, "drag-data-received", G_CALLBACK(LWindowDragDataReceived), i); g_signal_connect(Obj, "drag-drop", G_CALLBACK(LWindowDragDataDrop), i); g_signal_connect(Obj, "drag-end", G_CALLBACK(LWindowDragEnd), i); g_signal_connect(Obj, "drag-failed", G_CALLBACK(LWindowDragFailed), i); g_signal_connect(Obj, "drag-leave", G_CALLBACK(LWindowDragLeave), i); g_signal_connect(Obj, "drag-motion", G_CALLBACK(LWindowDragMotion), i); #if 0 g_signal_connect(Obj, "button-press-event", G_CALLBACK(GtkViewCallback), i); g_signal_connect(Obj, "button-release-event", G_CALLBACK(GtkViewCallback), i); g_signal_connect(Obj, "motion-notify-event", G_CALLBACK(GtkViewCallback), i); g_signal_connect(Obj, "scroll-event", G_CALLBACK(GtkViewCallback), i); #endif gtk_widget_add_events( Widget, GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | GDK_FOCUS_CHANGE_MASK | GDK_STRUCTURE_MASK | GDK_VISIBILITY_NOTIFY_MASK); gtk_window_set_title(Wnd, LBase::Name()); d->AttachState = LAttaching; // g_action_map_add_action_entries (G_ACTION_MAP(Wnd), app_entries, G_N_ELEMENTS (app_entries), Wnd); if ((_Root = lgi_widget_new(this, true))) { g_signal_connect(_Root, "size-allocate", G_CALLBACK(GtkRootResize), i); GtkContainer *AttachPoint = NULL; if (GTK_IS_DIALOG(Wnd)) { auto content = gtk_dialog_get_content_area(GTK_DIALOG(Wnd)); if (!content) { LAssert(!"No content area"); return false; } AttachPoint = GTK_CONTAINER(content); } else { AttachPoint = GTK_CONTAINER(Wnd); } LAssert(AttachPoint != NULL); gtk_container_add(AttachPoint, _Root); // Check it actually worked... (would a return value kill you GTK? no it would not) auto p = gtk_widget_get_parent(_Root); if (!p) { LAssert(!"Add failed"); return false; } gtk_widget_show(_Root); } // This call sets up the GdkWindow handle gtk_widget_realize(Widget); // Do a rough layout of child windows PourAll(); // Add icon if (d->Icon) { SetIcon(d->Icon); d->Icon.Empty(); } auto p = GetParent(); if (p) { auto pHnd = p->WindowHandle(); if (!pHnd) LgiTrace("%s:%i - SetParent() - no pHnd from %s.\n", _FL, p->GetClass()); else gtk_window_set_transient_for(GTK_WINDOW(Wnd), pHnd); } Status = true; } return Status; } bool LWindow::OnRequestClose(bool OsShuttingDown) { if (GetQuitOnClose()) { LCloseApp(); } return LView::OnRequestClose(OsShuttingDown); } bool LWindow::HandleViewMouse(LView *v, LMouse &m) { if (m.Down() && !m.IsMove()) { bool InPopup = false; for (LViewI *p = v; p; p = p->GetParent()) { if (dynamic_cast(p)) { InPopup = true; break; } } if (!InPopup && LPopup::CurrentPopups.Length()) { for (int i=0; iVisible()) p->Visible(false); } } } for (int i=0; iHooks.Length(); i++) { if (d->Hooks[i].Flags & LMouseEvents) { if (!d->Hooks[i].Target->OnViewMouse(v, m)) { return false; } } } return true; } bool LWindow::HandleViewKey(LView *v, LKey &k) { bool Status = false; LViewI *Ctrl = 0; #if DEBUG_HANDLEVIEWKEY bool Debug = 1; // k.vkey == LK_RETURN; char SafePrint = k.c16 < ' ' ? ' ' : k.c16; // if (Debug) { LgiTrace("%s/%p::HandleViewKey=%i ischar=%i %s%s%s%s (d->Focus=%s/%p)\n", v->GetClass(), v, k.c16, k.IsChar, (char*)(k.Down()?" Down":" Up"), (char*)(k.Shift()?" Shift":""), (char*)(k.Alt()?" Alt":""), (char*)(k.Ctrl()?" Ctrl":""), d->Focus?d->Focus->GetClass():0, d->Focus); } #endif // Any window in a popup always gets the key... LViewI *p; for (p = v->GetParent(); p; p = p->GetParent()) { if (dynamic_cast(p)) { #if DEBUG_HANDLEVIEWKEY if (Debug) LgiTrace("\tSending key to popup\n"); #endif return v->OnKey(k); } } // Give key to popups if (LAppInst && LAppInst->GetMouseHook() && LAppInst->GetMouseHook()->OnViewKey(v, k)) { #if DEBUG_HANDLEVIEWKEY if (Debug) LgiTrace("\tMouseHook got key\n"); #endif goto AllDone; } // Allow any hooks to see the key... for (int i=0; iHooks.Length(); i++) { #if DEBUG_HANDLEVIEWKEY // if (Debug) LgiTrace("\tHook[%i]\n", i); #endif if (d->Hooks[i].Flags & LKeyEvents) { LView *Target = d->Hooks[i].Target; #if DEBUG_HANDLEVIEWKEY if (Debug) LgiTrace("\tHook[%i].Target=%p %s\n", i, Target, Target->GetClass()); #endif if (Target->OnViewKey(v, k)) { Status = true; #if DEBUG_HANDLEVIEWKEY if (Debug) LgiTrace("\tHook[%i] ate '%c'(%i) down=%i alt=%i ctrl=%i sh=%i\n", i, SafePrint, k.c16, k.Down(), k.Alt(), k.Ctrl(), k.Shift()); #endif goto AllDone; } } } // Give the key to the window... if (v->OnKey(k)) { #if DEBUG_HANDLEVIEWKEY if (Debug) LgiTrace("\tView ate '%c'(%i) down=%i alt=%i ctrl=%i sh=%i\n", SafePrint, k.c16, k.Down(), k.Alt(), k.Ctrl(), k.Shift()); #endif Status = true; goto AllDone; } #if DEBUG_HANDLEVIEWKEY else if (Debug) LgiTrace("\t%s didn't eat '%c'(%i) down=%i alt=%i ctrl=%i sh=%i\n", v->GetClass(), SafePrint, k.c16, k.Down(), k.Alt(), k.Ctrl(), k.Shift()); #endif // Window didn't want the key... switch (k.vkey) { case LK_RETURN: #ifdef LK_KEYPADENTER case LK_KEYPADENTER: #endif { Ctrl = _Default; break; } case LK_ESCAPE: { Ctrl = FindControl(IDCANCEL); break; } } // printf("Ctrl=%p\n", Ctrl); if (Ctrl) { if (Ctrl->Enabled()) { if (Ctrl->OnKey(k)) { Status = true; #if DEBUG_HANDLEVIEWKEY if (Debug) LgiTrace("\tDefault Button ate '%c'(%i) down=%i alt=%i ctrl=%i sh=%i\n", SafePrint, k.c16, k.Down(), k.Alt(), k.Ctrl(), k.Shift()); #endif goto AllDone; } // else printf("OnKey()=false\n"); } // else printf("Ctrl=disabled\n"); } #if DEBUG_HANDLEVIEWKEY else if (Debug) LgiTrace("\tNo default ctrl to handle key.\n"); #endif if (Menu) { Status = Menu->OnKey(v, k); if (Status) { #if DEBUG_HANDLEVIEWKEY if (Debug) LgiTrace("\tMenu ate '%c' down=%i alt=%i ctrl=%i sh=%i\n", k.c16, k.Down(), k.Alt(), k.Ctrl(), k.Shift()); #endif } } // Tab through controls if (k.vkey == LK_TAB && k.Down() && !k.IsChar) { LViewI *Wnd = GetNextTabStop(v, k.Shift()); #if DEBUG_HANDLEVIEWKEY if (Debug) LgiTrace("\tTab moving focus shift=%i Wnd=%p\n", k.Shift(), Wnd); #endif if (Wnd) Wnd->Focus(true); } // Control shortcut? if (k.Down() && k.Alt() && k.c16 > ' ') { ShortcutMap Map; BuildShortcuts(Map); LViewI *c = Map.Find(ToUpper(k.c16)); if (c) { c->OnNotify(c, LNotifyActivate); return true; } } AllDone: if (d) d->LastKey = k; return Status; } void LWindow::Raise() { if (Wnd) gtk_window_present(Wnd); } LWindowZoom LWindow::GetZoom() { switch (d->State) { case GDK_WINDOW_STATE_ICONIFIED: return LZoomMin; case GDK_WINDOW_STATE_MAXIMIZED: return LZoomMax; default: break; } return LZoomNormal; } void LWindow::SetZoom(LWindowZoom i) { if (!Wnd) { // LgiTrace("%s:%i - No window.\n", _FL); return; } ThreadCheck(); switch (i) { case LZoomMin: { gtk_window_iconify(Wnd); break; } case LZoomNormal: { gtk_window_deiconify(Wnd); gtk_window_unmaximize(Wnd); break; } case LZoomMax: { gtk_window_maximize(Wnd); break; } default: { LgiTrace("%s:%i - Error: unsupported zoom.\n", _FL); break; } } } LViewI *LWindow::GetDefault() { return _Default; } void LWindow::SetDefault(LViewI *v) { if (v && v->GetWindow() == this) { if (_Default != v) { LViewI *Old = _Default; _Default = v; if (Old) Old->Invalidate(); if (_Default) _Default->Invalidate(); } } else { _Default = 0; } } bool LWindow::SetTitleBar(bool ShowTitleBar) { d->ShowTitleBar = ShowTitleBar; if (Wnd) { ThreadCheck(); gtk_window_set_decorated(Wnd, ShowTitleBar); } return true; } bool LWindow::Name(const char *n) { if (Wnd) { ThreadCheck(); gtk_window_set_title(Wnd, n); } return LBase::Name(n); } const char *LWindow::Name() { return LBase::Name(); } struct CallbackParams { LRect Menu; int Depth; CallbackParams() { Menu.ZOff(-1, -1); Depth = 0; } }; void ClientCallback(GtkWidget *w, CallbackParams *p) { const char *Name = gtk_widget_get_name(w); if (Name && !_stricmp(Name, "GtkMenuBar")) { GtkAllocation alloc = {0}; gtk_widget_get_allocation(w, &alloc); p->Menu.ZOff(alloc.width-1, alloc.height-1); // LgiTrace("GtkMenuBar = %s\n", p->Menu.GetStr()); } if (!p->Menu.Valid()) { p->Depth++; if (GTK_IS_CONTAINER(w)) gtk_container_forall(GTK_CONTAINER(w), (GtkCallback)ClientCallback, p); p->Depth--; } } LPoint LWindow::GetDpi() { return LScreenDpi(); } LPointF LWindow::GetDpiScale() { auto Dpi = GetDpi(); return LPointF((double)Dpi.x/96.0, (double)Dpi.y/96.0); } LRect &LWindow::GetClient(bool ClientSpace) { static LRect r; r = LView::GetClient(ClientSpace); if (Wnd) { CallbackParams p; gtk_container_forall(GTK_CONTAINER(Wnd), (GtkCallback)ClientCallback, &p); if (p.Menu.Valid()) { if (ClientSpace) r.y2 -= p.Menu.Y(); else r.y1 += p.Menu.Y(); } } return r; } bool LWindow::SerializeState(LDom *Store, const char *FieldName, bool Load) { if (!Store || !FieldName) return false; if (Load) { ::LVariant v; if (Store->GetValue(FieldName, v) && v.Str()) { LRect Position(0, 0, -1, -1); LWindowZoom State = LZoomNormal; // printf("SerializeState load %s\n", v.Str()); LToken t(v.Str(), ";"); for (int i=0; iName()); v = p[n]; ch += sprintf_s(s + ch, sizeof(s) - ch, "%s>%s", Buf, v->GetClass()); } return LAutoString(NewStr(s)); } #endif void LWindow::SetFocus(LViewI *ctrl, FocusType type) { #if DEBUG_SETFOCUS const char *TypeName = NULL; switch (type) { case GainFocus: TypeName = "Gain"; break; case LoseFocus: TypeName = "Lose"; break; case ViewDelete: TypeName = "Delete"; break; } #endif switch (type) { case GainFocus: { if (d->Focus == ctrl) { #if DEBUG_SETFOCUS LAutoString _ctrl = DescribeView(ctrl); LgiTrace("SetFocus(%s, %s) already has focus.\n", _ctrl.Get(), TypeName); #endif return; } if (d->Focus) { LView *gv = d->Focus->GetGView(); if (gv) { #if DEBUG_SETFOCUS LAutoString _foc = DescribeView(d->Focus); LgiTrace(".....defocus LView: %s\n", _foc.Get()); #endif gv->_Focus(false); } else if (IsActive()) { #if DEBUG_SETFOCUS LAutoString _foc = DescribeView(d->Focus); LgiTrace(".....defocus view: %s (active=%i)\n", _foc.Get(), IsActive()); #endif d->Focus->OnFocus(false); d->Focus->Invalidate(); } } d->Focus = ctrl; if (d->Focus) { #if DEBUG_SETFOCUS static int Count = 0; #endif LView *gv = d->Focus->GetGView(); if (gv) { #if DEBUG_SETFOCUS LAutoString _set = DescribeView(d->Focus); LgiTrace("LWindow::SetFocus(%s, %s) %i focusing LView\n", _set.Get(), TypeName, Count++); #endif gv->_Focus(true); } else if (IsActive()) { #if DEBUG_SETFOCUS LAutoString _set = DescribeView(d->Focus); LgiTrace("LWindow::SetFocus(%s, %s) %i focusing nonGView (active=%i)\n", _set.Get(), TypeName, Count++, IsActive()); #endif d->Focus->OnFocus(true); d->Focus->Invalidate(); } } break; } case LoseFocus: { #if DEBUG_SETFOCUS LAutoString _Ctrl = DescribeView(d->Focus); LAutoString _Focus = DescribeView(d->Focus); LgiTrace("LWindow::SetFocus(%s, %s) d->Focus=%s\n", _Ctrl.Get(), TypeName, _Focus.Get()); #endif if (ctrl == d->Focus) { d->Focus = NULL; } break; } case ViewDelete: { if (ctrl == d->Focus) { #if DEBUG_SETFOCUS LAutoString _Ctrl = DescribeView(d->Focus); LgiTrace("LWindow::SetFocus(%s, %s) on delete\n", _Ctrl.Get(), TypeName); #endif d->Focus = NULL; } break; } } } bool LWindow::SetWillFocus(bool f) { d->WillFocus = f; if (Wnd) { if (f) { } else { gtk_window_set_type_hint(Wnd, GDK_WINDOW_TYPE_HINT_POPUP_MENU); // printf("%s:%i - gtk_window_set_type_hint=GDK_WINDOW_TYPE_HINT_POPUP_MENU.\n", _FL); } } return true; } void LWindow::SetDragHandlers(bool On) { } void LWindow::SetParent(LViewI *p) { LView::SetParent(p); if (p && Wnd) { auto pHnd = p->WindowHandle(); if (!pHnd) LgiTrace("%s:%i - SetParent() - no pHnd from %s.\n", _FL, p->GetClass()); else if (!GTK_IS_WINDOW(Wnd)) LgiTrace("%s:%i - SetParent() - GTK_IS_WINDOW failed.\n", _FL); else gtk_window_set_transient_for(GTK_WINDOW(Wnd), pHnd); } } bool LWindow::IsAttached() { return d->AttachState == LAttaching || d->AttachState == LAttached; } void LWindow::OnTrayClick(LMouse &m) { if (m.Down() || m.IsContextMenu()) { LSubMenu RClick; OnTrayMenu(RClick); if (GetMouse(m, true)) { #if WINNATIVE SetForegroundWindow(Handle()); #endif int Result = RClick.Float(this, m.x, m.y); #if WINNATIVE PostMessage(Handle(), WM_NULL, 0, 0); #endif OnTrayMenuResult(Result); } } } void LWindow::Quit(bool DontDelete) { ThreadCheck(); if (Wnd) { d->AttachState = LDetaching; auto wnd = Wnd; Wnd = NULL; gtk_widget_destroy(GTK_WIDGET(wnd)); } } void LWindow::SetAlwaysOnTop(bool b) { }