diff --git a/include/lgi/common/App.h b/include/lgi/common/App.h --- a/include/lgi/common/App.h +++ b/include/lgi/common/App.h @@ -1,435 +1,435 @@ #ifndef _GAPP_H_ #define _GAPP_H_ ///////////////////////////////////////////////////////////////////////////////// #if WINNATIVE typedef DWORD OsProcessId; #else typedef int OsProcessId; #endif /// Returns the current process ID #define LProcessId() (LAppInst->GetProcessId()) /// Returns a pointer to the LApp object. /// /// \warning Don't use this before you have created your LApp object. i.e. in a constructor /// of a global static class which is initialized before the main begins executing. #define LAppInst (LApp::ObjInstance()) /// Process any pending messages in the applications message que and then return. #define LYield() LAppInst->Yield() /// Returns a system font pointer. /// /// \warning Don't use this before you have created your LApp object. i.e. in a constructor /// of a global static class which is initialized before the main begins executing. #define LSysFont (LAppInst->SystemNormal) /// Returns a bold system font pointer. /// /// \warning Don't use this before you have created your LApp object. i.e. in a constructor /// of a global static class which is initialized before the main begins executing. #define LSysBold (LAppInst->SystemBold) /// Exits the application right now! /// /// \warning This will cause data loss if you have any unsaved data. Equivilant to exit(0). LgiFunc void LExitApp(); /// Closes the application gracefully. /// /// This actually causes LApp::Run() to stop processing message and return. #define LCloseApp() LAppInst->Exit(false) #if defined(LINUX) && !defined(LGI_SDL) #define ThreadCheck() LAssert(InThread()) #else #define ThreadCheck() #endif /// Optional arguments to the LApp object struct LAppArguments { /// Don't initialize the skinning engine. bool NoSkin = false; /// Don't do crash handling bool NoCrashHandler = false; LAppArguments(const char *init = NULL) { auto a = LString(init).SplitDelimit(","); for (auto o: a) if (o.Equals("NoCrashHandler")) NoCrashHandler = true; else if (o.Equals("NoSkin")) NoSkin = true; } }; class LAppPrivate; #if LGI_COCOA && defined __OBJC__ #include "LCocoaView.h" @interface LNsApplication : NSApplication { } @property LAppPrivate *d; - (id)init; - (void)setPriv:(nonnull LAppPrivate*)priv; - (void)terminate:(nullable id)sender; - (void)dealloc; - (void)assert:(nonnull LCocoaAssert*)ca; - (void)onUrl:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)reply; @end ObjCWrapper(LNsApplication, OsApp) #endif /// \brief Singleton class for handling application wide settings and methods /// /// This should be the first class you create, passing in the arguments from the /// operating system. And once your initialization is complete the 'Run' method /// is called to enter the main application loop that processes messages for the /// life time of the application. class LgiClass LApp : virtual public LAppI, public LBase, public OsApplication { friend class LView; friend class LWindow; public: typedef LHashTbl, LWindowsClass*> ClassContainer; protected: // private member vars class LAppPrivate *d = NULL; #if defined LGI_SDL void OnSDLEvent(LMessage *m); #elif defined LGI_COCOA LAutoPtr Default; #elif defined WIN32 CRITICAL_SECTION StackTraceSync; friend LONG __stdcall _ExceptionFilter_Redir(LPEXCEPTION_POINTERS e); LONG __stdcall _ExceptionFilter(LPEXCEPTION_POINTERS e, char *ProductId); friend class LWindowsClass; ClassContainer *GetClasses(); #elif defined LINUX friend class LClipBoard; // virtual void OnEvents(); void DeleteMeLater(LViewI *v); void SetClipBoardContent(OsView Hnd, LVariant &v); bool GetClipBoardContent(OsView Hnd, LVariant &v, LArray &Types); #endif friend class LMouseHook; static LMouseHook *MouseHook; public: // Static publics #ifdef LINUX constexpr static const char *CfgLinuxKeysShift = "Linux.Keys.Shift"; constexpr static const char *CfgLinuxKeysCtrl = "Linux.Keys.Ctrl"; constexpr static const char *CfgLinuxKeysAlt = "Linux.Keys.Alt"; constexpr static const char *CfgLinuxKeysSystem = "Linux.Keys.System"; constexpr static const char *CfgLinuxMouseLeft = "Linux.Mouse.Left"; constexpr static const char *CfgLinuxMouseMiddle = "Linux.Mouse.Middle"; constexpr static const char *CfgLinuxMouseRight = "Linux.Mouse.Right"; constexpr static const char *CfgLinuxMouseBack = "Linux.Mouse.Back"; constexpr static const char *CfgLinuxMouseForward = "Linux.Mouse.Forward"; #endif constexpr static const char *CfgFontsGlyphSub = "Fonts.GlyphSub"; constexpr static const char *CfgFontsPointSizeOffset = "Fonts.PointSizeOffset"; constexpr static const char *CfgFontsSystemFont = "Fonts.SystemFont"; constexpr static const char *CfgFontsBoldFont = "Fonts.BoldFont"; constexpr static const char *CfgFontsMonoFont = "Fonts.MonoFont"; constexpr static const char *CfgFontsSmallFont = "Fonts.SmallFont"; constexpr static const char *CfgFontsCaptionFont = "Fonts.CaptionFont"; constexpr static const char *CfgFontsMenuFont = "Fonts.MenuFont"; /// Use 'LAppInst' to return a pointer to the LApp object static LApp *ObjInstance(); static class LSkinEngine *SkinEngine; // public member vars /// The system font LFont *SystemNormal = NULL; /// The system font in bold LFont *SystemBold = NULL; /// Pointer to the applications main window LWindow *AppWnd = NULL; /// Returns true if the LApp object initialized correctly bool IsOk(); /// Returns this processes ID OsProcessId GetProcessId(); /// Returns the thread currently running the active message loop OsThread _GetGuiThread(); OsThreadId GetGuiThreadId(); bool InThread(); /// Returns the number of CPU cores the machine has int GetCpuCount(); /// Construct the object LApp ( /// The arguments passed in by the OS. OsAppArguments &AppArgs, /// The application's name. const char *AppName, /// Optional args LAppArguments *ObjArgs = 0 ); /// Destroys the object virtual ~LApp(); /// Resets the arguments void SetAppArgs(OsAppArguments &AppArgs); /// Returns the arguemnts OsAppArguments *GetAppArgs(); /// Returns the n'th argument as a heap string. Free with DeleteArray(...). const char *GetArgumentAt(int n); /// Enters the message loop. bool Run ( /// Idle callback OnIdleProc IdleCallback = NULL, /// Param for IdleCallback void *IdleParam = NULL ); /// Processes any messages in the queue and then returns. [[deprecated]] bool Yield(); /// Event called to process the command line void OnCommandLine(); /// Event called to process files dropped on the application void OnReceiveFiles(LArray &Files); /// Event called to process URLs given to the application void OnUrl(const char *Url); /// Exits the event loop with the code specified void Exit ( /// The application exit code. int Code = 0 ); /// \brief Parses the command line for a switch /// \return true if the option exists. bool GetOption ( /// The option to look for. const char *Option, /// String to receive the value (if any) of the option LString &Value ); /// \brief Parses the command line for a switch /// \return true if the option exists. bool GetOption ( /// The option to look for. const char *Option, /// The buffer to receive the value of the command line parameter or NULL if you don't care. char *Dst = 0, /// The buffer size in bytes int DstSize = 0 ); /// Return the path to the Lgi config file... (not the same as the application options, more global Lgi apps settings) ::LString GetConfigPath(); /// Gets the application conf stored in lgi.conf ::LString GetConfig(const char *Variable); /// Sets a single tag in the config. (Not written to disk) void SetConfig(const char *Variable, const char *Value); /// Gets the control with the keyboard focus LViewI *GetFocus(); /// Gets the MIME type of a file /// \returns the mime type or NULL if unknown. LString GetFileMimeType ( /// The file to identify const char *File ); /// Gets the applications that can handle a file of a certain mime type - bool GetAppsForMimeType(char *Mime, LArray &Apps); + bool GetAppsForMimeType(const char *Mime, LArray &Apps); /// Get a system metric int32 GetMetric ( /// One of #LGI_MET_DECOR_X, #LGI_MET_DECOR_Y LSystemMetric Metric ); /// Get the mouse hook instance LMouseHook *GetMouseHook(); /// Gets the singleton symbol lookup class class LSymLookup *GetSymLookup(); /// \returns true if the process is running with elevated permissions bool IsElevated(); /// Gets the font cache class LFontCache *GetFontCache(); // OS Specific #if defined(LGI_SDL) /// This keeps track of the dirty rectangle and issues a M_INVALIDATE /// event when needed to keep the screen up to date. bool InvalidateRect(LRect &r); /// Push a new window to the top of the window stack bool PushWindow(LWindow *w); /// Remove the top most window LWindow *PopWindow(); /// Sets up mouse tracking beyond the current window... void CaptureMouse(bool capture); /// Returns the freetype version as a string. LString GetFreetypeVersion(); #elif defined(WIN32) HINSTANCE GetInstance(); int GetShow(); /// \returns true if the application is running under Wine on Linux. This is useful to know /// if you need to work around missing functionality in the Wine implementation. bool IsWine(); #elif defined(LINUX) class LLibrary *GetWindowManagerLib(); class DesktopInfo { friend class LApp; LString File; bool Dirty; struct KeyPair { LString Key, Value; }; struct Section { LString Name; LArray Values; KeyPair *Get(const char *Name, bool Create, bool &Dirty) { for (unsigned i=0; iKey.Equals(Name)) return kp; } if (Create) { KeyPair *kp = &Values.New(); kp->Key = Name; Dirty = true; return kp; } return NULL; } }; LArray
Data; bool Serialize(bool Write); Section *GetSection(const char *Name, bool Create); public: DesktopInfo(const char *file); LString Get(const char *Field, const char *Section = NULL); bool Set(const char *Field, const char *Value, const char *Section = NULL); bool Update() { return Dirty ? Serialize(true) : true; } const char *GetFile() { return File; } }; DesktopInfo *GetDesktopInfo(); bool SetApplicationIcon(const char *FileName); #elif LGI_COCOA && defined(__OBJC__) OsApp &Handle(); #endif #ifdef __GTK_H__ struct KeyModFlags { int Shift, Alt, Ctrl, System; bool Debug; KeyModFlags() { Shift = 0; Alt = 0; Ctrl = 0; System = 0; Debug = false; } const char *FlagName(int Flag); // Single flag to string int FlagValue(const char *Name); // Single name to bitmask ::LString FlagsToString(int Flags); // Turn multiple flags to string }; KeyModFlags *GetKeyModFlags(); void OnDetach(LViewI *View); #endif #if !LGI_VIEW_HANDLE bool PostEvent(LViewI *View, int Msg, LMessage::Param a = 0, LMessage::Param b = 0); #endif }; #endif diff --git a/include/lgi/common/Font.h b/include/lgi/common/Font.h --- a/include/lgi/common/Font.h +++ b/include/lgi/common/Font.h @@ -1,401 +1,401 @@ /// \file /// \author Matthew Allen #ifndef _GDCFONT_H_ #define _GDCFONT_H_ #include "string.h" #include "lgi/common/Rect.h" #include "LgiOsClasses.h" #include "lgi/common/Colour.h" #include "lgi/common/Capabilities.h" #include "lgi/common/Css.h" #include ////////////////////////////////////////////////////////////// // Defines #ifndef WIN32 // font weights #define FW_DONTCARE 0 #define FW_THIN 100 #define FW_EXTRALIGHT 200 #define FW_ULTRALIGHT 200 #define FW_LIGHT 300 /// The default font weight #define FW_NORMAL 400 #define FW_REGULAR 400 #define FW_MEDIUM 500 #define FW_SEMIBOLD 600 #define FW_DEMIBOLD 600 /// Bold font weight #define FW_BOLD 700 #define FW_EXTRABOLD 800 #define FW_ULTRABOLD 800 #define FW_HEAVY 900 #define FW_BLACK 900 // quality /// Default font quality #define DEFAULT_QUALITY 0 /// Specifically anti-aliased font #define ANTIALIASED_QUALITY 1 /// Specifically not anti-alias font #define NONANTIALIASED_QUALITY 2 #elif defined WIN32 #define WESTEUROPE_CHARSET BALTIC_CHARSET // ??? don't know #endif // OS specific font type #if defined(LGI_SDL) #define PrevOsChar(Ptr) Ptr-- #define NextOsChar(Ptr) Ptr++ #elif defined __GTK_H__ #include "LgiOsClasses.h" #define PrevOsChar(Ptr) Ptr-- #define NextOsChar(Ptr) Ptr++ #elif defined(WIN32) #define PrevOsChar(Ptr) Ptr-- #define NextOsChar(Ptr) Ptr++ #endif #define LGI_WHITESPACE_WEIGHT 0.15f // amount of foreground colour in visible whitespace #define MAX_UNICODE 0x10FFFF // maximum unicode char I can handle #define _HasUnicodeGlyph(map, u) ( (map[(u)>>3] & (1 << ((u) & 7))) != 0 ) enum LPxToIndexType { LgiTruncate, LgiNearest }; ////////////////////////////////////////////////////////////// // Classes class LFontType; /// Font parameters collection class LgiClass LTypeFace { protected: class LTypeFacePrivate *d; // Methods virtual void _OnPropChange(bool c) {} // if false then it's just a property change public: LTypeFace(); virtual ~LTypeFace(); /// Sets the font face name void Face(const char *s); /// Sets the font size void Size(LCss::Len); /// Sets the point size void PointSize(int i); /// Sets the tab size in device units (pixels) void TabSize(int i); /// Sets the quality resquested, use one of #DEFAULT_QUALITY, #ANTIALIASED_QUALITY or #NONANTIALIASED_QUALITY. void Quality(int i); /// Sets the foreground colour as a 24 bit RGB value void Fore(LSystemColour c); void Fore(LColour c); void Back(LSystemColour c); void Back(LColour c); /// Get the whitespace rendering colour LColour WhitespaceColour(); /// Sets the rendering colour of whitespace characters when LDisplayString::ShowVisibleTab() is true. void WhitespaceColour(LColour c); /// Sets the font's weight, use one the weight defines in LFont.h, e.g. #FW_NORMAL, #FW_BOLD void SetWeight(int Weight); /// Set a bold font void Bold(bool i) { SetWeight(i ? FW_BOLD : FW_NORMAL); } /// Sets the font to italic void Italic(bool i); /// Draws with an underline void Underline(bool i); /// Makes the text have no background. void Transparent(bool i); /// Turns glyph substitution on or off void SubGlyphs(bool i); /// \returns the font face const char *Face() const; /// \returns the point size (avoid, use 'Size' instead) int PointSize() const; /// \returns the size LCss::Len Size() const; /// \returns the tabsize in pixels int TabSize() const; /// \returns the quality setting int Quality() const; /// \returns the foreground colour. LColour Fore() const; /// \returns the background colour. LColour Back() const; /// \returns the font weight. int GetWeight() const; /// \returns true if this is a bold font. bool Bold() const { return GetWeight() >= FW_BOLD; } /// \returns true if this is a italic font. bool Italic() const; /// \returns true if this font is drawn with an underline. bool Underline() const; /// \returns true if no background will be drawn. bool Transparent() const; /// \returns true if glyph substitution will be done. bool SubGlyphs() const; /// \returns the amount of space above the baseline. double Ascent() const; /// \returns the amount of space below the baseline. double Descent() const; /// \returns the amount of normally unused space at the top of the Ascent. double Leading() const; /// \returns true if the font types are the same bool operator ==(const LTypeFace &t); /// Set the foreground and background in 24-bit colour. /// \sa LTypeFace::Fore() and LTypeFace::Back() virtual void Colour(LSystemColour Fore, LSystemColour Back = L_TRANSPARENT); /// Set the foreground and background colour. virtual void Colour(LColour Fore, LColour Back); }; /// \brief Font class. class LgiClass LFont : public LTypeFace { friend class LFontSystem; friend class LDisplayString; protected: class LFontPrivate *d; // Methods bool IsValid(); void _OnPropChange(bool Change); char16 *_ToUnicode(char *In, ssize_t &Len); bool GetOwnerUnderline(); virtual void _Measure(int &x, int &y, OsChar *Str, int Len); virtual int _CharAt(int x, OsChar *Str, int Len, LPxToIndexType Type); virtual void _Draw(LSurface *pDC, int x, int y, OsChar *Str, int Len, LRect *r, LColour &fore); public: /// Construct from face/pt size. LFont ( /// Font face name const char *face = 0, /// Size of the font LCss::Len size = LCss::LenInherit ); /// Construct from OS font handle LFont(OsFont Handle); /// Construct from type information LFont(LFontType &Type); /// Copy constructor LFont(LFont &Fnt); virtual ~LFont(); /// Creates a new font handle with the specified face / pt size virtual bool Create ( /// The new font face const char *Face = 0, /// The pt size LCss::Len Size = LCss::LenInherit, /// Creating a font for a particular surface (e.g. printing). LSurface *pSurface = 0 ); /// Creates a new font from type infomation bool Create(LFontType *Type, LSurface *pSurface = NULL); /// Creates the font from CSS defs. bool CreateFromCss(const char *Css); /// Creates the font from a CSS object. bool CreateFromCss(LCss *Css); /// Returns CSS styles to describe font LString FontToCss(); /// Clears any handles and memory associated with the object. virtual bool Destroy(); void WarnOnDelete(bool w); /// Returns the OS font handle virtual OsFont Handle(); /// Copies the font virtual LFont &operator =(const LFont &f); /// Returns the pixel height of the font virtual int GetHeight(); /// Gets the creation parameter passed in (0 by default). LSurface *GetSurface(); /// Get supported glyphs uchar *GetGlyphMap(); /// Converts printable characters to unicode. LAutoString ConvertToUnicode(char16 *Input, ssize_t Len = -1); #if USE_CORETEXT CFDictionaryRef GetAttributes(); #endif }; /// Font type information and system font query tools. class LgiClass LFontType { friend class LFont; friend class LTypeFace; protected: #if WINNATIVE LOGFONTW Info; #else LTypeFace Info; #endif LString Buf; public: LFontType(const char *face = 0, int pointsize = 0); virtual ~LFontType(); #ifdef WINNATIVE LOGFONTW *Handle() { return &Info; } #else LTypeFace *Handle() { return &Info; } #endif /// Gets the type face name const char *GetFace(); /// Sets the type face name void SetFace(const char *Face); /// Sets the point size int GetPointSize(); /// Sets the point size void SetPointSize(int PointSize); /// Put a user interface for the user to edit the font def void DoUI(LView *Parent, std::function Callback); /// Describe the font to the user as a string bool GetDescription(char *Str, int SLen); /// Read/Write the font def to storage // bool Serialize(ObjProperties *Options, char *OptName, bool Write); /// Read/Write the font def to storage bool Serialize(LDom *Options, const char *OptName, bool Write); /// Read the font from the LGI config bool GetConfigFont(const char *Tag); /// Read the font from LGI config (if there) and then the system settings bool GetSystemFont(const char *Which); /// Read from OS reference bool GetFromRef(OsFont Handle); /// Create a font based on this font def virtual LFont *Create(LSurface *pSurface = NULL); }; #ifndef LGI_STATIC /// Overall font system class class LgiClass LFontSystem : public LCapabilityClient { friend class LApp; friend class LDisplayString; static LFontSystem *Me; private: // System Font List LString::Array AllFonts; LString::Array SubFonts; // Fonts yet to be scanned for substitution // Missing Glyph Substitution uchar Lut[MAX_UNICODE+1]; LFont *Font[256]; class LFontSystemPrivate *d; public: /// Get a pointer to the font system. static LFontSystem *Inst(); // Object LFontSystem(); ~LFontSystem(); /// Enumerate all installed fonts bool EnumerateFonts(LString::Array &Fonts); /// Returns whether the current Lgi implementation supports glyph sub bool GetGlyphSubSupport(); /// Returns whether glyph sub is currently turned on bool GetDefaultGlyphSub(); /// Turns the glyph sub feature on or off void SetDefaultGlyphSub(bool i); /// Returns a font that can render the specified unicode code point LFont *GetGlyph ( /// A utf-32 character uint32_t u, /// The base font used for rendering LFont *UserFont ); /// This looks for a font that can contains the most glyphs for a given string, /// ideally it can render the whole thing. But the next best alternative is returned /// when no font matches all characters in the string. LFont *GetBestFont(char *Str); /// Add a custom font to the glyph lookup table bool AddFont(LAutoPtr Fnt); #ifdef __GTK_H__ // Pango stuff Gtk::PangoFontMap *GetFontMap(); Gtk::PangoContext *GetContext(); #endif void ResetLibCheck(); /// \returns true if iconv services are available. bool HasIconv(bool Quiet); /// Converts a string into a buffer using iconv ssize_t IconvConvert(const char *OutCs, char *Out, ssize_t OutLen, const char *InCs, const char *&In, ssize_t InLen); /// Converts a string into a stream using iconv ssize_t IconvConvert(const char *OutCs, LStreamI *Out, const char *InCs, const char *&In, ssize_t InLen); }; #endif #ifdef LINUX -extern bool _GetSystemFont(char *FontType, char *Font, int FontBufSize, int &PointSize); +extern bool _GetSystemFont(const char *FontType, char *Font, int FontBufSize, int &PointSize); #endif #endif diff --git a/private/common/ViewPriv.h b/private/common/ViewPriv.h --- a/private/common/ViewPriv.h +++ b/private/common/ViewPriv.h @@ -1,247 +1,247 @@ // Private LView definations #pragma once #if WINNATIVE #if !defined(_WIN32_WINNT) || _WIN32_WINNT < 0x501 #undef _WIN32_WINNT #define _WIN32_WINNT 0x501 #endif #include "commctrl.h" #include "Uxtheme.h" #define GViewFlags d->WndStyle #else #define GViewFlags WndFlags #endif #if defined(__GTK_H__) struct LCaptureThread : public LThread, public LCancel { int view = -1; public: constexpr static int EventMs = 150; LCaptureThread(LView *v); ~LCaptureThread(); int Main(); }; #elif defined(MAC) extern OsThread LgiThreadInPaint; #elif defined(HAIKU) #include extern void *IsAttached(BView *v); #endif -#define PAINT_VIRTUAL_CHILDREN 1 +#define PAINT_VIRTUAL_CHILDREN 0 extern bool In_SetWindowPos; extern LMouse &lgi_adjust_click(LMouse &Info, LViewI *Wnd, bool Capturing = false, bool Debug = false); #ifdef __GTK_H__ extern LPoint GtkAbsPos(Gtk::GtkWidget *w); extern LRect GtkGetPos(Gtk::GtkWidget *w); #endif #if !WINNATIVE #include "lgi/common/ThreadEvent.h" class LPulseThread : public LThread, public LCancel { LView *View = NULL; LString ViewClass; int Length = 0; LThreadEvent Event; uint64_t WarnTs = 0; LString MakeName(LView *v, const char *Type) { LString s; s.Printf("LPulseThread.%s.%s", v->GetClass(), Type); return s; } public: LPulseThread(LView *view, int len) : View(view), LThread(MakeName(view, "Thread")), Event(MakeName(view, "Event")) { LAssert(View); Length = len; ViewClass = View->GetClass(); Run(); } ~LPulseThread() { Cancel(); View = NULL; Event.Signal(); while (!IsExited()) LSleep(1); } int Main() { while (!IsCancelled() && LAppInst) { auto s = Event.Wait(Length); if (IsCancelled() || s == LThreadEvent::WaitError) break; if (View) { auto r = View->PostEvent(M_PULSE, 0, 0, 50/*milliseconds*/); #if 0 if (!r) { auto now = LCurrentTime(); if (now - WarnTs >= 5000) { WarnTs = now; printf("%s:%i - PulseThread::PostEvent failed for %p/%s.\n", _FL, View, ViewClass.Get()); } } #endif } } return 0; } }; #endif enum LViewFontType { /// The LView has a pointer to an externally owned font. GV_FontPtr, /// The LView owns the font object, and must free it. GV_FontOwned, /// The LApp's font cache owns the object. In this case, /// calling GetCssStyle on the LView will invalidate the /// font ptr causing it to be re-calculated. GV_FontCached, }; class LViewPrivate { public: // General LView *View = NULL; // Owning view LDragDropSource *DropSource = NULL; LDragDropTarget *DropTarget = NULL; bool IsThemed = false; int CtrlId = -1; int WantsPulse = -1; // Hierarchy LViewI *ParentI = NULL; LView *Parent = NULL; LViewI *Notify = NULL; // Size LPoint MinimumSize; // Font LFont *Font = NULL; LViewFontType FontOwnType = GV_FontPtr; // Style LAutoPtr Css; bool CssDirty = false; // This is set when 'Styles' changes, the next call to GetCss(...) parses // the styles into the 'Css' object. LString Styles; // Somewhat temporary object to store unparsed styles particular to // this view until runtime, where the view heirarchy is known. LString::Array Classes; // Event dispatch handle int SinkHnd = -1; // OS Specific #if WINNATIVE static OsView hPrevCapture; int WndStyle = 0; // Windows hWnd Style int WndExStyle = 0; // Windows hWnd ExStyle int WndDlgCode = 0; // Windows Dialog Code (WM_GETDLGCODE) LString WndClass; UINT_PTR TimerId = 0; HTHEME hTheme = NULL; #else // Cursor LPulseThread *PulseThread = NULL; LView *Popup = NULL; bool TabStop = false; bool WantsFocus = false; #if defined __GTK_H__ bool InPaint = false; bool GotOnCreate = false; #elif defined(MAC) && !defined(LGI_COCOA) static HIObjectClassRef BaseClass; #endif #endif #if defined(__GTK_H__) LCaptureThread *CaptureThread = NULL; LMouse PrevMouse; #elif defined(MAC) #ifdef LGI_COCOA LString ClassName; bool AttachEvent; #elif defined LGI_CARBON EventHandlerRef DndHandler; LAutoString AcceptedDropFormat; #endif #elif defined(LGI_SDL) SDL_TimerID PulseId; int PulseLength = -1; #elif defined(HAIKU) BView *Hnd = NULL; LArray MsgQue; // For before the window is attached... #endif LViewPrivate(LView *view); ~LViewPrivate(); LView *GetParent() { if (Parent) return Parent; if (ParentI) return ParentI->GetGView(); return 0; } }; diff --git a/src/common/Gdc2/Colour.cpp b/src/common/Gdc2/Colour.cpp --- a/src/common/Gdc2/Colour.cpp +++ b/src/common/Gdc2/Colour.cpp @@ -1,930 +1,936 @@ #include "lgi/common/Lgi.h" #include "lgi/common/Palette.h" #include "lgi/common/SkinEngine.h" #include "lgi/common/Json.h" const LColour LColour::Black(0, 0, 0); const LColour LColour::White(255, 255, 255); const LColour LColour::Red(255, 0, 0); const LColour LColour::Green(0, 192, 0); const LColour LColour::Blue(0, 0, 255); LColour::LColour() { space = CsNone; flat = 0; pal = NULL; } LColour::LColour(const char *Str) { space = CsNone; flat = 0; pal = NULL; SetStr(Str); } LColour::LColour(uint8_t idx8, LPalette *palette) { pal = NULL; c8(idx8, palette); } LColour::LColour(int r, int g, int b, int a) { pal = NULL; space = System32BitColourSpace; rgb.r = limit(r, 0, 255); rgb.g = limit(g, 0, 255); rgb.b = limit(b, 0, 255); rgb.a = limit(a, 0, 255); } LColour::LColour(uint32_t c, int bits, LPalette *palette) { pal = NULL; Set(c, bits, palette); } #ifdef __GTK_H__ LColour::LColour(Gtk::GdkRGBA gtk) { pal = NULL; Rgb(gtk.red * 255.0, gtk.green * 255.0, gtk.blue * 255.0, gtk.alpha * 255.0); } #endif int LColour::HlsValue(double fN1, double fN2, double fHue) const { if (fHue > 360.0) fHue -= 360.0; else if (fHue < 0.0) fHue += 360.0; if (fHue < 60.0) return (int) ((fN1 + (fN2 - fN1) * fHue / 60.0) * 255.0 + 0.5); else if (fHue < 180.0) return (int) ((fN2 * 255.0) + 0.5); else if (fHue < 240.0) return (int) ((fN1 + (fN2 - fN1) * (240.0 - fHue) / 60.0) * 255.0 + 0.5); return (int) ((fN1 * 255.0) + 0.5); } LColourSpace LColour::GetColourSpace() { return space; } bool LColour::SetColourSpace(LColourSpace cs) { if (space == CsNone) { space = cs; rgb.a = 255; return true; } if (space == cs) return true; LAssert(!"Impl conversion."); return false; } bool LColour::IsValid() const { return space != CsNone; } void LColour::Empty() { space = CsNone; } bool LColour::IsTransparent() { if (space == System32BitColourSpace) return rgb.a == 0; else if (space == CsIndex8) return !pal || index >= pal->GetSize(); return space == CsNone; } void LColour::Rgb(int r, int g, int b, int a) { c32(Rgba32(r, g, b, a)); } void LColour::Set(LSystemColour c) { *this = LColour(c); } void LColour::Set(uint32_t c, int bits, LPalette *palette) { pal = 0; switch (bits) { case 8: { index = c; space = CsIndex8; pal = palette; break; } case 15: { space = System32BitColourSpace; rgb.r = Rc15(c); rgb.g = Gc15(c); rgb.b = Bc15(c); rgb.a = 255; break; } case 16: { space = System32BitColourSpace; rgb.r = Rc16(c); rgb.g = Gc16(c); rgb.b = Bc16(c); rgb.a = 255; break; } case 24: case 48: { space = System32BitColourSpace; rgb.r = R24(c); rgb.g = G24(c); rgb.b = B24(c); rgb.a = 255; break; } case 32: case 64: { space = System32BitColourSpace; rgb.r = R32(c); rgb.g = G32(c); rgb.b = B32(c); rgb.a = A32(c); break; } default: { space = System32BitColourSpace; flat = 0; LgiTrace("Error: Unable to set colour %x, %i\n", c, bits); LAssert(!"Not a known colour depth."); } } } uint32_t LColour::Get(int bits) { switch (bits) { case 8: if (space == CsIndex8) return index; LAssert(!"Not supported."); break; case 24: return c24(); case 32: return c32(); } return 0; } uint8_t LColour::r() const { return R32(c32()); } void LColour::r(uint8_t i) { if (SetColourSpace(System32BitColourSpace)) rgb.r = i; else LAssert(0); } uint8_t LColour::g() const { return G32(c32()); } void LColour::g(uint8_t i) { if (SetColourSpace(System32BitColourSpace)) rgb.g = i; else LAssert(0); } uint8_t LColour::b() const { return B32(c32()); } void LColour::b(uint8_t i) { if (SetColourSpace(System32BitColourSpace)) rgb.b = i; else LAssert(0); } uint8_t LColour::a() const { return A32(c32()); } void LColour::a(uint8_t i) { if (SetColourSpace(System32BitColourSpace)) rgb.a = i; else LAssert(0); } uint8_t LColour::c8() const { return index; } void LColour::c8(uint8_t c, LPalette *p) { space = CsIndex8; pal = p; index = c; } uint32_t LColour::c24() const { if (space == System32BitColourSpace) { return Rgb24(rgb.r, rgb.g, rgb.b); } else if (space == CsIndex8) { if (pal) { // colour palette lookup if (index < pal->GetSize()) { GdcRGB *c = (*pal)[index]; if (c) { return Rgb24(c->r, c->g, c->b); } } return 0; } return Rgb24(index, index, index); // monochome } else if (space == CsHls32) { return Rgb32To24(c32()); } // Black... return 0; } void LColour::c24(uint32_t c) { space = System32BitColourSpace; rgb.r = R24(c); rgb.g = G24(c); rgb.b = B24(c); rgb.a = 255; pal = NULL; } uint32_t LColour::c32() const { if (space == System32BitColourSpace) { return Rgba32(rgb.r, rgb.g, rgb.b, rgb.a); } else if (space == CsIndex8) { if (pal) { // colour palette lookup if (index < pal->GetSize()) { GdcRGB *c = (*pal)[index]; if (c) { return Rgb32(c->r, c->g, c->b); } } return 0; } return Rgb32(index, index, index); // monochome } else if (space == CsHls32) { // Convert from HLS back to RGB LColour tmp = *this; tmp.ToRGB(); return tmp.c32(); } // Transparent? return 0; } void LColour::c32(uint32_t c) { space = System32BitColourSpace; pal = NULL; rgb.r = R32(c); rgb.g = G32(c); rgb.b = B32(c); rgb.a = A32(c); } LColour LColour::Invert() { LColour i(255-r(), 255-g(), 255-b()); return i; } LColour LColour::Mix(LColour Tint, float RatioOfTint) const { COLOUR c1 = c32(); COLOUR c2 = Tint.c32(); float RatioThis = 1.0f - RatioOfTint; int r = (int) ((RatioThis * R32(c1)) + (RatioOfTint * R32(c2)) + 0.5f); int g = (int) ((RatioThis * G32(c1)) + (RatioOfTint * G32(c2)) + 0.5f); int b = (int) ((RatioThis * B32(c1)) + (RatioOfTint * B32(c2)) + 0.5f); int a = (int) ((RatioThis * A32(c1)) + (RatioOfTint * A32(c2)) + 0.5f); return LColour(r, g, b, a); } uint32_t LColour::GetH() { ToHLS(); return hls.h; } bool LColour::HueIsUndefined() { ToHLS(); return hls.h == HUE_UNDEFINED; } uint32_t LColour::GetL() { ToHLS(); return hls.l; } uint32_t LColour::GetS() { ToHLS(); return hls.s; } bool LColour::ToHLS() { if (space == CsHls32) return true; uint32_t nMax, nMin, nDelta, c = c32(); int R = R32(c), G = G32(c), B = B32(c); double fHue; nMax = MAX(R, MAX(G, B)); nMin = MIN(R, MIN(G, B)); if (nMax == nMin) return false; hls.l = (nMax + nMin) / 2; if (hls.l < 128) hls.s = (uchar) ((255.0 * ((double)(nMax - nMin)) / (double)(nMax + nMin)) + 0.5); else hls.s = (uchar) ((255.0 * ((double)(nMax - nMin)) / (double)(511 - nMax - nMin)) + 0.5); nDelta = nMax - nMin; if (R == nMax) fHue = ((double) (G - B)) / (double) nDelta; else if (G == nMax) fHue = 2.0 + ((double) (B - R)) / (double) nDelta; else fHue = 4.0 + ((double) (R - G)) / (double) nDelta; fHue *= 60; if (fHue < 0.0) fHue += 360.0; hls.h = (uint16) (fHue + 0.5); space = CsHls32; pal = NULL; return true; } void LColour::SetHLS(uint16 h, uint8_t l, uint8_t s) { space = CsHls32; hls.h = h; hls.l = l; hls.s = s; } void LColour::ToRGB() { LHls32 Hls = hls; if (Hls.s == 0) { Rgb(0, 0, 0); } else { while (Hls.h >= 360) Hls.h -= 360; while (hls.h < 0) Hls.h += 360; double fHue = (double) Hls.h, fM1, fM2; double fLightness = ((double) Hls.l) / 255.0; double fSaturation = ((double) Hls.s) / 255.0; if (Hls.l < 128) fM2 = fLightness * (1 + fSaturation); else fM2 = fLightness + fSaturation - (fLightness * fSaturation); fM1 = 2.0 * fLightness - fM2; Rgb(HlsValue(fM1, fM2, fHue + 120.0), HlsValue(fM1, fM2, fHue), HlsValue(fM1, fM2, fHue - 120.0)); } } int LColour::GetGray(int BitDepth) const { if (BitDepth == 8) { int R = r() * 77; int G = g() * 151; int B = b() * 28; return (R + G + B) >> 8; } double Scale = 1 << BitDepth; int R = (int) ((double) r() * (0.3 * Scale) + 0.5); int G = (int) ((double) g() * (0.59 * Scale) + 0.5); int B = (int) ((double) b() * (0.11 * Scale) + 0.5); return (R + G + B) >> BitDepth; } uint32_t LColour::GetNative() { #ifdef WIN32 if (space == CsIndex8) { if (pal && index < pal->GetSize()) { GdcRGB *c = (*pal)[index]; if (c) { return RGB(c->r, c->g, c->b); } } return RGB(index, index, index); } else if (space == System32BitColourSpace) { return RGB(rgb.r, rgb.g, rgb.b); } else if (space == CsHls32) { LColour c(*this); c.ToRGB(); return RGB(c.r(), c.g(), c.b()); } else { LAssert(0); } #else LAssert(0); #endif return c32(); } char *LColour::GetStr() { static char Buf[4][32]; static int Idx = 0; int b = Idx++; if (Idx >= 4) Idx = 0; switch (space) { case System32BitColourSpace: if (rgb.a == 0xff) { sprintf_s( Buf[b], 32, "rgb(%i,%i,%i)", rgb.r, rgb.g, rgb.b); } else { sprintf_s( Buf[b], 32, "rgba(%i,%i,%i,%i)", rgb.r, rgb.g, rgb.b, rgb.a); } break; case CsIndex8: sprintf_s( Buf[b], 32, "index(%i)", index); break; case CsHls32: sprintf_s( Buf[b], 32, "hls(%i,%i,%i)", hls.h, hls.l, hls.s); break; default: sprintf_s( Buf[b], 32, "unknown(%i)", space); break; } return Buf[b]; } bool LColour::SetStr(const char *str) { if (!str) return false; LString Str = str; if (*str == '#') { // Web colour Str = Str.Strip("# \t\r\n"); if (Str.Length() == 3) { auto h = htoi(Str.Get()); uint8_t r = (h >> 8) & 0xf; uint8_t g = (h >> 4) & 0xf; uint8_t b = (h) & 0xf; Rgb(r | (r << 4), g | (g << 4), b | (b << 4)); } else if (Str.Length() == 6) { auto h = htoi(Str.Get()); uint8_t r = (h >> 16) & 0xff; uint8_t g = (h >> 8) & 0xff; uint8_t b = (h) & 0xff; Rgb(r, g, b); } else return false; return true; } char *s = strchr(Str, '('); if (!s) return false; char *e = strchr(s + 1, ')'); if (!s) return false; *s = 0; *e = 0; LString::Array Comp = LString(s+1).Split(","); if (!Stricmp(Str.Get(), "rgb")) { if (Comp.Length() == 3) Rgb((int)Comp[0].Int(), (int)Comp[1].Int(), (int)Comp[2].Int()); else if (Comp.Length() == 4) Rgb((int)Comp[0].Int(), (int)Comp[1].Int(), (int)Comp[2].Int(), (int)Comp[3].Int()); else return false; } else if (!Stricmp(Str.Get(), "hls")) { if (Comp.Length() == 3) SetHLS((uint16) Comp[0].Int(), (uint8_t) Comp[1].Int(), (uint8_t) Comp[2].Int()); else return false; } else if (!Stricmp(Str.Get(), "index")) { if (Comp.Length() == 1) { index = (uint8_t) Comp[0].Int(); space = CsIndex8; } else return false; } else return false; return true; } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// static LColour _LgiColours[L_MAXIMUM]; #define ReadColourConfig(def) LColour::GetConfigColour("Colour."#def, _LgiColours[def]) bool LColour::GetConfigColour(const char *Tag, LColour &c) { #ifdef LGI_STATIC return false; #else if (!Tag) return false; auto Col = LAppInst->GetConfig(Tag); if (!Col) return false; auto n = (int)Col.Strip("#").Int(16); c.Rgb( n>>16, n>>8, n ); return true; #endif } //////////////////////////////////////////////////////////////////////////// #ifdef __GTK_H__ COLOUR ColTo24(Gtk::GdkColor &c) { return Rgb24(c.red >> 8, c.green >> 8, c.blue >> 8); } #endif #if defined(WINDOWS) static LColour ConvertWinColour(uint32_t c) { return LColour(GetRValue(c), GetGValue(c), GetBValue(c)); } #endif void LColour::OnChange() { // Basic colours _LgiColours[L_BLACK].Rgb(0, 0, 0); // LC_BLACK _LgiColours[L_DKGREY].Rgb(0x40, 0x40, 0x40); // LC_DKGREY _LgiColours[L_MIDGREY].Rgb(0x80, 0x80, 0x80); // LC_MIDGREY _LgiColours[L_LTGREY].Rgb(0xc0, 0xc0, 0xc0); // LC_LTGREY _LgiColours[L_WHITE].Rgb(0xff, 0xff, 0xff); // LC_WHITE // Variable colours #if defined _XP_CTRLS - _LgiColours[L_SHADOW] = Rgb24(0x42, 0x27, 0x63); // LC_SHADOW - _LgiColours[L_LOW] = Rgb24(0x7a, 0x54, 0xa9); // LC_LOW - _LgiColours[L_MED] = Rgb24(0xbc, 0xa9, 0xd4); // LC_MED - _LgiColours[L_HIGH] = Rgb24(0xdd, 0xd4, 0xe9); // LC_HIGH - _LgiColours[L_LIGHT] = Rgb24(0xff, 0xff, 0xff); // LC_LIGHT - _LgiColours[L_DIALOG] = Rgb24(0xbc, 0xa9, 0xd4); // LC_DIALOG - _LgiColours[L_WORKSPACE] = Rgb24(0xeb, 0xe6, 0xf2); // LC_WORKSPACE - _LgiColours[L_TEXT] = Rgb24(0x35, 0x1f, 0x4f); // LC_TEXT - _LgiColours[L_FOCUS_SEL_BACK] = Rgb24(0xbf, 0x67, 0x93); // LC_FOCUS_SEL_BACK - _LgiColours[L_FOCUS_SEL_FORE] = Rgb24(0xff, 0xff, 0xff); // LC_FOCUS_SEL_FORE - _LgiColours[L_ACTIVE_TITLE] = Rgb24(0x70, 0x3a, 0xec); // LC_ACTIVE_TITLE - _LgiColours[L_ACTIVE_TITLE_TEXT] = Rgb24(0xff, 0xff, 0xff); // LC_ACTIVE_TITLE_TEXT - _LgiColours[L_INACTIVE_TITLE] = Rgb24(0x80, 0x80, 0x80); // LC_INACTIVE_TITLE - _LgiColours[L_INACTIVE_TITLE_TEXT] = Rgb24(0x40, 0x40, 0x40); // LC_INACTIVE_TITLE_TEXT - _LgiColours[L_MENU_BACKGROUND] = Rgb24(0xbc, 0xa9, 0xd4); // LC_MENU_BACKGROUND - _LgiColours[L_MENU_TEXT] = Rgb24(0x35, 0x1f, 0x4f); // LC_MENU_TEXT - _LgiColours[L_NON_FOCUS_SEL_BACK] = Rgb24(0xbc, 0xa9, 0xd4); // LC_NON_FOCUS_SEL_BACK - _LgiColours[L_NON_FOCUS_SEL_FORE] = Rgb24(0x35, 0x1f, 0x4f); // LC_NON_FOCUS_SEL_FORE - LAssert(i == LC_MAXIMUM); + _LgiColours[L_SHADOW] = Rgb24(0x42, 0x27, 0x63); // LC_SHADOW + _LgiColours[L_LOW] = Rgb24(0x7a, 0x54, 0xa9); // LC_LOW + _LgiColours[L_MED] = Rgb24(0xbc, 0xa9, 0xd4); // LC_MED + _LgiColours[L_HIGH] = Rgb24(0xdd, 0xd4, 0xe9); // LC_HIGH + _LgiColours[L_LIGHT] = Rgb24(0xff, 0xff, 0xff); // LC_LIGHT + _LgiColours[L_DIALOG] = Rgb24(0xbc, 0xa9, 0xd4); // LC_DIALOG + _LgiColours[L_WORKSPACE] = Rgb24(0xeb, 0xe6, 0xf2); // LC_WORKSPACE + _LgiColours[L_TEXT] = Rgb24(0x35, 0x1f, 0x4f); // LC_TEXT + _LgiColours[L_FOCUS_SEL_BACK] = Rgb24(0xbf, 0x67, 0x93); // LC_FOCUS_SEL_BACK + _LgiColours[L_FOCUS_SEL_FORE] = Rgb24(0xff, 0xff, 0xff); // LC_FOCUS_SEL_FORE + _LgiColours[L_ACTIVE_TITLE] = Rgb24(0x70, 0x3a, 0xec); // LC_ACTIVE_TITLE + _LgiColours[L_ACTIVE_TITLE_TEXT] = Rgb24(0xff, 0xff, 0xff); // LC_ACTIVE_TITLE_TEXT + _LgiColours[L_INACTIVE_TITLE] = Rgb24(0x80, 0x80, 0x80); // LC_INACTIVE_TITLE + _LgiColours[L_INACTIVE_TITLE_TEXT] = Rgb24(0x40, 0x40, 0x40); // LC_INACTIVE_TITLE_TEXT + _LgiColours[L_MENU_BACKGROUND] = Rgb24(0xbc, 0xa9, 0xd4); // LC_MENU_BACKGROUND + _LgiColours[L_MENU_TEXT] = Rgb24(0x35, 0x1f, 0x4f); // LC_MENU_TEXT + _LgiColours[L_NON_FOCUS_SEL_BACK] = Rgb24(0xbc, 0xa9, 0xd4); // LC_NON_FOCUS_SEL_BACK + _LgiColours[L_NON_FOCUS_SEL_FORE] = Rgb24(0x35, 0x1f, 0x4f); // LC_NON_FOCUS_SEL_FORE + LAssert(i == LC_MAXIMUM); #elif defined __GTK_H__ - Gtk::GtkSettings *set = Gtk::gtk_settings_get_default(); - if (!set) - { - printf("%s:%i - gtk_settings_get_for_screen failed.\n", _FL); - return; - } - - char PropName[] = "gtk-color-scheme"; - Gtk::gchararray Value = 0; - Gtk::g_object_get(set, PropName, &Value, NULL); - LString::Array Lines = LString(Value).SplitDelimit("\n"); - Gtk::g_free(Value); - g_object_unref(set); + Gtk::GtkSettings *set = Gtk::gtk_settings_get_default(); + if (!set) + { + printf("%s:%i - gtk_settings_get_for_screen failed.\n", _FL); + return; + } + + char PropName[] = "gtk-color-scheme"; + Gtk::gchararray Value = 0; + Gtk::g_object_get(set, PropName, &Value, NULL); + LString::Array Lines = LString(Value).SplitDelimit("\n"); + Gtk::g_free(Value); + g_object_unref(set); - LHashTbl, int> Colours(0, -1); - auto ScreenBits = GdcD->GetBits(); - for (int i=0; i, int> Colours(0, -1); + auto ScreenBits = GdcD->GetBits(); + for (int i=0; i> 8) & 0xff) | - ((c >> 16) & 0xff00) | - ((c >> 24) & 0xff0000); - } + *col++ = 0; + + char *val = col; + if (*val == ' ') val++; + if (*val == '#') val++; + uint64 c = htoi64(val); + COLOUR c24 = c; + if (ScreenBits == 32) + { + c24 = ((c >> 8) & 0xff) | + ((c >> 16) & 0xff00) | + ((c >> 24) & 0xff0000); + } - Colours.Add(var, c24); - // printf("Color %s = %x\n", var, c24); + Colours.Add(var, c24); + // printf("Color %s = %x\n", var, c24); + } } - } - #define LookupColour(name, default) ((Colours.Find(name) >= 0) ? LColour(Colours.Find(name),24) : default) + #define LookupColour(name, default) ((Colours.Find(name) >= 0) ? LColour(Colours.Find(name),24) : default) - LColour Med = LookupColour("bg_color", LColour(0xe8, 0xe8, 0xe8)); - LColour White(255, 255, 255); - LColour Black(0, 0, 0); - LColour Sel(0x33, 0x99, 0xff); - _LgiColours[L_SHADOW] = GdcMixColour(Med, Black, 0.25); // LC_SHADOW - _LgiColours[L_LOW] = GdcMixColour(Med, Black, 0.5); // LC_LOW - _LgiColours[L_MED] = Med; // LC_MED - _LgiColours[L_HIGH] = GdcMixColour(Med, White, 0.5); // LC_HIGH - _LgiColours[L_LIGHT] = GdcMixColour(Med, White, 0.25); // LC_LIGHT - _LgiColours[L_DIALOG] = Med; // LC_DIALOG - _LgiColours[L_WORKSPACE] = LookupColour("base_color", White); // LC_WORKSPACE - _LgiColours[L_TEXT] = LookupColour("text_color", Black); // LC_TEXT - _LgiColours[L_FOCUS_SEL_BACK] = LookupColour("selected_bg_color", Sel); // LC_FOCUS_SEL_BACK - _LgiColours[L_FOCUS_SEL_FORE] = LookupColour("selected_fg_color", White); // LC_FOCUS_SEL_FORE - _LgiColours[L_ACTIVE_TITLE] = LookupColour("selected_bg_color", Sel); // LC_ACTIVE_TITLE - _LgiColours[L_ACTIVE_TITLE_TEXT] = LookupColour("selected_fg_color", White); // LC_ACTIVE_TITLE_TEXT - _LgiColours[L_INACTIVE_TITLE].Rgb(0xc0, 0xc0, 0xc0); // LC_INACTIVE_TITLE - _LgiColours[L_INACTIVE_TITLE_TEXT].Rgb(0x80, 0x80, 0x80); // LC_INACTIVE_TITLE_TEXT - _LgiColours[L_MENU_BACKGROUND] = LookupColour("bg_color", White); // LC_MENU_BACKGROUND - _LgiColours[L_MENU_TEXT] = _LgiColours[L_TEXT]; // LC_MENU_TEXT - _LgiColours[L_NON_FOCUS_SEL_BACK] = _LgiColours[L_FOCUS_SEL_BACK].Mix(_LgiColours[L_WORKSPACE]); // LC_NON_FOCUS_SEL_BACK - _LgiColours[L_NON_FOCUS_SEL_FORE] = _LgiColours[L_TEXT]; // LC_NON_FOCUS_SEL_FORE - - // printf("_LgiColours[L_FOCUS_SEL_BACK]=%s\n", _LgiColours[L_FOCUS_SEL_BACK].GetStr()); - // printf("_LgiColours[L_NON_FOCUS_SEL_BACK]=%s\n", _LgiColours[L_NON_FOCUS_SEL_BACK].GetStr()); + LColour Med = LookupColour("bg_color", LColour(0xe8, 0xe8, 0xe8)); + LColour White(255, 255, 255); + LColour Black(0, 0, 0); + LColour Sel(0x33, 0x99, 0xff); + _LgiColours[L_SHADOW] = GdcMixColour(Med, Black, 0.25); // LC_SHADOW + _LgiColours[L_LOW] = GdcMixColour(Med, Black, 0.5); // LC_LOW + _LgiColours[L_MED] = Med; // LC_MED + _LgiColours[L_HIGH] = GdcMixColour(Med, White, 0.5); // LC_HIGH + _LgiColours[L_LIGHT] = GdcMixColour(Med, White, 0.25); // LC_LIGHT + _LgiColours[L_DIALOG] = Med; // LC_DIALOG + _LgiColours[L_WORKSPACE] = LookupColour("base_color", White); // LC_WORKSPACE + _LgiColours[L_TEXT] = LookupColour("text_color", Black); // LC_TEXT + _LgiColours[L_FOCUS_SEL_BACK] = LookupColour("selected_bg_color", Sel); // LC_FOCUS_SEL_BACK + _LgiColours[L_FOCUS_SEL_FORE] = LookupColour("selected_fg_color", White); // LC_FOCUS_SEL_FORE + _LgiColours[L_ACTIVE_TITLE] = LookupColour("selected_bg_color", Sel); // LC_ACTIVE_TITLE + _LgiColours[L_ACTIVE_TITLE_TEXT] = LookupColour("selected_fg_color", White); // LC_ACTIVE_TITLE_TEXT + _LgiColours[L_INACTIVE_TITLE].Rgb(0xc0, 0xc0, 0xc0); // LC_INACTIVE_TITLE + _LgiColours[L_INACTIVE_TITLE_TEXT].Rgb(0x80, 0x80, 0x80); // LC_INACTIVE_TITLE_TEXT + _LgiColours[L_MENU_BACKGROUND] = LookupColour("bg_color", White); // LC_MENU_BACKGROUND + _LgiColours[L_MENU_TEXT] = _LgiColours[L_TEXT]; // LC_MENU_TEXT + _LgiColours[L_NON_FOCUS_SEL_BACK] = _LgiColours[L_FOCUS_SEL_BACK].Mix(_LgiColours[L_WORKSPACE]); // LC_NON_FOCUS_SEL_BACK + _LgiColours[L_NON_FOCUS_SEL_FORE] = _LgiColours[L_TEXT]; // LC_NON_FOCUS_SEL_FORE + + // printf("_LgiColours[L_FOCUS_SEL_BACK]=%s\n", _LgiColours[L_FOCUS_SEL_BACK].GetStr()); + // printf("_LgiColours[L_NON_FOCUS_SEL_BACK]=%s\n", _LgiColours[L_NON_FOCUS_SEL_BACK].GetStr()); #elif defined(WINDOWS) - /* - for (int i=0; i<30; i++) - { - auto c = GetSysColor(i); - auto r = GetRValue(c); - auto g = GetGValue(c); - auto b = GetBValue(c); - LgiTrace("[%i]=%i,%i,%i %x,%x,%x\n", i, r, g, b, r, g, b); - } - */ + /* + for (int i=0; i<30; i++) + { + auto c = GetSysColor(i); + auto r = GetRValue(c); + auto g = GetGValue(c); + auto b = GetBValue(c); + LgiTrace("[%i]=%i,%i,%i %x,%x,%x\n", i, r, g, b, r, g, b); + } + */ - _LgiColours[L_SHADOW] = ConvertWinColour(GetSysColor(COLOR_3DDKSHADOW)); // LC_SHADOW - _LgiColours[L_LOW] = ConvertWinColour(GetSysColor(COLOR_3DSHADOW)); // LC_LOW - _LgiColours[L_MED] = ConvertWinColour(GetSysColor(COLOR_3DFACE)); // LC_MED - _LgiColours[L_HIGH] = ConvertWinColour(GetSysColor(COLOR_3DLIGHT)); // LC_HIGH - _LgiColours[L_LIGHT] = ConvertWinColour(GetSysColor(COLOR_3DHIGHLIGHT)); // LC_LIGHT - _LgiColours[L_DIALOG] = ConvertWinColour(GetSysColor(COLOR_3DFACE)); // LC_DIALOG - _LgiColours[L_WORKSPACE] = ConvertWinColour(GetSysColor(COLOR_WINDOW)); // LC_WORKSPACE - _LgiColours[L_TEXT] = ConvertWinColour(GetSysColor(COLOR_WINDOWTEXT)); // LC_TEXT - _LgiColours[L_FOCUS_SEL_BACK] = ConvertWinColour(GetSysColor(COLOR_HIGHLIGHT)); // LC_FOCUS_SEL_BACK - _LgiColours[L_FOCUS_SEL_FORE] = ConvertWinColour(GetSysColor(COLOR_HIGHLIGHTTEXT)); // LC_FOCUS_SEL_FORE - _LgiColours[L_ACTIVE_TITLE] = ConvertWinColour(GetSysColor(COLOR_ACTIVECAPTION)); // LC_ACTIVE_TITLE - _LgiColours[L_ACTIVE_TITLE_TEXT] = ConvertWinColour(GetSysColor(COLOR_CAPTIONTEXT)); // LC_ACTIVE_TITLE_TEXT - _LgiColours[L_INACTIVE_TITLE] = ConvertWinColour(GetSysColor(COLOR_INACTIVECAPTION)); // LC_INACTIVE_TITLE - _LgiColours[L_INACTIVE_TITLE_TEXT] = ConvertWinColour(GetSysColor(COLOR_INACTIVECAPTIONTEXT)); // LC_INACTIVE_TITLE_TEXT - _LgiColours[L_MENU_BACKGROUND] = ConvertWinColour(GetSysColor(COLOR_MENU)); // LC_MENU_BACKGROUND - _LgiColours[L_MENU_TEXT] = ConvertWinColour(GetSysColor(COLOR_MENUTEXT)); // LC_MENU_TEXT - _LgiColours[L_NON_FOCUS_SEL_BACK] = ConvertWinColour(GetSysColor(COLOR_3DLIGHT)); // LC_NON_FOCUS_SEL_BACK - _LgiColours[L_NON_FOCUS_SEL_FORE] = ConvertWinColour(GetSysColor(COLOR_BTNTEXT)); // LC_NON_FOCUS_SEL_FORE + _LgiColours[L_SHADOW] = ConvertWinColour(GetSysColor(COLOR_3DDKSHADOW)); // LC_SHADOW + _LgiColours[L_LOW] = ConvertWinColour(GetSysColor(COLOR_3DSHADOW)); // LC_LOW + _LgiColours[L_MED] = ConvertWinColour(GetSysColor(COLOR_3DFACE)); // LC_MED + _LgiColours[L_HIGH] = ConvertWinColour(GetSysColor(COLOR_3DLIGHT)); // LC_HIGH + _LgiColours[L_LIGHT] = ConvertWinColour(GetSysColor(COLOR_3DHIGHLIGHT)); // LC_LIGHT + _LgiColours[L_DIALOG] = ConvertWinColour(GetSysColor(COLOR_3DFACE)); // LC_DIALOG + _LgiColours[L_WORKSPACE] = ConvertWinColour(GetSysColor(COLOR_WINDOW)); // LC_WORKSPACE + _LgiColours[L_TEXT] = ConvertWinColour(GetSysColor(COLOR_WINDOWTEXT)); // LC_TEXT + _LgiColours[L_FOCUS_SEL_BACK] = ConvertWinColour(GetSysColor(COLOR_HIGHLIGHT)); // LC_FOCUS_SEL_BACK + _LgiColours[L_FOCUS_SEL_FORE] = ConvertWinColour(GetSysColor(COLOR_HIGHLIGHTTEXT)); // LC_FOCUS_SEL_FORE + _LgiColours[L_ACTIVE_TITLE] = ConvertWinColour(GetSysColor(COLOR_ACTIVECAPTION)); // LC_ACTIVE_TITLE + _LgiColours[L_ACTIVE_TITLE_TEXT] = ConvertWinColour(GetSysColor(COLOR_CAPTIONTEXT)); // LC_ACTIVE_TITLE_TEXT + _LgiColours[L_INACTIVE_TITLE] = ConvertWinColour(GetSysColor(COLOR_INACTIVECAPTION)); // LC_INACTIVE_TITLE + _LgiColours[L_INACTIVE_TITLE_TEXT] = ConvertWinColour(GetSysColor(COLOR_INACTIVECAPTIONTEXT)); // LC_INACTIVE_TITLE_TEXT + _LgiColours[L_MENU_BACKGROUND] = ConvertWinColour(GetSysColor(COLOR_MENU)); // LC_MENU_BACKGROUND + _LgiColours[L_MENU_TEXT] = ConvertWinColour(GetSysColor(COLOR_MENUTEXT)); // LC_MENU_TEXT + _LgiColours[L_NON_FOCUS_SEL_BACK] = ConvertWinColour(GetSysColor(COLOR_3DLIGHT)); // LC_NON_FOCUS_SEL_BACK + _LgiColours[L_NON_FOCUS_SEL_FORE] = ConvertWinColour(GetSysColor(COLOR_BTNTEXT)); // LC_NON_FOCUS_SEL_FORE #else // defaults for non-windows, plain grays - #if defined(LINUX) && !defined(LGI_SDL) - WmColour c; - Proc_LgiWmGetColour WmGetColour = 0; - LLibrary *WmLib = LAppInst->GetWindowManagerLib(); - if (WmLib) - { - WmGetColour = (Proc_LgiWmGetColour) WmLib->GetAddress("LgiWmGetColour"); - } + #if defined(LINUX) && !defined(LGI_SDL) + + WmColour c; + Proc_LgiWmGetColour WmGetColour = 0; + LLibrary *WmLib = LAppInst->GetWindowManagerLib(); + if (WmLib) + { + WmGetColour = (Proc_LgiWmGetColour) WmLib->GetAddress("LgiWmGetColour"); + } - #define SetCol(def) \ - if (WmGetColour && WmGetColour(i, &c)) \ - _LgiColours[i++] = Rgb24(c.r, c.g, c.b); \ - else \ - _LgiColours[i++] = def; + #define SetCol(def) \ + if (WmGetColour && WmGetColour(i, &c)) \ + _LgiColours[i++] = Rgb24(c.r, c.g, c.b); \ + else \ + _LgiColours[i++] = def; - #else // MAC + #else // MAC - #define SetCol(def) \ - _LgiColours[i++] = def; + #define SetCol(def) \ + _LgiColours[i++] = def; + + #endif - #endif + _LgiColours[L_SHADOW].Rgb(96, 96, 96); // LC_SHADOW + _LgiColours[L_LOW].Rgb(150, 150, 150); // LC_LOW + #ifdef HAIKU + _LgiColours[L_MED].Rgb(216, 216, 216); // LC_MED + #else + _LgiColours[L_MED].Rgb(230, 230, 230); // LC_MED + #endif + _LgiColours[L_HIGH].Rgb(240, 240, 240); // LC_HIGH + _LgiColours[L_LIGHT].Rgb(255, 255, 255); // LC_LIGHT + _LgiColours[L_DIALOG].Rgb(216, 216, 216); // LC_DIALOG + _LgiColours[L_WORKSPACE].Rgb(0xff, 0xff, 0xff); // LC_WORKSPACE + _LgiColours[L_TEXT].Rgb(0, 0, 0); // LC_TEXT + _LgiColours[L_FOCUS_SEL_BACK].Rgb(0x4a, 0x59, 0xa5); // LC_FOCUS_SEL_BACK + _LgiColours[L_FOCUS_SEL_FORE].Rgb(0xff, 0xff, 0xff); // LC_FOCUS_SEL_FORE + _LgiColours[L_ACTIVE_TITLE].Rgb(0, 0, 0x80); // LC_ACTIVE_TITLE + _LgiColours[L_ACTIVE_TITLE_TEXT].Rgb(0xff, 0xff, 0xff); // LC_ACTIVE_TITLE_TEXT + _LgiColours[L_INACTIVE_TITLE].Rgb(0x80, 0x80, 0x80); // LC_INACTIVE_TITLE + _LgiColours[L_INACTIVE_TITLE_TEXT].Rgb(0x40, 0x40, 0x40); // LC_INACTIVE_TITLE_TEXT + _LgiColours[L_MENU_BACKGROUND].Rgb(222, 222, 222); // LC_MENU_BACKGROUND + _LgiColours[L_MENU_TEXT].Rgb(0, 0, 0); // LC_MENU_TEXT + _LgiColours[L_NON_FOCUS_SEL_BACK].Rgb(222, 222, 222); // LC_NON_FOCUS_SEL_BACK + _LgiColours[L_NON_FOCUS_SEL_FORE].Rgb(0, 0, 0); // LC_NON_FOCUS_SEL_FORE - _LgiColours[L_SHADOW].Rgb(96, 96, 96); // LC_SHADOW - _LgiColours[L_LOW].Rgb(150, 150, 150); // LC_LOW - _LgiColours[L_MED].Rgb(230, 230, 230); // LC_MED - _LgiColours[L_HIGH].Rgb(240, 240, 240); // LC_HIGH - _LgiColours[L_LIGHT].Rgb(255, 255, 255); // LC_LIGHT - _LgiColours[L_DIALOG].Rgb(216, 216, 216); // LC_DIALOG - _LgiColours[L_WORKSPACE].Rgb(0xff, 0xff, 0xff); // LC_WORKSPACE - _LgiColours[L_TEXT].Rgb(0, 0, 0); // LC_TEXT - _LgiColours[L_FOCUS_SEL_BACK].Rgb(0x4a, 0x59, 0xa5); // LC_FOCUS_SEL_BACK - _LgiColours[L_FOCUS_SEL_FORE].Rgb(0xff, 0xff, 0xff); // LC_FOCUS_SEL_FORE - _LgiColours[L_ACTIVE_TITLE].Rgb(0, 0, 0x80); // LC_ACTIVE_TITLE - _LgiColours[L_ACTIVE_TITLE_TEXT].Rgb(0xff, 0xff, 0xff); // LC_ACTIVE_TITLE_TEXT - _LgiColours[L_INACTIVE_TITLE].Rgb(0x80, 0x80, 0x80); // LC_INACTIVE_TITLE - _LgiColours[L_INACTIVE_TITLE_TEXT].Rgb(0x40, 0x40, 0x40); // LC_INACTIVE_TITLE_TEXT - _LgiColours[L_MENU_BACKGROUND].Rgb(222, 222, 222); // LC_MENU_BACKGROUND - _LgiColours[L_MENU_TEXT].Rgb(0, 0, 0); // LC_MENU_TEXT - _LgiColours[L_NON_FOCUS_SEL_BACK].Rgb(222, 222, 222); // LC_NON_FOCUS_SEL_BACK - _LgiColours[L_NON_FOCUS_SEL_FORE].Rgb(0, 0, 0); // LC_NON_FOCUS_SEL_FORE #endif // Tweak if (LGetOs() == LGI_OS_WIN32 || LGetOs() == LGI_OS_WIN64) { // Win32 doesn't seem to get this right, so we just tweak it here _LgiColours[L_LIGHT] = _LgiColours[L_MED].Mix(_LgiColours[L_LIGHT]); } _LgiColours[L_DEBUG_CURRENT_LINE].Rgb(0xff, 0xe0, 0x00); #ifdef MAC _LgiColours[L_TOOL_TIP].Rgb(0xef, 0xef, 0xef); #else _LgiColours[L_TOOL_TIP].Rgb(255, 255, 231); #endif } LColour::LColour(LSystemColour sc) { if (sc < L_MAXIMUM) *this = _LgiColours[sc]; } LColour LSysColour(LSystemColour Colour) { return Colour < L_MAXIMUM ? _LgiColours[Colour] : LColour(); } bool LColourLoad(const char *Json) { #ifdef LGI_STATIC // Not supported due to no CSS in static build return false; #else if (!Json) return false; LHashTbl,LSystemColour> Map; #define _(name) Map.Add("L_" #name, L_##name); _SystemColour(); #undef _ LFile f(Json, O_READ); if (!f.IsOpen()) return false; LJson j(f.Read()); for (auto i: j.GetArray("colors")) { auto p = i.Get(); auto c = Map.Find(p.key); if (c > L_TRANSPARENT && c < L_MAXIMUM) { LCss::ColorDef cd(p.value); if (cd.Type == LCss::ColorRgb) _LgiColours[c].Set(cd.Rgb32, 32); else LAssert(!"Invalid colour type."); } } return true; #endif } diff --git a/src/common/Lgi/ViewCommon.cpp b/src/common/Lgi/ViewCommon.cpp --- a/src/common/Lgi/ViewCommon.cpp +++ b/src/common/Lgi/ViewCommon.cpp @@ -1,2686 +1,2689 @@ /// \file /// \author Matthew Allen #ifdef LINUX #include #endif #include "lgi/common/Lgi.h" #include "lgi/common/DragAndDrop.h" #include "lgi/common/TableLayout.h" #include "lgi/common/Button.h" #include "lgi/common/Css.h" #include "lgi/common/LgiRes.h" #include "lgi/common/EventTargetThread.h" #include "lgi/common/Popup.h" #include "lgi/common/CssTools.h" #include "ViewPriv.h" #if 0 #define DEBUG_CAPTURE(...) printf(__VA_ARGS__) #else #define DEBUG_CAPTURE(...) #endif ////////////////////////////////////////////////////////////////////////////////////// // Helper LPoint lgi_view_offset(LViewI *v, bool Debug = false) { LPoint Offset; for (LViewI *p = v; p; p = p->GetParent()) { if (dynamic_cast(p)) break; LRect pos = p->GetPos(); LRect cli = p->GetClient(false); if (Debug) { const char *cls = p->GetClass(); LgiTrace(" Off[%s] += %i,%i = %i,%i (%s)\n", cls, pos.x1, pos.y1, Offset.x + pos.x1, Offset.y + pos.y1, cli.GetStr()); } Offset.x += pos.x1 + cli.x1; Offset.y += pos.y1 + cli.y1; } return Offset; } LMouse &lgi_adjust_click(LMouse &Info, LViewI *Wnd, bool Capturing, bool Debug) { static LMouse Temp; Temp = Info; if (Wnd) { if (Debug #if 0 || Capturing #endif ) LgiTrace("AdjustClick Target=%s -> Wnd=%s, Info=%i,%i\n", Info.Target?Info.Target->GetClass():"", Wnd?Wnd->GetClass():"", Info.x, Info.y); if (Temp.Target != Wnd) { if (Temp.Target) { auto *TargetWnd = Temp.Target->GetWindow(); auto *WndWnd = Wnd->GetWindow(); if (TargetWnd == WndWnd) { LPoint TargetOff = lgi_view_offset(Temp.Target, Debug); if (Debug) LgiTrace(" WndOffset:\n"); LPoint WndOffset = lgi_view_offset(Wnd, Debug); Temp.x += TargetOff.x - WndOffset.x; Temp.y += TargetOff.y - WndOffset.y; #if 0 LRect c = Wnd->GetClient(false); Temp.x -= c.x1; Temp.y -= c.y1; if (Debug) LgiTrace(" CliOff -= %i,%i\n", c.x1, c.y1); #endif Temp.Target = Wnd; } } else { LPoint Offset; Temp.Target = Wnd; if (Wnd->WindowVirtualOffset(&Offset)) { LRect c = Wnd->GetClient(false); Temp.x -= Offset.x + c.x1; Temp.y -= Offset.y + c.y1; } } } } LAssert(Temp.Target != NULL); return Temp; } ////////////////////////////////////////////////////////////////////////////////////// // LView class methods LViewI *LView::_Capturing = 0; LViewI *LView::_Over = 0; #if LGI_VIEW_HASH struct ViewTbl : public LMutex { typedef LHashTbl, int> T; private: T Map; public: ViewTbl() : Map(2000), LMutex("ViewTbl") { } T *Lock() { if (!LMutex::Lock(_FL)) return NULL; return ⤅ } } ViewTblInst; bool LView::LockHandler(LViewI *v, LView::LockOp Op) { ViewTbl::T *m = ViewTblInst.Lock(); if (!m) return false; int Ref = m->Find(v); bool Status = false; switch (Op) { case OpCreate: { if (Ref == 0) Status = m->Add(v, 1); else LAssert(!"Already exists?"); break; } case OpDelete: { if (Ref == 1) Status = m->Delete(v); else LAssert(!"Either locked or missing."); break; } case OpExists: { Status = Ref > 0; break; } } ViewTblInst.Unlock(); return Status; } #endif LView::LView(OsView view) { #ifdef _DEBUG _Debug = false; #endif d = new LViewPrivate(this); #ifdef LGI_SDL _View = this; #elif LGI_VIEW_HANDLE && !defined(HAIKU) _View = view; #endif Pos.ZOff(-1, -1); WndFlags = GWF_VISIBLE; #ifndef LGI_VIEW_HASH #error "LGI_VIEW_HASH needs to be defined" #elif LGI_VIEW_HASH LockHandler(this, OpCreate); // printf("Adding %p to hash\n", (LViewI*)this); #endif } LView::~LView() { if (d->SinkHnd >= 0) { LEventSinkMap::Dispatch.RemoveSink(this); d->SinkHnd = -1; } #if LGI_VIEW_HASH LockHandler(this, OpDelete); #endif for (unsigned i=0; iOwner == this) pu->Owner = NULL; } _Delete(); // printf("%p::~LView delete %p th=%u\n", this, d, GetCurrentThreadId()); DeleteObj(d); // printf("%p::~LView\n", this); } int LView::AddDispatch() { if (d->SinkHnd < 0) d->SinkHnd = LEventSinkMap::Dispatch.AddSink(this); return d->SinkHnd; } LString LView::CssStyles(const char *Set) { if (Set) { d->Styles = Set; d->CssDirty = true; } return d->Styles; } LString::Array *LView::CssClasses() { return &d->Classes; } LArray LView::IterateViews() { LArray a; for (auto c: Children) a.Add(c); return a; } bool LView::AddView(LViewI *v, int Where) { LAssert(!Children.HasItem(v)); bool Add = Children.Insert(v, Where); if (Add) { LView *gv = v->GetGView(); if (gv && gv->_Window != _Window) { LAssert(!_InLock); gv->_Window = _Window; } v->SetParent(this); v->OnAttach(); OnChildrenChanged(v, true); } return Add; } bool LView::DelView(LViewI *v) { bool Has = Children.HasItem(v); bool b = Children.Delete(v); if (Has) OnChildrenChanged(v, false); Has = Children.HasItem(v); LAssert(!Has); return b; } bool LView::HasView(LViewI *v) { return Children.HasItem(v); } OsWindow LView::WindowHandle() { auto w = GetWindow(); auto h = w ? w->WindowHandle() : NULL; return h; } LWindow *LView::GetWindow() { if (!_Window) { // Walk up parent list and find someone who has a window auto *w = d->GetParent(); for (; w; w = w->d ? w->d->GetParent() : NULL) { if (w->_Window) { LAssert(!_InLock); _Window = w->_Window; break; } } } return dynamic_cast(_Window); } bool LView::Lock(const char *file, int line, int TimeOut) { #ifdef HAIKU if (!d || !d->Hnd) { // printf("%s:%i - no handle %p %p\n", _FL, d, d ? d->Hnd : NULL); return false; } if (d->Hnd->Parent() == NULL) { // printf("%s:%p - Lock() no parent.\n", GetClass(), this); return true; } if (TimeOut >= 0) { auto r = d->Hnd->LockLooperWithTimeout(TimeOut * 1000); if (r == B_OK) { _InLock++; // printf("%s:%p - Lock() cnt=%i par=%p.\n", GetClass(), this, _InLock, d->Hnd->Parent()); return true; } printf("%s:%i - Lock(%i) failed with %x.\n", _FL, TimeOut, r); return false; } auto r = d->Hnd->LockLooper(); if (r) { _InLock++; // printf("%s:%p - Lock() cnt=%i par=%p.\n", GetClass(), this, _InLock, d->Hnd->Parent()); return true; } printf("%s:%i - Lock(%s:%i) failed.\n", _FL, file, line); return false; #else if (!_Window) GetWindow(); _InLock++; // LgiTrace("%s::%p Lock._InLock=%i %s:%i\n", GetClass(), this, _InLock, file, line); if (_Window && _Window->_Lock) { if (TimeOut < 0) { return _Window->_Lock->Lock(file, line); } else { return _Window->_Lock->LockWithTimeout(TimeOut, file, line); } } return true; #endif } void LView::Unlock() { #ifdef HAIKU if (!d || !d->Hnd) { printf("%s:%i - Unlock() error, no hnd.\n", _FL); return; } if (!d->Hnd->Parent()) { // printf("%s:%p - Unlock() no parent.\n", GetClass(), this); return; } if (_InLock > 0) { // printf("%s:%p - Calling UnlockLooper: %i.\n", GetClass(), this, _InLock); d->Hnd->UnlockLooper(); _InLock--; // printf("%s:%p - UnlockLooper done: %i.\n", GetClass(), this, _InLock); } else { printf("%s:%i - Unlock() without lock.\n", _FL); } #else if (_Window && _Window->_Lock) { _Window->_Lock->Unlock(); } _InLock--; // LgiTrace("%s::%p Unlock._InLock=%i\n", GetClass(), this, _InLock); #endif } void LView::OnMouseClick(LMouse &m) { } void LView::OnMouseEnter(LMouse &m) { } void LView::OnMouseExit(LMouse &m) { } void LView::OnMouseMove(LMouse &m) { } bool LView::OnMouseWheel(double Lines) { return false; } bool LView::OnKey(LKey &k) { return false; } void LView::OnAttach() { List::I it = Children.begin(); for (LViewI *v = *it; v; v = *++it) { if (!v->GetParent()) v->SetParent(this); } #if 0 // defined __GTK_H__ if (_View && !DropTarget()) { // If one of our parents is drop capable we need to set a dest here LViewI *p; for (p = GetParent(); p; p = p->GetParent()) { if (p->DropTarget()) { break; } } if (p) { Gtk::gtk_drag_dest_set( _View, (Gtk::GtkDestDefaults)0, NULL, 0, Gtk::GDK_ACTION_DEFAULT); // printf("%s:%i - Drop dest for '%s'\n", _FL, GetClass()); } else { Gtk::gtk_drag_dest_unset(_View); // printf("%s:%i - Not setting drop dest '%s'\n", _FL, GetClass()); } } #endif } void LView::OnCreate() { } void LView::OnDestroy() { } void LView::OnFocus(bool f) { // printf("%s::OnFocus(%i)\n", GetClass(), f); } void LView::OnPulse() { } void LView::OnPosChange() { } bool LView::OnRequestClose(bool OsShuttingDown) { return true; } int LView::OnHitTest(int x, int y) { return -1; } void LView::OnChildrenChanged(LViewI *Wnd, bool Attaching) { } void LView::OnPaint(LSurface *pDC) { auto c = GetClient(); LCssTools Tools(this); Tools.PaintContent(pDC, c); } int LView::OnNotify(LViewI *Ctrl, LNotification Data) { if (!Ctrl) return 0; if (Ctrl == (LViewI*)this && Data.Type == LNotifyActivate) { // Default activation is to focus the current control. Focus(true); } else if (d && d->Parent) { // default behaviour is just to pass the // notification up to the parent // FIXME: eventually we need to call the 'LNotification' parent fn... return d->Parent->OnNotify(Ctrl, Data); } return 0; } int LView::OnCommand(int Cmd, int Event, OsView Wnd) { return 0; } void LView::OnNcPaint(LSurface *pDC, LRect &r) { int Border = Sunken() || Raised() ? _BorderSize : 0; if (Border == 2) { LEdge e; if (Sunken()) e = Focus() ? EdgeWin7FocusSunken : DefaultSunkenEdge; else e = DefaultRaisedEdge; #if WINNATIVE if (d->IsThemed) DrawThemeBorder(pDC, r); else #endif LWideBorder(pDC, r, e); } else if (Border == 1) { LThinBorder(pDC, r, Sunken() ? DefaultSunkenEdge : DefaultRaisedEdge); } } #if LGI_COCOA || defined(__GTK_H__) -/* -uint64 nPaint = 0; -uint64 PaintTime = 0; -*/ - -void LView::_Paint(LSurface *pDC, LPoint *Offset, LRect *Update) -{ /* - uint64 StartTs = Update ? LCurrentTime() : 0; - d->InPaint = true; + uint64 nPaint = 0; + uint64 PaintTime = 0; */ - // Create temp DC if needed... - LAutoPtr Local; - if (!pDC) - { - if (!Local.Reset(new LScreenDC(this))) - return; - pDC = Local; - } - - #if 0 - // This is useful for coverage checking - pDC->Colour(LColour(255, 0, 255)); - pDC->Rectangle(); - #endif - - // Non-Client drawing - LRect r; - if (Offset) - { - r = Pos; - r.Offset(Offset); - } - else + void LView::_Paint(LSurface *pDC, LPoint *Offset, LRect *Update) { - r = GetClient().ZeroTranslate(); - } + /* + uint64 StartTs = Update ? LCurrentTime() : 0; + d->InPaint = true; + */ + + // Create temp DC if needed... + LAutoPtr Local; + if (!pDC) + { + if (!Local.Reset(new LScreenDC(this))) + return; + pDC = Local; + } + + #if 0 + // This is useful for coverage checking + pDC->Colour(LColour(255, 0, 255)); + pDC->Rectangle(); + #endif - pDC->SetClient(&r); - LRect zr1 = r.ZeroTranslate(), zr2 = zr1; - OnNcPaint(pDC, zr1); - pDC->SetClient(NULL); - if (zr2 != zr1) - { - r.x1 -= zr2.x1 - zr1.x1; - r.y1 -= zr2.y1 - zr1.y1; - r.x2 -= zr2.x2 - zr1.x2; - r.y2 -= zr2.y2 - zr1.y2; - } - LPoint o(r.x1, r.y1); // Origin of client + // Non-Client drawing + LRect r; + if (Offset) + { + r = Pos; + r.Offset(Offset); + } + else + { + r = GetClient().ZeroTranslate(); + } - // Paint this view's contents... - pDC->SetClient(&r); - - #if 0 - if (_Debug) - { - #if defined(__GTK_H__) - Gtk::cairo_matrix_t matrix; - cairo_get_matrix(pDC->Handle(), &matrix); + pDC->SetClient(&r); + LRect zr1 = r.ZeroTranslate(), zr2 = zr1; + OnNcPaint(pDC, zr1); + pDC->SetClient(NULL); + if (zr2 != zr1) + { + r.x1 -= zr2.x1 - zr1.x1; + r.y1 -= zr2.y1 - zr1.y1; + r.x2 -= zr2.x2 - zr1.x2; + r.y2 -= zr2.y2 - zr1.y2; + } + LPoint o(r.x1, r.y1); // Origin of client - double ex[4]; - cairo_clip_extents(pDC->Handle(), ex+0, ex+1, ex+2, ex+3); - ex[0] += matrix.x0; ex[1] += matrix.y0; ex[2] += matrix.x0; ex[3] += matrix.y0; - LgiTrace("%s::_Paint, r=%s, clip=%g,%g,%g,%g - %g,%g\n", - GetClass(), r.GetStr(), - ex[0], ex[1], ex[2], ex[3], - matrix.x0, matrix.y0); - #elif LGI_COCOA - auto Ctx = pDC->Handle(); - CGAffineTransform t = CGContextGetCTM(Ctx); - LRect cr = CGContextGetClipBoundingBox(Ctx); - printf("%s::_Paint() pos=%s transform=%g,%g,%g,%g-%g,%g clip=%s r=%s\n", - GetClass(), - GetPos().GetStr(), - t.a, t.b, t.c, t.d, t.tx, t.ty, - cr.GetStr(), - r.GetStr()); - #endif - } - #endif + // Paint this view's contents... + pDC->SetClient(&r); + + #if 0 + if (_Debug) + { + #if defined(__GTK_H__) + Gtk::cairo_matrix_t matrix; + cairo_get_matrix(pDC->Handle(), &matrix); - OnPaint(pDC); - - pDC->SetClient(NULL); - - // Paint all the children... - for (auto i : Children) - { - LView *w = i->GetGView(); - if (w && w->Visible()) - { - if (!w->Pos.Valid()) - continue; - - #if 0 - if (w->_Debug) - LgiTrace("%s::_Paint %i,%i\n", w->GetClass(), o.x, o.y); + double ex[4]; + cairo_clip_extents(pDC->Handle(), ex+0, ex+1, ex+2, ex+3); + ex[0] += matrix.x0; ex[1] += matrix.y0; ex[2] += matrix.x0; ex[3] += matrix.y0; + LgiTrace("%s::_Paint, r=%s, clip=%g,%g,%g,%g - %g,%g\n", + GetClass(), r.GetStr(), + ex[0], ex[1], ex[2], ex[3], + matrix.x0, matrix.y0); + #elif LGI_COCOA + auto Ctx = pDC->Handle(); + CGAffineTransform t = CGContextGetCTM(Ctx); + LRect cr = CGContextGetClipBoundingBox(Ctx); + printf("%s::_Paint() pos=%s transform=%g,%g,%g,%g-%g,%g clip=%s r=%s\n", + GetClass(), + GetPos().GetStr(), + t.a, t.b, t.c, t.d, t.tx, t.ty, + cr.GetStr(), + r.GetStr()); #endif - w->_Paint(pDC, &o); } - } -} -#else -void LView::_Paint(LSurface *pDC, LPoint *Offset, LRect *Update) -{ - // Create temp DC if needed... - LAutoPtr Local; - if (!pDC) - { - Local.Reset(new LScreenDC(this)); - pDC = Local; - } - if (!pDC) - { - printf("%s:%i - No context to draw in.\n", _FL); - return; - } + #endif + + OnPaint(pDC); + + pDC->SetClient(NULL); - #if 0 - // This is useful for coverage checking - pDC->Colour(LColour(255, 0, 255)); - pDC->Rectangle(); - #endif - - bool HasClient = false; - LRect r(0, 0, Pos.X()-1, Pos.Y()-1), Client; - LPoint o; - if (Offset) - o = *Offset; + // Paint all the children... + for (auto i : Children) + { + LView *w = i->GetGView(); + if (w && w->Visible()) + { + if (!w->Pos.Valid()) + continue; - #if WINNATIVE - if (!_View) - #endif - { - // Non-Client drawing - Client = r; - OnNcPaint(pDC, Client); - HasClient = GetParent() && (Client != r); - if (HasClient) - { - Client.Offset(o.x, o.y); - pDC->SetClient(&Client); + #if 0 + if (w->_Debug) + LgiTrace("%s::_Paint %i,%i\n", w->GetClass(), o.x, o.y); + #endif + w->_Paint(pDC, &o); + } } } - r.Offset(o.x, o.y); +#else - // Paint this view's contents - if (Update) + void LView::_Paint(LSurface *pDC, LPoint *Offset, LRect *Update) { - LRect OldClip = pDC->ClipRgn(); - pDC->ClipRgn(Update); - OnPaint(pDC); - pDC->ClipRgn(OldClip.Valid() ? &OldClip : NULL); - } - else - { - OnPaint(pDC); + // Create temp DC if needed... + LAutoPtr Local; + if (!pDC) + { + Local.Reset(new LScreenDC(this)); + pDC = Local; + } + if (!pDC) + { + printf("%s:%i - No context to draw in.\n", _FL); + return; + } + + #if 0 + // This is useful for coverage checking + pDC->Colour(LColour(255, 0, 255)); + pDC->Rectangle(); + #endif + + bool HasClient = false; + LRect r(0, 0, Pos.X()-1, Pos.Y()-1), Client; + LPoint o; + if (Offset) + o = *Offset; + + #if WINNATIVE + if (!_View) + #endif + { + // Non-Client drawing + Client = r; + OnNcPaint(pDC, Client); + HasClient = GetParent() && (Client != r); + if (HasClient) + { + Client.Offset(o.x, o.y); + pDC->SetClient(&Client); + } + } + + r.Offset(o.x, o.y); + + // Paint this view's contents + if (Update) + { + LRect OldClip = pDC->ClipRgn(); + pDC->ClipRgn(Update); + OnPaint(pDC); + pDC->ClipRgn(OldClip.Valid() ? &OldClip : NULL); + } + else + { + OnPaint(pDC); + } + + #if PAINT_VIRTUAL_CHILDREN + // Paint any virtual children + for (auto i : Children) + { + LView *w = i->GetGView(); + if (w && w->Visible()) + { + #if LGI_VIEW_HANDLE + if (!w->Handle()) + #endif + { + LRect p = w->GetPos(); + p.Offset(o.x, o.y); + if (HasClient) + p.Offset(Client.x1 - r.x1, Client.y1 - r.y1); + + LPoint co(p.x1, p.y1); + // LgiTrace("%s::_Paint %i,%i\n", w->GetClass(), p.x1, p.y1); + pDC->SetClient(&p); + w->_Paint(pDC, &co); + pDC->SetClient(NULL); + } + } + } + #endif + + if (HasClient) + pDC->SetClient(0); } - #if PAINT_VIRTUAL_CHILDREN - // Paint any virtual children - for (auto i : Children) - { - LView *w = i->GetGView(); - if (w && w->Visible()) - { - #if LGI_VIEW_HANDLE - if (!w->Handle()) - #endif - { - LRect p = w->GetPos(); - p.Offset(o.x, o.y); - if (HasClient) - p.Offset(Client.x1 - r.x1, Client.y1 - r.y1); - - LPoint co(p.x1, p.y1); - // LgiTrace("%s::_Paint %i,%i\n", w->GetClass(), p.x1, p.y1); - pDC->SetClient(&p); - w->_Paint(pDC, &co); - pDC->SetClient(NULL); - } - } - } - #endif - - if (HasClient) - pDC->SetClient(0); -} #endif LViewI *LView::GetParent() { ThreadCheck(); return d ? d->Parent : NULL; } void LView::SetParent(LViewI *p) { ThreadCheck(); d->Parent = p ? p->GetGView() : NULL; d->ParentI = p; } void LView::SendNotify(LNotification note) { LViewI *n = d->Notify ? d->Notify : d->Parent; if (n) { if ( #if LGI_VIEW_HANDLE && !defined(HAIKU) !_View || #endif InThread()) { n->OnNotify(this, note); } else { // We are not in the same thread as the target window. So we post a message // across to the view. if (GetId() <= 0) { // We are going to generate a control Id to help the receiver of the // M_CHANGE message find out view later on. The reason for doing this // instead of sending a pointer to the object, is that the object // _could_ be deleted between the message being sent and being received. // Which would result in an invalid memory access on that object. LViewI *p = GetWindow(); if (!p) { // No window? Find the top most parent we can... p = this; while (p->GetParent()) p = p->GetParent(); } if (p) { // Give the control a valid ID int i; for (i=10; i<1000; i++) { if (!p->FindControl(i)) { printf("Giving the ctrl '%s' the id '%i' for SendNotify\n", GetClass(), i); SetId(i); break; } } } else { // Ok this is really bad... go random (better than nothing) SetId(5000 + LRand(2000)); } } LAssert(GetId() > 0); // We must have a valid ctrl ID at this point, otherwise // the receiver will never be able to find our object. // printf("Post M_CHANGE %i %i\n", GetId(), Data); n->PostEvent(M_CHANGE, (LMessage::Param) GetId(), (LMessage::Param) new LNotification(note)); } } } LViewI *LView::GetNotify() { ThreadCheck(); return d->Notify; } void LView::SetNotify(LViewI *p) { ThreadCheck(); d->Notify = p; } #define ADJ_LEFT 1 #define ADJ_RIGHT 2 #define ADJ_UP 3 #define ADJ_DOWN 4 int IsAdjacent(LRect &a, LRect &b) { if ( (a.x1 == b.x2 + 1) && !(a.y1 > b.y2 || a.y2 < b.y1)) { return ADJ_LEFT; } if ( (a.x2 == b.x1 - 1) && !(a.y1 > b.y2 || a.y2 < b.y1)) { return ADJ_RIGHT; } if ( (a.y1 == b.y2 + 1) && !(a.x1 > b.x2 || a.x2 < b.x1)) { return ADJ_UP; } if ( (a.y2 == b.y1 - 1) && !(a.x1 > b.x2 || a.x2 < b.x1)) { return ADJ_DOWN; } return 0; } LRect JoinAdjacent(LRect &a, LRect &b, int Adj) { LRect t; switch (Adj) { case ADJ_LEFT: case ADJ_RIGHT: { t.y1 = MAX(a.y1, b.y1); t.y2 = MIN(a.y2, b.y2); t.x1 = MIN(a.x1, b.x1); t.x2 = MAX(a.x2, b.x2); break; } case ADJ_UP: case ADJ_DOWN: { t.y1 = MIN(a.y1, b.y1); t.y2 = MAX(a.y2, b.y2); t.x1 = MAX(a.x1, b.x1); t.x2 = MIN(a.x2, b.x2); break; } } return t; } LRect *LView::FindLargest(LRegion &r) { ThreadCheck(); int Pixels = 0; LRect *Best = 0; static LRect Final; // do initial run through the list to find largest single region for (LRect *i = r.First(); i; i = r.Next()) { int Pix = i->X() * i->Y(); if (Pix > Pixels) { Pixels = Pix; Best = i; } } if (Best) { Final = *Best; Pixels = Final.X() * Final.Y(); int LastPixels = Pixels; LRect LastRgn = Final; int ThisPixels = Pixels; LRect ThisRgn = Final; LRegion TempList; for (LRect *i = r.First(); i; i = r.Next()) { TempList.Union(i); } TempList.Subtract(Best); do { LastPixels = ThisPixels; LastRgn = ThisRgn; // search for adjoining rectangles that maybe we can add for (LRect *i = TempList.First(); i; i = TempList.Next()) { int Adj = IsAdjacent(ThisRgn, *i); if (Adj) { LRect t = JoinAdjacent(ThisRgn, *i, Adj); int Pix = t.X() * t.Y(); if (Pix > ThisPixels) { ThisPixels = Pix; ThisRgn = t; TempList.Subtract(i); } } } } while (LastPixels < ThisPixels); Final = ThisRgn; } else return 0; return &Final; } LRect *LView::FindSmallestFit(LRegion &r, int Sx, int Sy) { ThreadCheck(); int X = 1000000; int Y = 1000000; LRect *Best = 0; for (LRect *i = r.First(); i; i = r.Next()) { if ((i->X() >= Sx && i->Y() >= Sy) && (i->X() < X || i->Y() < Y)) { X = i->X(); Y = i->Y(); Best = i; } } return Best; } LRect *LView::FindLargestEdge(LRegion &r, int Edge) { LRect *Best = 0; ThreadCheck(); for (LRect *i = r.First(); i; i = r.Next()) { if (!Best) { Best = i; } if ( ((Edge & GV_EDGE_TOP) && (i->y1 < Best->y1)) || ((Edge & GV_EDGE_RIGHT) && (i->x2 > Best->x2)) || ((Edge & GV_EDGE_BOTTOM) && (i->y2 > Best->y2)) || ((Edge & GV_EDGE_LEFT) && (i->x1 < Best->x1)) ) { Best = i; } if ( ( ((Edge & GV_EDGE_TOP) && (i->y1 == Best->y1)) || ((Edge & GV_EDGE_BOTTOM) && (i->y2 == Best->y2)) ) && ( i->X() > Best->X() ) ) { Best = i; } if ( ( ((Edge & GV_EDGE_RIGHT) && (i->x2 == Best->x2)) || ((Edge & GV_EDGE_LEFT) && (i->x1 == Best->x1)) ) && ( i->Y() > Best->Y() ) ) { Best = i; } } return Best; } LViewI *LView::FindReal(LPoint *Offset) { ThreadCheck(); if (Offset) { Offset->x = 0; Offset->y = 0; } #if !LGI_VIEW_HANDLE LViewI *w = GetWindow(); #endif LViewI *p = d->Parent; while (p && #if !LGI_VIEW_HANDLE p != w #else !p->Handle() #endif ) { if (Offset) { Offset->x += Pos.x1; Offset->y += Pos.y1; } p = p->GetParent(); } if (p && #if !LGI_VIEW_HANDLE p == w #else p->Handle() #endif ) { return p; } return NULL; } bool LView::HandleCapture(LView *Wnd, bool c) { ThreadCheck(); DEBUG_CAPTURE("%s::HandleCapture(%i)=%i\n", GetClass(), c, (int)(_Capturing == Wnd)); if (c) { if (_Capturing == Wnd) { DEBUG_CAPTURE(" %s already has capture\n", _Capturing?_Capturing->GetClass():0); } else { DEBUG_CAPTURE(" _Capturing=%s -> %s\n", _Capturing?_Capturing->GetClass():0, Wnd?Wnd->GetClass():0); _Capturing = Wnd; #if defined(__GTK_H__) if (d->CaptureThread) d->CaptureThread->Cancel(); d->CaptureThread = new LCaptureThread(this); #elif WINNATIVE LPoint Offset; LViewI *v = _Capturing->Handle() ? _Capturing : FindReal(&Offset); HWND h = v ? v->Handle() : NULL; if (h) SetCapture(h); else LAssert(0); #elif defined(LGI_SDL) #if SDL_VERSION_ATLEAST(2, 0, 4) SDL_CaptureMouse(SDL_TRUE); #else LAppInst->CaptureMouse(true); #endif #endif } } else if (_Capturing) { DEBUG_CAPTURE(" _Capturing=%s -> NULL\n", _Capturing?_Capturing->GetClass():0); _Capturing = NULL; #if defined(__GTK_H__) if (d->CaptureThread) { d->CaptureThread->Cancel(); d->CaptureThread = NULL; // It'll delete itself... } #elif WINNATIVE ReleaseCapture(); #elif defined(LGI_SDL) #if SDL_VERSION_ATLEAST(2, 0, 4) SDL_CaptureMouse(SDL_FALSE); #else LAppInst->CaptureMouse(false); #endif #endif } return true; } bool LView::IsCapturing() { // DEBUG_CAPTURE("%s::IsCapturing()=%p==%p==%i\n", GetClass(), _Capturing, this, (int)(_Capturing == this)); return _Capturing == this; } bool LView::Capture(bool c) { ThreadCheck(); DEBUG_CAPTURE("%s::Capture(%i)\n", GetClass(), c); return HandleCapture(this, c); } bool LView::Enabled() { ThreadCheck(); #if WINNATIVE if (_View) return IsWindowEnabled(_View) != 0; #endif return !TestFlag(GViewFlags, GWF_DISABLED); } void LView::Enabled(bool i) { ThreadCheck(); if (!i) SetFlag(GViewFlags, GWF_DISABLED); else ClearFlag(GViewFlags, GWF_DISABLED); #if LGI_VIEW_HANDLE && !defined(HAIKU) if (_View) { #if WINNATIVE EnableWindow(_View, i); #elif defined LGI_CARBON if (i) { OSStatus e = EnableControl(_View); if (e) printf("%s:%i - Error enabling control (%i)\n", _FL, (int)e); } else { OSStatus e = DisableControl(_View); if (e) printf("%s:%i - Error disabling control (%i)\n", _FL, (int)e); } #endif } #endif Invalidate(); } bool LView::Visible() { // This is a read only operation... which is kinda thread-safe... // ThreadCheck(); #if WINNATIVE if (_View) /* This takes into account all the parent windows as well... Which is kinda not what I want. I want this to reflect just this window. return IsWindowVisible(_View); */ return (GetWindowLong(_View, GWL_STYLE) & WS_VISIBLE) != 0; #endif return TestFlag(GViewFlags, GWF_VISIBLE); } void LView::Visible(bool v) { ThreadCheck(); if (v) SetFlag(GViewFlags, GWF_VISIBLE); else ClearFlag(GViewFlags, GWF_VISIBLE); #if defined(HAIKU) LLocker lck(d->Hnd, _FL); if (!IsAttached() || lck.Lock()) { const int attempts = 3; // printf("%s/%p:Visible(%i) hidden=%i\n", GetClass(), this, v, d->Hnd->IsHidden()); if (v) { bool parentHidden = false; for (auto p = d->Hnd->Parent(); p; p = p->Parent()) { if (p->IsHidden()) { parentHidden = true; break; } } if (!parentHidden) // Don't try and show if one of the parent's is hidden. { for (int i=0; iHnd->IsHidden(); i++) { // printf("\t%p Show\n", this); d->Hnd->Show(); } if (d->Hnd->IsHidden()) { printf("%s:%i - Failed to show %s.\n", _FL, GetClass()); for (auto p = d->Hnd->Parent(); p; p = p->Parent()) printf("\tparent: %s/%p ishidden=%i\n", p->Name(), p, p->IsHidden()); } } } else { for (int i=0; iHnd->IsHidden(); i++) { // printf("\t%p Hide\n", this); d->Hnd->Hide(); } if (!d->Hnd->IsHidden()) { printf("%s:%i - Failed to hide %s.\n", _FL, GetClass()); for (auto p = d->Hnd->Parent(); p; p = p->Parent()) printf("\tparent: %s/%p ishidden=%i\n", p->Name(), p, p->IsHidden()); } } // printf("\t%s/%p:Visible(%i) hidden=%i\n", GetClass(), this, v, d->Hnd->IsHidden()); } else LgiTrace("%s:%i - Can't lock.\n", _FL); #elif LGI_VIEW_HANDLE if (_View) { #if WINNATIVE ShowWindow(_View, (v) ? SW_SHOWNORMAL : SW_HIDE); #elif LGI_COCOA LAutoPool Pool; [_View.p setHidden:!v]; #elif LGI_CARBON Boolean is = HIViewIsVisible(_View); if (v != is) { OSErr e = HIViewSetVisible(_View, v); if (e) printf("%s:%i - HIViewSetVisible(%p,%i) failed with %i (class=%s)\n", _FL, _View, v, e, GetClass()); } #endif } else #endif { Invalidate(); } } bool LView::Focus() { ThreadCheck(); bool Has = false; #if defined(__GTK_H__) LWindow *w = GetWindow(); if (w) { bool Active = w->IsActive(); if (Active) Has = w->GetFocus() == static_cast(this); } #elif defined(HAIKU) LLocker lck(d->Hnd, _FL); if (lck.Lock()) { Has = d->Hnd->IsFocus(); lck.Unlock(); } #elif defined(WINNATIVE) if (_View) { HWND hFocus = GetFocus(); Has = hFocus == _View; } #elif LGI_COCOA Has = TestFlag(WndFlags, GWF_FOCUS); #elif LGI_CARBON LWindow *w = GetWindow(); if (w) { ControlRef Cur; OSErr e = GetKeyboardFocus(w->WindowHandle(), &Cur); if (e) LgiTrace("%s:%i - GetKeyboardFocus failed with %i\n", _FL, e); else Has = (Cur == _View); } #endif #if !LGI_CARBON if (Has) SetFlag(WndFlags, GWF_FOCUS); else ClearFlag(WndFlags, GWF_FOCUS); #endif return Has; } void LView::Focus(bool i) { ThreadCheck(); if (i) SetFlag(WndFlags, GWF_FOCUS); else ClearFlag(WndFlags, GWF_FOCUS); auto *Wnd = GetWindow(); if (Wnd) Wnd->SetFocus(this, i ? LWindow::GainFocus : LWindow::LoseFocus); #if LGI_VIEW_HANDLE && !defined(HAIKU) if (_View) #endif { #if defined(HAIKU) _Focus(i); #elif defined(LGI_SDL) || defined(__GTK_H__) // Nop: Focus is all handled by Lgi's LWindow class. #elif WINNATIVE if (i) { HWND hCur = GetFocus(); if (hCur != _View) { if (In_SetWindowPos) { assert(0); LgiTrace("%s:%i - SetFocus %p (%-30s)\n", _FL, Handle(), Name()); } SetFocus(_View); } } else { if (In_SetWindowPos) { assert(0); LgiTrace("%s:%i - SetFocus(%p)\n", _FL, GetDesktopWindow()); } SetFocus(GetDesktopWindow()); } #elif defined LGI_CARBON LViewI *Wnd = GetWindow(); if (Wnd && i) { OSErr e = SetKeyboardFocus(Wnd->WindowHandle(), _View, 1); if (e) { // e = SetKeyboardFocus(Wnd->WindowHandle(), _View, kControlFocusNextPart); // if (e) { HIViewRef p = HIViewGetSuperview(_View); // errCouldntSetFocus printf("%s:%i - SetKeyboardFocus failed: %i (%s, %p)\n", _FL, e, GetClass(), p); } } // else printf("%s:%i - SetFocus v=%p(%s)\n", _FL, _View, GetClass()); } else printf("%s:%i - no window?\n", _FL); #endif } } LDragDropSource *LView::DropSource(LDragDropSource *Set) { if (Set) d->DropSource = Set; return d->DropSource; } LDragDropTarget *LView::DropTarget(LDragDropTarget *Set) { if (Set) d->DropTarget = Set; return d->DropTarget; } #if defined LGI_CARBON extern pascal OSStatus LgiViewDndHandler(EventHandlerCallRef inHandlerCallRef, EventRef inEvent, void *inUserData); #endif #if defined __GTK_H__ // Recursively add drag dest to all view and all children bool GtkAddDragDest(LViewI *v, bool IsTarget) { if (!v) return false; LWindow *w = v->GetWindow(); if (!w) return false; auto wid = GtkCast(w->WindowHandle(), gtk_widget, GtkWidget); if (IsTarget) { Gtk::gtk_drag_dest_set( wid, (Gtk::GtkDestDefaults)0, NULL, 0, Gtk::GDK_ACTION_DEFAULT); } else { Gtk::gtk_drag_dest_unset(wid); } for (LViewI *c: v->IterateViews()) GtkAddDragDest(c, IsTarget); return true; } #endif bool LView::DropTarget(bool t) { ThreadCheck(); bool Status = false; if (t) SetFlag(GViewFlags, GWF_DROP_TARGET); else ClearFlag(GViewFlags, GWF_DROP_TARGET); #if WINNATIVE if (_View) { if (t) { if (!d->DropTarget) DragAcceptFiles(_View, t); else Status = RegisterDragDrop(_View, (IDropTarget*) d->DropTarget) == S_OK; } else { if (_View && d->DropTarget) Status = RevokeDragDrop(_View) == S_OK; } } #elif defined MAC && !defined(LGI_SDL) LWindow *Wnd = dynamic_cast(GetWindow()); if (Wnd) { Wnd->SetDragHandlers(t); if (!d->DropTarget) d->DropTarget = t ? Wnd : 0; } #if LGI_COCOA LWindow *w = GetWindow(); if (w) { OsWindow h = w->WindowHandle(); if (h) { NSMutableArray *a = [[NSMutableArray alloc] init]; if (a) { [a addObject:(NSString*)kUTTypeItem]; for (id item in NSFilePromiseReceiver.readableDraggedTypes) [a addObject:item]; [h.p.contentView registerForDraggedTypes:a]; [a release]; } } } #elif LGI_CARBON if (t) { static EventTypeSpec DragEvents[] = { { kEventClassControl, kEventControlDragEnter }, { kEventClassControl, kEventControlDragWithin }, { kEventClassControl, kEventControlDragLeave }, { kEventClassControl, kEventControlDragReceive }, }; if (!d->DndHandler) { OSStatus e = ::InstallControlEventHandler( _View, NewEventHandlerUPP(LgiViewDndHandler), GetEventTypeCount(DragEvents), DragEvents, (void*)this, &d->DndHandler); if (e) LgiTrace("%s:%i - InstallEventHandler failed (%i)\n", _FL, e); } SetControlDragTrackingEnabled(_View, true); } else { SetControlDragTrackingEnabled(_View, false); } #endif #elif defined __GTK_H__ Status = GtkAddDragDest(this, t); if (Status && !d->DropTarget) d->DropTarget = t ? GetWindow() : 0; #endif return Status; } bool LView::Sunken() { // ThreadCheck(); #if WINNATIVE return TestFlag(d->WndExStyle, WS_EX_CLIENTEDGE); #else return TestFlag(GViewFlags, GWF_SUNKEN); #endif } void LView::Sunken(bool i) { ThreadCheck(); #if WINNATIVE if (i) SetFlag(d->WndExStyle, WS_EX_CLIENTEDGE); else ClearFlag(d->WndExStyle, WS_EX_CLIENTEDGE); if (_View) SetWindowLong(_View, GWL_EXSTYLE, d->WndExStyle); #else if (i) SetFlag(GViewFlags, GWF_SUNKEN); else ClearFlag(GViewFlags, GWF_SUNKEN); #endif if (i) { if (!_BorderSize) _BorderSize = 2; } else _BorderSize = 0; } bool LView::Flat() { // ThreadCheck(); #if WINNATIVE return !TestFlag(d->WndExStyle, WS_EX_CLIENTEDGE) && !TestFlag(d->WndExStyle, WS_EX_WINDOWEDGE); #else return !TestFlag(GViewFlags, GWF_SUNKEN) && !TestFlag(GViewFlags, GWF_RAISED); #endif } void LView::Flat(bool i) { ThreadCheck(); #if WINNATIVE ClearFlag(d->WndExStyle, (WS_EX_CLIENTEDGE|WS_EX_WINDOWEDGE)); #else ClearFlag(GViewFlags, (GWF_RAISED|GWF_SUNKEN)); #endif } bool LView::Raised() { // ThreadCheck(); #if WINNATIVE return TestFlag(d->WndExStyle, WS_EX_WINDOWEDGE); #else return TestFlag(GViewFlags, GWF_RAISED); #endif } void LView::Raised(bool i) { ThreadCheck(); #if WINNATIVE if (i) SetFlag(d->WndExStyle, WS_EX_WINDOWEDGE); else ClearFlag(d->WndExStyle, WS_EX_WINDOWEDGE); #else if (i) SetFlag(GViewFlags, GWF_RAISED); else ClearFlag(GViewFlags, GWF_RAISED); #endif if (i) { if (!!_BorderSize) _BorderSize = 2; } else _BorderSize = 0; } int LView::GetId() { // This is needed by SendNotify function which is thread safe. // So no thread safety check here. return d->CtrlId; } void LView::SetId(int i) { // This is needed by SendNotify function which is thread safe. // So no thread safety check here. d->CtrlId = i; #if WINNATIVE if (_View) SetWindowLong(_View, GWL_ID, d->CtrlId); #elif defined __GTK_H__ #elif defined MAC #endif } bool LView::GetTabStop() { ThreadCheck(); #if WINNATIVE return TestFlag(d->WndStyle, WS_TABSTOP); #else return d->TabStop; #endif } void LView::SetTabStop(bool b) { ThreadCheck(); #if WINNATIVE if (b) SetFlag(d->WndStyle, WS_TABSTOP); else ClearFlag(d->WndStyle, WS_TABSTOP); #else d->TabStop = b; #endif } int64 LView::GetCtrlValue(int Id) { ThreadCheck(); LViewI *w = FindControl(Id); return (w) ? w->Value() : 0; } void LView::SetCtrlValue(int Id, int64 i) { ThreadCheck(); LViewI *w = FindControl(Id); if (w) w->Value(i); } const char *LView::GetCtrlName(int Id) { ThreadCheck(); LViewI *w = FindControl(Id); return (w) ? w->Name() : 0; } void LView::SetCtrlName(int Id, const char *s) { if (!IsAttached() || InThread()) { if (auto w = FindControl(Id)) w->Name(s); } else { PostEvent( M_SET_CTRL_NAME, (LMessage::Param)Id, (LMessage::Param)new LString(s)); } } bool LView::GetCtrlEnabled(int Id) { ThreadCheck(); LViewI *w = FindControl(Id); return (w) ? w->Enabled() : 0; } void LView::SetCtrlEnabled(int Id, bool Enabled) { if (!IsAttached() || InThread()) { if (auto w = FindControl(Id)) w->Enabled(Enabled); } else { PostEvent( M_SET_CTRL_ENABLE, (LMessage::Param)Id, (LMessage::Param)Enabled); } } bool LView::GetCtrlVisible(int Id) { ThreadCheck(); LViewI *w = FindControl(Id); if (!w) LgiTrace("%s:%i - Ctrl %i not found.\n", _FL, Id); return (w) ? w->Visible() : 0; } void LView::SetCtrlVisible(int Id, bool v) { if (!IsAttached() || InThread()) { if (auto w = FindControl(Id)) w->Visible(v); } else { PostEvent( M_SET_CTRL_VISIBLE, (LMessage::Param)Id, (LMessage::Param)v); } } bool LView::AttachChildren() { for (auto c: Children) { bool a = c->IsAttached(); if (!a) { if (!c->Attach(this)) { LgiTrace("%s:%i - failed to attach %s\n", _FL, c->GetClass()); return false; } } } return true; } LFont *LView::GetFont() { if (!d->Font && d->Css && LResources::GetLoadStyles()) { LFontCache *fc = LAppInst->GetFontCache(); if (fc) { LFont *f = fc->GetFont(d->Css); if (f) { if (d->FontOwnType == GV_FontOwned) DeleteObj(d->Font); d->Font = f; d->FontOwnType = GV_FontCached; } } } return d->Font ? d->Font : LSysFont; } void LView::SetFont(LFont *Font, bool OwnIt) { bool Change = d->Font != Font; if (Change) { if (d->FontOwnType == GV_FontOwned) { LAssert(d->Font != LSysFont); DeleteObj(d->Font); } d->FontOwnType = OwnIt ? GV_FontOwned : GV_FontPtr; d->Font = Font; #if WINNATIVE if (_View) SendMessage(_View, WM_SETFONT, (WPARAM) (Font ? Font->Handle() : 0), 0); #endif for (LViewI *p = GetParent(); p; p = p->GetParent()) { LTableLayout *Tl = dynamic_cast(p); if (Tl) { Tl->InvalidateLayout(); break; } } Invalidate(); } } bool LView::IsOver(LMouse &m) { return (m.x >= 0) && (m.y >= 0) && (m.x < Pos.X()) && (m.y < Pos.Y()); } bool LView::WindowVirtualOffset(LPoint *Offset) { bool Status = false; if (Offset) { Offset->x = 0; Offset->y = 0; for (LViewI *Wnd = this; Wnd; Wnd = Wnd->GetParent()) { #if !LGI_VIEW_HANDLE auto IsWnd = dynamic_cast(Wnd); if (!IsWnd) #else if (!Wnd->Handle()) #endif { LRect r = Wnd->GetPos(); LViewI *Par = Wnd->GetParent(); if (Par) { LRect c = Par->GetClient(false); Offset->x += r.x1 + c.x1; Offset->y += r.y1 + c.y1; } else { Offset->x += r.x1; Offset->y += r.y1; } Status = true; } else break; } } return Status; } LString _ViewDesc(LViewI *v) { LString s; s.Printf("%s/%s/%i", v->GetClass(), v->Name(), v->GetId()); return s; } LViewI *LView::WindowFromPoint(int x, int y, int DebugDepth) { char Tabs[64]; if (DebugDepth) { memset(Tabs, 9, DebugDepth); Tabs[DebugDepth] = 0; LgiTrace("%s%s %i\n", Tabs, _ViewDesc(this).Get(), Children.Length()); } // We iterate over the child in reverse order because if they overlap the // end of the list is on "top". So they should get the click or whatever // before the the lower windows. auto it = Children.rbegin(); int n = (int)Children.Length() - 1; for (LViewI *c = *it; c; c = *--it) { LRect CPos = c->GetPos(); if (CPos.Overlap(x, y) && c->Visible()) { LRect CClient; CClient = c->GetClient(false); int Ox = CPos.x1 + CClient.x1; int Oy = CPos.y1 + CClient.y1; if (DebugDepth) { LgiTrace("%s[%i] %s Pos=%s Client=%s m(%i,%i)->(%i,%i)\n", Tabs, n--, _ViewDesc(c).Get(), CPos.GetStr(), CClient.GetStr(), x, y, x - Ox, y - Oy); } LViewI *Child = c->WindowFromPoint(x - Ox, y - Oy, DebugDepth ? DebugDepth + 1 : 0); if (Child) return Child; } else if (DebugDepth) { LgiTrace("%s[%i] MISSED %s Pos=%s m(%i,%i)\n", Tabs, n--, _ViewDesc(c).Get(), CPos.GetStr(), x, y); } } if (x >= 0 && y >= 0 && x < Pos.X() && y < Pos.Y()) { return this; } return NULL; } LColour LView::StyleColour(int CssPropType, LColour Default, int Depth) { LColour c = Default; if ((CssPropType >> 8) == LCss::TypeColor) { LViewI *v = this; for (int i=0; v && iGetParent()) { auto Style = v->GetCss(); if (Style) { auto Colour = (LCss::ColorDef*) Style->PropAddress((LCss::PropType)CssPropType); if (Colour) { if (Colour->Type == LCss::ColorRgb) { c.Set(Colour->Rgb32, 32); break; } else if (Colour->Type == LCss::ColorTransparent) { c.Empty(); break; } } } if (dynamic_cast(v) || dynamic_cast(v)) break; } } return c; } bool LView::InThread() { #if WINNATIVE HWND Hnd = _View; for (LViewI *p = GetParent(); p && !Hnd; p = p->GetParent()) { Hnd = p->Handle(); } auto CurThreadId = GetCurrentThreadId(); auto GuiThreadId = LAppInst->GetGuiThreadId(); DWORD ViewThread = Hnd ? GetWindowThreadProcessId(Hnd, NULL) : GuiThreadId; return CurThreadId == ViewThread; #elif defined(HAIKU) return true; #else OsThreadId Me = GetCurrentThreadId(); OsThreadId Gui = LAppInst ? LAppInst->GetGuiThreadId() : 0; #if 0 if (Gui != Me) LgiTrace("%s:%i - Out of thread:" #ifdef LGI_COCOA "%llx, %llx" #else "%x, %x" #endif "\n", _FL, Gui, Me); #endif return Gui == Me; #endif } bool LView::PostEvent(int Cmd, LMessage::Param a, LMessage::Param b, int64_t timeoutMs) { #ifdef LGI_SDL return LPostEvent(this, Cmd, a, b); #elif defined(HAIKU) if (!d || !d->Hnd) { // printf("%s:%i - Bad pointers %p %p\n", _FL, d, d ? d->Hnd : NULL); return false; } BWindow *bWnd = NULL; LWindow *wnd = dynamic_cast(this); if (wnd) { bWnd = wnd->WindowHandle(); } else { // Look for an attached view to lock... for (LViewI *v = this; v; v = v->GetParent()) { auto vhnd = v->Handle(); if (vhnd && ::IsAttached(vhnd)) { bWnd = vhnd->Window(); break; } } } BMessage m(Cmd); auto r = m.AddInt64(LMessage::PropA, a); if (r != B_OK) printf("%s:%i - AddUInt64 failed.\n", _FL); r = m.AddInt64(LMessage::PropB, b); if (r != B_OK) printf("%s:%i - AddUInt64 failed.\n", _FL); r = m.AddPointer(LMessage::PropView, this); if (r != B_OK) printf("%s:%i - AddPointer failed.\n", _FL); if (bWnd) { r = bWnd->PostMessage(&m); if (r != B_OK) printf("%s:%i - PostMessage failed.\n", _FL); return r == B_OK; } // Not attached yet... d->MsgQue.Add(new BMessage(m)); // printf("%s:%i - PostEvent.MsgQue=%i\n", _FL, (int)d->MsgQue.Length()); return true; #elif WINNATIVE if (!_View) return false; BOOL Res = ::PostMessage(_View, Cmd, a, b); if (!Res) { auto Err = GetLastError(); int asd=0; } return Res != 0; #elif !LGI_VIEW_HANDLE return LAppInst->PostEvent(this, Cmd, a, b); #else if (_View) return LPostEvent(_View, Cmd, a, b); LAssert(0); return false; #endif } bool LView::Invalidate(LRegion *r, bool Repaint, bool NonClient) { if (r) { for (int i=0; iLength(); i++) { bool Last = i == r->Length()-1; Invalidate((*r)[i], Last ? Repaint : false, NonClient); } return true; } return false; } LButton *FindDefault(LViewI *w) { LButton *But = 0; for (auto c: w->IterateViews()) { But = dynamic_cast(c); if (But && But->Default()) { break; } But = FindDefault(c); if (But) { break; } } return But; } bool LView::Name(const char *n) { LBase::Name(n); #if LGI_VIEW_HANDLE && !defined(HAIKU) if (_View) { #if WINNATIVE auto Temp = LBase::NameW(); SetWindowTextW(_View, Temp ? Temp : L""); #endif } #endif Invalidate(); return true; } const char *LView::Name() { #if WINNATIVE if (_View) { LView::NameW(); } #endif return LBase::Name(); } bool LView::NameW(const char16 *n) { LBase::NameW(n); #if WINNATIVE if (_View && n) { auto Txt = LBase::NameW(); SetWindowTextW(_View, Txt); } #endif Invalidate(); return true; } const char16 *LView::NameW() { #if WINNATIVE if (_View) { int Length = GetWindowTextLengthW(_View); if (Length > 0) { char16 *Buf = new char16[Length+1]; if (Buf) { Buf[0] = 0; int Chars = GetWindowTextW(_View, Buf, Length+1); Buf[Chars] = 0; LBase::NameW(Buf); } DeleteArray(Buf); } else { LBase::NameW(0); } } #endif return LBase::NameW(); } LViewI *LView::FindControl(int Id) { LAssert(Id != -1); if (GetId() == Id) { return this; } for (auto c : Children) { LViewI *Ctrl = c->FindControl(Id); if (Ctrl) { return Ctrl; } } return 0; } LPoint LView::GetMinimumSize() { return d->MinimumSize; } void LView::SetMinimumSize(LPoint Size) { d->MinimumSize = Size; bool Change = false; LRect p = Pos; if (X() < d->MinimumSize.x) { p.x2 = p.x1 + d->MinimumSize.x - 1; Change = true; } if (Y() < d->MinimumSize.y) { p.y2 = p.y1 + d->MinimumSize.y - 1; Change = true; } if (Change) { SetPos(p); } } bool LView::SetColour(LColour &c, bool Fore) { LCss *css = GetCss(true); if (!css) return false; if (Fore) { if (c.IsValid()) css->Color(LCss::ColorDef(LCss::ColorRgb, c.c32())); else css->DeleteProp(LCss::PropColor); } else { if (c.IsValid()) css->BackgroundColor(LCss::ColorDef(LCss::ColorRgb, c.c32())); else css->DeleteProp(LCss::PropBackgroundColor); } return true; } /* bool LView::SetCssStyle(const char *CssStyle) { if (!d->Css && !d->Css.Reset(new LCss)) return false; const char *Defs = CssStyle; bool b = d->Css->Parse(Defs, LCss::ParseRelaxed); if (b && d->FontOwnType == GV_FontCached) { d->Font = NULL; d->FontOwnType = GV_FontPtr; } return b; } */ void LView::SetCss(LCss *css) { d->Css.Reset(css); } LCss *LView::GetCss(bool Create) { if (Create && !d->Css) d->Css.Reset(new LCss); if (d->CssDirty && d->Css) { const char *Defs = d->Styles; if (d->Css->Parse(Defs, LCss::ParseRelaxed)) d->CssDirty = false; } return d->Css; } LPoint &LView::GetWindowBorderSize() { static LPoint s; ZeroObj(s); #if WINNATIVE if (_View) { RECT Wnd, Client; GetWindowRect(Handle(), &Wnd); GetClientRect(Handle(), &Client); s.x = (Wnd.right-Wnd.left) - (Client.right-Client.left); s.y = (Wnd.bottom-Wnd.top) - (Client.bottom-Client.top); } #elif defined __GTK_H__ #elif defined MAC s.x = 0; s.y = 22; #endif return s; } #ifdef _DEBUG #if defined(LGI_CARBON) void DumpHiview(HIViewRef v, int Depth = 0) { char Sp[256]; memset(Sp, ' ', Depth << 2); Sp[Depth<<2] = 0; printf("%sHIView=%p", Sp, v); if (v) { Boolean vis = HIViewIsVisible(v); Boolean en = HIViewIsEnabled(v, NULL); HIRect pos; HIViewGetFrame(v, &pos); char cls[128]; ZeroObj(cls); GetControlProperty(v, 'meme', 'clas', sizeof(cls), NULL, cls); printf(" vis=%i en=%i pos=%g,%g-%g,%g cls=%s", vis, en, pos.origin.x, pos.origin.y, pos.size.width, pos.size.height, cls); } printf("\n"); for (HIViewRef c = HIViewGetFirstSubview(v); c; c = HIViewGetNextView(c)) { DumpHiview(c, Depth + 1); } } #elif defined(__GTK_H__) void DumpGtk(Gtk::GtkWidget *w, Gtk::gpointer Depth = NULL) { using namespace Gtk; if (!w) return; char Sp[65] = {0}; if (Depth) memset(Sp, ' ', *((int*)Depth)*2); auto *Obj = G_OBJECT(w); LViewI *View = (LViewI*) g_object_get_data(Obj, "LViewI"); GtkAllocation a; gtk_widget_get_allocation(w, &a); LgiTrace("%s%p(%s) = %i,%i-%i,%i\n", Sp, w, View?View->GetClass():G_OBJECT_TYPE_NAME(Obj), a.x, a.y, a.width, a.height); if (GTK_IS_CONTAINER(w)) { auto *c = GTK_CONTAINER(w); if (c) { int Next = Depth ? *((int*)Depth)+1 : 1; gtk_container_foreach(c, DumpGtk, &Next); } } } #elif defined(HAIKU) || defined(WINDOWS) template void _Dump(int Depth, T v) { LString Sp, Name; Sp.Length(Depth<<1); memset(Sp.Get(), ' ', Depth<<1); Sp.Get()[Depth<<1] = 0; #if defined(HAIKU) LRect Frame = v->Frame(); Name = v->Name(); bool IsHidden = v->IsHidden(); #else RECT rc; GetWindowRect(v, &rc); LRect Frame = rc; wchar_t txt[256]; GetWindowTextW(v, txt, CountOf(txt)); Name = txt; bool IsHidden = !(GetWindowLong(v, GWL_STYLE) & WS_VISIBLE); #endif LgiTrace("%s%p::%s frame=%s vis=%i\n", Sp.Get(), v, Name.Get(), Frame.GetStr(), !IsHidden); #if defined(HAIKU) for (int32 i=0; iCountChildren(); i++) _Dump(Depth + 1, v->ChildAt(i)); #else for (auto h = GetWindow(v, GW_CHILD); h; h = GetWindow(h, GW_HWNDNEXT)) _Dump(Depth + 1, h); #endif } #endif void LView::_Dump(int Depth) { char Sp[65] = {0}; memset(Sp, ' ', Depth*2); #if 0 char s[256]; sprintf_s(s, sizeof(s), "%s%p::%s %s (_View=%p)\n", Sp, this, GetClass(), GetPos().GetStr(), _View); LgiTrace(s); List::I i = Children.Start(); for (LViewI *c = *i; c; c = *++i) { LView *v = c->GetGView(); if (v) v->_Dump(Depth+1); } #elif defined(LGI_CARBON) DumpHiview(_View); #elif defined(__GTK_H__) // DumpGtk(_View); #else #if defined(HAIKU) LLocker lck(WindowHandle(), _FL); if (lck.Lock()) #endif ::_Dump(0, WindowHandle()); #endif } #endif //////////////////////////////////////////////////////////////////////////////////////////////////// static LArray *AllFactories = NULL; #if defined(WIN32) static HANDLE FactoryEvent; #else static pthread_once_t FactoryOnce = PTHREAD_ONCE_INIT; static void GFactoryInitFactories() { AllFactories = new LArray; } #endif LViewFactory::LViewFactory() { #if defined(WIN32) char16 Name[64]; swprintf_s(Name, CountOf(Name), L"LgiFactoryEvent.%i", GetCurrentProcessId()); HANDLE h = CreateEventW(NULL, false, false, Name); DWORD err = GetLastError(); if (err != ERROR_ALREADY_EXISTS) { FactoryEvent = h; AllFactories = new LArray; } else { LAssert(AllFactories != NULL); } #else pthread_once(&FactoryOnce, GFactoryInitFactories); #endif if (AllFactories) AllFactories->Add(this); } LViewFactory::~LViewFactory() { if (AllFactories) { AllFactories->Delete(this); if (AllFactories->Length() == 0) { DeleteObj(AllFactories); #if defined(WIN32) CloseHandle(FactoryEvent); #endif } } } LView *LViewFactory::Create(const char *Class, LRect *Pos, const char *Text) { if (ValidStr(Class) && AllFactories) { for (int i=0; iLength(); i++) { LView *v = (*AllFactories)[i]->NewView(Class, Pos, Text); if (v) { return v; } } } return 0; } #ifdef _DEBUG #if defined(__GTK_H__) using namespace Gtk; #include "LgiWidget.h" #endif void LView::Debug() { _Debug = true; #if defined LGI_COCOA d->ClassName = GetClass(); #endif } #endif diff --git a/src/haiku/App.cpp b/src/haiku/App.cpp --- a/src/haiku/App.cpp +++ b/src/haiku/App.cpp @@ -1,1052 +1,1052 @@ #include #include #include #include #include #include #include #include "lgi/common/Lgi.h" #include "lgi/common/SkinEngine.h" #include "lgi/common/Array.h" #include "lgi/common/Variant.h" #include "lgi/common/Token.h" #include "lgi/common/FontCache.h" #include "AppPriv.h" #include "MimeType.h" #define DEBUG_MSG_TYPES 0 #define DEBUG_HND_WARNINGS 0 #define IDLE_ALWAYS 0 LString LgiArgsAppPath; //////////////////////////////////////////////////////////////// struct OsAppArgumentsPriv { ::LArray Ptr; ~OsAppArgumentsPriv() { Ptr.DeleteArrays(); } }; OsAppArguments::OsAppArguments(int args, const char **arg) { d = new OsAppArgumentsPriv; for (int i=0; iPtr.Add(NewStr(arg[i])); } Args = d->Ptr.Length(); Arg = &d->Ptr[0]; } OsAppArguments::~OsAppArguments() { DeleteObj(d); } bool OsAppArguments::Get(const char *Name, const char **Val) { if (!Name) return false; for (int i=0; iPtr.DeleteArrays(); if (!CmdLine) return; for (char *s = CmdLine; *s; ) { while (*s && strchr(WhiteSpace, *s)) s++; if (*s == '\'' || *s == '\"') { char delim = *s++; char *e = strchr(s, delim); if (e) d->Ptr.Add(NewStr(s, e - s)); else break; s = e + 1; } else { char *e = s; while (*e && !strchr(WhiteSpace, *e)) e++; d->Ptr.Add(NewStr(s, e-s)); s = e; } } Args = d->Ptr.Length(); Arg = &d->Ptr[0]; } OsAppArguments &OsAppArguments::operator =(OsAppArguments &a) { d->Ptr.DeleteArrays(); for (int i=0; iPtr.Add(NewStr(a.Arg[i])); } Args = d->Ptr.Length(); Arg = &d->Ptr[0]; return *this; } //////////////////////////////////////////////////////////////// #if HAS_SHARED_MIME #include "GFilterUtils.h" #include "mime-types.h" class LSharedMime : public LLibrary { public: LSharedMime() : #ifdef _DEBUG LLibrary("libsharedmime1d") #else LLibrary("libsharedmime1") #endif { } DynFunc0(int, mimetypes_init); DynFunc1(const char*, mimetypes_set_default_type, const char *, default_type); DynFunc2(const char*, mimetypes_get_file_type, const char*, pathname, mimetypes_flags, flags); DynFunc2(const char*, mimetypes_get_data_type, const void*, data, int, length); DynFunc3(bool, mimetypes_decode, const char *, type, char **, media_type, char **, sub_type); DynFunc2(char *, mimetypes_convert_filename, const char *, pathname, const char *, mime_type); DynFunc3(bool, mimetypes_add_mime_dir, const char *, path, bool, high_priority, bool, rescan); DynFunc2(const char *, mimetypes_get_type_info, const char *, mime_type, const char *, lang); }; #endif #if 1 ///////////////////////////////////////////////////////////////////////////// // // Attempts to cleanup and call drkonqi to process the crash // void LgiCrashHandler(int Sig) { // Don't get into an infinite loop signal(SIGSEGV, SIG_DFL); #ifndef _MSC_VER // Our pid int MyPid = getpid(); printf("LgiCrashHandler trigger MyPid=%i\n", MyPid); #endif exit(-1); } #endif ///////////////////////////////////////////////////////////////////////////// #ifndef XK_Num_Lock #define XK_Num_Lock 0xff7f #endif #ifndef XK_Shift_Lock #define XK_Shift_Lock 0xffe6 #endif #ifndef XK_Caps_Lock #define XK_Caps_Lock 0xffe5 #endif struct Msg { LViewI *v; int m; LMessage::Param a, b; void Set(LViewI *V, int M, LMessage::Param A, LMessage::Param B) { v = V; m = M; a = A; b = B; } }; // Out of thread messages... must lock before access. class LMessageQue : public LMutex { public: typedef ::LArray MsgArray; LMessageQue() : LMutex("LMessageQue") { } MsgArray *Lock(const char *file, int line) { if (!LMutex::Lock(file, line)) return NULL; return &q; } operator bool() { return q.Length() > 0; } private: MsgArray q; } MsgQue; ///////////////////////////////////////////////////////////////////////////// LApp *TheApp = NULL; LSkinEngine *LApp::SkinEngine; LMouseHook *LApp::MouseHook; LApp::LApp(OsAppArguments &AppArgs, const char *name, LAppArguments *Args) : OsApplication(AppArgs.Args, AppArgs.Arg) { TheApp = this; LgiArgsAppPath = AppArgs.Arg[0]; Name(name); d = new LAppPrivate(this); if (LIsRelativePath(LgiArgsAppPath)) { char Cwd[MAX_PATH_LEN]; getcwd(Cwd, sizeof(Cwd)); LMakePath(Cwd, sizeof(Cwd), Cwd, LgiArgsAppPath); LgiArgsAppPath = Cwd; } int WCharSz = sizeof(wchar_t); #if defined(_MSC_VER) LAssert(WCharSz == 2); ::LFile::Path Dlls(LgiArgsAppPath); Dlls--; SetDllDirectoryA(Dlls); #else LAssert(WCharSz == 4); #endif #ifdef _MSC_VER SetEnvironmentVariable(_T("GTK_CSD"), _T("0")); #else setenv("GTK_CSD", "0", true); #endif // We want our printf's NOW! setvbuf(stdout,(char *)NULL,_IONBF,0); // print mesgs immediately. // Setup the file and graphics sub-systems d->FileSystem = new LFileSystem; d->GdcSystem = new GdcDevice; SetAppArgs(AppArgs); srand(LCurrentTime()); LColour::OnChange(); MouseHook = new LMouseHook; d->GetConfig(); // System font setup LFontType SysFontType; if (SysFontType.GetSystemFont("System")) { SystemNormal = SysFontType.Create(); if (SystemNormal) SystemNormal->Transparent(true); SystemBold = SysFontType.Create(); if (SystemBold) { SystemBold->Bold(true); SystemBold->Transparent(true); SystemBold->Create(); } } else { printf("%s:%i - Couldn't get system font setting.\n", __FILE__, __LINE__); } if (!SystemNormal) { LgiMsg(0, "Error: Couldn't create system font.", "Lgi Error: LApp::LApp", MB_OK); LExitApp(); } if (!GetOption("noskin")) { extern LSkinEngine *CreateSkinEngine(LApp *App); SkinEngine = CreateSkinEngine(this); } } LApp::~LApp() { DeleteObj(AppWnd); DeleteObj(SystemNormal); DeleteObj(SystemBold); DeleteObj(SkinEngine); DeleteObj(MouseHook); DeleteObj(d->FileSystem); DeleteObj(d->GdcSystem); DeleteObj(LFontSystem::Me); DeleteObj(d); TheApp = 0; } LApp *LApp::ObjInstance() { return TheApp; } bool LApp::IsOk() { bool Status = #ifndef __clang__ (this != 0) && #endif (d != 0); LAssert(Status); return Status; } LMouseHook *LApp::GetMouseHook() { return MouseHook; } int LApp::GetMetric(LSystemMetric Metric) { switch (Metric) { case LGI_MET_DECOR_X: return 8; case LGI_MET_DECOR_Y: return 8 + 19; case LGI_MET_DECOR_CAPTION: return 19; default: break; } return 0; } LViewI *LApp::GetFocus() { // GtkWidget *w = gtk_window_get_focus(GtkWindow *window); return NULL; } OsThread LApp::_GetGuiThread() { return d->GuiThread; } OsThreadId LApp::GetGuiThreadId() { return d->GuiThreadId; } OsProcessId LApp::GetProcessId() { #ifdef WIN32 return GetCurrentProcessId(); #else return getpid(); #endif } OsAppArguments *LApp::GetAppArgs() { return IsOk() ? &d->Args : 0; } void LApp::SetAppArgs(OsAppArguments &AppArgs) { if (IsOk()) { d->Args = AppArgs; } } bool LApp::InThread() { OsThreadId Me = GetCurrentThreadId(); OsThreadId Gui = GetGuiThreadId(); // printf("Me=%i Gui=%i\n", Me, Gui); return Gui == Me; } bool LApp::Run(OnIdleProc IdleCallback, void *IdleParam) { if (!InThread()) { printf("%s:%i - Error: Out of thread.\n", _FL); return false; } printf("Running main loop...\n"); d->Run(); printf("Main loop finished.\n"); return true; } bool LApp::Yield() { return false; } void LApp::Exit(int Code) { if (Code) { // hard exit ::exit(Code); } else { // soft exit printf("Quitting main loop...\n"); if (d->Lock()) { d->Quit(); d->Unlock(); } } } void LApp::OnUrl(const char *Url) { if (AppWnd) AppWnd->OnUrl(Url); } void LApp::OnReceiveFiles(::LArray &Files) { if (AppWnd) AppWnd->OnReceiveFiles(Files); else LAssert(!"You probably want to set 'AppWnd' before calling LApp::Run... maybe."); } const char *LApp::GetArgumentAt(int n) { return n >= 0 && n < d->Args.Args ? NewStr(d->Args.Arg[n]) : 0; } bool LApp::GetOption(const char *Option, char *Dest, int DestLen) { ::LString Buf; if (GetOption(Option, Buf)) { if (Dest) { if (DestLen > 0) { strcpy_s(Dest, DestLen, Buf); } else return false; } return true; } return false; } bool LApp::GetOption(const char *Option, ::LString &Buf) { if (IsOk() && Option) { int OptLen = strlen(Option); for (int i=1; iArgs.Args; i++) { auto *a = d->Args.Arg[i]; if (!a) continue; if (strchr("-/\\", a[0])) { if (strnicmp(a+1, Option, OptLen) == 0) { const char *Arg = NULL; if (strlen(a+1+OptLen) > 0) { Arg = a + 1 + OptLen; } else if (i < d->Args.Args - 1) { Arg = d->Args.Arg[i + 1]; } if (Arg) { if (strchr("\'\"", *Arg)) { char Delim = *Arg++; char *End = strchr(Arg, Delim); if (End) { auto Len = End-Arg; if (Len > 0) Buf.Set(Arg, Len); else return false; } else return false; } else { Buf = Arg; } } return true; } } } } return false; } void LApp::OnCommandLine() { ::LArray Files; for (int i=1; iArgs; i++) { auto a = GetAppArgs()->Arg[i]; if (LFileExists(a)) { Files.Add(NewStr(a)); } } // call app if (Files.Length() > 0) { OnReceiveFiles(Files); } // clear up Files.DeleteArrays(); } LString LApp::GetFileMimeType(const char *File) { BMimeType mt; auto r = BMimeType::GuessMimeType(File, &mt); if (r != B_OK) { LgiTrace("%s:%i - GuessMimeType(%s) failed: %i\n", _FL, File, r); return LString(); } return mt.Type(); } -bool LApp::GetAppsForMimeType(char *Mime, LArray<::LAppInfo> &Apps) +bool LApp::GetAppsForMimeType(const char *Mime, LArray &Apps) { // Find alternative version of the MIME type (e.g. x-type and type). char AltMime[256]; strcpy(AltMime, Mime); char *s = strchr(AltMime, '/'); if (s) { s++; int Len = strlen(s) + 1; if (strnicmp(s, "x-", 2) == 0) { memmove(s, s+2, Len - 2); } else { memmove(s+2, s, Len); s[0] = 'x'; s[1] = '-'; } } LGetAppsForMimeType(Mime, Apps); LGetAppsForMimeType(AltMime, Apps); return Apps.Length() > 0; } #if defined(LINUX) LLibrary *LApp::GetWindowManagerLib() { if (this != NULL && !d->WmLib) { char Lib[32]; WindowManager Wm = LGetWindowManager(); switch (Wm) { case WM_Kde: strcpy(Lib, "liblgikde3"); break; case WM_Gnome: strcpy(Lib, "liblgignome2"); break; default: strcpy(Lib, "liblgiother"); break; } #ifdef _DEBUG strcat(Lib, "d"); #endif d->WmLib = new LLibrary(Lib, true); if (d->WmLib) { if (d->WmLib->IsLoaded()) { Proc_LgiWmInit WmInit = (Proc_LgiWmInit) d->WmLib->GetAddress("LgiWmInit"); if (WmInit) { WmInitParams Params; // Params.Dsp = XObject::XDisplay(); Params.Args = d->Args.Args; Params.Arg = d->Args.Arg; WmInit(&Params); } // else printf("%s:%i - Failed to find method 'LgiWmInit' in WmLib.\n", __FILE__, __LINE__); } // else printf("%s:%i - couldn't load '%s.so'\n", __FILE__, __LINE__, Lib); } // else printf("%s:%i - alloc error\n", __FILE__, __LINE__); } return d->WmLib && d->WmLib->IsLoaded() ? d->WmLib : 0; } void LApp::DeleteMeLater(LViewI *v) { d->DeleteLater.Add(v); } void LApp::SetClipBoardContent(OsView Hnd, ::LVariant &v) { // Store the clipboard data we will serve d->ClipData = v; } bool LApp::GetClipBoardContent(OsView Hnd, ::LVariant &v, ::LArray &Types) { return false; } #endif LSymLookup *LApp::GetSymLookup() { return NULL; } bool LApp::IsElevated() { #ifdef WIN32 LAssert(!"What API works here?"); return false; #else return geteuid() == 0; #endif } int LApp::GetCpuCount() { return 1; } LFontCache *LApp::GetFontCache() { if (!d->FontCache) d->FontCache.Reset(new LFontCache(SystemNormal)); return d->FontCache; } #ifdef LINUX LApp::DesktopInfo::DesktopInfo(const char *file) { File = file; Dirty = false; if (File) Serialize(false); } bool LApp::DesktopInfo::Serialize(bool Write) { ::LFile f; if (Write) { ::LFile::Path p(File); p--; if (!p.Exists()) return false; } else if (!LFileExists(File)) return false; if (!f.Open(File, Write?O_WRITE:O_READ)) { LgiTrace("%s:%i - Failed to open '%s'\n", _FL, File.Get()); return false; } if (Write) { f.SetSize(0); for (unsigned i=0; i= 0) { int e = l.Find("]", ++s); if (e >= 0) { Cur = &Data.New(); Cur->Name = l(s, e - s + 1); } } else if ((s = l.Find("=")) >= 0) { if (!Cur) Cur = &Data.New(); KeyPair &kp = Cur->Values.New(); kp.Key = l(0, s).Strip(); kp.Value = l(++s, -1).Strip(); // printf("Read '%s': '%s'='%s'\n", Cur->Name.Get(), kp.Key.Get(), kp.Value.Get()); } } } return true; } LApp::DesktopInfo::Section *LApp::DesktopInfo::GetSection(const char *Name, bool Create) { for (unsigned i=0; iGet(Field, false, Dirty); if (kp) { return kp->Value; } } } return ::LString(); } bool LApp::DesktopInfo::Set(const char *Field, const char *Value, const char *Sect) { if (!Field) return false; Section *s = GetSection(Sect ? Sect : DefaultSection, true); if (!s) return false; KeyPair *kp = s->Get(Field, true, Dirty); if (!kp) return false; if (kp->Value != Value) { kp->Value = Value; Dirty = true; } return true; } LApp::DesktopInfo *LApp::GetDesktopInfo() { auto sExe = LGetExeFile(); ::LFile::Path Exe(sExe); ::LFile::Path Desktop(LSP_HOME); ::LString Leaf; Leaf.Printf("%s.desktop", Exe.Last().Get()); Desktop += ".local/share/applications"; Desktop += Leaf; const char *Ex = Exe; const char *Fn = Desktop; if (d->DesktopInfo.Reset(new DesktopInfo(Desktop))) { // Do a sanity check... ::LString s = d->DesktopInfo->Get("Name"); if (!s && Name()) d->DesktopInfo->Set("Name", Name()); s = d->DesktopInfo->Get("Exec"); if (!s || s != (const char*)sExe) d->DesktopInfo->Set("Exec", sExe); s = d->DesktopInfo->Get("Type"); if (!s) d->DesktopInfo->Set("Type", "Application"); s = d->DesktopInfo->Get("Categories"); if (!s) d->DesktopInfo->Set("Categories", "Application;"); s = d->DesktopInfo->Get("Terminal"); if (!s) d->DesktopInfo->Set("Terminal", "false"); d->DesktopInfo->Update(); } return d->DesktopInfo; } bool LApp::SetApplicationIcon(const char *FileName) { DesktopInfo *di = GetDesktopInfo(); if (!di) return false; ::LString IcoPath = di->Get("Icon"); if (IcoPath == FileName) return true; di->Set("Icon", FileName); return di->Update(); } #endif //////////////////////////////////////////////////////////////// OsApplication *OsApplication::Inst = 0; class OsApplicationPriv { public: OsApplicationPriv() { } }; OsApplication::OsApplication(int Args, const char **Arg) { Inst = this; d = new OsApplicationPriv; } OsApplication::~OsApplication() { DeleteObj(d); Inst = 0; } //////////////////////////////////////////////////////////////// int LMessage::Msg() { return what; } LMessage::Param LMessage::A() { int64 a = 0; if (FindInt64(PropA, &a) != B_OK) { int32 c = CountNames(B_ANY_TYPE); printf("%s:%i - Failed to find PropA (%i names)\n", _FL, c); for (int32 i=0; iPostEvent(what, A(), B()); } /////////////////////////////////////////////////////////////////////////////////////////////////////////////// LLocker::LLocker(BHandler *h, const char *File, int Line) { hnd = h; file = File; line = Line; } LLocker::~LLocker() { Unlock(); } bool LLocker::Lock() { if (locked) { printf("%s:%i - Locker already locked.\n", file, line); LAssert(!"Locker already locked."); return false; } if (!hnd) { // printf("%s:%i - Locker hnd is NULL.\n", file, line); return false; } while (!locked) { status_t result = hnd->LockLooperWithTimeout(1000 * 1000); if (result == B_OK) { locked = true; break; } else if (result == B_TIMED_OUT) { // Warn about failure to lock... thread_id Thread = hnd->Looper()->LockingThread(); printf("%s:%i - Warning: can't lock. Myself=%i\n", _FL, LGetCurrentThread(), Thread); } else if (result == B_BAD_VALUE) { break; } else { // Warn about error printf("%s:%i - Error from LockLooperWithTimeout = 0x%x\n", _FL, result); } } return locked; } status_t LLocker::LockWithTimeout(int64 time) { LAssert(!locked); LAssert(hnd != NULL); status_t result = hnd->LockLooperWithTimeout(time); if (result == B_OK) locked = true; return result; } void LLocker::Unlock() { if (locked) { hnd->UnlockLooper(); locked = false; } } diff --git a/src/haiku/General.cpp b/src/haiku/General.cpp --- a/src/haiku/General.cpp +++ b/src/haiku/General.cpp @@ -1,374 +1,367 @@ // Linux Implementation of General LGI functions #include #include #include #include #include #include "MimeType.h" #include "StringList.h" #include "Path.h" #define _POSIX_TIMERS #include #include "lgi/common/Lgi.h" #include "lgi/common/SubProcess.h" #include "lgi/common/TextLabel.h" #include "lgi/common/Button.h" #include "lgi/common/Token.h" #include #include #include #define DEBUG_GET_APPS_FOR_MIMETYPE 0 //////////////////////////////////////////////////////////////// LString LCurrentUserName() { struct passwd *pw = getpwuid(geteuid()); if (pw) return pw->pw_name; return LString(); } void LSleep(uint32_t ms) { struct timespec request, remain; ZeroObj(request); ZeroObj(remain); request.tv_sec = ms / 1000; request.tv_nsec = (ms % 1000) * 1000000; while (nanosleep(&request, &remain) == -1) request = remain; } void _lgi_assert(bool b, const char *test, const char *file, int line) { static bool Asserting = false; if (!b && !Asserting) { Asserting = true; printf("%s:%i - Assert failed:\n%s\n", file, line, test); LString msg; msg.Printf("Assert failed: %s\n%s:%i", test, file, line); BAlert *alert = new BAlert("Assert", msg, "Abort", "Debug", "Ignore"); auto result = alert->Go(); printf("alert result=%i\n", result); switch (result) { case 0: // Abort { exit(-1); break; } case 1: // Debug { int *i = 0; *i = 0; break; } // default: Ignore.. } Asserting = false; } } //////////////////////////////////////////////////////////////////////// // Implementations LMessage CreateMsg(int m, int a, int b) { return LMessage(m, a, b); } bool LGetMimeTypeExtensions(const char *Mime, LArray &Ext) { int Start = Ext.Length(); char *e; #define HardCodeExtention(Mime, Ext1, Ext2) \ else if (!stricmp(Mime, Mime)) \ { if (Ext1) Ext.Add(Ext1); \ if (Ext2) Ext.Add(Ext2); } const char *Null = NULL; if (!Mime); HardCodeExtention("text/calendar", "ics", Null) HardCodeExtention("text/x-vcard", "vcf", Null) HardCodeExtention("text/mbox", "mbx", "mbox"); return Ext.Length() > Start; } LString LGetFileMimeType(const char *File) { return LAppInst->GetFileMimeType(File); } -bool _GetSystemFont(char *FontType, char *Font, int FontBufSize, int &PointSize) -{ - bool Status = false; - - return Status; -} - static bool MimeTypeToAppInfo(LAppInfo &app, LString MimeType) { BMimeType appType; auto r = appType.SetTo(MimeType); if (r != B_OK) { LgiTrace("%s:%i - appType.SetTo(%s) failed: %i.\n", _FL, MimeType.Get(), r); return false; } entry_ref ref; r = appType.GetAppHint(&ref); if (r != B_OK) { LgiTrace("%s:%i - GetAppHint failed: %i\n", _FL, r); return false; } app.MimeType = MimeType; app.Name = ref.name; BEntry entry(&ref); BPath path; if (entry.GetPath(&path) == B_OK) app.Path = path.Path(); return true; } bool LGetAppsForMimeType(const char *Mime, LArray &Apps, int Limit) { BMimeType mt(Mime); BMessage msg; auto r = mt.GetSupportingApps(&msg); if (r != B_OK) { printf("%s:%i - GetSupportingApps for %s failed: %i.\n", _FL, Mime, r); return false; } BStringList lst; r = msg.FindStrings("applications", &lst); if (r != B_OK) { printf("%s:%i - No apps string list: %i.\n", _FL, r); return false; } for (int i=0; iPrintf("'%s' doesn't exist.\n", File); } if (App[0]) { bool FileAdded = false; LString AppPath; LString EscFile = LString::Escape(File, -1, " &"); LString a = App; int Pos; while ((Pos = a.Find("%")) >= 0) { char *s = a.Get() + Pos; printf("a='%s'\n", a.Get()); switch (s[1]) { case 'f': case 'F': { a = a(0,Pos) + EscFile + a(Pos+2,-1); FileAdded = true; break; } case 'u': case 'U': { if (IsUrl) a = a(0,Pos) + EscFile + a(Pos+2,-1); else a = a(0,Pos) + LString("file:") + EscFile + a(Pos+2,-1); FileAdded = true; break; } default: { // we don't understand this command a = a(0,Pos) + a(Pos+2,-1); break; } } printf("a='%s'\n", a.Get()); } if (!FileAdded) { a += " "; a += EscFile; } a += " > /dev/null 2> /dev/null &"; int e; if (Dir) chdir(Dir); printf("a=\n%s\n", a.Get()); if (e = system(a)) { if (Error) *Error = LErrorCodeToString(errno); return false; } return true; } } return false; } bool LPlaySound(const char *FileName, int ASync) { LAssert(!"Impl me."); return false; } diff --git a/src/haiku/ScreenDC.cpp b/src/haiku/ScreenDC.cpp --- a/src/haiku/ScreenDC.cpp +++ b/src/haiku/ScreenDC.cpp @@ -1,471 +1,479 @@ /*hdr ** FILE: LScreenDC.cpp ** AUTHOR: Matthew Allen ** DATE: 29/11/2021 ** DESCRIPTION: Haiku screen DC ** ** Copyright (C) 2021, Matthew Allen ** fret@memecode.com */ #include #include #include "lgi/common/Lgi.h" #include #include +#define LOGGING 0 #define VIEW_CHECK(...) if (!d->v) return __VA_ARGS__; class LScreenPrivate { public: int x = 0, y = 0, Bits = 32; bool Own = false; LColour Col; LRect Client; LView *View = NULL; OsView v = NULL; LScreenPrivate() { Client.ZOff(-1, -1); } ~LScreenPrivate() { } }; // Translates are cumulative... so we undo the last one before resetting it. ///////////////////////////////////////////////////////////////////////////////////////////////////// LScreenDC::LScreenDC() { d = new LScreenPrivate; d->x = GdcD->X(); d->y = GdcD->Y(); d->Bits = GdcD->GetBits(); } LScreenDC::LScreenDC(LView *view, void *param) { d = new LScreenPrivate; if (d->View = view) d->v = view->Handle(); d->Bits = GdcD->GetBits(); /* if (d->v) d->Client = d->v->Frame(); else LgiTrace("%s:%i - LScreenDC::LScreenDC - No view?\n", _FL); */ } LScreenDC::~LScreenDC() { DeleteObj(d); } OsPainter LScreenDC::Handle() { return d->v; } ::LString LScreenDC::Dump() { ::LString s; s.Printf("LScreenDC size=%i,%i\n", d->x, d->y); return s; } bool LScreenDC::SupportsAlphaCompositing() { return true; } LPoint LScreenDC::GetDpi() { return LScreenDpi(); } bool LScreenDC::GetClient(LRect *c) { if (!c) return false; *c = d->Client; return true; } -void LScreenDC::GetOrigin(int &x, int &y) -{ - if (d->Client.Valid()) - { - x = OriginX + d->Client.x1; - y = OriginY + d->Client.y1; - } - else - { - x = OriginX; - y = OriginY; - } -} - LString GetClip(BView *v) { BRegion r; v->GetClippingRegion(&r); LRect lr = r.Frame(); return lr.GetStr(); } +void LScreenDC::GetOrigin(int &x, int &y) +{ + x = OriginX; + y = OriginY; + + #if LOGGING + printf("%p.GetOrigin=%i+%i=%i, %i+%i=%i\n", + this, + OriginX, d->Client.x1, x, + OriginY, d->Client.y1, y); + #endif +} + void LScreenDC::SetOrigin(int x, int y) { VIEW_CHECK() if (d->Client.Valid()) { - OriginX = x - d->Client.x1; - OriginY = y - d->Client.y1; - // The clipping region is relative to the offset. // Remove it here and reinstate it after setting the origin. d->v->ConstrainClippingRegion(NULL); } - else - { - OriginX = x; - OriginY = y; - } + + OriginX = x; + OriginY = y; - d->v->SetOrigin(-OriginX, -OriginY); + #if LOGGING + printf("%p.SetOrigin=%i,%i (%i,%i) = %i,%i\n", + this, + x, y, + d->Client.x1, d->Client.y1, + d->Client.x1 - OriginX, + d->Client.y1 - OriginY); + #endif + + d->v->SetOrigin( d->Client.x1 - OriginX, + d->Client.y1 - OriginY); if (d->Client.Valid()) { // Reset the clipping region related to the origin. auto clp = d->Client.ZeroTranslate(); clp.Offset(OriginX, OriginY); d->v->ClipToRect(clp); } } void LScreenDC::SetClient(LRect *c) { VIEW_CHECK() if (c) { d->Client = *c; - OriginX = c->x1; - OriginY = c->y1; - - d->v->SetOrigin(-OriginX, -OriginY); + #if LOGGING + printf("SetClient(%s)\n", d->Client.GetStr()); + #endif + d->v->SetOrigin(OriginX, OriginY); auto clp = d->Client.ZeroTranslate(); clp.Offset(OriginX, OriginY); d->v->ClipToRect(clp); - + /* BRegion r; d->v->GetClippingRegion(&r); LRect lr = r.Frame(); printf("SetClient %s = %s (%i,%i)\n", c->GetStr(), lr.GetStr(), OriginX, OriginY); */ } else { d->v->ConstrainClippingRegion(NULL); d->v->SetOrigin(OriginX = 0, OriginX = 0); + d->Client.ZOff(-1, -1); - d->Client.ZOff(-1, -1); + #if LOGGING + printf("SetClient()\n"); + #endif } } LRect *LScreenDC::GetClient() { return &d->Client; } uint LScreenDC::LineStyle() { return LSurface::LineStyle(); } uint LScreenDC::LineStyle(uint32_t Bits, uint32_t Reset) { return LSurface::LineStyle(Bits); } int LScreenDC::GetFlags() { return 0; } LRect LScreenDC::ClipRgn() { return Clip; } LRect LScreenDC::ClipRgn(LRect *c) { LRect Prev = Clip; if (c) { Clip = *c; d->v->ClipToRect(Clip); } else { Clip.ZOff(-1, -1); d->v->ConstrainClippingRegion(NULL); } return Prev; } COLOUR LScreenDC::Colour() { return d->Col.Get(GetBits()); } COLOUR LScreenDC::Colour(COLOUR c, int Bits) { auto b = Bits ? Bits : GetBits(); d->Col.Set(c, b); return Colour(d->Col).Get(b); } LColour LScreenDC::Colour(LColour c) { LColour Prev = d->Col; d->Col = c; if (d->v) { d->v->SetLowColor(c); d->v->SetHighColor(c); } else LgiTrace("%s:%i - No view.\n", _FL); return Prev; } int LScreenDC::Op(int ROP, NativeInt Param) { return GDC_SET; // not supported.. } int LScreenDC::Op() { return GDC_SET; // not supported.. } int LScreenDC::X() { return d->Client.Valid() ? d->Client.X() : d->x; } int LScreenDC::Y() { return d->Client.Valid() ? d->Client.Y() : d->y; } int LScreenDC::GetBits() { return d->Bits; } LPalette *LScreenDC::Palette() { return NULL; // not supported. } void LScreenDC::Palette(LPalette *pPal, bool bOwnIt) { } void LScreenDC::Set(int x, int y) { VIEW_CHECK() d->v->StrokeLine(BPoint(x, y), BPoint(x, y)); } COLOUR LScreenDC::Get(int x, int y) { return 0; } void LScreenDC::HLine(int x1, int x2, int y) { VIEW_CHECK() d->v->StrokeLine(BPoint(x1, y), BPoint(x2, y)); } void LScreenDC::VLine(int x, int y1, int y2) { VIEW_CHECK() d->v->StrokeLine(BPoint(x, y1), BPoint(x, y2)); } void LScreenDC::Line(int x1, int y1, int x2, int y2) { VIEW_CHECK() d->v->StrokeLine(BPoint(x1, y1), BPoint(x2, y2)); } void LScreenDC::Circle(double cx, double cy, double radius) { VIEW_CHECK() d->v->StrokeArc(BPoint(cx, cy), radius, radius, 0, 360); } void LScreenDC::FilledCircle(double cx, double cy, double radius) { VIEW_CHECK() d->v->FillArc(BPoint(cx, cy), radius, radius, 0, 360); } void LScreenDC::Arc(double cx, double cy, double radius, double start, double end) { VIEW_CHECK() d->v->StrokeArc(BPoint(cx, cy), radius, radius, start, end); } void LScreenDC::FilledArc(double cx, double cy, double radius, double start, double end) { VIEW_CHECK() d->v->FillArc(BPoint(cx, cy), radius, radius, start, end); } void LScreenDC::Ellipse(double cx, double cy, double x, double y) { VIEW_CHECK() d->v->StrokeArc(BPoint(cx, cy), x, y, 0, 360); } void LScreenDC::FilledEllipse(double cx, double cy, double x, double y) { VIEW_CHECK() d->v->FillArc(BPoint(cx, cy), x, y, 0, 360); } void LScreenDC::Box(int x1, int y1, int x2, int y2) { VIEW_CHECK() d->v->StrokeRect(LRect(x1, y1, x2, y2)); } void LScreenDC::Box(LRect *a) { VIEW_CHECK() if (a) d->v->StrokeRect(*a); else Box(0, 0, X()-1, Y()-1); } void LScreenDC::Rectangle(int x1, int y1, int x2, int y2) { VIEW_CHECK() if (x2 >= x1 && y2 >= y1) { // auto o = d->v->Origin(); // printf("Rect %i,%i,%i,%i %g,%g\n", x1, y1, x2, y2, o.x, o.y); d->v->FillRect(LRect(x1, y1, x2, y2)); } } void LScreenDC::Rectangle(LRect *a) { VIEW_CHECK() LRect r = a ? *a : Bounds(); BRect br(r.x1, r.y1, r.x2, r.y2); d->v->FillRect(br); } void LScreenDC::Polygon(int Points, LPoint *Data) { VIEW_CHECK() if (!Data || Points <= 0) return; } void LScreenDC::Blt(int x, int y, LSurface *Src, LRect *a) { VIEW_CHECK() if (!Src) { LgiTrace("%s:%i - No source.\n", _FL); return; } if (Src->IsScreen()) { LgiTrace("%s:%i - Can't do screen->screen blt.\n", _FL); return; } BBitmap *bmp = Src->GetBitmap(); if (!bmp) { LAssert(!"no bmp."); LgiTrace("%s:%i - No bitmap.\n", _FL); return; } d->v->SetDrawingMode(B_OP_ALPHA); d->v->SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_COMPOSITE); if (a) { BRect bitmapRect = *a; BRect viewRect(x, y, x + a->X() - 1, y + a->Y() - 1); d->v->DrawBitmap(bmp, bitmapRect, viewRect); #if 0 printf("ScreenDC::Blt %g,%g/%gx%g -> %g,%g/%gx%g %i,%i\n", bitmapRect.left, bitmapRect.top, bitmapRect.Width(), bitmapRect.Height(), viewRect.left, viewRect.top, viewRect.Width(), viewRect.Height(), a->X(), a->Y()); #endif } else { BRect bitmapRect = bmp->Bounds(); BRect viewRect(x, y, x + Src->X() - 1, y + Src->Y() - 1); d->v->DrawBitmap(bmp, bitmapRect, viewRect); #if 0 printf("ScreenDC::Blt %g,%g/%gx%g -> %g,%g/%gx%g %i,%i\n", bitmapRect.left, bitmapRect.top, bitmapRect.Width(), bitmapRect.Height(), viewRect.left, viewRect.top, viewRect.Width(), viewRect.Height(), x, y); #endif } d->v->SetDrawingMode(B_OP_COPY); } void LScreenDC::StretchBlt(LRect *Dest, LSurface *Src, LRect *s) { VIEW_CHECK() LAssert(0); } void LScreenDC::Bezier(int Threshold, LPoint *Pt) { VIEW_CHECK() LAssert(0); } void LScreenDC::FloodFill(int x, int y, int Mode, COLOUR Border, LRect *Bounds) { VIEW_CHECK() LAssert(0); }