diff --git a/include/common/LgiClasses.h b/include/common/LgiClasses.h --- a/include/common/LgiClasses.h +++ b/include/common/LgiClasses.h @@ -1,1846 +1,1849 @@ /** \file \author Matthew Allen \date 19/12/1997 \brief Gui class definitions Copyright (C) 1997-2004, Matthew Allen */ ///////////////////////////////////////////////////////////////////////////////////// // Includes #ifndef __GUI_H #define __GUI_H #if defined BEOS #include #endif #include "GMutex.h" #include "LgiOsClasses.h" #include "GMem.h" #include "GArray.h" #include "LgiCommon.h" #include "GXmlTree.h" #ifndef WIN32 #include "GDragAndDrop.h" #endif ///////////////////////////////////////////////////////////////////////////////////// // Externs extern long MouseWatcher(void *Ptr); extern bool LgiCheckFile(char *Path, int PathSize); LgiFunc bool LgiPostEvent(OsView Wnd, int Event, GMessage::Param a = 0, GMessage::Param b = 0); LgiFunc GViewI *GetNextTabStop(GViewI *v, bool Back); /// Converts an OS error code into a text string LgiClass GAutoString LgiErrorCodeToString(uint32 ErrorCode); #if defined(MAC) && !defined(COCOA) LgiFunc void DumpHnd(HIViewRef v, int depth = 0); #endif /// Virtual base class for receiving events class LgiClass GTarget { public: virtual GMessage::Result OnEvent(GMessage *Msg) { return 0; } }; #if 0 /// Fill colour definition class LgiClass GViewFill { public: enum FillType { None, Solid, RefBitmap, OwnBitmap, }; protected: FillType Type; GColour Col; GSurface *pDC; #ifdef WIN32 HBRUSH hBrush; #endif public: GViewFill(GColour c); GViewFill(COLOUR c, int Bits = -1); GViewFill(GSurface *dc, bool Copy = true); GViewFill(const GViewFill &f); virtual ~GViewFill(); void Empty(); GViewFill &operator =(GColour col) { Empty(); Type = Solid; Col = col; return *this; } void SetRgba32(int r, int g, int b, int a = 0xff) { Empty(); Type = Solid; Col.Rgb(r, g, b, a); } GColour GetFlat() const { return Col; } bool IsTransparent() { return Type == Solid && Col.a() == 0; } void Fill(GSurface *pDC, GRect *r = 0, GdcPt2 *Origin = 0); }; #endif ///////////////////////////////////////////////////////////////////////////////// #if WIN32NATIVE typedef DWORD OsProcessId; #else typedef int OsProcessId; #endif /// Returns the current process ID #define LgiProcessId() (LgiApp->GetProcessId()) /// Returns a pointer to the GApp object. /// /// \warning Don't use this before you have created your GApp object. i.e. in a constructor /// of a global static class which is initialized before the main begins executing. #define LgiApp (GApp::ObjInstance()) /// Returns a system font pointer. /// /// \warning Don't use this before you have created your GApp object. i.e. in a constructor /// of a global static class which is initialized before the main begins executing. #define SysFont (LgiApp->SystemNormal) /// Returns a bold system font pointer. /// /// \warning Don't use this before you have created your GApp object. i.e. in a constructor /// of a global static class which is initialized before the main begins executing. #define SysBold (LgiApp->SystemBold) /// Exits the application right now! /// /// \warning This will cause data loss if you have any unsaved data. Equivilant to exit(0). LgiFunc void LgiExitApp(); /// Closes the application gracefully. /// /// This actually causes GApp::Run() to stop processing message and return. #define LgiCloseApp() LgiApp->Exit(false) #ifdef LINUX #define ThreadCheck() LgiAssert(InThread()) #else #define ThreadCheck() #endif /// Optional arguments to the GApp object struct GAppArguments { /// Don't initialize the skinning engine. bool NoSkin; }; /// \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 GApp : virtual public GAppI, public GBase, public OsApplication { friend class GView; protected: // private member vars class GAppPrivate *d; #if defined WIN32 CRITICAL_SECTION StackTraceSync; friend LONG __stdcall _ExceptionFilter_Redir(LPEXCEPTION_POINTERS e); LONG __stdcall _ExceptionFilter(LPEXCEPTION_POINTERS e, char *ProductId); friend class GWin32Class; List *GetClasses(); #elif defined ATHEOS char *_AppFile; #elif defined BEOS void RefsReceived(BMessage *Msg); #elif defined LINUX friend class GClipBoard; virtual void OnEvents(); void DeleteMeLater(GViewI *v); void SetClipBoardContent(OsView Hnd, GVariant &v); bool GetClipBoardContent(OsView Hnd, GVariant &v, GArray &Types); #endif friend class GMouseHook; static GMouseHook *MouseHook; public: // Static publics /// Use 'LgiApp' to return a pointer to the GApp object static GApp *ObjInstance(); static class GSkinEngine *SkinEngine; // public member vars /// The system font GFont *SystemNormal; /// The system font in bold GFont *SystemBold; /// Pointer to the applications main window GWindow *AppWnd; /// Returns true if the GApp object initialized correctly bool IsOk(); /// Returns this processes ID OsProcessId GetProcessId(); /// Returns the thread currently running the active message loop OsThreadId GetGuiThread(); /// Returns the number of CPU cores the machine has int GetCpuCount(); /// Construct the object GApp ( /// The arguments passed in by the OS. OsAppArguments &AppArgs, /// The application's name. const char *AppName, /// Optional args GAppArguments *ObjArgs = 0 ); /// Destroys the object virtual ~GApp(); /// Returns the version of Lgi used. String returned is in the form '#.#.#' const char *GetLgiVersion() { return LGI_VER; } /// Resets the arguments void SetAppArgs(OsAppArguments &AppArgs); /// Returns the arguemnts OsAppArguments *GetAppArgs(); /// Returns the n'th argument as a heap string. Free with DeleteArray(...). char *GetArgumentAt(int n); /// Enters the message loop. bool Run ( /// If true this function will return when the application exits (with LgiCloseApp()). /// Otherwise if false only pending events will be processed and then the function returns. bool Loop = true, /// Idle callback OnIdleProc IdleCallback = NULL, /// Param for IdleCallback void *IdleParam = NULL ); /// Event called to process the command line void OnCommandLine(); /// Event called to process files dropped on the application void OnReceiveFiles(GArray &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, /// The buffer to receive the value. GAutoString &Buf ); /// \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 ); /// Gets the application conf stored in lgi.conf GXmlTag *GetConfig(const char *Tag); /// Sets a single tag in the config. (Not written to disk) void SetConfig(GXmlTag *Tag); /// Gets the control with the keyboard focus GViewI *GetFocus(); /// Gets the MIME type of a file /// \returns the mime type or NULL if unknown. GAutoString 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, GArray &Apps); /// Get a system metric int32 GetMetric ( /// One of #LGI_MET_DECOR_X, #LGI_MET_DECOR_Y LgiSystemMetric Metric ); /// Get the mouse hook instance GMouseHook *GetMouseHook(); /// Gets the singleton symbol lookup class class GSymLookup *GetSymLookup(); + /// \returns true if the process is running with elevated permissions + bool IsElevated(); + // OS Specific #if defined WIN32 static bool IsWin9x; 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(); #endif #ifdef LINUX class GLibrary *GetWindowManagerLib(); void RegisterHandle(GView *v); void UnregisterHandle(GView *v); bool InThread(); #endif }; /// \brief The base class for all windows in the GUI. /// /// This is the core object that all on screen windows inherit from. It encapsulates /// a HWND on Win32, a BView on BeOS, a Window on X11 + Mac OS X. Used by itself it's not a /// top level window, for that see the GWindow class. /// /// To create a top level window see GWindow or GDialog. /// /// For a GView with scroll bars use GLayout. /// class LgiClass GView : virtual public GViewI, virtual public GBase { friend class GWindow; friend class GLayout; friend class GControl; friend class GMenu; friend class GSubMenu; friend class GWnd; friend class GScrollBar; friend class GFileTarget; friend class GDialog; friend class GDragDropTarget; friend class GPopup; friend bool SysOnKey(GView *w, GMessage *m); #if defined(__GTK_H__) friend Gtk::gboolean lgi_widget_expose(Gtk::GtkWidget *widget, Gtk::GdkEventExpose *e); friend Gtk::gboolean lgi_widget_click(Gtk::GtkWidget *widget, Gtk::GdkEventButton *ev); friend Gtk::gboolean lgi_widget_motion(Gtk::GtkWidget *widget, Gtk::GdkEventMotion *ev); friend Gtk::gboolean GViewCallback(Gtk::GtkWidget *widget, Gtk::GdkEvent *event, GView *view); friend Gtk::gboolean PopupEvent(Gtk::GtkWidget *widget, Gtk::GdkEvent *event, class GPopup *This); friend Gtk::gboolean GtkViewCallback(Gtk::GtkWidget *widget, Gtk::GdkEvent *event, GView *This); virtual Gtk::gboolean OnGtkEvent(Gtk::GtkWidget *widget, Gtk::GdkEvent *event); #elif defined WIN32 friend class GWin32Class; friend LRESULT CALLBACK DlgRedir(OsView hWnd, UINT m, WPARAM a, LPARAM b); static void CALLBACK TimerProc(OsView hwnd, UINT uMsg, UINT_PTR idEvent, uint32 dwTime); #elif defined MAC #if defined(COCOA) #else friend OSStatus LgiWindowProc(EventHandlerCallRef, EventRef, void *); friend OSStatus LgiRootCtrlProc(EventHandlerCallRef, EventRef, void *); friend OSStatus CarbonControlProc(EventHandlerCallRef, EventRef, void *); friend OSStatus GViewProc(EventHandlerCallRef, EventRef, void *); #endif #elif defined BEOS friend class GButtonRedir; friend class _OsEditFrame; friend class BViewRedir; friend long _lgi_pulse_thread(void *ptr); friend GView *_lgi_search_children(GView *v, int &x, int &y); #endif GRect Pos; int _InLock; protected: class GViewPrivate *d; class GDragDropTarget *&DropTargetPtr(); OsView _View; // OS specific handle to view object GView *_Window; GMutex *_Lock; uint16 _BorderSize; uint16 _IsToolBar; int WndFlags; static GViewI *_Capturing; static GViewI *_Over; #if defined WIN32 uint32 GetStyle(); void SetStyle(uint32 i); uint32 GetExStyle(); void SetExStyle(uint32 i); uint32 GetDlgCode(); void SetDlgCode(uint32 i); /// \brief Gets the win32 class passed to CreateWindowEx() const char *GetClassW32(); /// \brief Sets the win32 class passed to CreateWindowEx() void SetClassW32(const char *s); /// \brief Creates a class to pass to CreateWindowEx(). If this methed is not /// explicitly called then the string from GetClass() is used to create a class, /// which is usually the name of the object. GWin32Class *CreateClassW32(const char *Class = 0, HICON Icon = 0, int AddStyles = 0); virtual int SysOnNotify(int Code) { return 0; } #elif defined BEOS struct OsMouseInfo; friend long _lgi_mouse_thread(OsMouseInfo *Info); OsMouseInfo *_MouseInfo; OsThread _CaptureThread; OsThread _PulseThread; int _PulseRate; BWindow *_QuitMe; void _Key(const char *bytes, int32 numBytes, bool down); virtual bool QuitRequested() {} #elif defined MAC OsView _CreateCustomView(); bool _Attach(GViewI *parent); #if defined(COCOA) #else virtual bool _OnGetInfo(HISize &size, HISize &line, HIRect &bounds, HIPoint &origin) { return false; } virtual void _OnScroll(HIPoint &origin) {} #endif #endif // Complex Region searches /// Finds the largest rectangle in the region GRect *FindLargest(GRegion &r); /// Finds the smallest rectangle that would fit a window 'Sx' by 'Sy' GRect *FindSmallestFit(GRegion &r, int Sx, int Sy); /// Finds the largest rectangle on the specified GRect *FindLargestEdge ( /// The region to search GRegion &r, /// The edge to look at: /// \sa GV_EDGE_TOP, GV_EDGE_RIGHT, GV_EDGE_BOTTOM or GV_EDGE_LEFT int Edge ); void _Delete(); GViewI *FindReal(GdcPt2 *Offset = 0); bool HandleCapture(GView *Wnd, bool c); virtual void _Paint(GSurface *pDC = 0, int Ox = 0, int Oy = 0); #if !WIN32NATIVE GView *&PopupChild(); virtual bool _Mouse(GMouse &m, bool Move); void _Focus(bool f); #endif virtual bool OnViewMouse(GView *v, GMouse &m) { return true; } virtual bool OnViewKey(GView *v, GKey &k) { return false; } virtual void OnNcPaint(GSurface *pDC, GRect &r); /// List of children views. friend class GViewIter; List Children; public: /// \brief Creates a view/window. /// /// On non-Win32 platforms the default argument is the class that redirects the /// C++ virtual event handlers to the GView handlers. Which is usually the /// 'DefaultOsView' class. If you pass NULL in a DefaultOsView will be created to /// do the job. On BeOS you can subclass the native controls by passing in an /// instance of the BView based class. GView ( /// The handle that the OS knows the window by OsView wnd = 0 ); /// Destructor virtual ~GView(); /// Returns the OS handle of the view OsView Handle() { return _View; } /// Returns the ptr to a GView GView *GetGView() { return this; } /// Returns the OS handle of the top level window virtual OsWindow WindowHandle(); // Attaching windows / heirarchy bool AddView(GViewI *v, int Where = -1); bool DelView(GViewI *v); bool HasView(GViewI *v); GViewIterator *IterateViews(); /// \brief Attaches the view to a parent view. /// /// Each GView starts in an un-attached state. When you attach it to a Parent GView /// the view gains a OS-specific handle and becomes visible on the screen (if the /// Visible() property is TRUE). However if a view is inserted into the Children list /// of a GView and it's parent pointer is set correctly it will still paint on the /// screen without the OS knowing about it. This is known in Lgi as a "virtual window" /// and is primarily used to cut down on windowing resources. Mouse clicks are handled /// by the parent window and passed down to the virtual children. Virtual children /// are somewhat limited. They can't receive focus, or participate in drag and drop /// operations. If you want to see an example have a look at the GToolBar code. virtual bool Attach ( /// The parent view or NULL for a top level window GViewI *p ); /// Attachs all the views in the Children list if not already attached. virtual bool AttachChildren(); /// Detachs a window from it's parent. virtual bool Detach(); /// Returns true if the window is attached virtual bool IsAttached(); /// Destroys the window async virtual void Quit(bool DontDelete = false); // Properties /// Gets the top level window that this view belongs to GWindow *GetWindow(); /// Gets the parent view. GViewI *GetParent(); /// \brief Sets the parent view. /// /// This doesn't attach the window so that it will display. You should use GView::Attach for that. virtual void SetParent(GViewI *p); /// Script handler to receive UI events. GEventsI *Script; bool OnScriptEvent(GViewI *Ctrl) { return false; } /// Sends a notification to the notify target or the parent chain void SendNotify(int Data = 0); /// Gets the window that receives event notifications GViewI *GetNotify(); /// \brief Sets the view to receive event notifications. /// /// The notify window will receive events when this view changes. By /// default the parent view receives the events. virtual void SetNotify(GViewI *n); /// \brief Each top level window (GWindow) has a lock. By calling this function /// you lock the whole GWindow and all it's children. bool Lock ( /// The file name of the caller const char *file, /// The line number of the caller int line, /// The timeout in milli-seconds or -1 to block until locked. int TimeOut = -1 ); /// Unlocks the GWindow and that this view belongs to. void Unlock(); /// Called to process every message received by this window. GMessage::Result OnEvent(GMessage *Msg); /// true if the view is enabled bool Enabled(); /// Sets the enabled state void Enabled(bool e); /// true if the view is visible bool Visible(); /// Hides/Shows the view void Visible ( /// True if you want to show the view, False to hide the view/ bool v ); /// true if the view has keyboard focus bool Focus(); /// Sets the keyboard focus state on the view. void Focus(bool f); /// true if this view is a drop target bool DropTarget(); /// Sets the drop target state of this view bool DropTarget(bool t); /// \brief Gives this view a 1 or 2 px sunken border. /// /// The size is set by the _BorderSize member variable. This border is /// not considered part of the client area. Mouse and drawing coordinates /// do not take it into account. bool Sunken(); /// Sets a sunken border around the control void Sunken(bool i); /// true if the view has a flat border bool Flat(); /// Sets the flat border state void Flat(bool i); /// \brief true if the view has a raised border /// /// The size is set by the _BorderSize member variable. This border is /// not considered part of the client area. Mouse and drawing coordinates /// do not take it into account. bool Raised(); /// Sets the raised border state void Raised(bool i); /// Draws an OS themed border void DrawThemeBorder(GSurface *pDC, GRect &r); /// \brief true if the control is currently executing in the GUI thread /// /// Some OS functions are not thread safe, and can only be called in the GUI /// thread. In the Linux implementation the GUI thread can change from time /// to time. On Win32 it stays the same. In any case if this function returns /// true it's safe to do just about anything. bool InThread(); /// \brief Asyncronously posts an event to be received by this window /// /// This calls PostMessage on Win32 and XSendEvent on X11. XSendEvent is called /// with a ClientMessage with the a and b parameters in the data section. bool PostEvent ( /// The command ID. /// \sa Should be M_USER or higher for custom events. int Cmd, /// The first 32-bits of data. Equivilent to wParam on Win32. GMessage::Param a = 0, /// The second 32-bits of data. Equivilent to lParam on Win32. GMessage::Param b = 0 ); /// \brief Sets the utf-8 text associated with this view /// /// Name and NameW are interchangable. Using them in any order will convert the /// text between utf-8 and wide to satify any requirement. Generally once the opposing /// version of the string is required both the utf-8 and wide copies of the string /// remain cached in RAM until the Name is changed. bool Name(const char *n); /// Returns the utf-8 text associated with this view char *Name(); /// Sets the wide char text associated with this view virtual bool NameW(const char16 *n); /// \brief Returns the wide char text associated with this view /// /// On Win32 the wide characters are 16 bits, on unix systems they are 32-bit /// characters. virtual char16 *NameW(); /// \brief Gets the font this control should draw with. /// /// The default font is the system font, owned by the GApp object. virtual GFont *GetFont(); /// \brief Sets the font for this control /// /// The lifetime of the font passed in is the responsibility of the caller. /// The GView object assumes the pointer will be valid at all times. virtual void SetFont(GFont *Fnt, bool OwnIt = false); /// Returns the cursor that should be displayed for the given location /// \returns a cursor type. i.e. LCUR_Normal from LgiDefs.h LgiCursor GetCursor(int x, int y); /* /// \brief Sets the mouse cursor to display when the mouse is over this control. /// /// This currently only works on Win32, as I can't get the X11 cursor functions to /// work. They seem horribly broken. (Surprise surprise) bool SetCursor ( /// The cursor to change to. /// \sa the defines starting with LCUR_Normal from LgiDefs.h LgiCursor Cursor ); */ /// \brief Get the position of the view relitive to it's parent. virtual GRect &GetPos() { return Pos; } /// Get the client region of the window relitive to itself (ie always 0,0-x,y) virtual GRect &GetClient(bool InClientSpace = true); /// Set the position of the view in terms of it's parent virtual bool SetPos(GRect &p, bool Repaint = false); /// Gets the width of the view in pixels int X() { return Pos.X(); } /// Gets the height of the view in pixels. int Y() { return Pos.Y(); } /// Gets the minimum size of the view GdcPt2 GetMinimumSize(); /// \brief Set the minimum size of the view. /// /// Only works for top level windows. void SetMinimumSize(GdcPt2 Size); /// Sets the style of the control bool SetCssStyle(const char *CssStyle); /// Gets the style of the control class GCss *GetCss(bool Create = false); /// Sets the CSS foreground or background colour bool SetColour(GColour &c, bool Fore); /// Moves a top level window on screen. void MoveOnScreen(); /// Moves a top level to the center of the screen void MoveToCenter(); /// Moves a top level window to where the mouse is void MoveToMouse(); /// The class' name. Should be overriden in child classes to return the /// right class name. Mostly used for debugging, but in the win32 port it /// is also the default WIN32 class name passed to RegisterClass() in /// GView::CreateClass(). /// /// \returns the Class' name for debugging const char *GetClass() { return "GView"; } /// \brief Captures all mouse events to this view /// /// Once you have mouse capture all mouse events will be passed to this /// view. i.e. during a mouse click. bool Capture(bool c); /// true if this view is capturing mouse events. bool IsCapturing(); /// \brief Gets the current mouse location /// \return true on success bool GetMouse ( /// The mouse location information returned GMouse &m, /// Get the location in screen coordinates bool ScreenCoords = false ); /// \brief Gets the ID associated with the view /// /// The ID of a view is designed to associate controls defined in resource /// files with a object at runtime via a C header file define. int GetId(); /// Sets the view's ID. void SetId(int i); /// true if this control is a tab stop. bool GetTabStop(); /// \brief Sets whether this control is a tab stop. /// /// A top stop is a control that receives focus if the user scrolls through the controls /// with the tab key. void SetTabStop(bool b); /// Gets the integer representation of the view's contents virtual int64 Value() { return 0; } /// Sets the integer representation of the view's contents virtual void Value(int64 i) {} /// Find a view by it's os handle virtual GViewI *FindControl(OsView hnd); /// Returns the view by it's ID virtual GViewI *FindControl ( // The ID to look for int Id ); /// Gets the value of the control identified by the ID int64 GetCtrlValue(int Id); /// Sets the value of the control identified by the ID void SetCtrlValue(int Id, int64 i); /// Gets the name (text) of the control identified by the ID char *GetCtrlName(int Id); /// Sets the name (text) of the control identified by the ID void SetCtrlName(int Id, const char *s); /// Gets the enabled state of the control identified by the ID bool GetCtrlEnabled(int Id); /// Sets the enabled state of the control identified by the ID void SetCtrlEnabled(int Id, bool Enabled); /// Gets the visible state of the control identified by the ID bool GetCtrlVisible(int Id); /// Sets the visible state of the control identified by the ID void SetCtrlVisible(int Id, bool Visible); /// Causes the given area of the view to be repainted to update the screen bool Invalidate ( /// The rectangle of the view to repaint, or NULL for the entire view GRect *r = NULL, /// true if you want to wait for the update to happen bool Repaint = false, /// false to update in client coordinates, true to update the non client region bool NonClient = false ); /// Causes the given area of the view to be repainted to update the screen bool Invalidate ( /// The region of the view to repaint GRegion *r, /// true if you want to wait for the update to happen bool Repaint = false, /// false to update in client coordinates, true to update the non client region bool NonClient = false ); /// true if the mouse event is over the view bool IsOver(GMouse &m); /// returns the sub window located at the point x,y GViewI *WindowFromPoint(int x, int y, bool Debug = false); /// Sets a timer to call the OnPulse() event void SetPulse ( /// The milliseconds between calls to OnPulse() or -1 to disable int Ms = -1 ); /// Convert a point form view coordinates to screen coordinates void PointToScreen(GdcPt2 &p); /// Convert a point form screen coordinates to view coordinates void PointToView(GdcPt2 &p); /// Get the x,y offset from the virtual window to the first real view in the parent chain bool WindowVirtualOffset(GdcPt2 *Offset); /// Get the size of the window borders GdcPt2 &GetWindowBorderSize(); /// Layout all the child views virtual bool Pour ( /// The available space to lay out the views into GRegion &r ) { return false; } /// The mouse was clicked over this view void OnMouseClick ( /// The event parameters GMouse &m ); /// Mouse moves into the area over the control void OnMouseEnter ( /// The event parameters GMouse &m ); /// Mouse leaves the area over the control void OnMouseExit ( /// The event parameters GMouse &m ); /// The mouse moves over the control void OnMouseMove ( /// The event parameters GMouse &m ); /// The mouse wheel was scrolled. bool OnMouseWheel ( /// The amount scrolled double Lines ); /// A key was pressed while this view has focus bool OnKey(GKey &k); /// The view is attached void OnCreate(); /// The view is detached void OnDestroy(); /// The view gains or loses the keyboard focus void OnFocus ( /// True if the control is receiving focus bool f ); /// \brief Called every so often by the timer system. /// \sa SetPulse() void OnPulse(); /// Called when the view position changes void OnPosChange(); /// Called on a top level window when something requests to close the window bool OnRequestClose ( /// True if the operating system is shutting down. bool OsShuttingDown ); /// Return the type of cursor that should be visible when the mouse is at x,y /// e.g. #LCUR_Normal int OnHitTest ( /// The x coordinate in view coordinates int x, /// The y coordinate in view coordinates int y ); /// Called when the contents of the Children list have changed. void OnChildrenChanged(GViewI *Wnd, bool Attaching); /// Called to paint the onscreen representation of the view void OnPaint(GSurface *pDC); /// \brief Called when a child view or view with it's SetNotify() set to this window changes. /// /// The event by default will bubble up to the GWindow at the top of the window heirarchy visiting /// each GView on the way. If it reaches a GView that processes it then the event stops propergating /// up the heirarchy. int OnNotify(GViewI *Ctrl, int Flags); /// Called when a menu command is activated by the user. int OnCommand(int Cmd, int Event, OsView Wnd); /// Called after the view is attached to a new parent void OnAttach(); /// Called to get layout information for the control bool OnLayout(GViewLayoutInfo &Inf) { return false; } #if defined(_DEBUG) bool _Debug; void Debug(); void _Dump(int Depth = 0); #endif }; /////////////////////////////////////////////////////////////////////////////////////////////// // Control or View window /// FindLargestEdge parameter /// \sa GView::FindLargest(GRegion &, int) #define GV_EDGE_TOP 0x0001 /// FindLargestEdge parameter /// \sa GView::FindLargest(GRegion &, int) #define GV_EDGE_RIGHT 0x0002 /// FindLargestEdge parameter /// \sa GView::FindLargest(GRegion &, int) #define GV_EDGE_BOTTOM 0x0004 /// FindLargestEdge parameter /// \sa GView::FindLargest(GRegion &, int) #define GV_EDGE_LEFT 0x0008 /// Id of the vertical scroll bar in a GLayout control #define IDC_VSCROLL 14000 /// Id of the horizontal scroll bar in a GLayout control #define IDC_HSCROLL 14001 #ifdef MAC #define XPLATFORM_GLAYOUT 1 #else #define XPLATFORM_GLAYOUT 0 #endif /// \brief A GView with scroll bars /// /// This class adds scroll bars to the standard GView base class. The scroll bars can be /// directly accessed using the VScroll and HScroll member variables. Although you should /// always do a NULL check on the pointer before using, if the scroll bar is not activated /// using GLayout::SetScrollBars then VScroll and/or HScroll will by NULL. When the scroll /// bar is used to scroll the GLayout control you will receive an event on GView::OnNotify /// with the control ID of the scrollbar, which is either #IDC_VSCROLL or #IDC_HSCROLL. class LgiClass GLayout : public GView { friend class GScroll; friend class GView; // Private variables bool _SettingScrollBars; bool _PourLargest; protected: /// The vertical scroll bar GScrollBar *VScroll; /// The horizontal scroll bar GScrollBar *HScroll; /// Sets which of the scroll bars is visible virtual bool SetScrollBars ( /// Make the horizontal scroll bar visible bool x, /// Make the vertical scroll bar visible bool y ); #if defined(XPLATFORM_GLAYOUT) void AttachScrollBars(); bool _SetScrollBars(bool x, bool y); #endif #if defined(MAC) && !XPLATFORM_GLAYOUT friend class GLayoutScrollBar; HISize Line; OsView RealWnd; bool _OnGetInfo(HISize &size, HISize &line, HIRect &bounds, HIPoint &origin); void _OnScroll(HIPoint &origin); void OnScrollConfigure(); #endif public: GLayout(); ~GLayout(); const char *GetClass() { return "GLayout"; } /// Gets the current scroll bar values. virtual void GetScrollPos(int &x, int &y); /// Sets the current scroll bar values virtual void SetScrollPos(int x, int y); /// Gets the "pour largest" setting bool GetPourLargest(); /// \brief Sets the "pour largest" setting /// /// When "pour largest" is switched on the pour function automatically /// lays the control into the largest rectangle available. This is useful /// for putting a single GView into a splitter pane or a tab view and having /// it just take up all the space. void SetPourLargest(bool i); /// Handles the incoming events. GMessage::Result OnEvent(GMessage *Msg); /// Lay out all the children views into the client area according to their /// own internal rules. Space is given in a first come first served basis. bool Pour(GRegion &r); // Impl #if defined(__GTK_H__) || defined(MAC) || defined(BEOS) bool Attach(GViewI *p); bool Detach(); GRect &GetClient(bool InClientSpace = true); #if defined(MAC) && !XPLATFORM_GLAYOUT bool Invalidate(GRect *r = NULL, bool Repaint = false, bool NonClient = false); bool Focus(); void Focus(bool f); bool SetPos(GRect &p, bool Repaint = false); #else void OnPosChange(); int OnNotify(GViewI *c, int f); void OnNcPaint(GSurface *pDC, GRect &r); #endif #endif GViewI *FindControl(int Id); }; /// Client draws the content. #define GIC_OWNER_DRAW 0x01 /// Column header is text. #define GIC_ASK_TEXT 0x02 /// Column header is an image. #define GIC_ASK_IMAGE 0x04 /// Not used. #define GIC_OWN_LIST 0x08 /// Drag is over the control #define GIC_IN_DRAG_OP 0x10 class LgiClass GItemContainer { protected: int Flags; GImageList *ImageList; public: GItemContainer(); virtual ~GItemContainer(); // Props bool OwnerDraw() { return TestFlag(Flags, GIC_OWNER_DRAW); } void OwnerDraw(bool b) { if (b) SetFlag(Flags, GIC_OWNER_DRAW); else ClearFlag(Flags, GIC_OWNER_DRAW); } bool AskText() { return TestFlag(Flags, GIC_ASK_TEXT); } void AskText(bool b) { if (b) SetFlag(Flags, GIC_ASK_TEXT); else ClearFlag(Flags, GIC_ASK_TEXT); } bool AskImage() { return TestFlag(Flags, GIC_ASK_IMAGE); } void AskImage(bool b) { if (b) SetFlag(Flags, GIC_ASK_IMAGE); else ClearFlag(Flags, GIC_ASK_IMAGE); } bool InsideDragOp() { return TestFlag(Flags, GIC_IN_DRAG_OP); } void InsideDragOp(bool b) { if (b) SetFlag(Flags, GIC_IN_DRAG_OP); else ClearFlag(Flags, GIC_IN_DRAG_OP); } // Image List GImageList *GetImageList() { return ImageList; } bool SetImageList(GImageList *List, bool Own = true); bool LoadImageList(char *File, int x, int y); bool OwnList() { return TestFlag(Flags, GIC_OWN_LIST); } void OwnList(bool b) { if (b) SetFlag(Flags, GIC_OWN_LIST); else ClearFlag(Flags, GIC_OWN_LIST); } }; /////////////////////////////////////////////////////////////////////////////////////////////////// // Menus #include "GMenu.h" /////////////////////////////////////////////////////////////////////////////////////////////////// /// The available states for a top level window enum GWindowZoom { /// Minimized GZoomMin, /// Restored/Normal GZoomNormal, /// Maximized GZoomMax }; enum GWindowHookType { GNoEvents = 0, /// \sa GWindow::RegisterHook() GMouseEvents = 1, /// \sa GWindow::RegisterHook() GKeyEvents = 2, /// \sa GWindow::RegisterHook() GKeyAndMouseEvents = GMouseEvents | GKeyEvents, }; /// A top level window. class LgiClass GWindow : public GView #ifndef WIN32 , public GDragDropTarget #endif { friend class BViewRedir; friend class GView; friend class GButton; friend class XWindow; friend class GDialog; #if defined(MAC) && !defined(COCOA) friend pascal OSStatus LgiWindowProc(EventHandlerCallRef inHandlerCallRef, EventRef inEvent, void *inUserData); #endif bool _QuitOnClose; protected: #if WIN32NATIVE GRect OldPos; GWindow *_Dialog; #else OsWindow Wnd; void _OnViewDelete(); void _SetDynamic(bool i); #endif #if defined BEOS friend class GMenu; friend class GView; #elif defined __GTK_H__ friend class GMenu; Gtk::GtkWidget *_Root, *_VBox, *_MenuBar; void _Paint(GSurface *pDC = 0, int Ox = 0, int Oy = 0); void OnGtkDelete(); Gtk::gboolean OnGtkEvent(Gtk::GtkWidget *widget, Gtk::GdkEvent *event); #endif /// The default button GViewI *_Default; /// The menu on the window GMenu *Menu; class GWindowPrivate *d; void SetChildDialog(GDialog *Dlg); void SetDragHandlers(bool On); public: #ifdef __GTK_H__ GWindow(Gtk::GtkWidget *w = 0); #else GWindow(); #endif ~GWindow(); const char *GetClass() { return "GWindow"; } /// Lays out the child views into the client area. virtual void Pour(); /// Returns the current menu object GMenu *GetMenu() { return Menu; } /// Set the menu object. void SetMenu(GMenu *m) { Menu = m; } /// Gets the "quit on close" setting. bool GetQuitOnClose() { return _QuitOnClose; } /// \brief Sets the "quit on close" setting. /// /// When this is switched on the application will quit the main message /// loop when this GWindow is closed. This is really useful for your /// main application window. Otherwise the UI will disappear but the /// application is still running. void SetQuitOnClose(bool i) { _QuitOnClose = i; } bool GetSnapToEdge(); void SetSnapToEdge(bool b); /// Gets the current zoom setting GWindowZoom GetZoom(); /// Sets the current zoom void SetZoom(GWindowZoom i); /// Raises the window to the top of the stack. void Raise(); void OnPosChange(); GMessage::Result OnEvent(GMessage *Msg); void OnPaint(GSurface *pDC); bool HandleViewMouse(GView *v, GMouse &m); bool HandleViewKey(GView *v, GKey &k); bool OnRequestClose(bool OsShuttingDown); bool Obscured(); bool Visible(); void Visible(bool i); bool IsActive(); GRect &GetPos(); // Focus setting GViewI *GetFocus(); enum FocusType { GainFocus, LoseFocus, ViewDelete }; void SetFocus(GViewI *ctrl, FocusType type); /// Registers a watcher to receive OnView... messages before they /// are passed through to the intended recipient. bool RegisterHook ( /// The target view. GView *Target, /// Combination of #GMouseEvents and #GKeyEvents OR'd together. GWindowHookType EventType, /// Not implemented int Priority = 0 ); /// Unregisters a hook target bool UnregisterHook(GView *Target); /// Called when the window zoom state changes. virtual void OnZoom(GWindowZoom Action) {} /// Called when the tray icon is clicked. (if present) virtual void OnTrayClick(GMouse &m); /// Called when the tray icon menu is about to be displayed. virtual void OnTrayMenu(GSubMenu &m) {} /// Called when the tray icon menu item has been selected. virtual void OnTrayMenuResult(int MenuId) {} /// Called when files are dropped on the window. virtual void OnReceiveFiles(GArray &Files) {} /// Called when a URL is sent to the window virtual void OnUrl(const char *Url) {}; #if !WIN32NATIVE bool Attach(GViewI *p); // Props OsWindow WindowHandle() { return Wnd; } bool Name(const char *n); char *Name(); bool SetPos(GRect &p, bool Repaint = false); GRect &GetClient(bool InClientSpace = true); // D'n'd int WillAccept(List &Formats, GdcPt2 Pt, int KeyState); int OnDrop(char *Format, GVariant *Data, GdcPt2 Pt, int KeyState); // Events void OnChildrenChanged(GViewI *Wnd, bool Attaching); void OnCreate(); #endif #if defined MAC bool &CloseRequestDone(); bool PostEvent(int Cmd, GMessage::Param a = 0, GMessage::Param b = 0); void Quit(bool DontDelete = false); #ifndef COCOA OSErr HandlerCallback(DragTrackingMessage *tracking, DragRef theDrag); #endif int OnCommand(int Cmd, int Event, OsView Wnd); GViewI *WindowFromPoint(int x, int y, bool Debug = false); virtual void OnFrontSwitch(bool b); #elif defined __GTK_H__ void OnMap(bool m); #endif /// Gets the default view GViewI *GetDefault(); /// Sets the default view void SetDefault(GViewI *v); /// Saves/loads the window's state, e.g. position, minimized/maximized etc bool SerializeState ( /// The data store for reading/writing GDom *Store, /// The field name to use for storing settings under const char *FieldName, /// TRUE if loading the settings into the window, FALSE if saving to the store. bool Load ); }; //////////////////////////////////////////////////////////////////////////// /// Puts a tool tip on screen when the mouse wanders over a region. class LgiClass GToolTip : public GView { class GToolTipPrivate *d; public: GToolTip(); ~GToolTip(); /// Create a tip int NewTip ( /// The text to display char *Name, /// The region the mouse has to be in to trigger the tip GRect &Pos ); /// Delete the tip. void DeleteTip(int Id); bool Attach(GViewI *p); }; //////////////////////////////////////////////////////////////////////////// // Dialog stuff #include "LgiWidgets.h" //////////////////////////////////////////////////////////////////////////// // Progress meters stuff #include "Progress.h" #include "GProgress.h" //////////////////////////////////////////////////////////////////////// #include "GFileSelect.h" #include "GFindReplaceDlg.h" #include "GToolBar.h" #include "GThread.h" //////////////////////////////////////////////////////////////////////////////////////////////// /// Displays 2 views side by side class LgiClass GSplitter : public GLayout { class GSplitterPrivate *d; void CalcRegions(bool Follow = false); bool OverSplit(int x, int y); public: GSplitter(); ~GSplitter(); const char *GetClass() { return "GSplitter"; } /// Get the position of the split in px int64 Value(); // Use to set/get the split position /// Sets the position of the split void Value(int64 i); /// True if the split is vertical bool IsVertical(); /// Sets the split to horizontal or vertical void IsVertical(bool v); /// True if the split follows the opposite bool DoesSplitFollow(); /// Sets the split to follow the opposite void DoesSplitFollow(bool i); /// Return the left/top view GView *GetViewA(); /// Detach the left/top view void DetachViewA(); /// Sets the left/top view void SetViewA(GView *a, bool Border = true); /// Return the right/bottom view GView *GetViewB(); /// Detach the right/bottom view void DetachViewB(); /// Sets the right/bottom view void SetViewB(GView *b, bool Border = true); /// Get the size of the bar that splits the views int BarSize(); /// Set the bar size void BarSize(int i); bool Border(); void Border(bool i); GViewI *FindControl(OsView hCtrl); bool Attach(GViewI *p); bool Pour(GRegion &r); void OnPaint(GSurface *pDC); void OnPosChange(); void OnMouseClick(GMouse &m); void OnMouseMove(GMouse &m); void OnMouseExit(GMouse &m); int OnHitTest(int x, int y); void OnChildrenChanged(GViewI *Wnd, bool Attaching); LgiCursor GetCursor(int x, int y); }; //////////////////////////////////////////////////////////////////////////////////////////////// #define STATUSBAR_SEPARATOR 4 #define GSP_SUNKEN 0x0001 class LgiClass GStatusBar : public GLayout { friend class GStatusPane; protected: void RePour(); public: GStatusBar(); ~GStatusBar(); const char *GetClass() { return "GStatusBar"; } bool Pour(GRegion &r); void OnPaint(GSurface *pDC); GStatusPane *AppendPane(const char *Text, int Width); bool AppendPane(GStatusPane *Pane); }; class LgiClass GStatusPane : public GView { friend class GStatusBar; protected: int Flags; int Width; GSurface *pDC; public: GStatusPane(); ~GStatusPane(); const char *GetClass() { return "GStatusPane"; } char *Name() { return GBase::Name(); } bool Name(const char *n); void OnPaint(GSurface *pDC); int GetWidth(); void SetWidth(int x); bool Sunken(); void Sunken(bool i); GSurface *Bitmap(); void Bitmap(GSurface *pdc); }; ///////////////////////////////////////////////////////////////////////////////////////////// class LgiClass GCommand : public GBase //, public GFlags { int Flags; bool PrevValue; public: int Id; GToolButton *ToolButton; GMenuItem *MenuItem; GKey *Accelerator; char *TipHelp; GCommand(); ~GCommand(); bool Enabled(); void Enabled(bool e); bool Value(); void Value(bool v); }; ///////////////////////////////////////////////////////////////////////// /// Put an icon in the system tray class LgiClass GTrayIcon : public GBase // public GFlags { friend class GTrayWnd; class GTrayIconPrivate *d; public: /// Constructor GTrayIcon ( /// The owner GWindow GWindow *p ); ~GTrayIcon(); /// Add an icon to the list bool Load(const TCHAR *Str); /// Is it visible? bool Visible(); /// Show / Hide the tray icon void Visible(bool v); /// The index of the icon visible int64 Value(); /// Set the index of the icon you want visible void Value(int64 v); /// Call this in your window's OnEvent handler virtual GMessage::Result OnEvent(GMessage *Msg); }; ////////////////////////////////////////////////////////////////// #include "GInput.h" #include "GPrinter.h" ////////////////////////////////////////////////////////////////// /// \brief A BeOS style alert window, kinda like a Win32 MessageBox /// /// The best thing about this class is you can name the buttons very specifically. /// It's always non-intuitive to word a question to the user in such a way so thats /// it's obvious to answer with "Ok" or "Cancel". But if the user gets a question /// with customised "actions" as buttons they'll love you. /// /// The button pressed is returned as a index from the DoModal() function. Starting /// at '1'. i.e. Btn2 -> returns 2. class LgiClass GAlert : public GDialog { public: /// Constructor GAlert ( /// The parent view GViewI *parent, /// The dialog title const char *Title, /// The body of the message const char *Text, /// The first button text const char *Btn1, /// The [optional] 2nd buttons text const char *Btn2 = 0, /// The [optional] 3rd buttons text const char *Btn3 = 0 ); void SetAppModal(); int OnNotify(GViewI *Ctrl, int Flags); }; /// Timer class to help do something every so often class LgiClass DoEvery { int64 LastTime; int64 Period; public: /// Constructor DoEvery ( /// Timeout in ms int p = 1000 ); /// Reset the timer void Init ( /// Timeout in ms int p = -1 ); /// Returns true when the time has expired. Resets automatically. bool DoNow(); }; /// \brief Factory for creating view's by name. /// /// Inherit from this to add a new factory to create objects. Override /// NewView() to create your control. class LgiClass GViewFactory { /** \brief Create a view by name \code if (strcmp(Class, "MyControl") == 0) { return new MyControl; } \endcode */ virtual GView *NewView ( /// The name of the class to create const char *Class, /// The initial position of the view GRect *Pos, /// The initial text of the view const char *Text ) = 0; public: GViewFactory(); virtual ~GViewFactory(); /// Create a view by name. static GView *Create(const char *Class, GRect *Pos = 0, const char *Text = 0); }; ////////////////////////////////////////////////////////// // Colour LgiFunc void LgiInitColours(); LgiFunc COLOUR LgiColour(int Colour); // Graphics LgiFunc void LgiDrawBox(GSurface *pDC, GRect &r, bool Sunken, bool Fill); LgiFunc void LgiWideBorder(GSurface *pDC, GRect &r, int Type); LgiFunc void LgiThinBorder(GSurface *pDC, GRect &r, int Type); LgiFunc void LgiFlatBorder(GSurface *pDC, GRect &r, int Width = -1); // Helpers #ifdef __GTK_H__ extern Gtk::gboolean GtkViewCallback(Gtk::GtkWidget *widget, Gtk::GdkEvent *event, GView *This); #endif #ifdef LINUX /// Ends a x windows startup session LgiFunc void LgiFinishXWindowsStartup(class GViewI *Wnd); #endif /// \brief Displays a message box /// \returns The button clicked. The return value is one of #IDOK, #IDCANCEL, #IDYES or #IDNO. LgiFunc int LgiMsg ( /// The parent view or NULL if none available GViewI *Parent, /// The message's text. This is a printf format string that you can pass arguments to const char *Msg, /// The title of the message box window const char *Title = 0, /// The type of buttons below the message. Can be one of: /// #MB_OK, #MB_OKCANCEL, #MB_YESNO or #MB_YESNOCANCEL. int Type = MB_OK, ... ); /// Contains all the infomation about a display/monitor attached to the system. /// \sa LgiGetDisplays struct GDisplayInfo { /// The position and dimensions of the display. On windows the left/upper /// most display will be positioned at 0,0 and each furthur display will have /// co-ordinates that join to one edge of that initial rectangle. GRect r; /// The number of bits per pixel int BitDepth; /// The refreash rate int Refresh; /// The device's path, system specific char *Device; /// A descriptive name of the device, usually the video card char *Name; /// The name of any attached monitor char *Monitor; GDisplayInfo() { r.ZOff(-1, -1); BitDepth = 0; Refresh = 0; Device = 0; Name = 0; Monitor = 0; } ~GDisplayInfo() { DeleteArray(Device); DeleteArray(Name); DeleteArray(Monitor); } }; /// Returns infomation about the displays attached to the system. /// \returns non-zero on success. LgiFunc bool LgiGetDisplays ( /// [out] The array of display info structures. The caller should free these /// objects using Displays.DeleteObjects(). GArray &Displays, /// [out] Optional bounding rectangle of all displays. Can be NULL if your don't /// need that information. GRect *AllDisplays = 0 ); /// This class makes it easy to profile a function and write out timings at the end class LgiClass GProfile { struct Sample { uint64 Time; const char *Name; Sample(uint64 t = 0, const char *n = 0) { Time = t; Name = n; } }; GArray s; int MinMs; public: GProfile(const char *Name); virtual ~GProfile(); void HideResultsIfBelow(int Ms); virtual void Add(const char *Name); }; #endif diff --git a/include/common/LgiDefs.h b/include/common/LgiDefs.h --- a/include/common/LgiDefs.h +++ b/include/common/LgiDefs.h @@ -1,559 +1,580 @@ /** \file \author Matthew Allen \date 24/9/1999 \brief Defines and types Copyright (C) 1999-2004, Matthew Allen */ #ifndef _LGIDEFS_H_ #define _LGIDEFS_H_ #include "LgiInc.h" // Unsafe typedefs, for backward compatibility typedef unsigned char uchar; typedef unsigned short ushort; typedef unsigned int uint; typedef unsigned long ulong; // Length safe typedesf, use these in new code #ifndef BEOS /// 8-bit signed int type (size safe, garenteed to be 8 bits) typedef char int8; /// 8-bit unsigned int type (size safe, garenteed to be 8 bits) typedef unsigned char uint8; #else #include #endif /// 16-bit signed int type (size safe, garenteed to be 16 bits) typedef short int16; /// 16-bit unsigned int type (size safe, garenteed to be 16 bits) typedef unsigned short uint16; #ifndef BEOS /// 32-bit signed int type (size safe, garenteed to be 32 bits) typedef int int32; /// 32-bit unsigned int type (size safe, garenteed to be 32 bits) typedef unsigned int uint32; #endif #ifdef _MSC_VER /// 64-bit signed int type (size safe, garenteed to be 64 bits) typedef signed __int64 int64; /// 64-bit unsigned int type (size safe, garenteed to be 64 bits) typedef unsigned __int64 uint64; #else /// 64-bit signed int type (size safe, garenteed to be 64 bits) typedef signed long long int64; /// 64-bit unsigned int type (size safe, garenteed to be 64 bits) typedef unsigned long long uint64; #endif #if !defined(LINUX) /// \brief Wide unicode char /// /// This is 16 bits on Win32 and Mac, but 32 bits on unix platforms. There are a number /// of wide character string function available for manipulating wide char strings. /// /// Firstly to convert to and from utf-8 there is: ///
    ///
  • LgiNewUtf8To16() ///
  • LgiNewUtf16To8() ///
/// /// Wide versions of standard library functions are available: ///
    ///
  • StrchrW() ///
  • StrrchrW() ///
  • StrnchrW() ///
  • StrstrW() ///
  • StristrW() ///
  • StrnstrW() ///
  • StrnistrW() ///
  • StrcmpW() ///
  • StricmpW() ///
  • StrncmpW() ///
  • StrnicmpW() ///
  • StrcpyW() ///
  • StrncpyW() ///
  • StrlenW() ///
  • StrcatW() ///
  • HtoiW() ///
  • NewStrW() ///
  • TrimStrW() ///
  • ValidStrW() ///
  • MatchStrW() ///
#if defined(__MINGW32__) || defined(BEOS) typedef wchar_t char16; #else #if _MSC_VER > 1300 typedef wchar_t char16; #else typedef unsigned short char16; #endif #endif #else // LINUX typedef unsigned int char16; #endif #if !WIN32NATIVE #ifdef UNICODE typedef char16 TCHAR; #ifndef _T #define _T(arg) L##arg #endif #else typedef char TCHAR; #ifndef _T #define _T(arg) arg #endif #endif #endif #if defined(_MSC_VER) #if _MSC_VER >= 1400 #ifdef _WIN64 typedef __int64 NativeInt; typedef unsigned __int64 UNativeInt; #else typedef _W64 int NativeInt; typedef _W64 unsigned int UNativeInt; #endif #else typedef int NativeInt; typedef unsigned int UNativeInt; #endif #else #if __LP64__ typedef int64 NativeInt; typedef uint64 UNativeInt; #else typedef int NativeInt; typedef unsigned int UNativeInt; #endif #endif /// Generic pointer to any base type. Used when addressing continuous data of /// different types. typedef union { int8 *s8; uint8 *u8; int16 *s16; uint16 *u16; int32 *s32; uint32 *u32; int64 *s64; uint64 *u64; NativeInt *ni; UNativeInt *uni; char *c; char16 *w; float *f; double *d; #ifdef __cplusplus bool *b; #else unsigned char *b; #endif void **vp; int i; } GPointer; // Basic macros // #define abs(a) (((a) > 0) ? (a) : -(a)) #define min(a,b) (((a) < (b)) ? (a) : (b)) #define max(a,b) (((a) > (b)) ? (a) : (b)) #define limit(i,l,u) (((i)<(l)) ? (l) : (((i)>(u)) ? (u) : (i))) #define makelong(a, b) ((a)<<16 | (b&0xFFFF)) #define loword(a) (a&0xFFFF) #define hiword(a) (a>>16) #define LgiSwap(a, b) { int n = a; a = b; b = n; } /// Returns true if 'c' is an ascii character #define IsAlpha(c) (((c) >= 'a' && (c) <= 'z') || ((c) >= 'A' && (c) <= 'Z')) /// Returns true if 'c' is a digit (number) #define IsDigit(c) ((c) >= '0' && (c) <= '9') // Byte swapping #define LgiSwap16(a) ( (((a) & 0xff00) >> 8) | \ (((a) & 0x00ff) << 8) ) #define LgiSwap32(a) ( (((a) & 0xff000000) >> 24) | \ (((a) & 0x00ff0000) >> 8) | \ (((a) & 0x0000ff00) << 8) | \ (((a) & 0x000000ff) << 24) ) #ifdef __GNUC__ #define LgiSwap64(a) ( (((a) & 0xff00000000000000LLU) >> 56) | \ (((a) & 0x00ff000000000000LLU) >> 40) | \ (((a) & 0x0000ff0000000000LLU) >> 24) | \ (((a) & 0x000000ff00000000LLU) >> 8) | \ (((a) & 0x00000000ff000000LLU) << 8) | \ (((a) & 0x0000000000ff0000LLU) << 24) | \ (((a) & 0x000000000000ff00LLU) << 40) | \ (((a) & 0x00000000000000ffLLU) << 56) ) #else #define LgiSwap64(a) ( (((a) & 0xff00000000000000) >> 56) | \ (((a) & 0x00ff000000000000) >> 40) | \ (((a) & 0x0000ff0000000000) >> 24) | \ (((a) & 0x000000ff00000000) >> 8) | \ (((a) & 0x00000000ff000000) << 8) | \ (((a) & 0x0000000000ff0000) << 24) | \ (((a) & 0x000000000000ff00) << 40) | \ (((a) & 0x00000000000000ff) << 56) ) #endif // Good ol NULLy #ifndef NULL #define NULL 0 #endif // Slashes and quotes #define IsSlash(c) (((c)=='/')||((c)=='\\')) #define IsQuote(c) (((c)=='\"')||((c)=='\'')) // Some objectish ones #define ZeroObj(obj) memset(&obj, 0, sizeof(obj)) #ifndef CountOf #define CountOf(array) (sizeof(array)/sizeof(array[0])) #endif #ifndef MEMORY_DEBUG #define DeleteObj(obj) if (obj) { delete obj; obj = 0; } #define DeleteArray(obj) if (obj) { delete [] obj; obj = 0; } #endif // Asserts LgiFunc void _lgi_assert(bool b, const char *test, const char *file, int line); #define LgiAssert(b) _lgi_assert(b, #b, __FILE__, __LINE__) // Flags #define SetFlag(i, f) (i) |= (f) #define ClearFlag(i, f) (i) &= ~(f) #define TestFlag(i, f) (((i) & (f)) != 0) // Defines /// Enum of all the operating systems we might be running on. enum LgiOs { /// \brief Unknown OS /// \sa LgiGetOs LGI_OS_UNKNOWN = 0, /// \brief Windows 95, 98[se] or ME. (Not supported) /// \sa LgiGetOs LGI_OS_WIN9X, /// \brief 32bit NT, 2k, XP, Vista, 7, 8 or later. (XP and later supported) /// \sa LgiGetOs LGI_OS_WIN32, /// \brief 64bit NT, 2k, XP, Vista, 7, 8 or later. (XP and later supported) /// \sa LgiGetOs LGI_OS_WIN64, /// \brief BeOS/Haiku. (Somewhat supported) /// \sa LgiGetOs LGI_OS_HAIKU, /// \brief Linux. (Kernels v2.4 and up supported) /// \sa LgiGetOs LGI_OS_LINUX, /// \brief There was an Atheos port at one point. (Not supported) /// \sa LgiGetOs LGI_OS_MAC_OS_X, /// One higher than the maximum OS define LGI_OS_MAX, }; // System Colours /// Black #define LC_BLACK LgiColour(0) /// Dark grey #define LC_DKGREY LgiColour(1) /// Medium grey #define LC_MIDGREY LgiColour(2) /// Light grey #define LC_LTGREY LgiColour(3) /// White #define LC_WHITE LgiColour(4) /// 3d dark shadow #define LC_SHADOW LgiColour(5) /// 3d light shadow #define LC_LOW LgiColour(6) /// Flat colour for dialogs, windows and buttons #define LC_MED LgiColour(7) /// 3d dark hilight #define LC_HIGH LgiColour(8) /// 3d light hilight #define LC_LIGHT LgiColour(9) /// Dialog colour #define LC_DIALOG LgiColour(10) /// Workspace area #define LC_WORKSPACE LgiColour(11) /// Default text colour #define LC_TEXT LgiColour(12) /// Selection background colour when in focus #define LC_FOCUS_SEL_BACK LgiColour(13) /// Selection foreground colour when in focus #define LC_FOCUS_SEL_FORE LgiColour(14) #define LC_ACTIVE_TITLE LgiColour(15) #define LC_ACTIVE_TITLE_TEXT LgiColour(16) #define LC_INACTIVE_TITLE LgiColour(17) #define LC_INACTIVE_TITLE_TEXT LgiColour(18) #define LC_MENU_BACKGROUND LgiColour(19) #define LC_MENU_TEXT LgiColour(20) /// Selection background colour when not in focus #define LC_NON_FOCUS_SEL_BACK LgiColour(21) /// Selection forground colour when not in focus #define LC_NON_FOCUS_SEL_FORE LgiColour(22) #define LC_MAXIMUM 23 // Cursors enum LgiCursor { /// Blank/invisible cursor LCUR_Blank, /// Normal arrow LCUR_Normal, /// Upwards arrow LCUR_UpArrow, /// Downwards arrow LCUR_DownArrow, /// Left arrow LCUR_LeftArrow, /// Right arrow LCUR_RightArrow, /// Crosshair LCUR_Cross, /// Hourglass/watch LCUR_Wait, /// Ibeam/text entry LCUR_Ibeam, /// Vertical resize (|) LCUR_SizeVer, /// Horizontal resize (-) LCUR_SizeHor, /// Diagonal resize (/) LCUR_SizeBDiag, /// Diagonal resize (\) LCUR_SizeFDiag, /// All directions resize LCUR_SizeAll, /// Vertical splitting LCUR_SplitV, /// Horziontal splitting LCUR_SplitH, /// A pointing hand LCUR_PointingHand, /// A slashed circle LCUR_Forbidden, /// Copy Drop LCUR_DropCopy, /// Copy Move LCUR_DropMove, }; // General Event Flags #define LGI_EF_LCTRL 0x00000001 #define LGI_EF_RCTRL 0x00000002 #define LGI_EF_CTRL (LGI_EF_LCTRL | LGI_EF_RCTRL) #define LGI_EF_LALT 0x00000004 #define LGI_EF_RALT 0x00000008 #define LGI_EF_ALT (LGI_EF_LALT | LGI_EF_RALT) #define LGI_EF_LSHIFT 0x00000010 #define LGI_EF_RSHIFT 0x00000020 #define LGI_EF_SHIFT (LGI_EF_LSHIFT | LGI_EF_RSHIFT) #define LGI_EF_DOWN 0x00000040 #define LGI_EF_DOUBLE 0x00000080 #define LGI_EF_CAPS_LOCK 0x00000100 #define LGI_EF_IS_CHAR 0x00000200 #define LGI_EF_IS_NOT_CHAR 0x00000400 #define LGI_EF_SYSTEM 0x00000800 // Windows key/Apple key etc // Mouse Event Flags #define LGI_EF_LEFT 0x00001000 #define LGI_EF_MIDDLE 0x00002000 #define LGI_EF_RIGHT 0x00004000 #define LGI_EF_MOVE 0x00008000 // Emit compiler warnings #define __STR2__(x) #x #define __STR1__(x) __STR2__(x) #define __LOC__ __FILE__ "("__STR1__(__LINE__)") : Warning: " // To use just do #pragma message(__LOC__"My warning message") // Simple definition of breakable unicode characters #define LGI_BreakableChar(c) ( \ (c) == '\n' || \ (c) == ' ' || \ (c) == '\t' || \ ( (c) >= 0x3040 && (c) <= 0x30FF ) || \ ( (c) >= 0x3300 && (c) <= 0x9FAF ) \ ) // Os metrics enum LgiSystemMetric { /// Get the standard window horizontal border size /// \sa GApp::GetMetric() LGI_MET_DECOR_X = 1, /// Get the standard window vertical border size including caption bar. /// \sa GApp::GetMetric() LGI_MET_DECOR_Y, /// Get the standard window vertical border size including caption bar. /// \sa GApp::GetMetric() LGI_MET_DECOR_CAPTION, /// Get the height of a single line menu bar /// \sa GApp::GetMetric() LGI_MET_MENU, /// This is non-zero if the system is theme aware LGI_MET_THEME_AWARE }; /// \brief Types of system paths available for querying /// \sa LgiGetSystemPath enum LgiSystemPath { /// The location of the operating system folder /// [Win32] = e.g. C:\Windows /// [Mac] = /System /// [Linux] /boot LSP_OS, + /// The system library folder /// [Win32] = e.g. C:\Windows\System32 /// [Mac] = /Library /// [Linux] = /usr/lib LSP_OS_LIB, + /// A folder for storing temporary files. These files are usually /// deleted automatically later by the system. /// [Win32] = ~\Local Settings\Temp /// [Mac] = ~/Library/Caches/TemporaryItems /// [Linux] = /tmp LSP_TEMP, + /// System wide application data /// [Win32] = ~\..\All Users\Application Data /// [Mac] = /System/Library /// [Linux] = /usr LSP_COMMON_APP_DATA, + /// User specific application data /// [Win32] = ~\Application Data /// [Mac] = ~/Library /// [Linux] = /usr LSP_USER_APP_DATA, + /// Machine + user specific application data (probably should not use) /// [Win32] = ~\Local Settings\Application Data /// [Mac] = ~/Library /// [Linux] = /usr/local LSP_LOCAL_APP_DATA, + /// Desktop dir /// i.e. ~/Desktop LSP_DESKTOP, + /// Home dir /// i.e. ~ LSP_HOME, + + /// Application install folder: + /// [Win] c:\Program Files + /// [Mac] /Applications + /// [Linux] /usr/bin + LSP_USER_APPS, + /// The running application's path. /// [Mac] This doesn't include the "Contents/MacOS" part of the path inside the bundle. LSP_EXE, + /// The system trash folder. LSP_TRASH, + /// The app's install folder. /// [Win32] = $ExePath (sans '\Release' or '\Debug') /// [Mac/Linux] = $ExePath /// Where $ExePath is the folder the application is running from LSP_APP_INSTALL, + /// The app's root folder (Where config and data should be stored) /// GOptionsFile uses LSP_APP_ROOT as the default location. /// [Win32] = ~\Application Data\$AppName /// [Mac] = ~/Library/$AppName /// [Linux] = ~/.$AppName /// Where $AppName = GApp::GetName. /// If the given folder doesn't exist it will be created. LSP_APP_ROOT, + /// This is the user's documents folder /// [Win32] ~\My Documents /// [Mac] ~\Documents LSP_USER_DOCUMENTS, + /// This is the user's music folder /// [Win32] ~\My Music /// [Mac] ~\Music LSP_USER_MUSIC, + /// This is the user's video folder /// [Win32] ~\My Videos /// [Mac] ~\Movies LSP_USER_VIDEO, + /// This is the user's download folder /// ~\Downloads LSP_USER_DOWNLOADS, }; // Deprecated method defines #ifdef __GNUC__ #define DEPRECATED_PRE #define DEPRECATED_POST __attribute__ ((deprecated)) #elif defined(_MSC_VER) #define DEPRECATED_PRE __declspec(deprecated) #define DEPRECATED_POST #else #pragma message("WARNING: You need to implement DEPRECATED for this compiler") #define DEPRECATED_PRE #define DEPRECATED_POST #endif // #ifdef _DEBUG #define DeclDebugArgs , const char *_file, int _line #define PassDebugArgs , __FILE__, __LINE__ #else #define DeclDebugArgs #define PassDebugArgs #endif #define _FL __FILE__, __LINE__ #define CALL_MEMBER_FN(obj, memFn) ((obj).*(memFn)) #include "GAutoPtr.h" #endif diff --git a/src/common/Lgi/Lgi.cpp b/src/common/Lgi/Lgi.cpp --- a/src/common/Lgi/Lgi.cpp +++ b/src/common/Lgi/Lgi.cpp @@ -1,2172 +1,2196 @@ // // Cross platform LGI functions // #include #include #include #include #ifdef _WINDOWS #include #include #else #include #endif #include "Lgi.h" #include "GToken.h" #include "GCapabilities.h" #if defined(LINUX) #include "LgiWinManGlue.h" #elif defined(_WINDOWS) #include "GRegKey.h" #endif #if defined POSIX #include #include #include #include #include "GProcess.h" #elif defined BEOS #include #endif #if defined(WIN32) && defined(__GTK_H__) #include "../win32/GSymLookup.h" #else #include "GSymLookup.h" #endif #include "GLibrary.h" ////////////////////////////////////////////////////////////////////////// // Misc stuff #if defined MAC && !defined COCOA bool _get_path_FSRef(FSRef &fs, GStringPipe &a) { HFSUniStr255 Name; ZeroObj(Name); FSRef Parent; FSCatalogInfo Cat; ZeroObj(Cat); OSErr e = FSGetCatalogInfo(&fs, kFSCatInfoVolume|kFSCatInfoNodeID, &Cat, &Name, NULL, &Parent); if (!e) { if (_get_path_FSRef(Parent, a)) { GAutoString u(LgiNewUtf16To8((char16*)Name.unicode, Name.length * sizeof(char16))); // printf("CatInfo = '%s' %x %x\n", u.Get(), Cat.nodeID, Cat.volume); if (u && Cat.nodeID > 2) { a.Print("%s%s", DIR_STR, u.Get()); } } return true; } return false; } GAutoString FSRefPath(FSRef &fs) { GStringPipe a; if (_get_path_FSRef(fs, a)) { return GAutoString(a.NewStr()); } return GAutoString(); } #endif bool LgiPostEvent(OsView Wnd, int Event, GMessage::Param a, GMessage::Param b) { #if WIN32NATIVE return PostMessage(Wnd, Event, a, b); #elif defined(__GTK_H__) if (Wnd) { GMessage m(0); m.Set(Event, a, b); return m.Send(Wnd); } else { printf("%s:%i - Warning: LgiPostEvent failed because View=0\n", _FL); } #elif defined(BEOS) if (Wnd) { BMessage Msg(Event); Msg.AddInt32("a", a); Msg.AddInt32("b", b); BMessenger m(Wnd); return m.SendMessage(&Msg) == B_OK; } #elif defined(MAC) && !defined COCOA #if 0 int64 Now = LgiCurrentTime(); static int64 Last = 0; static int Count = 0; Count++; if (Now > Last + 1000) { printf("Sent %i events in the last %ims\n", Count, (int)(Now-Last)); Last = Now; Count = 0; } #endif EventRef Ev; OSStatus e = CreateEvent(NULL, kEventClassUser, kEventUser, 0, // EventTime kEventAttributeNone, &Ev); if (e) { printf("%s:%i - CreateEvent failed with %i\n", __FILE__, __LINE__, (int)e); } else { EventTargetRef t = GetControlEventTarget(Wnd); e = SetEventParameter(Ev, kEventParamLgiEvent, typeUInt32, sizeof(Event), &Event); if (e) printf("%s:%i - error %i\n", __FILE__, __LINE__, (int)e); e = SetEventParameter(Ev, kEventParamLgiA, typeUInt32, sizeof(a), &a); if (e) printf("%s:%i - error %i\n", __FILE__, __LINE__, (int)e); e = SetEventParameter(Ev, kEventParamLgiB, typeUInt32, sizeof(b), &b); if (e) printf("%s:%i - error %i\n", __FILE__, __LINE__, (int)e); // printf("Sent event %x,%x,%x\n", Event, a, b); bool Status = false; #if 1 e = SetEventParameter(Ev, kEventParamPostTarget, typeEventTargetRef, sizeof(t), &t); if (e) printf("%s:%i - error %i\n", __FILE__, __LINE__, (int)e); e = PostEventToQueue(GetMainEventQueue(), Ev, kEventPriorityStandard); if (e) printf("%s:%i - error %i\n", __FILE__, __LINE__, (int)e); else Status = true; #else e = SendEventToEventTarget(Ev, GetControlEventTarget(Wnd)); if (e) printf("%s:%i - error %i\n", __FILE__, __LINE__, e); else Status = true; #endif ReleaseEvent(Ev); return Status; } #endif return false; } void LgiExitApp() { exit(0); } ////////////////////////////////////////////////////////////////////////// #ifdef WIN32 bool RegisterActiveXControl(char *Dll) { GLibrary Lib(Dll); if (Lib.IsLoaded()) { #ifdef _MSC_VER typedef HRESULT (STDAPICALLTYPE *p_DllRegisterServer)(void); p_DllRegisterServer DllRegisterServer = (p_DllRegisterServer)Lib.GetAddress("DllRegisterServer"); if (DllRegisterServer) { return DllRegisterServer() == S_OK; } #else LgiAssert(!"Not impl."); #endif } return false; } #endif ////////////////////////////////////////////////////////////////////////// /// \brief Returns the operating system that Lgi is running on. /// \sa Returns one of the defines starting with LGI_OS_UNKNOWN in LgiDefs.h int LgiGetOs ( /// Returns the version of the OS or NULL if you don't care GArray *Ver ) { #if defined(WIN32) || defined(WIN64) static int Os = LGI_OS_UNKNOWN; static int Version = 0, Revision = 0; if (Os == LGI_OS_UNKNOWN) { OSVERSIONINFO v; v.dwOSVersionInfoSize=sizeof(v); GetVersionEx(&v); Version = v.dwMajorVersion; Revision = v.dwMinorVersion; #ifdef WIN32 BOOL IsWow64 = FALSE; IsWow64Process(GetCurrentProcess(), &IsWow64); #endif Os = (v.dwPlatformId == VER_PLATFORM_WIN32_NT) ? #ifdef WIN32 (IsWow64 ? LGI_OS_WIN64 : LGI_OS_WIN32) #else LGI_OS_WIN64 #endif : LGI_OS_WIN9X; } if (Ver) { Ver->Add(Version); Ver->Add(Revision); } return Os; #elif defined BEOS if (Ver) { Ver->Add(5); Ver->Add(0); } return LGI_OS_BEOS; #elif defined LINUX if (Ver) { utsname Buf; if (!uname(&Buf)) { GToken t(Buf.release, "."); for (int i=0; iAdd(atoi(t[i])); } } } return LGI_OS_LINUX; #elif defined MAC && !defined COCOA if (Ver) { SInt32 i; if (Gestalt(gestaltSystemVersionMajor, &i) == noErr) { Ver->Add(i); if (Gestalt(gestaltSystemVersionMinor, &i) == noErr) { Ver->Add(i); if (Gestalt(gestaltSystemVersionBugFix, &i) == noErr) { Ver->Add(i); } } } else if (Gestalt(gestaltSystemVersion, &i) == noErr) { char s[10]; sprintf_s(s, sizeof(s), "%x", (int)i); char *e = s + strlen(s) - 1; char a[3] = { e[-1], 0 }; char b[3] = { e[0], 0 }; e[-1] = 0; Ver->Add(atoi(s)); Ver->Add(htoi(a)); Ver->Add(htoi(b)); } } return LGI_OS_MAC_OS_X; #else return LGI_OS_UNKNOWN; #endif } const char *LgiGetOsName() { const char *Str[LGI_OS_MAX] = { "Unknown", "Win9x", "Win32", "Win64", "Haiku", "Linux", "MacOSX", }; return Str[LgiGetOs()]; } #ifdef WIN32 #define RecursiveFileSearch_Wildcard "*.*" #else // unix'ish OS #define RecursiveFileSearch_Wildcard "*" #endif bool LgiRecursiveFileSearch(const char *Root, GArray *Ext, GArray *Files, uint64 *Size, uint64 *Count, RecursiveFileSearch_Callback Callback, void *UserData) { bool Status = false; // validate args if (!Root) return 0; // get directory enumerator GDirectory Dir; Status = true; // enumerate the directory contents for (bool Found = Dir.First(Root); Found; Found = Dir.Next()) { char Name[256]; if (!Dir.Path(Name, sizeof(Name))) continue; if (Callback) { if (!Callback(UserData, Name, &Dir)) { continue; } } if (Dir.IsDir()) { // dir LgiRecursiveFileSearch( Name, Ext, Files, Size, Count, Callback, UserData); } else { // process file bool Match = true; // if no Ext's then default to match if (Ext) { for (int i=0; iLength(); i++) { const char *e = (*Ext)[i]; char *RawFile = strrchr(Name, DIR_CHAR); if (RawFile && (Match = MatchStr(e, RawFile+1))) { break; } } } if (Match) { // file matched... process: if (Files) { Files->Add(NewStr(Name)); } if (Size) { *Size += Dir.GetSize(); } if (Count) { Count++; } Status = true; } } } return Status; } #define LGI_TRACE_TO_FILE // #include #ifndef WIN32 #define _vsnprintf vsnprintf #endif void LgiTrace(const char *Msg, ...) { #if defined _INC_MALLOC && WIN32NATIVE if (_heapchk() != _HEAPOK) { return; } #endif if (Msg) { #ifdef WIN32 static GMutex Sem; Sem.Lock(_FL); #endif char Buffer[2049] = ""; #ifdef LGI_TRACE_TO_FILE GFile f; static char LogPath[MAX_PATH] = ""; if (LogPath[0] == 0) { if (LgiGetExeFile(LogPath, sizeof(LogPath))) { #ifdef MAC char *Dir = strrchr(LogPath, DIR_CHAR); if (Dir) { char Part[256]; strcpy_s(Part, sizeof(Part), Dir+1); LogPath[0] = 0; LgiMakePath(LogPath, sizeof(LogPath), LogPath, "~/Library/Logs"); LgiMakePath(LogPath, sizeof(LogPath), LogPath, Dir+1); strcat_s(LogPath, sizeof(LogPath), ".txt"); } else #endif { char *Dot = strrchr(LogPath, '.'); if (Dot && !strchr(Dot, DIR_CHAR)) strcpy(Dot+1, "txt"); else strcat(LogPath, ".txt"); } if (f.Open(LogPath, O_WRITE)) { f.Close(); } else { char Leaf[64]; char *Dir = strrchr(LogPath, DIR_CHAR); if (Dir) { strcpy_s(Leaf, sizeof(Leaf), Dir + 1); LgiGetSystemPath(LSP_APP_ROOT, LogPath, sizeof(LogPath)); if (!DirExists(LogPath)) FileDev->CreateFolder(LogPath); LgiMakePath(LogPath, sizeof(LogPath), LogPath, Leaf); } else goto OnError; } } else { // Well what to do now? I give up OnError: strcpy_s(LogPath, sizeof(LogPath), "trace.txt"); } } #endif va_list Arg; va_start(Arg, Msg); _vsnprintf(Buffer, sizeof(Buffer)-1, Msg, Arg); va_end(Arg); #ifdef LGI_TRACE_TO_FILE if (f.Open(LogPath, O_WRITE)) { f.Seek(0, SEEK_END); f.Write(Buffer, strlen(Buffer)); f.Close(); } #endif #if defined WIN32 OutputDebugString(Buffer); Sem.Unlock(); #else printf("%s", Buffer); #endif } } #ifndef LGI_STATIC #define STACK_SIZE 12 void LgiStackTrace(const char *Msg, ...) { GSymLookup::Addr Stack[STACK_SIZE]; ZeroObj(Stack); GSymLookup *Lu = LgiApp->GetSymLookup(); int Frames = Lu ? Lu->BackTrace(0, 0, Stack, STACK_SIZE) : 0; if (Msg) { char Buffer[2049] = ""; #ifdef LGI_TRACE_TO_FILE GFile f; if (LgiGetExeFile(Buffer, sizeof(Buffer))) { char *Dot = strrchr(Buffer, '.'); if (Dot && !strchr(Dot, DIR_CHAR)) { strcpy(Dot+1, "txt"); } else { strcat(Buffer, ".txt"); } f.Open(Buffer, O_WRITE); } #endif va_list Arg; va_start(Arg, Msg); int Len = _vsnprintf(Buffer, sizeof(Buffer)-1, Msg, Arg); va_end(Arg); Lu->Lookup(Buffer+Len, sizeof(Buffer)-Len-1, Stack, Frames); #ifdef LGI_TRACE_TO_FILE if (f.IsOpen()) { f.Seek(0, SEEK_END); f.Write(Buffer, strlen(Buffer)); f.Close(); } #endif #if defined WIN32 OutputDebugString(Buffer); #else printf("Trace: %s", Buffer); #endif } } #endif bool LgiTrimDir(char *Path) { if (Path) { char *p = strrchr(Path, DIR_CHAR); if (p) { *p = 0; return true; } } return false; } GAutoString LgiMakeRelativePath(const char *Base, const char *Path) { GStringPipe Status; if (Base && Path) { #ifdef WIN32 bool SameNs = strnicmp(Base, Path, 3) == 0; #else bool SameNs = true; #endif if (SameNs) { GToken b(Base + 1, ":" DIR_STR); GToken p(Path + 1, ":" DIR_STR); int Same = 0; while (b[Same] && p[Same] && stricmp(b[Same], p[Same]) == 0) { Same++; } for (int i = Same; i= b.Length()) { Status.Print(".%s", DIR_STR); } for (int n = Same; n Same) { Status.Push(DIR_STR); } Status.Push(p[n]); } } } return GAutoString(Status.NewStr()); } bool LgiIsRelativePath(const char *Path) { if (!Path) return false; if (*Path == '.') return true; #ifdef WIN32 if (IsAlpha(Path[0]) && Path[1] == ':') // Drive letter path return false; #endif if (*Path == DIR_CHAR) return false; if (strstr(Path, "://")) // Protocol def return false; return true; // Correct or not??? } bool LgiMakePath(char *Str, int StrSize, const char *Path, const char *File) { LgiAssert(Str != 0 && StrSize > 0 && Path != 0 && File != 0); if (StrSize <= 4) { printf("%s:%i - LgiMakeFile buf size=%i?\n", _FL, StrSize); } if (Str && Path && File) { if (Str != Path) { strcpy_s(Str, StrSize, Path); } char Dir[] = { '/', '\\', 0 }; #define EndStr() Str[strlen(Str)-1] #define EndDir() if (!strchr(Dir, EndStr())) strcat(Str, DIR_STR); int Len = strlen(Str); char *End = Str + (Len ? Len - 1 : 0); if (strchr(Dir, *End) && End > Str) { *End = 0; } GToken T(File, Dir); for (int i=0; i= StrSize - 1) return false; Str[Len++] = DIR_CHAR; Str[Len] = 0; } int SegLen = strlen(T[i]); if (Len + SegLen + 1 > StrSize) { return false; } strcpy(Str + Len, T[i]); } } } return true; } bool LgiGetTempPath(char *Dst, int DstSize) { return LgiGetSystemPath(LSP_TEMP, Dst, DstSize); } bool LgiGetSystemPath(LgiSystemPath Which, char *Dst, int DstSize) { bool Status = false; #if defined(WIN32) #if !defined(CSIDL_MYDOCUMENTS) #define CSIDL_MYDOCUMENTS 0x0005 #endif #if !defined(CSIDL_MYMUSIC) #define CSIDL_MYMUSIC 0x000d #endif #if !defined(CSIDL_MYVIDEO) #define CSIDL_MYVIDEO 0x000e #endif #if !defined(CSIDL_LOCAL_APPDATA) #define CSIDL_LOCAL_APPDATA 0x001c #endif #if !defined(CSIDL_COMMON_APPDATA) #define CSIDL_COMMON_APPDATA 0x0023 #endif #if !defined(CSIDL_APPDATA) #define CSIDL_APPDATA 0x001a #endif #endif if (Dst) { #ifdef LINUX // Ask our window manager add-on if it knows the path GLibrary *WmLib = LgiApp ? LgiApp->GetWindowManagerLib() : NULL; if (WmLib) { Proc_LgiWmGetPath WmGetPath = (Proc_LgiWmGetPath) WmLib->GetAddress("LgiWmGetPath"); if (WmGetPath && WmGetPath(Which, Dst, DstSize)) { return true; } } #endif switch (Which) { case LSP_USER_DOWNLOADS: { #if defined(WIN32) && defined(_MSC_VER) // OMG!!!! Really? #ifndef REFKNOWNFOLDERID typedef GUID KNOWNFOLDERID; #define REFKNOWNFOLDERID const KNOWNFOLDERID & GUID FOLDERID_Downloads = {0x374DE290,0x123F,0x4565,{0x91,0x64,0x39,0xC4,0x92,0x5E,0x46,0x7B}}; #endif GLibrary Shell("Shell32.dll"); typedef HRESULT (STDAPICALLTYPE *pSHGetKnownFolderPath)(REFKNOWNFOLDERID rfid, DWORD dwFlags, HANDLE hToken, PWSTR *ppszPath); pSHGetKnownFolderPath SHGetKnownFolderPath = (pSHGetKnownFolderPath)Shell.GetAddress("SHGetKnownFolderPath"); if (SHGetKnownFolderPath) { PWSTR ptr = NULL; HRESULT r = SHGetKnownFolderPath(FOLDERID_Downloads, 0, NULL, &ptr); if (SUCCEEDED(r)) { GAutoString u8(LgiNewUtf16To8(ptr)); if (u8) { strcpy_s(Dst, DstSize, u8); Status = true; } CoTaskMemFree(ptr); } } if (!Status) { GRegKey k(false, "HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders"); char *p = k.GetStr("{374DE290-123F-4565-9164-39C4925E467B}"); if (DirExists(p)) { strcpy_s(Dst, DstSize, p); Status = true; } } if (!Status) { GAutoString MyDoc(GetWindowsFolder(CSIDL_MYDOCUMENTS)); if (MyDoc) { LgiMakePath(Dst, DstSize, MyDoc, "Downloads"); Status = DirExists(Dst); } } if (!Status) { GAutoString MyDoc(GetWindowsFolder(CSIDL_PROFILE)); if (MyDoc) { LgiMakePath(Dst, DstSize, MyDoc, "Downloads"); Status = DirExists(Dst); } } #else LgiAssert(!"Not implemented"); #endif break; } case LSP_USER_DOCUMENTS: { #if defined(WIN32) && defined(_MSC_VER) GAutoString f(GetWindowsFolder(CSIDL_PERSONAL)); if (f) { strcpy_s(Dst, DstSize, f); Status = true; } #else LgiAssert(!"Not implemented"); #endif break; } case LSP_USER_MUSIC: { #if defined WIN32 GAutoString f(GetWindowsFolder(CSIDL_MYMUSIC)); if (f) { strcpy_s(Dst, DstSize, f); Status = true; } #elif defined MAC && !defined COCOA FSRef Ref; OSErr e = FSFindFolder(kUserDomain, kMusicDocumentsFolderType, kDontCreateFolder, &Ref); if (e) printf("%s:%i - FSFindFolder failed e=%i\n", _FL, e); else { GAutoString a = FSRefPath(Ref); if (a) { strcpy_s(Dst, DstSize, a); return true; } } #else LgiAssert(!"Not implemented"); #endif break; } case LSP_USER_VIDEO: { #if defined WIN32 GAutoString f(GetWindowsFolder(CSIDL_MYVIDEO)); if (f) { strcpy_s(Dst, DstSize, f); Status = true; } #elif defined MAC && !defined COCOA FSRef Ref; OSErr e = FSFindFolder(kUserDomain, kMovieDocumentsFolderType, kDontCreateFolder, &Ref); if (e) printf("%s:%i - FSFindFolder failed e=%i\n", _FL, e); else { GAutoString a = FSRefPath(Ref); if (a) { strcpy_s(Dst, DstSize, a); return true; } } #else LgiAssert(!"Not implemented"); #endif break; } + case LSP_USER_APPS: + { + #if defined WINDOWS + GAutoString f(GetWindowsFolder( + #ifdef WIN64 + CSIDL_PROGRAM_FILES + #else + CSIDL_PROGRAM_FILESX86 + #endif + )); + if (!f) return false; + strcpy_s(Dst, DstSize, f); + Status = true; + #elif defined MAC + strcpy_s(Dst, DstSize, "/Applications"); + Status = true; + #elif defined LINUX + strcpy_s(Dst, DstSize, "/usr/bin"); + Status = true; + #elif defined BEOS + LgiAssert(!"Impl me."); + #endif + break; + } case LSP_APP_INSTALL: { if (LgiGetExePath(Dst, DstSize)) { #if defined WIN32 char *Last = strrchr(Dst, DIR_CHAR); if (Last) { if (stristr(Last, #ifdef _DEBUG "Debug" #else "Release" #endif )) *Last = 0; } #elif defined MAC char *Last = strrchr(Dst, DIR_CHAR); if (Last) { if (!stricmp(Last, #ifdef _DEBUG "/Debug" #else "/Release" #endif )) *Last = 0; } if ((Last = strrchr(Dst, DIR_CHAR))) { if (!stricmp(Last, "/build")) *Last = 0; } #endif return true; } break; } case LSP_APP_ROOT: { #ifndef LGI_STATIC if (!LgiApp) { LgiAssert(0); break; } char *Name = LgiApp->Name(); if (!Name) { LgiAssert(0); break; } GAutoString Base; #if defined MAC && !defined COCOA FSRef Ref; OSErr e = FSFindFolder(kUserDomain, kDomainLibraryFolderType, kDontCreateFolder, &Ref); if (e) { printf("%s:%i - FSFindFolder failed e=%i\n", _FL, e); LgiAssert(0); } else { Base = FSRefPath(Ref); } #elif defined WIN32 Base.Reset(GetWindowsFolder(CSIDL_APPDATA)); #elif defined LINUX char Dot[128]; snprintf(Dot, sizeof(Dot), ".%s", Name); Name = Dot; struct passwd *pw = getpwuid(getuid()); if (pw) { Base.Reset(NewStr(pw->pw_dir)); } else LgiAssert(0); #else LgiAssert(0); #endif if (Base) { LgiMakePath(Dst, DstSize, Base, Name); Status = true; } else #endif { LgiAssert(0); } break; } case LSP_OS: { #if defined WIN32 char p[MAX_PATH]; if (GetWindowsDirectory(p, sizeof(p)) > 0) { strcpy_s(Dst, DstSize, p); Status = true; } #elif defined MAC && !defined COCOA FSRef Ref; OSErr e = FSFindFolder(kOnAppropriateDisk, kSystemFolderType, kDontCreateFolder, &Ref); if (e) printf("%s:%i - FSFindFolder failed e=%i\n", __FILE__, __LINE__, e); else { GAutoString u = FSRefPath(Ref); if (u) { strcpy_s(Dst, DstSize, u); Status = true; } } #else strcpy_s(Dst, DstSize, "/boot"); // it'll do for now... Status = true; #endif break; } case LSP_OS_LIB: { #if defined WIN32 char p[MAX_PATH]; if (GetSystemDirectory(p, sizeof(p)) > 0) { strcpy_s(Dst, DstSize, p); Status = true; } #elif defined MAC strcpy_s(Dst, DstSize, "/Library"); Status = true; #else strcpy_s(Dst, DstSize, "/lib"); // it'll do for now... Status = true; #endif break; } case LSP_TEMP: { #if defined WIN32 if (LgiGetOs() == LGI_OS_WIN9X) { char t[256]; if (GetTempPath(sizeof(t), t) > 0) { char *utf = LgiToNativeCp(t); if (utf) { strcpy_s(Dst, DstSize, utf); DeleteArray(utf); Status = true; } } } else { char16 t[256]; if (GetTempPathW(CountOf(t), t) > 0) { char *utf = LgiNewUtf16To8(t); if (utf) { strcpy_s(Dst, DstSize, utf); DeleteArray(utf); Status = true; } } } #elif defined MAC && !defined COCOA FSRef Ref; OSErr e = FSFindFolder(kUserDomain, kTemporaryFolderType, kCreateFolder, &Ref); if (e) LgiTrace("%s:%i - FSFindFolder failed e=%i\n", _FL, e); else { GAutoString u = FSRefPath(Ref); if (u) { strcpy_s(Dst, DstSize, u); Status = true; } else LgiTrace("%s:%i - FSRefPath failed.\n", _FL); } #else strcpy_s(Dst, DstSize, "/tmp"); // it'll do for now... Status = true; #endif break; } case LSP_COMMON_APP_DATA: { #if defined WIN32 GAutoString f(GetWindowsFolder(CSIDL_COMMON_APPDATA)); if (f) { strcpy_s(Dst, DstSize, f); Status = true; } #elif defined MAC && !defined COCOA FSRef Ref; OSErr e = FSFindFolder(kOnSystemDisk, kDomainLibraryFolderType, kDontCreateFolder, &Ref); if (e) printf("%s:%i - FSFindFolder failed e=%i\n", __FILE__, __LINE__, e); else { GAutoString u = FSRefPath(Ref); if (u) { strcpy_s(Dst, DstSize, u); Status = true; } } #elif defined LINUX strcpy_s(Dst, DstSize, "/usr"); Status = true; #elif defined BEOS strcpy_s(Dst, DstSize, "/boot/apps"); Status = true; #endif break; } case LSP_USER_APP_DATA: { #if defined WIN32 GAutoString f(GetWindowsFolder(CSIDL_APPDATA)); if (f) { strcpy_s(Dst, DstSize, f); Status = true; } #elif defined MAC && !defined COCOA FSRef Ref; OSErr e = FSFindFolder(kUserDomain, kDomainLibraryFolderType, kDontCreateFolder, &Ref); if (e) printf("%s:%i - FSFindFolder failed e=%i\n", __FILE__, __LINE__, e); else { GAutoString u = FSRefPath(Ref); if (u) { strcpy_s(Dst, DstSize, u); Status = true; } } #elif defined LINUX strcpy_s(Dst, DstSize, "/usr"); Status = true; #endif break; } case LSP_LOCAL_APP_DATA: { #if defined WIN32 GAutoString f(GetWindowsFolder(CSIDL_LOCAL_APPDATA)); if (f) { strcpy_s(Dst, DstSize, f); Status = true; } #else LgiAssert(!"Impl me."); #endif break; } case LSP_DESKTOP: { #if defined(_WINDOWS) && defined(_MSC_VER) GAutoString f(GetWindowsFolder(CSIDL_DESKTOPDIRECTORY)); if (f) { strcpy_s(Dst, DstSize, f); Status = true; } #elif defined MAC && !defined COCOA FSRef Ref; OSErr e = FSFindFolder(kOnAppropriateDisk, kDesktopFolderType, kDontCreateFolder, &Ref); if (e) printf("%s:%i - FSFindFolder failed e=%i\n", __FILE__, __LINE__, e); else { GAutoString u = FSRefPath(Ref); if (u) { strcpy_s(Dst, DstSize, u); Status = true; } } #elif defined POSIX struct passwd *pw = getpwuid(getuid()); if (pw) { #ifdef LINUX WindowManager wm = LgiGetWindowManager(); if (wm == WM_Gnome) sprintf_s(Dst, sizeof(Dst), "%s/.gnome-desktop", pw->pw_dir); else #endif sprintf_s(Dst, sizeof(Dst), "%s/Desktop", pw->pw_dir); Status = true; } #elif defined BEOS strcpy_s(Dst, DstSize, "/boot/home/Desktop"); Status = true; #endif break; } case LSP_HOME: { #if defined WIN32 #ifndef CSIDL_PROFILE #define CSIDL_PROFILE 0x0028 #endif GAutoString f(GetWindowsFolder(CSIDL_PROFILE)); if (f) { strcpy_s(Dst, DstSize, f); Status = true; } #elif defined POSIX struct passwd *pw = getpwuid(getuid()); if (pw) { strcpy_s(Dst, DstSize, pw->pw_dir); Status = true; } #elif defined BEOS strcpy_s(Dst, DstSize, "/boot/home"); Status = true; #endif break; } case LSP_EXE: { if (LgiGetExeFile(Dst, DstSize)) { LgiTrimDir(Dst); Status = true; } break; } case LSP_TRASH: { #if defined LINUX switch (LgiGetWindowManager()) { case WM_Kde: { static char KdeTrash[256] = ""; if (!ValidStr(KdeTrash)) { // Ask KDE where the current trash is... GProcess p; GStringPipe o; if (p.Run("kde-config", "--userpath trash", 0, true, 0, &o)) { char *s = o.NewStr(); if (s) { // Store it.. strcpy_s(KdeTrash, sizeof(KdeTrash), s); DeleteArray(s); // Clear out any crap at the end... char *e = KdeTrash + strlen(KdeTrash) - 1; while (e > KdeTrash && strchr(" \r\n\t/", *e)) { *e-- = 0; } } else { printf("%s:%i - No output from 'kde-config'.\n", __FILE__, __LINE__); } } else { printf("%s:%i - Run 'kde-config' failed.\n", __FILE__, __LINE__); } } if (ValidStr(KdeTrash)) { strcpy_s(Dst, DstSize, KdeTrash); Status = true; } break; } default: { printf("%s:%i - Unknown window manager.\n", __FILE__, __LINE__); break; } } #elif defined MAC && !defined COCOA FSRef Ref; OSErr e = FSFindFolder(kUserDomain, kTrashFolderType, kDontCreateFolder, &Ref); if (e) printf("%s:%i - FSFindFolder failed e=%i\n", __FILE__, __LINE__, e); else { GAutoString u = FSRefPath(Ref); if (u) { strcpy_s(Dst, DstSize, u); Status = true; } } #elif defined WIN32 // This should work but doesn't... *shrug* // char *f = GetWin32Folder(CSIDL_BITBUCKET); #else #endif break; } } } return Status; } bool LgiGetExeFile(char *Dst, int DstSize) { if (Dst) { #if defined WIN32 if (LgiGetOs() == LGI_OS_WIN9X) { char Exe[256]; if (GetModuleFileName(NULL, Exe, sizeof(Exe)) > 0) { char *e = LgiFromNativeCp(Exe); if (e) { strlwr(e); strcpy_s(Dst, DstSize, e); DeleteArray(e); return true; } else { LgiMsg(0, "LgiFromNativeCp returned 0, ANSI CodePage=%i (%s)", "LgiGetExeFile Error", MB_OK, GetACP(), LgiAnsiToLgiCp()); } } char m[256]; sprintf_s(m, sizeof(m), "GetModuleFileName failed err: %08.8X", GetLastError()); MessageBox(0, m, "LgiGetExeFile Error", MB_OK); LgiExitApp(); } else { char16 Exe[256]; if (GetModuleFileNameW(NULL, Exe, sizeof(Exe)) > 0) { char *e = LgiNewUtf16To8(Exe); if (e) { strcpy_s(Dst, DstSize, e); DeleteArray(e); return true; } } } #elif defined BEOS app_info Info; if (LgiApp->GetAppInfo(&Info) == B_OK) { BEntry e(&Info.ref); BPath p; if (e.GetPath(&p) == B_OK) { strcpy_s(Dst, DstSize, p.Path()); return true; } } #elif defined ATHEOS os::Directory AppPath; if (AppPath.SetTo("^/.") == 0) { std::string p; if (AppPath.GetPath(&p) == 0) { sprintf_s(Dst, DstSize, "%s%s%s", p.c_str(), DIR_STR, LgiApp->_AppFile); return true; } } #elif defined LINUX static char ExePathCache[256] = ""; bool Status = false; // this is _REALLY_ lame way to do it... but hey there aren't any // other better ways to get the full path of the running executable if (!ExePathCache[0]) { // First try the self method int Len = readlink("/proc/self/exe", ExePathCache, sizeof(ExePathCache)); Status = FileExists(ExePathCache); // printf("readlink=%i Status=%i Exe='%s'\n", Len, Status, ExePathCache); if (!Status) { ExePathCache[0] = 0; // Next try the map file method char ProcFile[256]; sprintf_s(ProcFile, sizeof(ProcFile), "/proc/%i/maps", getpid()); int fd = open(ProcFile, O_RDONLY); if (fd >= 0) { int Len = 16 << 10; // is this enough? // no better way of determining the length of proc info? char *Buf = new char[Len+1]; if (Buf) { int r = read(fd, Buf, Len); Buf[r] = 0; char *s = strchr(Buf, '/'); if (s) { char *e = strchr(s, '\n'); if (e) { *e = 0; strcpy_s(ExePathCache, sizeof(ExePathCache), s); Status = true; } } DeleteArray(Buf); } close(fd); } else { // Non proc system (like cygwin for example) // char Cmd[256]; // sprintf_s(Cmd, sizeof(Cmd), "ps | grep \"%i\"", getpid()); GStringPipe Ps; GProcess p; if (p.Run("ps", 0, 0, true, 0, &Ps)) { char *PsOutput = Ps.NewStr(); if (PsOutput) { GToken n(PsOutput, "\r\n"); for (int i=0; !Status && i 7) { int LinePid = 0; for (int i=0; i Ext; GArray Files; Ext.Add((char*)Name); #if DEBUG_FIND_FILE printf("%s:%i - Exe='%s'\n", __FILE__, __LINE__, Exe); #endif if (LgiRecursiveFileSearch(Exe, &Ext, &Files) && Files.Length()) { Result = Files[0]; Files.DeleteAt(0); } Files.DeleteArrays(); } return Result; } #if defined WIN32 static LARGE_INTEGER Freq = {0}; static bool CurTimeInit = false; #endif uint64 LgiCurrentTime() { #if defined WIN32 if (!CurTimeInit) { CurTimeInit = true; if (!QueryPerformanceFrequency(&Freq)) Freq.QuadPart = 0; } if (Freq.QuadPart) { // Return performance counter in ms LARGE_INTEGER i; if (QueryPerformanceCounter(&i)) { return i.QuadPart * 1000 / Freq.QuadPart; } // Now what?? Give up and go back to tick count I guess. Freq.QuadPart = 0; } // Fall back for systems without a performance counter. return GetTickCount(); #elif defined BEOS return system_time() / 1000; #elif defined MAC && !defined COCOA UnsignedWide t; Microseconds(&t); uint64 i = ((uint64)t.hi << 32) | t.lo; return i / 1000; #else timeval tv; gettimeofday(&tv, 0); return (tv.tv_sec * 1000) + (tv.tv_usec / 1000); #endif } uint64 LgiMicroTime() { #if defined WIN32 if (!CurTimeInit) { CurTimeInit = true; if (!QueryPerformanceFrequency(&Freq)) Freq.QuadPart = 0; } if (Freq.QuadPart) { // Return performance counter in ms LARGE_INTEGER i; if (QueryPerformanceCounter(&i)) { return i.QuadPart * 1000000 / Freq.QuadPart; } } return 0; #elif defined BEOS LgiAssert(!"Not impl."); return 0; #elif defined MAC && !defined COCOA UnsignedWide t; Microseconds(&t); return ((uint64)t.hi << 32) | t.lo; #else timeval tv; gettimeofday(&tv, 0); return (tv.tv_sec * 1000000) + tv.tv_usec; #endif } int LgiIsReleaseBuild() { #ifdef _DEBUG return 0; #else return 1; #endif } void LgiFormatSize(char *Str, int SLen, uint64 Size) { int64 K = 1024; int64 M = K * K; int64 G = K * M; if (Size == 1) { strcpy_s(Str, SLen, "1 byte"); } else if (Size < K) { sprintf_s(Str, SLen, "%u bytes", (int)Size); } else if (Size < 10 * K) { double d = (double)(int64)Size; sprintf_s(Str, SLen, "%.2f K", d / K); } else if (Size < M) { sprintf_s(Str, SLen, "%u K", (int) (Size / K)); } else if (Size < G) { double d = (double)(int64)Size; sprintf_s(Str, SLen, "%.2f M", d / M); } else { double d = (double)(int64)Size; sprintf_s(Str, SLen, "%.2f G", d / G); } } char *LgiTokStr(const char *&s) { char *Status = 0; if (s && *s) { // Skip whitespace static char Delim[] = ", \t\r\n"; while (*s && strchr(Delim, *s)) s++; if (*s) { if (strchr("\'\"", *s)) { char Delim = *s++; const char *e = strchr(s, Delim); if (!e) { // error, no end delimiter e = s; while (*e) e++; } Status = NewStr(s, e - s); s = *e ? e + 1 : e; } else { const char *e = s; while (*e && !strchr(Delim, *e)) e++; Status = NewStr(s, e - s); s = e; } } } return Status; } ////////////////////////////////////////////////////////////////////////////// DoEvery::DoEvery(int p) // p = timeout in ms { Init(p); } void DoEvery::Init(int p) // p = timeout in ms { LastTime = LgiCurrentTime(); if (p > 0) { Period = p; } } bool DoEvery::DoNow() { int64 Now = LgiCurrentTime(); if (LastTime + Period < Now) { LastTime = Now; return true; } return false; } /////////////////////////////////////////////////////////////////////////////////// #if 0 GViewFill::GViewFill(GColour c) { Type = Solid; Col = c; #ifdef WIN32 hBrush = NULL; #endif } GViewFill::GViewFill(COLOUR c, int Bits) { Type = Solid; Col.c32(CBit(32, c, Bits)); #ifdef WIN32 hBrush = NULL; #endif } GViewFill::GViewFill(GSurface *dc, bool Copy) { Col.c32(0); Type = Copy ? OwnBitmap : RefBitmap; #ifndef LGI_STATIC if (Copy) pDC = new GMemDC(dc); else pDC = dc; #endif #ifdef WIN32 hBrush = NULL; #endif } GViewFill::GViewFill(const GViewFill &f) { Col = f.GetFlat(); Type = f.Type; if (Type == OwnBitmap) { #ifndef LGI_STATIC pDC = new GMemDC(f.pDC); #endif } else if (Type == RefBitmap) { pDC = f.pDC; } #ifdef WIN32 hBrush = NULL; #endif } GViewFill::~GViewFill() { Empty(); } void GViewFill::Empty() { if (Type == OwnBitmap) DeleteObj(pDC); Type = None; pDC = 0; Col.c32(0); #ifdef WIN32 if (hBrush) { DeleteObject(hBrush); hBrush = NULL; } #endif } void GViewFill::Fill(GSurface *pDC, GRect *r, GdcPt2 *Origin) { #ifndef LGI_STATIC if (Type == Solid) { pDC->Colour(Col); pDC->Rectangle(r); } else if (Type == OwnBitmap || Type == RefBitmap) { if (pDC) { GRect a; if (!r) { a.ZOff(pDC->X()-1, pDC->Y()-1); r = &a; } for (int y = Origin ? (Origin->y % pDC->Y()) - pDC->Y() : 0; y < r->Y(); y += pDC->Y()) { for (int x = Origin ? (Origin->x % pDC->X()) - pDC->X() : 0; xX(); x += pDC->X()) { pDC->Blt(r->x1 + x, r->y1 + y, pDC); } } } else { LgiAssert(0); } } #endif } #ifdef WIN32 /* HBRUSH GViewFill::GetBrush() { if (!hBrush) { LOGBRUSH LogBrush; LogBrush.lbStyle = BS_SOLID; LogBrush.lbColor = GetFlat().c24(); LogBrush.lbHatch = 0; hBrush = CreateBrushIndirect(&LogBrush); } return hBrush; } */ #endif #endif ////////////////////////////////////////////////////////////////////// bool GCapabilityClient::NeedsCapability(const char *Name, const char *Param) { for (int i=0; iNeedsCapability(Name, Param); return Targets.Length() > 0; } GCapabilityClient::~GCapabilityClient() { for (int i=0; iClients.Delete(this); } void GCapabilityClient::Register(GCapabilityTarget *t) { if (t) { Targets.Add(t); t->Clients.Add(this); } } GCapabilityTarget::~GCapabilityTarget() { for (int i=0; iTargets.Delete(this); } ///////////////////////////////////////////////////////////////////// GProfile::GProfile(const char *Name) { MinMs = -1; Add(Name); } GProfile::~GProfile() { Add("End"); if (MinMs > 0) { uint64 TotalMs = s.Last().Time - s[0].Time; if (TotalMs < MinMs) { return; } } for (int i=0; i #include #include #include "GSkinEngine.h" #include "GSymLookup.h" #include "GDocView.h" #include "GToken.h" #include HINSTANCE _lgi_app_instance = 0; extern LPTOP_LEVEL_EXCEPTION_FILTER _PrevExceptionHandler; OsAppArguments::OsAppArguments() { _Default(); } OsAppArguments::OsAppArguments(int Args, char **Arg) { _Default(); Set(Args, Arg); } OsAppArguments &OsAppArguments::operator =(OsAppArguments &p) { hInstance = p.hInstance; Pid = p.Pid; nCmdShow = p.nCmdShow; lpCmdLine = 0; CmdLine.Reset(NewStrW(p.lpCmdLine)); lpCmdLine = CmdLine; return *this; } void OsAppArguments::_Default() { hInstance = 0; lpCmdLine = 0; Pid = GetCurrentProcessId(); nCmdShow = SW_RESTORE; } void OsAppArguments::Set(int Args, char **Arg) { GStringPipe p; for (int i=0; i Classes; GSymLookup *SymLookup; GAppPrivate() { LinuxWine = -1; SymLookup = 0; QuitReceived = false; SkinLib = 0; ThemeAware = true; GuiThread = LgiGetCurrentThread(); } ~GAppPrivate() { DeleteObj(SkinLib); DeleteObj(SymLookup); } }; ///////////////////////////////////////////////////////////////////////////// LONG __stdcall _ExceptionFilter_Redir(LPEXCEPTION_POINTERS e) { if (LgiApp) return LgiApp->_ExceptionFilter(e, LgiApp->d->ProductId); else LgiTrace("_ExceptionFilter_Redir error: No application ptr.\n"); return 0; } ///////////////////////////////////////////////////////////////////////////// GSkinEngine *GApp::SkinEngine = 0; GMouseHook *GApp::MouseHook = 0; GMouseHook *GApp::GetMouseHook() { return MouseHook; } static GAutoString ParseStr(GPointer &p, bool Pad = true) { char16 *Key = p.w; // Skip 'key' string while (*p.w) p.w++; // Skip NULL p.w++; if (Pad) { // Align to 32-bit boundry while ((NativeInt)p.u8 & 3) p.u8++; } return GAutoString(LgiNewUtf16To8(Key)); } static GAutoString ParseVer(void *Resource, char *Part) { GToken Parts(Part, "."); if (Parts.Length() == 3) { GPointer p; p.u8 = (uint8*)Resource; uint16 Len = *p.u16++; uint16 ValueLen = *p.u16++; uint16 Type = *p.u16++; GAutoString Key = ParseStr(p); // Read VS_FIXEDFILEINFO structure DWORD dwSig = *p.u32++; DWORD dwStrucVersion = *p.u32++; DWORD dwFileVersionMS = *p.u32++; DWORD dwFileVersionLS = *p.u32++; DWORD dwProductVersionMS = *p.u32++; DWORD dwProductVersionLS = *p.u32++; DWORD dwFileFlagsMask = *p.u32++; DWORD dwFileFlags = *p.u32++; DWORD dwFileOS = *p.u32++; DWORD dwFileType = *p.u32++; DWORD dwFileSubtype = *p.u32++; DWORD dwFileDateMS = *p.u32++; DWORD dwFileDateLS = *p.u32++; // Align to 32-bit boundry while ((NativeInt)p.u8 & 3) p.u8++; // Children while (p.u8 < (uint8*)Resource + Len) { // Read StringFileInfo structures... uint8 *fStart = p.u8; uint16 fLength = *p.u16++; uint16 fValueLength = *p.u16++; uint16 fType = *p.u16++; GAutoString fKey = ParseStr(p); if (strcmp(fKey, "StringFileInfo")) break; while (p.u8 < fStart + fLength) { // Read StringTable entries uint8 *tStart = p.u8; uint16 tLength = *p.u16++; uint16 tValueLength = *p.u16++; uint16 tType = *p.u16++; GAutoString tKey = ParseStr(p); while (p.u8 < tStart + tLength) { // Read String entries uint8 *sStart = p.u8; uint16 sLength = *p.u16++; uint16 sValueLength = *p.u16++; uint16 sType = *p.u16++; GAutoString sKey = ParseStr(p); GAutoString sValue; if (p.u8 < sStart + sLength) sValue = ParseStr(p); if (!stricmp(Parts[0], fKey) && !stricmp(Parts[1], tKey) && !stricmp(Parts[2], sKey)) { return sValue; } } } } } return GAutoString(); } ///////////////////////////////////////////////////////////////////////////// #include #include extern int MouseRollMsg; typedef HRESULT (CALLBACK *fDllGetVersion)(DLLVERSIONINFO *); bool GApp::Win9x = LgiGetOs() == LGI_OS_WIN9X; GApp::GApp(OsAppArguments &AppArgs, const char *AppName, GAppArguments *ObjArgs) { // GApp instance LgiAssert(TheApp == 0); TheApp = this; LgiAssert(AppName); Name(AppName); int64 Time = LgiCurrentTime(); #define DumpTime(str) /* \ { int64 n = LgiCurrentTime(); \ LgiTrace("%s=%ims\n", str, (int)(n-Time)); \ Time = n; } */ // Sanity Checks LgiAssert(sizeof(int8) == 1); LgiAssert(sizeof(uint8) == 1); LgiAssert(sizeof(int16) == 2); LgiAssert(sizeof(uint16) == 2); LgiAssert(sizeof(int32) == 4); LgiAssert(sizeof(uint32) == 4); LgiAssert(sizeof(int64) == 8); LgiAssert(sizeof(uint64) == 8); LgiAssert(sizeof(char16) == 2); DumpTime("start"); // Private data d = new GAppPrivate; char Mime[256]; sprintf_s(Mime, sizeof(Mime), "application/x-%s", AppName); d->Mime.Reset(NewStr(Mime)); DumpTime("priv"); // Setup exception handler HRSRC hRsrc = ::FindResource(NULL, MAKEINTRESOURCE(VS_VERSION_INFO), RT_VERSION); HGLOBAL hGlobal = ::LoadResource(NULL, hRsrc); LPVOID pVersionResource = ::LockResource(hGlobal); GAutoString ProductName, ProductVer; // replace "040904e4" with the language ID of your resources if (pVersionResource) { ProductName = ParseVer(pVersionResource, "StringFileInfo.0c0904b0.ProductName"); ProductVer = ParseVer(pVersionResource, "StringFileInfo.0c0904b0.ProductVersion"); } if (ProductName && ProductVer) { char s[256]; sprintf_s(s, sizeof(s), "%s-%s", ProductName.Get(), ProductVer.Get()); d->ProductId.Reset(NewStr(s)); } InitializeCriticalSection(&StackTraceSync); #if !defined(_DEBUG) _PrevExceptionHandler = SetUnhandledExceptionFilter(_ExceptionFilter_Redir); #endif DumpTime("exception handler"); // Initialize windows dll's OleInitialize(NULL); CoInitialize(NULL); InitCommonControls(); { /* GLibrary ComCtl32("ComCtl32.dll"); DLLVERSIONINFO info; ZeroObj(info); info.cbSize = sizeof(info); fDllGetVersion DllGetVersion = (fDllGetVersion)ComCtl32.GetAddress("DllGetVersion"); if (DllGetVersion) { HRESULT ret = DllGetVersion(&info); d->ThemeAware = info.dwMajorVersion >= 6; LgiTrace("ComCtl32.dll v%i.%i found (ret=%x)\n", info.dwMajorVersion, info.dwMinorVersion, ret); ) */ GArray Ver; LgiGetOs(&Ver); if (Ver.Length() > 1) { // LgiTrace("Windows v%i.%i\n", Ver[0], Ver[1]); if (Ver[0] < 6) { d->ThemeAware = false; } } #ifdef _MSC_VER if (!d->ThemeAware) { SetThemeAppProperties(0); } #endif } DumpTime("init common ctrls"); // Setup LGI Sub-systems GFontSystem::Inst(); DumpTime("font sys"); d->FileSystem = new GFileSystem; DumpTime("file sys"); d->GdcSystem = new GdcDevice; DumpTime("gdc"); // Vars... d->Config = 0; AppWnd = 0; SetAppArgs(AppArgs); DumpTime("vars"); // System font SystemNormal = 0; SystemBold = 0; GFontType SysFontType; if (SysFontType.GetSystemFont("System")) { SystemNormal = SysFontType.Create(); if (SystemNormal) { // Force load SystemNormal->Create(); } SystemBold = SysFontType.Create(); if (SystemBold) { SystemBold->Bold(true); SystemBold->Create(); } } if (!SystemNormal) { LgiMsg(0, "Error: Couldn't create system font.", "Lgi Error"); LgiExitApp(); } DumpTime("fonts"); // Other vars and init hNormalCursor = LoadCursor(NULL, IDC_ARROW); LgiRandomize(LgiCurrentTime()*LgiGetCurrentThread()); MouseRollMsg = RegisterWindowMessage("MSWHEEL_ROLLMSG"); DumpTime("cursor/rand/msg"); LgiInitColours(); DumpTime("colours"); // Setup mouse hook MouseHook = new GMouseHook; DumpTime("ms hook"); if ( #if 0 // defined(LGI_STATIC) && _MSC_VER < 1600 0 #else ( !ObjArgs || !ObjArgs->NoSkin ) && !GetOption("noskin") #endif ) { // Load library char SkinLibName[] = #ifdef __GNUC__ "lib" #endif "lgiskin" #if _MSC_VER == 1300 "7" #endif #if _MSC_VER == 1400 "8" #endif #if _MSC_VER == 1500 "9" #endif #if _MSC_VER == 1600 "10" #endif #ifdef _DEBUG "d" #endif ; #if HAS_SHARED_OBJECT_SKIN d->SkinLib = new GLibrary(SkinLibName); if (d->SkinLib) { if (d->SkinLib->IsLoaded()) { Proc_CreateSkinEngine CreateSkinEngine = (Proc_CreateSkinEngine)d->SkinLib->GetAddress(LgiSkinEntryPoint); if (CreateSkinEngine) { SkinEngine = CreateSkinEngine(this); } } else { DeleteObj(d->SkinLib); printf("Failed to load '%s'\n", SkinLibName); } } #else extern GSkinEngine *CreateSkinEngine(GApp *App); SkinEngine = CreateSkinEngine(this); #endif } DumpTime("skin"); } GApp::~GApp() { DeleteObj(AppWnd); DeleteObj(SystemNormal); DeleteObj(SystemBold); DeleteObj(MouseHook); TheApp = 0; DeleteObj(SkinEngine); DeleteObj(GFontSystem::Me); DeleteObj(d->FileSystem); DeleteObj(d->GdcSystem); DeleteObj(d->Config); d->Classes.DeleteObjects(); CoUninitialize(); OleUninitialize(); DeleteObj(d); DeleteCriticalSection(&StackTraceSync); } bool GApp::IsOk() { bool Status = (this != 0) && (d != 0) && (d->FileSystem != 0) && (d->GdcSystem != 0); if (!Status) LgiAssert(!"Hash table error"); return Status; } int GApp::GetCpuCount() { SYSTEM_INFO si; ZeroObj(si); GetSystemInfo(&si); return si.dwNumberOfProcessors; } OsThreadId GApp::GetGuiThread() { return d->GuiThread; } List *GApp::GetClasses() { return IsOk() ? &d->Classes : 0; } OsAppArguments *GApp::GetAppArgs() { return IsOk() ? &d->Args : 0; } GXmlTag *GApp::GetConfig(const char *Tag) { if (IsOk() && !d->Config) { const char File[] = "lgi.conf"; char Path[256]; if (LgiGetExePath(Path, sizeof(Path))) { if (Path[strlen(Path)-1] != DIR_CHAR) strcat(Path, DIR_STR); strcat(Path, File); if (FileExists(Path)) { d->Config = new GXmlTag("Config"); if (d->Config) { GFile f; if (f.Open(Path, O_READ)) { GXmlTree t; t.Read(d->Config, &f, 0); } } } } if (!d->Config) { d->Config = new GXmlTag("Options"); } } if (Tag && d->Config) { return d->Config->GetTag(Tag); } return 0; } void GApp::SetConfig(GXmlTag *Tag) { if (IsOk() && Tag) { GXmlTag *Old = GetConfig(Tag->Tag); if (Old) { Old->RemoveTag(); DeleteObj(Old); } if (!d->Config) { GetConfig(0); } if (d->Config) { d->Config->InsertTag(Tag); } } } char *GApp::GetArgumentAt(int n) { if (d->Args.lpCmdLine) { char16 *s = d->Args.lpCmdLine; for (int i=0; i<=n; i++) { char16 *e = 0; while (*s && strchr(WhiteSpace, *s)) s++; if (*s == '\'' || *s == '\"') { char16 Delim = *s++; e = StrchrW(s, Delim); } else { for (e = s; *e && !strchr(WhiteSpace, *e); e++) ; } if (i == n) { return LgiNewUtf16To8(s, (e - s) * sizeof(char16)); } s = *e ? e + 1 : e; } } return 0; } bool GApp::GetOption(const char *Option, GAutoString &Buf) { if (!ValidStr(Option)) { return false; } char16 *c = d->Args.lpCmdLine; char16 *Opt = LgiNewUtf8To16(Option); int OptLen = StrlenW(Opt); while (c && *c) { if (*c == '/' || *c == '-') { c++; char16 *e = c; while (*e && (IsAlpha(*e)) || StrchrW(L"_-", *e)) e++; if (e - c == OptLen && !StrnicmpW(c, Opt, OptLen)) { c += OptLen; if (*c) { // skip leading whitespace while (*c && strchr(WhiteSpace, *c)) { c++; } // write str out if they want it char16 End = (*c == '\'' || *c == '\"') ? *c++ : ' '; char16 *e = StrchrW(c, End); if (!e) e = c + StrlenW(c); Buf.Reset(LgiNewUtf16To8(c, (NativeInt)e-(NativeInt)c)); } // yeah we got the option DeleteArray(Opt); return true; } } c = StrchrW(c, ' '); if (c) c++; } DeleteArray(Opt); return false; } bool GApp::GetOption(const char *Option, char *Dest, int DestLen) { GAutoString Buf; if (GetOption(Option, Buf)) { if (Dest) strcpy_s(Dest, DestLen, &Buf[0]); return true; } return false; } // #include "GInput.h" void GApp::SetAppArgs(OsAppArguments &AppArgs) { d->Args = AppArgs; } void GApp::OnCommandLine() { char WhiteSpace[] = " \r\n\t"; char *CmdLine = LgiNewUtf16To8(d->Args.lpCmdLine); if (ValidStr(CmdLine)) { // LgiTrace("CmdLine='%s'\n", CmdLine); GArray Files; char *Delim = "\'\""; char *s; for (s = CmdLine; *s; ) { // skip ws while (*s && strchr(WhiteSpace, *s)) s++; // read to end of token char *e = s; if (strchr(Delim, *s)) { char Delim = *s++; e = strchr(s, Delim); if (!e) e = s + strlen(s); } else { for (; *e && !strchr(WhiteSpace, *e); e++) ; } char *Arg = NewStr(s, e - s); if (Arg) { if (FileExists(Arg)) { Files.Add(Arg); } else { DeleteArray(Arg); } } // next s = (*e) ? e + 1 : e; } // call app if (Files.Length() > 0) { OnReceiveFiles(Files); } // clear up Files.DeleteArrays(); } DeleteArray(CmdLine); } void GApp::OnUrl(const char *Url) { if (AppWnd) AppWnd->OnUrl(Url); } void GApp::OnReceiveFiles(GArray &Files) { if (AppWnd) AppWnd->OnReceiveFiles(Files); } int32 GApp::GetMetric(LgiSystemMetric Metric) { int32 Status = 0; switch (Metric) { case LGI_MET_DECOR_X: { Status = GetSystemMetrics(SM_CXFRAME) * 2; break; } case LGI_MET_DECOR_Y: { Status = GetSystemMetrics(SM_CYFRAME) * 2; Status += GetSystemMetrics(SM_CYCAPTION); break; } case LGI_MET_DECOR_CAPTION: { Status = GetSystemMetrics(SM_CYCAPTION); break; } case LGI_MET_MENU: { Status = GetSystemMetrics(SM_CYMENU); break; } case LGI_MET_THEME_AWARE: { Status = d->ThemeAware; break; } } return Status; } GViewI *GApp::GetFocus() { HWND h = ::GetFocus(); if (h) { return GWindowFromHandle(h); } return 0; } HINSTANCE GApp::GetInstance() { if (this && IsOk()) { return d->Args.hInstance; } return (HINSTANCE)GetCurrentProcess(); } OsProcessId GApp::GetProcessId() { if (this) { return IsOk() ? d->Args.Pid : 0; } return GetCurrentProcessId(); } int GApp::GetShow() { return IsOk() ? d->Args.nCmdShow : 0; } class GWnd { public: GMutex *GetLock(GWindow *w) { return w->_Lock; } }; bool GApp::Run(bool Loop, OnIdleProc IdleCallback, void *IdleParam) { MSG Msg; bool status = true; ZeroObj(Msg); if (Loop) { OnCommandLine(); if (IdleCallback) { bool DontWait = true; while (!d->QuitReceived) { while (1) { bool Status; if (DontWait) { Status = PeekMessage(&Msg, NULL, 0, 0, PM_REMOVE); } else { Status = GetMessage(&Msg, NULL, 0, 0) > 0; DontWait = true; } #if 0 char m[256]; sprintf_s(m, sizeof(m), "Msg=%i hwnd=%p %i,%i\n", Msg.message, Msg.hwnd, Msg.wParam, Msg.lParam); OutputDebugStringA(m); #endif if (!Status || Msg.message == WM_QUIT) break; #ifdef _DEBUG int64 Last = LgiCurrentTime(); #endif TranslateMessage(&Msg); DispatchMessage(&Msg); #ifdef _DEBUG int64 Now = LgiCurrentTime(); if (Now - Last > 1000) { LgiTrace("%s:%i - Msg Loop Blocked: %i ms (Msg: 0x%.4x)\n", _FL, (int) (Now - Last), Msg.message); } #endif } if (Msg.message == WM_QUIT) { d->QuitReceived = true; } else { DontWait = IdleCallback(IdleParam); } } } else { while (!d->QuitReceived && GetMessage(&Msg, NULL, 0, 0) > 0) { #ifdef _DEBUG int64 Last = LgiCurrentTime(); #endif TranslateMessage(&Msg); DispatchMessage(&Msg); #ifdef _DEBUG int64 Now = LgiCurrentTime(); if (Now - Last > 1000) { LgiTrace("%s:%i - Msg Loop Blocked: %i ms (Msg: %i)\n", __FILE__, __LINE__, (int) (Now - Last), Msg.message); } #endif } } } else { while ( PeekMessage(&Msg, NULL, 0, 0, PM_REMOVE) && Msg.message != WM_QUIT) { TranslateMessage(&Msg); DispatchMessage(&Msg); } if (Msg.message == WM_QUIT) { d->QuitReceived = true; } } return Msg.message != WM_QUIT; } void GApp::Exit(int Code) { if (Code) { exit(0); } else { PostQuitMessage(Code); } } GAutoString GApp::GetFileMimeType(const char *File) { GAutoString r; char m[128]; if (LgiGetFileMimeType(File, m, sizeof(m))) r.Reset(NewStr(m)); return r; } bool GApp::GetAppsForMimeType(char *Mime, GArray &Apps) { LgiAssert(!"Not impl."); return false; } GSymLookup *GApp::GetSymLookup() { if (!this) return 0; if (!d->SymLookup) d->SymLookup = new GSymLookup; return d->SymLookup; } bool GApp::IsWine() { if (d->LinuxWine < 0) { HMODULE hntdll = GetModuleHandle("ntdll.dll"); if (hntdll) { typedef const char * (CDECL *pwine_get_version)(void); pwine_get_version wine_get_version = (pwine_get_version)GetProcAddress(hntdll, "wine_get_version"); d->LinuxWine = wine_get_version != 0; } } return d->LinuxWine > 0; } + +bool GApp::IsElevated() +{ + bool fRet = false; + HANDLE hToken = NULL; + if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken )) + { + TOKEN_ELEVATION Elevation; + DWORD cbSize = sizeof(TOKEN_ELEVATION); + if (GetTokenInformation(hToken, TokenElevation, &Elevation, sizeof(Elevation), &cbSize)) + fRet = Elevation.TokenIsElevated; + } + if (hToken) + CloseHandle(hToken); + return fRet; +} \ No newline at end of file