diff --git a/Lgi.xml b/Lgi.xml
--- a/Lgi.xml
+++ b/Lgi.xml
@@ -1,599 +1,599 @@
-
-
-
-
+
-
+
+
+
+
Makefile.linux
Makefile.win64
Makefile.macosx
gcc
0
./include
./private/common
./include
./private/common
./include/lgi/linux
./include/lgi/linux/Gtk
./private/linux
./include/lgi/linux
./include/lgi/linux/Gtk
./private/linux
./include/lgi/win
./private/win
./include/lgi/win
./private/win
./include/lgi/haiku
./private/haiku
./include/lgi/haiku
./private/haiku
/usr/include/libappindicator3-0.1
`pkg-config --cflags gtk+-3.0`
`pkg-config --cflags gstreamer-1.0`
/usr/include/libappindicator3-0.1
`pkg-config --cflags gtk+-3.0`
`pkg-config --cflags gstreamer-1.0`
magic
appindicator3
crypt
-static-libgcc
`pkg-config --libs gtk+-3.0`
magic
appindicator3
crypt
-static-libgcc
`pkg-config --libs gtk+-3.0`
-static-libgcc
gnu
network
be
-static-libgcc
gnu
network
be
lgi-gtk3
lgi-gtk3
DynamicLibrary
LGI_LIBRARY
LGI_LIBRARY
POSIX
_GNU_SOURCE
POSIX
_GNU_SOURCE
diff --git a/include/lgi/common/Tree.h b/include/lgi/common/Tree.h
--- a/include/lgi/common/Tree.h
+++ b/include/lgi/common/Tree.h
@@ -1,307 +1,305 @@
/// \file
/// \author Matthew Allen (fret@memecode.com)
/// \brief A tree/heirarchy control
#ifndef __GTREE2_H
#define __GTREE2_H
#include "lgi/common/ItemContainer.h"
#include
enum LTreeItemRect
{
TreeItemPos,
TreeItemThumb,
TreeItemText,
TreeItemIcon
};
class LTreeItem;
class LgiClass LTreeNode
{
protected:
class LTree *Tree;
LTreeItem *Parent;
List Items;
virtual LTreeItem *Item() { return 0; }
virtual LRect *Pos() { return 0; }
virtual void _ClearDs(int Col);
void _Visible(bool v);
void SetLayoutDirty();
public:
LTreeNode();
virtual ~LTreeNode();
/// Inserts a tree item as a child at 'Pos'
LTreeItem *Insert(LTreeItem *Obj = NULL, ssize_t Pos = -1);
/// Removes this node from it's parent, for permanent separation.
void Remove();
/// Detachs the item from the tree so it can be re-inserted else where.
void Detach();
/// Gets the node after this one at the same level.
LTreeItem *GetNext();
/// Gets the node before this one at the same level.
LTreeItem *GetPrev();
/// Gets the first child node.
LTreeItem *GetChild();
/// Gets the parent of this node.
LTreeItem *GetParent() { return Parent; }
/// Gets the owning tree. May be NULL if not attached to a tree.
LTree *GetTree() { return Tree; }
/// Returns true if this is the root node.
bool IsRoot();
/// Returns the index of this node in the list of item owned by it's parent.
ssize_t IndexOf();
/// \returns number of child.
size_t Length();
/// \returns if the object is in the tree
bool HasItem(LTreeItem *obj, bool recurse = true);
List::I begin()
{
return Items.begin();
}
List::I end()
{
return Items.end();
}
/// Sorts the child items
template
bool Sort(int (*Compare)(LTreeItem*, LTreeItem*, T user_param), T user_param = 0)
{
if (!Compare)
return false;
Items.Sort(Compare, user_param);
SetLayoutDirty();
return true;
}
/// Calls a f(n) for each
int ForEach(std::function Fn);
virtual bool Expanded() { return false; }
virtual void Expanded(bool b) {}
virtual void OnVisible(bool v) {}
};
/// The item class for a tree. This defines a node in the heirarchy.
class LgiClass LTreeItem : public LItem, public LTreeNode
{
friend class LTree;
friend class LTreeNode;
protected:
class LTreeItemPrivate *d;
// Private methods
void _RePour();
void _Pour(LPoint *Limit, int ColumnPx, int Depth, bool Visible);
void _Remove();
void _MouseClick(LMouse &m);
void _SetTreePtr(LTree *t);
LTreeItem *_HitTest(int x, int y, bool Debug = false);
LRect *_GetRect(LTreeItemRect Which);
LPoint _ScrollPos();
LTreeItem *Item() override { return this; }
LRect *Pos() override;
virtual void _PourText(LPoint &Size);
virtual void _PaintText(LItem::ItemPaintCtx &Ctx);
void _ClearDs(int Col) override;
virtual void OnPaintColumn(LItem::ItemPaintCtx &Ctx, int i, LItemColumn *c);
int GetColumnSize(int Col);
protected:
LString::Array Str;
int Sys_Image = -1;
public:
LTreeItem();
virtual ~LTreeItem();
LItemContainer *GetContainer() override;
/// \brief Get the text for the node
///
/// You can either return a string stored internally to your
/// object by implementing this function in your item class
/// or use the SetText function to store the string in this
/// class.
const char *GetText(int i = 0) override;
/// \brief Sets the text for the node.
///
/// This will allocate and store the string in this class.
bool SetText(const char *s, int i=0) override;
/// Returns the icon index into the parent tree's LImageList.
int GetImage(int Flags = 0) override;
/// Sets the icon index into the parent tree's LImageList.
void SetImage(int i) override;
/// Tells the item to update itself on the screen when the
/// LTreeItem::GetText data has changed.
void Update() override;
/// Returns true if the tree item is currently selected.
bool Select() override;
/// Selects or deselects the tree item.
void Select(bool b) override;
/// Returns true if the node has children and is open.
bool Expanded() override;
/// Opens or closes the node to show or hide the children.
void Expanded(bool b) override;
/// Scrolls the tree view so this node is visible.
void ScrollTo() override;
/// Gets the bounding box of the item.
LRect *GetPos(int Col = -1) override;
/// True if the node is the drop target
bool IsDropTarget();
/// Called when the node expands/contracts to show or hide it's children.
virtual void OnExpand(bool b);
/// Paints the item
void OnPaint(ItemPaintCtx &Ctx) override;
void OnPaint(LSurface *pDC) override { LAssert(0); }
};
/// A tree control.
class LgiClass LTree :
public LItemContainer,
public ResObject,
public LTreeNode
{
friend class LTreeItem;
friend class LTreeNode;
class LTreePrivate *d;
// Private methods
void _Pour();
void _OnSelect(LTreeItem *Item);
void _Update(LRect *r = 0, bool Now = false);
void _UpdateBelow(int y, bool Now = false);
void _UpdateScrollBars();
List *GetSelLst();
protected:
// Options
bool Lines;
bool Buttons;
bool LinesAtRoot;
bool EditLabels;
LRect rItems;
LPoint _ScrollPos();
LTreeItem *GetAdjacent(LTreeItem *From, bool Down);
void OnDragEnter();
void OnDragExit();
void ClearDs(int Col) override;
public:
LTree(int id, int x = 0, int y = 0, int cx = 100, int cy = 100, const char *name = NULL);
~LTree();
const char *GetClass() override { return "LTree"; }
/// Called when an item is clicked
virtual void OnItemClick(LTreeItem *Item, LMouse &m);
/// Called when an item is dragged from it's position
virtual void OnItemBeginDrag(LTreeItem *Item, LMouse &m);
/// Called when an item is expanded/contracted to show or hide it's children
virtual void OnItemExpand(LTreeItem *Item, bool Expand);
/// Called when an item is selected
virtual void OnItemSelect(LTreeItem *Item);
// Implementation
void OnMouseClick(LMouse &m) override;
void OnMouseMove(LMouse &m) override;
bool OnMouseWheel(double Lines) override;
void OnPaint(LSurface *pDC) override;
void OnFocus(bool b) override;
void OnPosChange() override;
bool OnKey(LKey &k) override;
int OnNotify(LViewI *Ctrl, LNotification n) override;
LMessage::Result OnEvent(LMessage *Msg) override;
void OnPulse() override;
int GetContentSize(int ColumnIdx) override;
LCursor GetCursor(int x, int y) override;
- bool Lock(const char *file, int line, int TimeOut = -1) override;
- void Unlock() override;
/// Add a item to the tree
LTreeItem *Insert(LTreeItem *Obj = 0, ssize_t Pos = -1);
/// Remove and delete an item
bool Delete(LTreeItem *Obj);
/// Remove but don't delete an item
bool Remove(LTreeItem *Obj);
/// Gets the item at an index
LTreeItem *ItemAt(size_t Pos) { return Items[Pos]; }
/// \returns if the object is in the tree
bool HasItem(LTreeItem *obj, bool recurse = true);
/// Select the item 'Obj'
bool Select(LTreeItem *Obj);
/// Returns the first selected item
LTreeItem *Selection();
/// Gets the whole selection and puts it in 'n'
template
bool GetSelection(LArray &n)
{
n.Empty();
auto s = GetSelLst();
for (auto i : *s)
{
T *ptr = dynamic_cast(i);
if (ptr)
n.Add(ptr);
}
return n.Length() > 0;
}
/// Gets an array of all items
template
bool GetAll(LArray &n)
{
n.Empty();
return ForAllItems([&n](LTreeItem *item)
{
T *t = dynamic_cast(item);
if (t)
n.Add(t);
});
}
/// Call a function for every item
bool ForAllItems(std::function Callback);
/// Returns the item at an x,y location
LTreeItem *ItemAtPoint(int x, int y, bool Debug = false);
/// Temporarily selects one of the items as the drop target during a
/// drag and drop operation. Call SelectDropTarget(0) when done.
void SelectDropTarget(LTreeItem *Item);
/// Delete all items (frees the items)
void Empty();
/// Remove reference to items (doesn't free the items)
void RemoveAll();
/// Call 'Update' on all tree items
void UpdateAllItems() override;
// Visual style
enum ThumbStyle
{
TreePlus,
TreeTriangle
};
void SetVisualStyle(ThumbStyle Btns, bool JoiningLines);
};
#endif
diff --git a/include/lgi/haiku/LgiOsDefs.h b/include/lgi/haiku/LgiOsDefs.h
--- a/include/lgi/haiku/LgiOsDefs.h
+++ b/include/lgi/haiku/LgiOsDefs.h
@@ -1,518 +1,518 @@
/**
\file
\author Matthew Allen
\brief Haiku defs.
Debugging memory issues in Haiku:
LD_PRELOAD=/boot/system/lib/libroot_debug.so MALLOC_DEBUG=g ./my_app
*/
#ifndef __LGI_OS_DEFS_H
#define __LGI_OS_DEFS_H
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "lgi/common/LgiDefs.h"
#include
#define XP_CTRLS 1
#define POSIX 1
#define LGI_64BIT 1
#define LGI_VIEW_HANDLE 1
#define LGI_VIEW_HASH 1
#define LGI_HAIKU 1
#undef stricmp
#include "lgi/common/LgiInc.h"
class LgiClass OsAppArguments
{
struct OsAppArgumentsPriv *d;
public:
int Args;
const char **Arg;
OsAppArguments(int args, const char **arg);
~OsAppArguments();
void Set(char *CmdLine);
bool Get(const char *Name, const char **Val = NULL);
OsAppArguments &operator =(OsAppArguments &a);
};
// Process
typedef int OsProcess;
typedef int OsProcessId;
typedef BView *OsView;
typedef BWindow *OsWindow;
typedef char OsChar;
typedef BView *OsPainter;
typedef BFont *OsFont;
typedef BBitmap *OsBitmap;
#define LGetCurrentProcess getpid
class OsApplication
{
class OsApplicationPriv *d;
protected:
static OsApplication *Inst;
public:
OsApplication(int Args, const char **Arg);
~OsApplication();
static OsApplication *GetInst() { LAssert(Inst != NULL); return Inst; }
};
// Threads
typedef pthread_t OsThread;
typedef pid_t OsThreadId;
typedef pthread_mutex_t OsSemaphore;
-#define LGetCurrentThread() pthread_self()
+#define LGetCurrentThread() find_thread(NULL)
LgiFunc OsThreadId GetCurrentThreadId();
#include "lgi/common/Message.h"
// Sockets
#define ValidSocket(s) ((s)>=0)
#ifndef WIN32
#define INVALID_SOCKET -1
#endif
typedef int OsSocket;
/// Sleep the current thread for i milliseconds.
#ifdef WIN32
LgiFunc void LSleep(DWORD i);
#else
LgiFunc void LSleep(uint32_t i);
#endif
#ifndef WIN32
#define atoi64 atoll
#else
#define atoi64 _atoi64
#endif
#define _snprintf snprintf
#define _vsnprintf vsnprintf
#define wcscpy_s(dst, len, src) wcsncpy(dst, src, len)
/// Drag and drop format for a file
#define LGI_FileDropFormat "text/uri-list"
#define LGI_StreamDropFormat "application/x-file-stream" // FIXME
#define LGI_IllegalFileNameChars "\t\r\n/\\:*?\"<>|"
#ifdef WINDOWS
#define LGI_WideCharset "ucs-2"
#else
#define LGI_WideCharset "utf-32"
#endif
#ifdef _MSC_VER
#define _MSC_VER_VS2019 2000 // MSVC++ 16.0 (speculative)
#define _MSC_VER_VS2017 1910 // MSVC++ 15.0
#define _MSC_VER_VS2015 1900 // MSVC++ 14.0
#define _MSC_VER_VS2013 1800 // MSVC++ 12.0
#define _MSC_VER_VS2012 1700 // MSVC++ 11.0
#define _MSC_VER_VS2010 1600 // MSVC++ 10.0
#define _MSC_VER_VS2008 1500 // MSVC++ 9.0
#define _MSC_VER_VS2005 1400 // MSVC++ 8.0
#define _MSC_VER_VS2003 1310 // MSVC++ 7.1
#define _MSC_VER_VC7 1300 // MSVC++ 7.0
#define _MSC_VER_VC6 1200 // MSVC++ 6.0
#define _MSC_VER_VC5 1100 // MSVC++ 5.0
#if _MSC_VER >= _MSC_VER_VS2015
#define _MSC_VER_STR "14"
#elif _MSC_VER >= _MSC_VER_VS2013
#define _MSC_VER_STR "12"
#elif _MSC_VER >= _MSC_VER_VS2012
#define _MSC_VER_STR "11"
#elif _MSC_VER >= _MSC_VER_VS2010
#define _MSC_VER_STR "10"
#else
#define _MSC_VER_STR "9"
#endif
#define LPrintfInt64 "%I64i"
#define LPrintfHex64 "%I64x"
#if LGI_64BIT
#define LPrintfSizeT "%I64u"
#define LPrintfSSizeT "%I64d"
#else
#define LPrintfSizeT "%u"
#define LPrintfSSizeT "%d"
#endif
#else
#define LPrintfInt64 "%lld"
#define LPrintfHex64 "%llx"
#define LPrintfSizeT "%zu"
#define LPrintfSSizeT "%zi"
#endif
#ifndef SND_ASYNC
#define SND_ASYNC 0x0001
#endif
#define DOUBLE_CLICK_THRESHOLD 5
#define DOUBLE_CLICK_TIME 400
// Window flags
#define GWF_VISIBLE 0x00000001
#define GWF_DISABLED 0x00000002
#define GWF_FOCUS 0x00000004
#define GWF_OVER 0x00000008
#define GWF_DROP_TARGET 0x00000010
#define GWF_SUNKEN 0x00000020
#define GWF_FLAT 0x00000040
#define GWF_RAISED 0x00000080
#define GWF_BORDER 0x00000100
#define GWF_DIALOG 0x00000200
#define GWF_DESTRUCTOR 0x00000400
#define GWF_QUIT_WND 0x00000800
// Menu flags
#ifndef WIN32
#define ODS_SELECTED 0x1
#define ODS_DISABLED 0x2
#define ODS_CHECKED 0x4
#endif
/// Edge type: Sunken
#define SUNKEN 1
/// Edge type: Raised
#define RAISED 2
/// Edge type: Chiseled
#define CHISEL 3
/// Edge type: Flat
#define FLAT 4
#ifdef WIN32
/// The directory separator character on Linux as a char
#define DIR_CHAR '\\'
/// The directory separator character on Linux as a string
#define DIR_STR "\\"
/// The path list separator character for Linux
#define LGI_PATH_SEPARATOR ";"
/// The pattern that matches all files in Linux
#define LGI_ALL_FILES "*.*"
/// The stardard extension for dynamically linked code
#define LGI_LIBRARY_EXT "dll"
/// The standard executable extension
#define LGI_EXECUTABLE_EXT ".exe"
#else
/// The directory separator character on Linux as a char
#define DIR_CHAR '/'
/// The directory separator character on Linux as a string
#define DIR_STR "/"
/// The path list separator character for Linux
#define LGI_PATH_SEPARATOR ":"
/// The pattern that matches all files in Linux
#define LGI_ALL_FILES "*"
/// The stardard extension for dynamically linked code
#ifdef MAC
#define LGI_LIBRARY_EXT "dylib"
#else
#define LGI_LIBRARY_EXT "so"
#endif
/// The standard executable extension
#define LGI_EXECUTABLE_EXT ""
#endif
/// The standard end of line string for Linux
#define EOL_SEQUENCE "\n"
/// Tests a char for being a slash
#define IsSlash(c) (((c)=='/')||((c)=='\\'))
/// Tests a char for being a quote
#define IsQuote(c) (((c)=='\"')||((c)=='\''))
#ifndef WIN32
/// ID's returned by LgiMsg.
/// \sa LgiMsg
enum MessageBoxResponse
{
IDOK = 1,
IDCANCEL = 2,
IDABORT = 3,
IDRETRY = 4,
IDIGNORE = 5,
IDYES = 6,
IDNO = 7,
IDTRYAGAIN = 10,
IDCONTINUE = 11,
};
/// Standard message box types.
/// \sa LgiMsg
enum MessageBoxType
{
MB_OK = 0,
MB_OKCANCEL = 1,
MB_ABORTRETRYIGNORE = 2,
MB_YESNOCANCEL = 3,
MB_YESNO = 4,
MB_RETRYCANCEL = 5,
MB_CANCELTRYCONTINUE = 6
};
#define MB_SYSTEMMODAL 0x1000
#endif
/// The CTRL key is pressed
/// \sa LKey
#define LGI_VKEY_CTRL 0x001
/// The ALT key is pressed
/// \sa LKey
#define LGI_VKEY_ALT 0x002
/// The SHIFT key is pressed
/// \sa LKey
#define LGI_VKEY_SHIFT 0x004
/// The left mouse button is pressed
/// \sa LMouse
#define LGI_VMOUSE_LEFT 0x008
/// The middle mouse button is pressed
/// \sa LMouse
#define LGI_VMOUSE_MIDDLE 0x010
/// The right mouse button is pressed
/// \sa LMouse
#define LGI_VMOUSE_RIGHT 0x020
/// The ctrl key is pressed
/// \sa LMouse
#define LGI_VMOUSE_CTRL 0x040
/// The alt key is pressed
/// \sa LMouse
#define LGI_VMOUSE_ALT 0x080
/// The shift key is pressed
/// \sa LMouse
#define LGI_VMOUSE_SHIFT 0x100
/// The mouse event is a down click
/// \sa LMouse
#define LGI_VMOUSE_DOWN 0x200
/// The mouse event is a double click
/// \sa LMouse
#define LGI_VMOUSE_DOUBLE 0x400
enum LgiKey
{
/* The keyboard syms have been cleverly chosen to map to ASCII */
LK_UNKNOWN = 0,
LK_FIRST = 0,
LK_BACKSPACE = B_BACKSPACE,
LK_TAB = B_TAB,
LK_RETURN = B_RETURN,
LK_ESCAPE = B_ESCAPE,
LK_SPACE = B_SPACE,
LK_EXCLAIM = '!',
LK_QUOTEDBL = '\"',
LK_HASH = '#',
LK_DOLLAR = '$',
LK_AMPERSAND = '&',
LK_QUOTE = '\'',
LK_LEFTPAREN = '(',
LK_RIGHTPAREN = ')',
LK_ASTERISK = '*',
LK_PLUS = '+',
LK_COMMA = ',',
LK_MINUS = '-',
LK_PERIOD = '.',
LK_SLASH = '/',
LK_0 = '0',
LK_1 = '1',
LK_2 = '2',
LK_3 = '3',
LK_4 = '4',
LK_5 = '5',
LK_6 = '6',
LK_7 = '7',
LK_8 = '8',
LK_9 = '9',
LK_COLON = ':',
LK_SEMICOLON = ';',
LK_LESS = '<',
LK_EQUALS = '=',
LK_GREATER = '>',
LK_QUESTION = '?',
LK_AT = '@',
/*
Skip uppercase letters
*/
LK_LEFTBRACKET = '[',
LK_BACKSLASH = '\\',
LK_RIGHTBRACKET = ']',
LK_CARET = '^',
LK_UNDERSCORE = '_',
LK_BACKQUOTE = 96,
LK_a = 'a',
LK_b = 'b',
LK_c = 'c',
LK_d = 'd',
LK_e = 'e',
LK_f = 'f',
LK_g = 'g',
LK_h = 'h',
LK_i = 'i',
LK_j = 'j',
LK_k = 'k',
LK_l = 'l',
LK_m = 'm',
LK_n = 'n',
LK_o = 'o',
LK_p = 'p',
LK_q = 'q',
LK_r = 'r',
LK_s = 's',
LK_t = 't',
LK_u = 'u',
LK_v = 'v',
LK_w = 'w',
LK_x = 'x',
LK_y = 'y',
LK_z = 'z',
/* End of ASCII mapped keysyms */
/* Arrows + Home/End pad */
LK_HOME = B_HOME,
LK_LEFT = B_LEFT_ARROW,
LK_UP = B_UP_ARROW,
LK_RIGHT = B_RIGHT_ARROW,
LK_DOWN = B_DOWN_ARROW,
LK_PAGEUP = B_PAGE_UP,
LK_PAGEDOWN = B_PAGE_DOWN,
LK_END = B_END,
LK_INSERT = B_INSERT,
LK_DELETE = B_DELETE,
/* Numeric keypad */
LK_KEYPADENTER ,
LK_KP0 ,
LK_KP1 ,
LK_KP2 ,
LK_KP3 ,
LK_KP4 ,
LK_KP5 ,
LK_KP6 ,
LK_KP7 ,
LK_KP8 ,
LK_KP9 ,
LK_KP_PERIOD ,
LK_KP_DELETE ,
LK_KP_MULTIPLY ,
LK_KP_PLUS ,
LK_KP_MINUS ,
LK_KP_DIVIDE ,
LK_KP_EQUALS ,
/* Function keys */
LK_F1,
LK_F2 ,
LK_F3 ,
LK_F4 ,
LK_F5 ,
LK_F6 ,
LK_F7 ,
LK_F8 ,
LK_F9 ,
LK_F10 ,
LK_F11 ,
LK_F12 ,
LK_F13 ,
LK_F14 ,
LK_F15 ,
/* Key state modifier keys */
LK_NUMLOCK ,
LK_CAPSLOCK ,
LK_SCROLLOCK ,
LK_LSHIFT ,
LK_RSHIFT ,
LK_LCTRL ,
LK_RCTRL ,
LK_LALT ,
LK_RALT ,
LK_LMETA ,
LK_RMETA ,
LK_LSUPER , /* "Windows" key */
LK_RSUPER ,
/* Miscellaneous function keys */
LK_HELP ,
LK_PRINT ,
LK_SYSREQ ,
LK_BREAK ,
LK_MENU ,
LK_UNDO ,
LK_REDO ,
LK_EURO , /* Some european keyboards */
LK_COMPOSE , /* Multi-key compose key */
LK_MODE , /* "Alt Gr" key (could be wrong) */
LK_POWER , /* Power Macintosh power key */
LK_CONTEXTKEY ,
/* Add any other keys here */
LK_LAST
};
/////////////////////////////////////////////////////////////////////////////////////
// Externs
#define swprintf_s swprintf
#ifndef _MSC_VER
#define vsprintf_s vsnprintf
#endif
#ifndef WINNATIVE // __CYGWIN__
#ifdef _MSC_VER
#else
// LgiFunc char *strnistr(char *a, char *b, int n);
#define _strnicmp strncasecmp // LgiFunc int _strnicmp(char *a, char *b, int i);
LgiFunc char *strupr(char *a);
LgiFunc char *strlwr(char *a);
LgiFunc int stricmp(const char *a, const char *b);
#define _stricmp strcasecmp // LgiFunc int _stricmp(const char *a, const char *b);
#endif
#define sprintf_s snprintf
#if __BIG_ENDIAN__
#define htonll(x) (x)
#define ntohll(x) (x)
#else
#define htonll(x) ( ((uint64)htonl((x) & 0xFFFFFFFF) << 32) | htonl((x) >> 32) )
#define ntohll(x) ( ((uint64)ntohl((x) & 0xFFFFFFFF) << 32) | ntohl((x) >> 32) )
#endif
#else
LgiFunc class LViewI *GWindowFromHandle(OsView hWnd);
LgiFunc int GetMouseWheelLines();
LgiFunc int WinPointToHeight(int Pt, HDC hDC = NULL);
LgiFunc int WinHeightToPoint(int Ht, HDC hDC = NULL);
#if _MSC_VER >= 1400
#define snprintf sprintf_s
#endif
/// Convert a string d'n'd format to an OS dependant integer.
LgiFunc int FormatToInt(char *s);
/// Convert a Os dependant integer d'n'd format to a string.
LgiFunc char *FormatToStr(int f);
#endif
#endif
diff --git a/src/common/Gdc2/Font/DisplayString.cpp b/src/common/Gdc2/Font/DisplayString.cpp
--- a/src/common/Gdc2/Font/DisplayString.cpp
+++ b/src/common/Gdc2/Font/DisplayString.cpp
@@ -1,2275 +1,2269 @@
//////////////////////////////////////////////////////////////////////
// Includes
#include
#include
#include
#include
#include "lgi/common/Lgi.h"
#include "lgi/common/Variant.h"
#include "lgi/common/FontSelect.h"
#include "lgi/common/GdiLeak.h"
#include "lgi/common/DisplayString.h"
#include "lgi/common/PixelRops.h"
#include "lgi/common/UnicodeString.h"
#ifdef FontChange
#undef FontChange
#endif
#ifdef LGI_SDL
#include "ftsynth.h"
#endif
#if WINNATIVE
static OsChar GDisplayStringDots[] = {'.', '.', '.', 0};
#endif
//345678123456781234567812345678
// 2nd
#define DEBUG_CHAR_AT 0
#define DEBUG_BOUNDS 0
#if defined(__GTK_H__) || (defined(MAC) && !defined(LGI_SDL))
#define DISPLAY_STRING_FRACTIONAL_NATIVE 1
#else
#define DISPLAY_STRING_FRACTIONAL_NATIVE 0
#endif
#if defined(__GTK_H__)
struct Block : public LRect
{
/// This points to somewhere in Ds->Str
OsChar *Str = NULL;
/// Bytes in this block
int Bytes = 0;
/// Utf-8 characters in this block
int Chars = 0;
/// Alternative font to get characters from (NULL if using the display string's font)
LFont *Fnt = NULL;
/// Layout for this block. Shouldn't ever be NULL. But shouldn't crash otherwise.
Gtk::PangoLayout *Hnd = NULL;
~Block()
{
if (Hnd)
g_object_unref(Hnd);
}
};
struct GDisplayStringPriv
{
LDisplayString *Ds;
LArray Blocks;
bool Debug;
int LastTabOffset;
GDisplayStringPriv(LDisplayString *str) : Ds(str)
{
#if 0
Debug = Stristr(Ds->Str, "(Jumping).wma") != 0;
#else
Debug = false;
#endif
LastTabOffset = -1;
}
~GDisplayStringPriv()
{
}
void Create(Gtk::GtkPrintContext *PrintCtx)
{
auto *Fs = LFontSystem::Inst();
auto *Fnt = Ds->Font;
auto Tbl = Fnt->GetGlyphMap();
int Chars = 0;
LUtf8Ptr p(Ds->Str);
auto *Start = p.GetPtr();
if (Tbl)
{
int32 w;
Block *b = NULL;
auto DisplayCtx = LFontSystem::Inst()->GetContext();
while ((w = (int32)p))
{
LFont *f;
if (w >= 0x80 && !_HasUnicodeGlyph(Tbl, w))
f = Fs->GetGlyph(w, Ds->Font);
else
f = Ds->Font;
if (!b || (f != NULL && f != Fnt))
{
// Finish old block
if (b)
{
b->Bytes = p.GetPtr() - Start;
b->Chars = Chars;
Chars = 0;
}
Start = p.GetPtr();
if (f)
{
// Start new block...
b = &Blocks.New();
b->Str = (char*)Start;
b->Bytes = -1; // unknown at this point
if (f != Ds->Font)
// External font
b->Fnt = f;
// Create a pango layout
if (PrintCtx)
b->Hnd = Gtk::gtk_print_context_create_pango_layout(PrintCtx);
else
b->Hnd = Gtk::pango_layout_new(DisplayCtx);
}
// else no font supports glyph
Fnt = f;
}
// else no change in font
p++;
Chars++;
}
if (b)
{
// Finish the last block
b->Bytes = p.GetPtr() - Start;
b->Chars = Chars;
}
if (Debug)
{
// Print the block array
for (size_t i=0; iStrWords)
{
p++;
Chars++;
}
auto &b = Blocks.New();
b.Str = (char*)Start;
b.Bytes = p.GetPtr() - Start;
b.Chars = Chars;
if (PrintCtx)
b.Hnd = Gtk::gtk_print_context_create_pango_layout(PrintCtx);
else
b.Hnd = Gtk::pango_layout_new(LFontSystem::Inst()->GetContext());
}
/* This could get stuck in an infinite loop. Leaving out for the moment.
for (auto &b: Blocks)
{
if (b.Hnd == NULL && b.Fnt == NULL)
{
Blocks.Length(0);
goto Start;
}
}
*/
}
void UpdateTabs(int Offset, int Size, bool Debug = false)
{
if (Ds->Font &&
Ds->Font->TabSize())
{
int Len = 16;
LastTabOffset = Offset;
Gtk::PangoTabArray *t = Gtk::pango_tab_array_new(Len, true);
if (t)
{
for (int i=0; i
bool StringConvert(Out *&out, ssize_t &OutWords, const In *in, ssize_t InLen)
{
if (!in)
{
out = 0;
OutWords = 0;
return false;
}
auto InSz = sizeof(In);
auto OutSz = sizeof(Out);
// Work out input size
ssize_t InWords;
if (InLen >= 0)
InWords = InLen;
else
for (InWords = 0; in[InWords]; InWords++)
;
if (InSz == OutSz)
{
// No conversion optimization
out = (Out*)Strdup(in, InWords);
OutWords = out ? InWords : 0;
return out != 0;
}
else
{
// Convert the string to new word size
static const char *Cp[] = { NULL, "utf-8", "utf-16", NULL, "utf-32"};
LAssert(OutSz <= 4 && InSz <= 4 && Cp[OutSz] && Cp[InSz]);
out = (Out*) LNewConvertCp(Cp[OutSz], in, Cp[InSz], InWords*sizeof(In));
OutWords = Strlen(out);
return out != NULL;
}
return false;
}
//////////////////////////////////////////////////////////////////////
#define SubtractPtr(a, b) ( ((a)-(b)) / sizeof(*a) )
#define VisibleTabChar 0x2192
#define IsTabChar(c) (c == '\t') // || (c == VisibleTabChar && VisibleTab))
#if USE_CORETEXT
#include
void LDisplayString::CreateAttrStr()
{
if (!Wide)
return;
if (AttrStr)
{
CFRelease(AttrStr);
AttrStr = NULL;
}
wchar_t *w = Wide;
CFStringRef string = CFStringCreateWithBytes(kCFAllocatorDefault, (const uint8_t*)w, StrlenW(w) * sizeof(*w), kCFStringEncodingUTF32LE, false);
if (string)
{
CFDictionaryRef attributes = Font->GetAttributes();
if (attributes)
AttrStr = CFAttributedStringCreate(kCFAllocatorDefault, string, attributes);
// else LAssert(0);
CFRelease(string);
}
}
#endif
LDisplayString::LDisplayString(LFont *f, const char *s, ssize_t l, LSurface *pdc)
{
pDC = pdc;
Font = f;
#if LGI_DSP_STR_CACHE
StringConvert(Wide, WideWords, s, l);
#endif
StringConvert(Str, StrWords, s, l);
x = y = 0;
xf = 0;
yf = 0;
DrawOffsetF = 0;
LaidOut = 0;
AppendDots = 0;
VisibleTab = 0;
#if defined __GTK_H__
d = new GDisplayStringPriv(this);
if (Font && Str)
{
LAssert(StrWords >= 0);
if (StrWords > 0)
d->Create(pDC ? pDC->GetPrintContext() : NULL);
}
#elif defined MAC && !defined(LGI_SDL)
Hnd = 0;
#if USE_CORETEXT
AttrStr = NULL;
#endif
if (Font && Str && StrWords > 0)
{
#if USE_CORETEXT
CreateAttrStr();
#else
ATSUCreateTextLayout(&Hnd);
#endif
}
#endif
}
LDisplayString::LDisplayString(LFont *f, const char16 *s, ssize_t l, LSurface *pdc)
{
pDC = pdc;
Font = f;
#if LGI_DSP_STR_CACHE
StringConvert(Wide, WideWords, s, l);
#endif
StringConvert(Str, StrWords, s, l);
x = y = 0;
xf = 0;
yf = 0;
DrawOffsetF = 0;
LaidOut = 0;
AppendDots = 0;
VisibleTab = 0;
#if defined __GTK_H__
d = new GDisplayStringPriv(this);
if (Font && Str && StrWords > 0)
d->Create(pDC ? pDC->GetPrintContext() : NULL);
#elif defined MAC && !defined(LGI_SDL)
Hnd = NULL;
#if USE_CORETEXT
AttrStr = NULL;
#endif
if (Font && Str && StrWords > 0)
{
#if USE_CORETEXT
CreateAttrStr();
#else
OSStatus e = ATSUCreateTextLayout(&Hnd);
if (e) printf("%s:%i - ATSUCreateTextLayout failed with %i.\n", _FL, (int)e);
#endif
}
#endif
}
#ifdef _MSC_VER
LDisplayString::LDisplayString(LFont *f, const uint32_t *s, ssize_t l, LSurface *pdc)
{
pDC = pdc;
Font = f;
x = y = 0;
xf = 0;
yf = 0;
DrawOffsetF = 0;
LaidOut = 0;
AppendDots = 0;
VisibleTab = 0;
#if LGI_DSP_STR_CACHE
StringConvert(Wide, WideWords, s, l);
#endif
StringConvert(Str, StrWords, s, l);
#if defined __GTK_H__
d = new GDisplayStringPriv(this);
if (Font && Str && StrWords > 0)
d->Create(pDC ? pDC->GetPrintContext() : NULL);
#endif
}
#endif
LDisplayString::~LDisplayString()
{
#if defined(LGI_SDL)
Img.Reset();
#elif defined __GTK_H__
DeleteObj(d);
#elif defined MAC
#if USE_CORETEXT
if (Hnd)
{
CFRelease(Hnd);
Hnd = NULL;
}
if (AttrStr)
{
CFRelease(AttrStr);
AttrStr = NULL;
}
#else
if (Hnd)
ATSUDisposeTextLayout(Hnd);
#endif
#endif
DeleteArray(Str);
#if LGI_DSP_STR_CACHE
DeleteArray(Wide);
#endif
}
void LDisplayString::DrawWhiteSpace(LSurface *pDC, char Ch, LRect &r)
{
if (Ch == '\t')
{
r.Inset(3, 3);
if (r.Y()/2 == 0)
r.y2++;
int Cy = (r.Y() >> 1);
pDC->Line(r.x1, r.y1+Cy, r.x2, r.y1+Cy);
pDC->Line(r.x2, r.y1+Cy, r.x2-Cy, r.y1);
pDC->Line(r.x2, r.y1+Cy, r.x2-Cy, r.y2);
}
else // Space
{
int x = r.x1 + (r.X()>>1) - 1;
int Cy = r.y1 + (int)ceil(Font->Ascent()) - 2;
pDC->Rectangle(x, Cy, x+1, Cy+1);
}
}
void LDisplayString::Layout(bool Debug)
{
if (LaidOut || !Font)
return;
LaidOut = 1;
#if defined(LGI_SDL)
FT_Face Fnt = Font->Handle();
FT_Error error;
if (!Fnt || !Str)
return;
// Create an array of glyph indexes
LArray Glyphs;
for (OsChar *s = Str; *s; s++)
{
FT_UInt index = FT_Get_Char_Index(Fnt, *s);
if (index)
Glyphs.Add(index);
}
// Measure the string...
LPoint Sz;
int FontHt = Font->GetHeight();
int AscentF = (int) (Font->Ascent() * FScale);
int LoadMode = FT_LOAD_FORCE_AUTOHINT;
for (unsigned i=0; iglyph->metrics.horiBearingY;
Sz.x += Fnt->glyph->metrics.horiAdvance;
Sz.y = MAX(Sz.y, PyF + Fnt->glyph->metrics.height);
}
}
// Create the memory context to draw into
xf = Sz.x;
x = ((Sz.x + FScale - 1) >> FShift) + 1;
yf = FontHt << FShift;
y = FontHt; // ((Sz.y + FScale - 1) >> FShift) + 1;
if (Img.Reset(new LMemDC(x, y, CsIndex8)))
{
// Clear the context to black
Img->Colour(0);
Img->Rectangle();
bool IsItalic = Font->Italic();
int CurX = 0;
int FBaseline = Fnt->size->metrics.ascender;
for (unsigned i=0; iglyph);
error = FT_Render_Glyph(Fnt->glyph, FT_RENDER_MODE_NORMAL);
if (error == 0)
{
FT_Bitmap &bmp = Fnt->glyph->bitmap;
if (bmp.buffer)
{
// Copy rendered glyph into our image memory
int Px = (CurX + (FScale >> 1)) >> FShift;
int PyF = AscentF - Fnt->glyph->metrics.horiBearingY;
int Py = PyF >> FShift;
int Skip = 0;
if (Fnt->glyph->format == FT_GLYPH_FORMAT_BITMAP)
{
Px += Fnt->glyph->bitmap_left;
Skip = Fnt->glyph->bitmap_left < 0 ? -Fnt->glyph->bitmap_left : 0;
Py = (AscentF >> FShift) - Fnt->glyph->bitmap_top;
}
LAssert(Px + bmp.width <= Img->X());
for (int y=0; y= 0);
out += Px+Skip;
memcpy(out, in+Skip, bmp.width-Skip);
}
/*
else
{
LAssert(!"No scanline?");
break;
}
*/
}
}
if (i < Glyphs.Length() - 1)
{
FT_Vector kerning;
FT_Get_Kerning(Fnt, Glyphs[i], Glyphs[i+1], FT_KERNING_DEFAULT, &kerning);
CurX += Fnt->glyph->metrics.horiAdvance + kerning.x;
}
else
{
CurX += Fnt->glyph->metrics.horiAdvance;
}
}
}
}
}
else LgiTrace("::Layout Create MemDC failed\n");
#elif defined(__GTK_H__)
y = Font->GetHeight();
yf = y * PANGO_SCALE;
if (!d->Blocks.Length() || !Font->Handle())
return;
LUtf8Ptr Utf(Str);
int32 Wide;
while (*Utf.GetPtr())
{
Wide = Utf;
if (!Wide)
{
LgiTrace("%s:%i - Not utf8\n", _FL);
return;
}
Utf++;
}
LFontSystem *FSys = LFontSystem::Inst();
Gtk::pango_context_set_font_description(FSys->GetContext(), Font->Handle());
int TabSizePx = Font->TabSize();
int TabSizeF = TabSizePx * FScale;
int TabOffsetF = DrawOffsetF % TabSizeF;
int OffsetF = TabOffsetF ? TabSizeF - TabOffsetF : 0;
d->UpdateTabs(OffsetF / FScale, Font->TabSize());
if (Font->Underline())
{
Gtk::PangoAttrList *attrs = Gtk::pango_attr_list_new();
Gtk::PangoAttribute *attr = Gtk::pango_attr_underline_new(Gtk::PANGO_UNDERLINE_SINGLE);
Gtk::pango_attr_list_insert(attrs, attr);
for (auto &b: d->Blocks)
Gtk::pango_layout_set_attributes(b.Hnd, attrs);
Gtk::pango_attr_list_unref(attrs);
}
int Fx = 0;
for (auto &b: d->Blocks)
{
int bx = 0, by = 0;
if (b.Hnd)
{
if (!LIsUtf8(b.Str, b.Bytes))
{
LgiTrace("Invalid UTF8: '%.*S'\n", (int)b.Bytes, b.Str);
}
else
{
Gtk::pango_layout_set_text(b.Hnd, b.Str, b.Bytes);
}
Gtk::pango_layout_get_size(b.Hnd, &bx, &by);
}
else if (b.Fnt)
{
b.Fnt->_Measure(bx, by, b.Str, b.Bytes);
bx <<= FShift;
by <<= FShift;
}
b.ZOff(bx-1, by-1);
b.Offset(Fx, 0);
xf += bx;
yf = MAX(yf, by);
}
x = (xf + PANGO_SCALE - 1) / PANGO_SCALE;
#if 1
y = Font->GetHeight();
#else
y = (yf + PANGO_SCALE - 1) / PANGO_SCALE;
#endif
if (y > Font->GetHeight())
{
printf("%s:%i - Height error: %i > %i\n", _FL, y, Font->GetHeight());
}
#elif defined MAC && !defined(LGI_SDL)
#if USE_CORETEXT
int height = Font->GetHeight();
y = height;
if (AttrStr)
{
LAssert(!Hnd);
Hnd = CTLineCreateWithAttributedString(AttrStr);
if (Hnd)
{
CGFloat ascent = 0.0;
CGFloat descent = 0.0;
CGFloat leading = 0.0;
double width = CTLineGetTypographicBounds(Hnd, &ascent, &descent, &leading);
x = ceil(width);
xf = width * FScale;
yf = height * FScale;
}
}
#else
if (!Hnd || !Str)
return;
OSStatus e = ATSUSetTextPointerLocation(Hnd, Str, 0, len, len);
if (e)
{
char *a = 0;
StringConvert(a, NULL, Str, len);
printf("%s:%i - ATSUSetTextPointerLocation failed with errorcode %i (%s)\n", _FL, (int)e, a);
DeleteArray(a);
return;
}
e = ATSUSetRunStyle(Hnd, Font->Handle(), 0, len);
if (e)
{
char *a = 0;
StringConvert(a, NULL, Str, len);
printf("%s:%i - ATSUSetRunStyle failed with errorcode %i (%s)\n", _FL, (int)e, a);
DeleteArray(a);
return;
}
ATSUTextMeasurement fTextBefore;
ATSUTextMeasurement fTextAfter;
if (pDC)
{
OsPainter dc = pDC->Handle();
ATSUAttributeTag Tags[1] = {kATSUCGContextTag};
ByteCount Sizes[1] = {sizeof(CGContextRef)};
ATSUAttributeValuePtr Values[1] = {&dc};
e = ATSUSetLayoutControls(Hnd, 1, Tags, Sizes, Values);
if (e) printf("%s:%i - ATSUSetLayoutControls failed (e=%i)\n", _FL, (int)e);
}
ATSUTab Tabs[32];
for (int i=0; iTabSize()) << 16;
Tabs[i].tabType = kATSULeftTab;
}
e = ATSUSetTabArray(Hnd, Tabs, CountOf(Tabs));
if (e) printf("%s:%i - ATSUSetTabArray failed (e=%i)\n", _FL, (int)e);
e = ATSUGetUnjustifiedBounds( Hnd,
kATSUFromTextBeginning,
kATSUToTextEnd,
&fTextBefore,
&fTextAfter,
&fAscent,
&fDescent);
if (e)
{
char *a = 0;
StringConvert(a, NULL, Str, len);
printf("%s:%i - ATSUGetUnjustifiedBounds failed with errorcode %i (%s)\n", _FL, (int)e, a);
DeleteArray(a);
return;
}
xf = fTextAfter - fTextBefore;
yf = fAscent + fDescent;
x = (xf + 0xffff) >> 16;
y = (yf + 0xffff) >> 16;
ATSUSetTransientFontMatching(Hnd, true);
#endif
#elif defined(HAIKU)
if (!Font)
{
LgiTrace("%s:%i - Missing pointer: %p\n", _FL, Font);
return;
}
BFont *fnt = Font->Handle();
if (!fnt)
{
LgiTrace("%s:%i - Missing handle. %p/%p\n", _FL, fnt);
return;
}
int tabSize = Font->TabSize() ? Font->TabSize() : 32;
font_height height = {0};
fnt->GetHeight(&height);
yf = y = height.ascent + height.descent + height.leading;
if (!Str)
return;
LUtf8Ptr start(Str);
int isTab = -1;
for (LUtf8Ptr p(Str); true; p++)
{
int32_t ch = p;
if (isTab < 0)
{
isTab = IsTabChar(ch);
}
else if (!ch || IsTabChar(ch) ^ isTab)
{
auto &l = Info.New();
l.Str = start.GetPtr();
l.Len = p.GetPtr() - start.GetPtr();
const char *strArr[] = { l.Str };
const int32 strLen[] = { l.Len };
float width[1] = { 0 };
fnt->GetStringWidths(strArr, strLen, 1, width);
if (isTab)
{
// Handle tab(s)
for (int t=0; tHandle())
Font->Create();
y = Font->GetHeight();
LFontSystem *Sys = LFontSystem::Inst();
if (Sys && Str)
{
LFont *f = Font;
bool GlyphSub = Font->SubGlyphs();
Info[i].Str = Str;
int TabSize = Font->TabSize() ? Font->TabSize() : 32;
bool WasTab = IsTabChar(*Str);
f = GlyphSub ? Sys->GetGlyph(*Str, Font) : Font;
if (f && f != Font)
{
f->Size(Font->Size());
f->SetWeight(Font->GetWeight());
if (!f->Handle())
f->Create();
}
bool Debug = WasTab;
uint32_t u32;
for (LUnicodeString u(Str, StrWords); true; u++)
{
u32 = *u;
LFont *n = GlyphSub ? Sys->GetGlyph(u32, Font) : Font;
bool Change = n != f || // The font changed
(IsTabChar(u32) ^ WasTab) || // Entering/leaving a run of tabs
!u32 || // Hit a NULL character
(u.Get() - Info[i].Str) >= 1000; // This is to stop very long segments not rendering
if (Change)
{
// End last segment
if (n && n != Font)
{
n->Size(Font->Size());
n->SetWeight(Font->GetWeight());
if (!n->Handle())
n->Create();
}
Info[i].Len = (int) (u.Get() - Info[i].Str);
if (Info[i].Len)
{
if (WasTab)
{
// Handle tab(s)
for (int t=0; tGetHeight() > Font->GetHeight()))
{
Info[i].SizeDelta = -1;
f->PointSize(Font->PointSize() + Info[i].SizeDelta);
f->Create();
}
#endif
if (!f)
{
// no font, so ? out the chars... as they aren't available anyway
// printf("Font Cache Miss, Len=%i\n\t", Info[i].Len);
m = Font;
for (int n=0; n_Measure(sx, sy, Info[i].Str, Info[i].Len);
x += Info[i].X = sx > 0xffff ? 0xffff : sx;
}
auto Ch = Info[i].First();
Info[i].FontId = !f || Font == f ? 0 : Sys->Lut[Ch];
i++;
}
f = n;
// Start next segment
WasTab = IsTabChar(u32);
Info[i].Str = u.Get();
}
if (!u32) break;
}
if (Info.Length() > 0 && Info.Last().Len == 0)
{
Info.Length(Info.Length()-1);
}
}
xf = x;
yf = y;
#endif
}
int LDisplayString::GetDrawOffset()
{
return DrawOffsetF >> FShift;
}
void LDisplayString::SetDrawOffset(int Px)
{
if (LaidOut)
LAssert(!"No point setting TabOrigin after string is laid out.\n");
DrawOffsetF = Px << FShift;
}
bool LDisplayString::ShowVisibleTab()
{
return VisibleTab;
}
void LDisplayString::ShowVisibleTab(bool i)
{
VisibleTab = i;
}
bool LDisplayString::IsTruncated()
{
return AppendDots;
}
void LDisplayString::TruncateWithDots(int Width)
{
Layout();
#if defined __GTK_H__
int Fx = 0;
int Fwid = Width << FShift;
for (auto &b: d->Blocks)
{
if (Fwid < Fx + b.X())
{
if (b.Hnd)
{
Gtk::pango_layout_set_ellipsize(b.Hnd, Gtk::PANGO_ELLIPSIZE_END);
Gtk::pango_layout_set_width(b.Hnd, Fwid - Fx);
Gtk::pango_layout_set_single_paragraph_mode(b.Hnd, true);
}
else if (b.Fnt)
{
}
break;
}
Fx += b.X();
}
#elif WINNATIVE
if (Width < X() + 8)
{
ssize_t c = CharAt(Width);
if (c >= 0 && c < StrWords)
{
if (c > 0) c--; // fudge room for dots
if (c > 0) c--;
AppendDots = 1;
if (Info.Length())
{
int Width = 0;
int Pos = 0;
for (int i=0; i= Pos &&
c < Pos + Info[i].Len)
{
Info[i].Len = (int) (c - Pos);
Info[i].Str[Info[i].Len] = 0;
LFont *f = Font;
if (Info[i].FontId)
{
LFontSystem *Sys = LFontSystem::Inst();
f = Sys->Font[Info[i].FontId];
f->PointSize(Font->PointSize() + Info[i].SizeDelta);
if (!f->Handle())
{
f->Create();
}
}
else
{
f = Font;
}
if (f)
{
int Sx, Sy;
f->_Measure(Sx, Sy, Info[i].Str, Info[i].Len);
Info[i].X = Sx;
Width += Info[i].X;
}
Info.Length(i + 1);
break;
}
Pos += Info[i].Len;
Width += Info[i].X;
}
int DotsX, DotsY;
Font->_Measure(DotsX, DotsY, GDisplayStringDots, 3);
x = Width + DotsX;
}
}
}
#elif defined(LGI_SDL)
#elif defined(MAC)
#if USE_CORETEXT
if (Hnd)
{
/*
CFAttributedStringRef truncationString = CFAttributedStringCreate(NULL, CFSTR("\u2026"), Font->GetAttributes());
if (truncationString)
{
CTLineRef truncationToken = CTLineCreateWithAttributedString(truncationString);
CFRelease(truncationString);
if (truncationToken)
{
CTLineRef TruncatedLine = CTLineCreateTruncatedLine(Hnd, Width, kCTLineTruncationEnd, truncationToken);
CFRelease(truncationToken);
if (TruncatedLine)
{
CFRelease(Hnd);
Hnd = TruncatedLine;
}
}
}
*/
}
#endif
#endif
}
ssize_t LDisplayString::CharAt(int Px, LPxToIndexType Type)
{
Layout();
if (Px < 0)
{
return 0;
}
else if (Px >= (int)x)
{
#if defined __GTK_H__
if (Str)
{
LUtf8Str u(Str);
return u.GetChars();
}
return 0;
#else
#if LGI_DSP_STR_CACHE
return WideWords;
#else
return StrWords;
#endif
#endif
}
int Status = -1;
#if defined(__GTK_H__)
int Fx = 0;
int Fpos = Px << FShift;
Status = 0;
for (auto &b: d->Blocks)
{
int Index = 0, Trailing = 0;
int Foffset = Fpos - Fx;
if (b.Hnd && Gtk::pango_layout_xy_to_index(b.Hnd, Foffset, 0, &Index, &Trailing))
{
if (d->Debug)
printf("CharAt(%g) x=%g Status=%i Foffset=%g index=%i trailing=%i\n",
(double)Fpos/FScale, (double)b.X()/FScale, Status,
(double)Foffset/FScale, Index, Trailing);
LUtf8Str u(Str);
while ((OsChar*)u.GetPtr() < Str + Index + Trailing)
{
u++;
Status++;
}
return Status;
}
else
{
if (d->Debug)
printf("CharAt(%g) x=%g Status=%i Chars=%i\n",
(double)Fpos/FScale, (double)b.X()/FScale, Status, b.Chars);
Status += b.Chars;
}
Fx += b.X();
}
#elif defined(LGI_SDL)
LAssert(!"Impl me");
#elif defined(MAC)
if (Hnd && Str)
{
#if USE_CORETEXT
CGPoint pos = { (CGFloat)Px, 1.0f };
CFIndex utf16 = CTLineGetStringIndexForPosition(Hnd, pos);
// 'utf16' is in UTF-16, and the API needs to return a UTF-32 index...
// So convert between the 2 here...
LAssert(Str != NULL);
int utf32 = 0;
for (int i=0; Str[i] && iTabSize() ? Font->TabSize() : 32;
int Cx = 0;
int Char = 0;
#if DEBUG_CHAR_AT
printf("CharAt(%i) Str='%s'\n", Px, Str);
#endif
for (int i=0; i= Cx && Px < Cx + Info[i].X)
{
// The position is in this block of characters
if (IsTabChar(Info[i].Str[0]))
{
// Search through tab block
for (int t=0; t= Cx && Px < Cx + TabX)
{
Status = Char;
#if DEBUG_CHAR_AT
printf("\tIn tab block %i\n", i);
#endif
break;
}
Cx += TabX;
Char++;
}
}
else
{
// Find the pos in this block
LFont *f = Font;
#if defined(WIN32)
if (Info[i].FontId)
{
f = Sys->Font[Info[i].FontId];
f->Colour(Font->Fore(), Font->Back());
f->Size(Font->Size());
if (!f->Handle())
{
f->Create();
}
}
#endif
int Fit = f->_CharAt(Px - Cx, Info[i].Str, Info[i].Len, Type);
#if DEBUG_CHAR_AT
printf("\tNon tab block %i, Fit=%i, Px-Cx=%i-%i=%i, Str='%.5s'\n",
i, Fit, Px, Cx, Px-Cx, Info[i].Str);
#endif
if (Fit >= 0)
Status = Char + Fit;
else
Status = -1;
break;
}
}
else
{
// Not in this block, skip the whole lot
Cx += Info[i].X;
Char += Info[i].Len;
}
}
if (Status < 0)
{
Status = Char;
}
}
#endif
return Status;
}
ssize_t LDisplayString::Length()
{
return StrWords;
}
int LDisplayString::X()
{
Layout();
return x;
}
int LDisplayString::Y()
{
Layout();
return y;
}
LPoint LDisplayString::Size()
{
Layout();
return LPoint(x, y);
}
#if defined LGI_SDL
template
bool CompositeText8Alpha(LSurface *Out, LSurface *In, LFont *Font, int px, int py, LBlitRegions &Clip)
{
OutPx map[256];
if (!Out || !In || !Font)
return false;
// FIXME, do blt clipping here...
// Create colour map of the foreground/background colours
uint8_t *Div255 = Div255Lut;
LColour fore = Font->Fore();
LRgb24 fore_px;
fore_px.r = fore.r();
fore_px.g = fore.g();
fore_px.b = fore.b();
if (Font->Transparent())
{
for (int a=0; a<256; a++)
{
map[a].r = Div255[a * fore_px.r];
map[a].g = Div255[a * fore_px.g];
map[a].b = Div255[a * fore_px.b];
map[a].a = a;
}
}
else
{
LColour back = Font->Back();
LRgb24 back_px;
back_px.r = back.r();
back_px.g = back.g();
back_px.b = back.b();
for (int a=0; a<256; a++)
{
int oma = 255 - a;
map[a].r = (oma * back_px.r) + (a * fore_px.r) / 255;
map[a].g = (oma * back_px.g) + (a * fore_px.g) / 255;
map[a].b = (oma * back_px.b) + (a * fore_px.b) / 255;
map[a].a = 255;
}
}
uint8_t *StartOfBuffer = (*Out)[0];
uint8_t *EndOfBuffer = StartOfBuffer + (Out->GetRowStep() * Out->Y());
for (unsigned y=Clip.SrcClip.y1; y<=Clip.SrcClip.y2; y++)
{
OutPx *d = ((OutPx*) (*Out)[py + y]) + Clip.DstClip.x1;
uint8_t *i = (*In)[y];
if (!i) return false;
uint8_t *e = i + Clip.DstClip.X();
LAssert((uint8_t*)d >= StartOfBuffer);
if (Font->Transparent())
{
uint8_t a, o;
OutPx *s;
while (i < e)
{
// Alpha blend map and output pixel
a = *i++;
switch (a)
{
case 0:
break;
case 255:
// Copy
*d = map[a];
break;
default:
// Blend
o = 255 - a;
s = map + a;
#define NonPreMulOver32NoAlpha(c) d->c = ((s->c * a) + (Div255[d->c * 255] * o)) / 255
NonPreMulOver32NoAlpha(r);
NonPreMulOver32NoAlpha(g);
NonPreMulOver32NoAlpha(b);
break;
}
d++;
}
}
else
{
while (i < e)
{
// Copy rop
*d++ = map[*i++];
}
}
LAssert((uint8_t*)d <= EndOfBuffer);
}
return true;
}
template
bool CompositeText8NoAlpha(LSurface *Out, LSurface *In, LFont *Font, int px, int py, LBlitRegions &Clip)
{
LRgba32 map[256];
if (!Out || !In || !Font)
return false;
// FIXME, do blt clipping here...
// Create colour map of the foreground/background colours
uint8_t *DivLut = Div255Lut;
LColour fore = Font->Fore();
LRgb24 fore_px;
fore_px.r = fore.r();
fore_px.g = fore.g();
fore_px.b = fore.b();
if (Font->Transparent())
{
for (int a=0; a<256; a++)
{
map[a].r = DivLut[a * fore_px.r];
map[a].g = DivLut[a * fore_px.g];
map[a].b = DivLut[a * fore_px.b];
map[a].a = a;
}
}
else
{
LColour back = Font->Back();
LRgb24 back_px;
back_px.r = back.r();
back_px.g = back.g();
back_px.b = back.b();
for (int a=0; a<256; a++)
{
int oma = 255 - a;
map[a].r = DivLut[(oma * back_px.r) + (a * fore_px.r)];
map[a].g = DivLut[(oma * back_px.g) + (a * fore_px.g)];
map[a].b = DivLut[(oma * back_px.b) + (a * fore_px.b)];
map[a].a = 255;
}
}
uint8_t *StartOfBuffer = (*Out)[0];
uint8_t *EndOfBuffer = StartOfBuffer + (Out->GetRowStep() * Out->Y());
for (int y=Clip.SrcClip.y1; y<=Clip.SrcClip.y2; y++)
{
OutPx *dst = (OutPx*) (*Out)[py + y];
if (!dst)
continue;
dst += Clip.DstClip.x1;
if ((uint8_t*)dst < StartOfBuffer)
continue;
uint8_t *i = (*In)[y];
if (!i) return false;
uint8_t *e = i + Clip.DstClip.X();
LRgba32 *src;
LAssert((uint8_t*)dst >= StartOfBuffer);
if (Font->Transparent())
{
uint8_t a, oma;
while (i < e)
{
// Alpha blend map and output pixel
a = *i++;
src = map + a;
switch (a)
{
case 0:
break;
case 255:
// Copy
dst->r = src->r;
dst->g = src->g;
dst->b = src->b;
break;
default:
// Blend
OverPm32toPm24(src, dst);
break;
}
dst++;
}
}
else
{
while (i < e)
{
// Copy rop
src = map + *i++;
dst->r = src->r;
dst->g = src->g;
dst->b = src->b;
dst++;
}
}
LAssert((uint8_t*)dst <= EndOfBuffer);
}
return true;
}
template
bool CompositeText5NoAlpha(LSurface *Out, LSurface *In, LFont *Font, int px, int py, LBlitRegions &Clip)
{
OutPx map[256];
if (!Out || !In || !Font)
return false;
// FIXME, do blt clipping here...
#define MASK_5BIT 0x1f
#define MASK_6BIT 0x3f
// Create colour map of the foreground/background colours
uint8_t *Div255 = Div255Lut;
LColour fore = Font->Fore();
LRgb24 fore_px;
fore_px.r = fore.r();
fore_px.g = fore.g();
fore_px.b = fore.b();
if (Font->Transparent())
{
for (int a=0; a<256; a++)
{
map[a].r = (int)Div255[a * fore_px.r] >> 3;
map[a].g = (int)Div255[a * fore_px.g] >> 2;
map[a].b = (int)Div255[a * fore_px.b] >> 3;
}
}
else
{
LColour back = Font->Back();
LRgb24 back_px;
back_px.r = back.r();
back_px.g = back.g();
back_px.b = back.b();
for (int a=0; a<256; a++)
{
int oma = 255 - a;
map[a].r = Div255[(oma * back_px.r) + (a * fore_px.r)] >> 3;
map[a].g = Div255[(oma * back_px.g) + (a * fore_px.g)] >> 2;
map[a].b = Div255[(oma * back_px.b) + (a * fore_px.b)] >> 3;
}
}
uint8_t *StartOfBuffer = (*Out)[0];
uint8_t *EndOfBuffer = StartOfBuffer + (Out->GetRowStep() * Out->Y());
for (unsigned y=Clip.SrcClip.y1; y<=Clip.SrcClip.y2; y++)
{
OutPx *dst = ((OutPx*) (*Out)[py + y]);
if (!dst)
continue;
dst += Clip.DstClip.x1;
uint8_t *i = (*In)[y];
if (!i) return false;
uint8_t *e = i + Clip.DstClip.X();
LAssert((uint8_t*)dst >= StartOfBuffer);
if (Font->Transparent())
{
uint8_t a;
OutPx *src;
while (i < e)
{
// Alpha blend map and output pixel
a = *i++;
switch (a)
{
case 0:
break;
case 255:
// Copy
*dst = map[a];
break;
default:
{
// Blend
#if 0
uint8_t oma = 255 - a;
src = map + a;
LRgb24 d = { G5bitTo8bit(dst->r),
G6bitTo8bit(dst->g),
G5bitTo8bit(dst->b)};
LRgb24 s = { G5bitTo8bit(src->r),
G6bitTo8bit(src->g),
G5bitTo8bit(src->b)};
dst->r = Div255[(oma * d.r) + (a * s.r)] >> 3;
dst->g = Div255[(oma * d.g) + (a * s.g)] >> 2;
dst->b = Div255[(oma * d.b) + (a * s.b)] >> 3;
#else
uint8_t a5 = a >> 3;
uint8_t a6 = a >> 2;
uint8_t oma5 = MASK_5BIT - a5;
uint8_t oma6 = MASK_6BIT - a6;
src = map + a;
dst->r = ((oma5 * (uint8_t)dst->r) + (a5 * (uint8_t)src->r)) / MASK_5BIT;
dst->g = ((oma6 * (uint8_t)dst->g) + (a6 * (uint8_t)src->g)) / MASK_6BIT;
dst->b = ((oma5 * (uint8_t)dst->b) + (a5 * (uint8_t)src->b)) / MASK_5BIT;
#endif
break;
}
}
dst++;
}
}
else
{
while (i < e)
{
// Copy rop
*dst++ = map[*i++];
}
}
LAssert((uint8_t*)dst <= EndOfBuffer);
}
return true;
}
#endif
void LDisplayString::Draw(LSurface *pDC, int px, int py, LRect *r, bool Debug)
{
Layout();
#if DISPLAY_STRING_FRACTIONAL_NATIVE
// GTK / Mac use fractional pixels, so call the fractional version:
LRect rc;
if (r)
{
rc = *r;
rc.x1 <<= FShift;
rc.y1 <<= FShift;
#if defined(MAC) && !defined(__GTK_H__)
rc.x2 <<= FShift;
rc.y2 <<= FShift;
#else
rc.x2 = (rc.x2 + 1) << FShift;
rc.y2 = (rc.y2 + 1) << FShift;
#endif
}
FDraw(pDC, px << FShift, py << FShift, r ? &rc : NULL, Debug);
#elif defined(HAIKU)
if (!Font || !pDC)
{
LgiTrace("%s:%i - No ptr: %p/%p.\n", _FL, Font, pDC);
return;
}
BFont *fnt = Font->Handle();
BView *view = pDC->Handle();
if (!fnt || !view)
{
LgiTrace("%s:%i - No handle: %p/%p(%s).\n", _FL, fnt, view, pDC->GetClass());
return;
}
- if (!Info.Length())
- {
- LgiTrace("%s:%i - No layout for '%s'.\n", _FL, Str);
- return;
- }
-
font_height height = {0};
fnt->GetHeight(&height);
if (!Font->Transparent())
{
view->SetHighColor(Font->Back());
if (r)
view->FillRect(*r);
else
view->FillRect(BRect(px, py, px+x, py+y));
}
view->SetFont(fnt);
view->SetHighColor(Font->Fore());
int cx = px;
for (auto &i: Info)
{
view->DrawString(i.Str, i.Len, BPoint(cx, py + height.ascent));
cx += i.X;
}
#elif defined(LGI_SDL)
if (Img && pDC && pDC->Y() > 0 && (*pDC)[0])
{
int Ox = 0, Oy = 0;
pDC->GetOrigin(Ox, Oy);
LBlitRegions Clip(pDC, px-Ox, py-Oy, Img, r);
LColourSpace DstCs = pDC->GetColourSpace();
switch (DstCs)
{
#define DspStrCase(px_fmt, comp) \
case Cs##px_fmt: \
CompositeText##comp(pDC, Img, Font, px-Ox, py-Oy, Clip); \
break;
DspStrCase(Rgb16, 5NoAlpha)
DspStrCase(Bgr16, 5NoAlpha)
DspStrCase(Rgb24, 8NoAlpha)
DspStrCase(Bgr24, 8NoAlpha)
DspStrCase(Rgbx32, 8NoAlpha)
DspStrCase(Bgrx32, 8NoAlpha)
DspStrCase(Xrgb32, 8NoAlpha)
DspStrCase(Xbgr32, 8NoAlpha)
DspStrCase(Rgba32, 8Alpha)
DspStrCase(Bgra32, 8Alpha)
DspStrCase(Argb32, 8Alpha)
DspStrCase(Abgr32, 8Alpha)
default:
LgiTrace("%s:%i - LDisplayString::Draw Unsupported colour space.\n", _FL);
// LAssert(!"Unsupported colour space.");
break;
#undef DspStrCase
}
}
else
LgiTrace("::Draw argument error.\n");
#elif defined WINNATIVE
if (Info.Length() && pDC && Font)
{
LFontSystem *Sys = LFontSystem::Inst();
COLOUR Old = pDC->Colour();
int TabSize = Font->TabSize() ? Font->TabSize() : 32;
int Ox = px;
LColour cFore = Font->Fore();
LColour cBack = Font->Back();
LColour cWhitespace;
if (VisibleTab)
{
cWhitespace = Font->WhitespaceColour();
LAssert(cWhitespace.IsValid());
}
for (int i=0; iFont[Info[i].FontId];
f->Colour(cFore, cBack);
auto Sz = Font->Size();
Sz.Value += Info[i].SizeDelta;
f->Size(Sz);
f->Transparent(Font->Transparent());
f->Underline(Font->Underline());
if (!f->Handle())
{
f->Create();
}
}
else
{
f = Font;
}
if (f)
{
LRect b;
if (r)
{
b.x1 = i ? px : r->x1;
b.y1 = r->y1;
b.x2 = i < Info.Length() - 1 ? px + Info[i].X - 1 : r->x2;
b.y2 = r->y2;
}
else
{
b.x1 = px;
b.y1 = py;
b.x2 = px + Info[i].X - 1;
b.y2 = py + Y() - 1;
}
if (b.Valid())
{
if (IsTabChar(*Info[i].Str))
{
// Invisible tab... draw blank space
if (!Font->Transparent())
{
pDC->Colour(cBack);
pDC->Rectangle(&b);
}
if (VisibleTab)
{
int X = px;
for (int n=0; nColour(cWhitespace);
DrawWhiteSpace(pDC, '\t', r);
X += Dx;
}
}
}
else
{
// Draw the character(s)
LColour Fg = f->Fore();
LAssert(Fg.IsValid());
f->_Draw(pDC, px, py, Info[i].Str, Info[i].Len, &b, Fg);
if (VisibleTab)
{
OsChar *start = Info[i].Str;
OsChar *s = start;
OsChar *e = s + Info[i].Len;
int Sp = -1;
while (s < e)
{
if (*s == ' ')
{
int Sx, Sy;
if (Sp < 0) f->_Measure(Sp, Sy, s, 1);
f->_Measure(Sx, Sy, start, (int)(s - start));
LRect r(0, 0, Sp-1, Sy-1);
r.Offset(px + Sx, py);
pDC->Colour(cWhitespace);
DrawWhiteSpace(pDC, ' ', r);
}
s++;
}
}
}
}
}
// Inc my position
px += Info[i].X;
}
if (AppendDots)
{
int Sx, Sy;
Font->_Measure(Sx, Sy, GDisplayStringDots, 3);
LRect b;
if (r)
{
b.x1 = px;
b.y1 = r->y1;
b.x2 = min(px + Sx - 1, r->x2);
b.y2 = r->y2;
}
else
{
b.x1 = px;
b.y1 = py;
b.x2 = px + Sx - 1;
b.y2 = py + Y() - 1;
}
LColour Fg = Font->Fore();
Font->_Draw(pDC, px, py, GDisplayStringDots, 3, &b, Fg);
}
pDC->Colour(Old);
}
else if (r &&
Font &&
!Font->Transparent())
{
pDC->Colour(Font->Back());
pDC->Rectangle(r);
}
#endif
}
int LDisplayString::GetDrawOffsetF()
{
return DrawOffsetF;
}
void LDisplayString::SetDrawOffsetF(int Fpx)
{
if (LaidOut)
LAssert(!"No point setting TabOrigin after string is laid out.\n");
DrawOffsetF = Fpx;
}
int LDisplayString::FX()
{
Layout();
return xf;
}
int LDisplayString::FY()
{
Layout();
return yf;
}
LPoint LDisplayString::FSize()
{
Layout();
return LPoint(xf, yf);
}
void LDisplayString::FDraw(LSurface *pDC, int fx, int fy, LRect *frc, bool Debug)
{
Layout(Debug);
#if !DISPLAY_STRING_FRACTIONAL_NATIVE
// Windows doesn't use fractional pixels, so call the integer version:
LRect rc;
if (frc)
{
rc = *frc;
rc.x1 >>= FShift;
rc.y1 >>= FShift;
rc.x2 >>= FShift;
rc.y2 >>= FShift;
}
Draw(pDC, fx >> FShift, fy >> FShift, frc ? &rc : NULL, Debug);
#elif defined __GTK_H__
Gtk::cairo_t *cr = pDC->Handle();
if (!cr)
{
LAssert(!"Can't get cairo.");
return;
}
pango_context_set_font_description(LFontSystem::Inst()->GetContext(), Font->Handle());
cairo_save(cr);
LColour b = Font->Back();
double Dx = ((double)fx / FScale);
double Dy = ((double)fy / FScale);
double rx, ry, rw, rh;
if (!Font->Transparent())
{
// Background fill
cairo_set_source_rgb(cr, (double)b.r()/255.0, (double)b.g()/255.0, (double)b.b()/255.0); cairo_new_path(cr);
if (frc)
{
rx = ((double)frc->x1 / FScale);
ry = ((double)frc->y1 / FScale);
rw = (double)frc->X() / FScale;
rh = (double)frc->Y() / FScale;
}
else
{
rx = Dx;
ry = Dy;
rw = x;
rh = y;
}
cairo_rectangle(cr, rx, ry, rw, rh);
cairo_fill(cr);
if (frc)
{
cairo_rectangle(cr, rx, ry, rw, rh);
cairo_clip(cr);
}
}
else if (frc)
{
rx = ((double)frc->x1 / FScale);
ry = ((double)frc->y1 / FScale);
rw = (double)frc->X() / FScale;
rh = (double)frc->Y() / FScale;
cairo_rectangle(cr, rx, ry, rw, rh);
cairo_clip(cr);
}
cairo_translate(cr, Dx, Dy);
LColour f = Font->Fore();
for (auto &b: d->Blocks)
{
double Bx = ((double)b.X()) / FScale;
#if DEBUG_BOUNDS
double By = Font->GetHeight();
cairo_set_source_rgb(cr, 1.0, 0.0, 0.0);
cairo_rectangle(cr, 0, 0, Bx, By);
cairo_rectangle(cr, 1, 1, Bx - 2.0, By - 2.0);
cairo_set_fill_rule(cr, Gtk::CAIRO_FILL_RULE_EVEN_ODD);
cairo_fill(cr);
#endif
if (b.Hnd)
{
cairo_set_source_rgb( cr,
(double)f.r()/255.0,
(double)f.g()/255.0,
(double)f.b()/255.0);
pango_cairo_show_layout(cr, b.Hnd);
if (VisibleTab && Str)
{
LUtf8Str Ptr(Str);
auto Ws = Font->WhitespaceColour();
pDC->Colour(Ws);
for (int32 u, Idx = 0 ; (u = Ptr); Idx++)
{
if (IsTabChar(u) || u == ' ')
{
Gtk::PangoRectangle pos;
Gtk::pango_layout_index_to_pos(b.Hnd, Idx, &pos);
LRect r(0, 0, pos.width / FScale, pos.height / FScale);
r.Offset(pos.x / FScale, pos.y / FScale);
DrawWhiteSpace(pDC, u, r);
}
Ptr++;
}
}
}
else if (b.Fnt)
{
b.Fnt->Transparent(Font->Transparent());
b.Fnt->Back(Font->Back());
b.Fnt->_Draw(pDC, 0, 0, b.Str, b.Bytes, NULL, f);
}
else LAssert(0);
cairo_translate(cr, Bx, 0);
}
cairo_restore(cr);
#elif defined MAC && !defined(LGI_SDL)
int Ox = 0, Oy = 0;
int px = fx >> FShift;
int py = fy >> FShift;
LRect rc;
if (frc)
rc.Set( frc->x1 >> FShift,
frc->y1 >> FShift,
frc->x2 >> FShift,
frc->y2 >> FShift);
if (pDC && !pDC->IsScreen())
pDC->GetOrigin(Ox, Oy);
if (pDC && !Font->Transparent())
{
LColour Old = pDC->Colour(Font->Back());
if (frc)
{
pDC->Rectangle(&rc);
}
else
{
LRect a(px, py, px + x - 1, py + y - 1);
pDC->Rectangle(&a);
}
pDC->Colour(Old);
}
if (Hnd && pDC && StrWords > 0)
{
OsPainter dc = pDC->Handle();
#if USE_CORETEXT
int y = (pDC->Y() - py + Oy);
CGContextSaveGState(dc);
pDC->Colour(Font->Fore());
if (pDC->IsScreen())
{
if (frc)
{
CGRect rect = rc;
rect.size.width += 1.0;
rect.size.height += 1.0;
CGContextClipToRect(dc, rect);
}
CGContextTranslateCTM(dc, 0, pDC->Y()-1);
CGContextScaleCTM(dc, 1.0, -1.0);
}
else
{
if (frc)
{
CGContextSaveGState(dc);
CGRect rect = rc;
rect.origin.x -= Ox;
rect.origin.y = pDC->Y() - rect.origin.y + Oy - rect.size.height;
rect.size.width += 1.0;
rect.size.height += 1.0;
CGContextClipToRect(dc, rect);
}
}
CGFloat Tx = (CGFloat)fx / FScale - Ox;
CGFloat Ty = (CGFloat)y - Font->Ascent();
CGContextSetTextPosition(dc, Tx, Ty);
CTLineDraw(Hnd, dc);
CGContextRestoreGState(dc);
#else
ATSUAttributeTag Tags[1] = {kATSUCGContextTag};
ByteCount Sizes[1] = {sizeof(CGContextRef)};
ATSUAttributeValuePtr Values[1] = {&dc};
e = ATSUSetLayoutControls(Hnd, 1, Tags, Sizes, Values);
if (e)
{
printf("%s:%i - ATSUSetLayoutControls failed (e=%i)\n", _FL, (int)e);
}
else
{
// Set style attr
ATSURGBAlphaColor c;
LColour Fore = Font->Fore();
c.red = (double) Fore.r() / 255.0;
c.green = (double) Fore.g() / 255.0;
c.blue = (double) Fore.b() / 255.0;
c.alpha = 1.0;
ATSUAttributeTag Tags[] = {kATSURGBAlphaColorTag};
ATSUAttributeValuePtr Values[] = {&c};
ByteCount Lengths[] = {sizeof(c)};
e = ATSUSetAttributes( Font->Handle(),
CountOf(Tags),
Tags,
Lengths,
Values);
if (e)
{
printf("%s:%i - Error setting font attr (e=%i)\n", _FL, (int)e);
}
else
{
int y = (pDC->Y() - py + Oy);
if (pDC->IsScreen())
{
CGContextSaveGState(dc);
if (frc)
{
CGRect rect = rc;
rect.size.width += 1.0;
rect.size.height += 1.0;
CGContextClipToRect(dc, rect);
}
CGContextTranslateCTM(dc, 0, pDC->Y()-1);
CGContextScaleCTM(dc, 1.0, -1.0);
e = ATSUDrawText(Hnd, kATSUFromTextBeginning, kATSUToTextEnd, fx - Long2Fix(Ox), Long2Fix(y) - fAscent);
CGContextRestoreGState(dc);
}
else
{
if (frc)
{
CGContextSaveGState(dc);
CGRect rect = rc;
rect.origin.x -= Ox;
rect.origin.y = pDC->Y() - rect.origin.y + Oy - rect.size.height;
rect.size.width += 1.0;
rect.size.height += 1.0;
CGContextClipToRect(dc, rect);
}
e = ATSUDrawText(Hnd, kATSUFromTextBeginning, kATSUToTextEnd, Long2Fix(px - Ox), Long2Fix(y) - fAscent);
if (frc)
CGContextRestoreGState(dc);
}
if (e)
{
char *a = 0;
StringConvert(a, NULL, Str, len);
printf("%s:%i - ATSUDrawText failed with %i, len=%i, str=%.20s\n", _FL, (int)e, len, a);
DeleteArray(a);
}
}
}
#endif
}
#endif
}
diff --git a/src/common/Lgi/LgiCommon.cpp b/src/common/Lgi/LgiCommon.cpp
--- a/src/common/Lgi/LgiCommon.cpp
+++ b/src/common/Lgi/LgiCommon.cpp
@@ -1,2816 +1,2841 @@
//
// Cross platform LGI functions
//
#if LGI_COCOA
#import
#endif
#define _WIN32_WINNT 0x501
#include
#include
#include
#include
#ifdef WINDOWS
#include
#include "lgi/common/RegKey.h"
#include
#include
#else
#include
#define _getcwd getcwd
#endif
#include "lgi/common/Lgi.h"
#include "lgi/common/Capabilities.h"
#if defined(LINUX) && !defined(LGI_SDL)
#include "LgiWinManGlue.h"
#elif defined(WINDOWS)
#include "lgi/common/RegKey.h"
#endif
#if defined POSIX
#include
#include
#include
#include
#include "lgi/common/SubProcess.h"
#endif
#ifdef HAIKU
#include
#include
#else
#include "SymLookup.h"
#endif
#include "lgi/common/Library.h"
#include "lgi/common/Net.h"
#if defined(__GTK_H__)
namespace Gtk {
#include "LgiWidget.h"
}
#endif
//////////////////////////////////////////////////////////////////////////
// Misc stuff
#if LGI_COCOA || defined(__GTK_H__)
LString LgiArgsAppPath;
#endif
#if defined MAC
#import
#if defined LGI_CARBON
bool _get_path_FSRef(FSRef &fs, LStringPipe &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))
{
LAutoString u((char*)LNewConvertCp("utf-8", Name.unicode, "utf-16", Name.length * sizeof(Name.unicode[0]) ));
// 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;
}
LAutoString FSRefPath(FSRef &fs)
{
LStringPipe a;
if (_get_path_FSRef(fs, a))
{
return LAutoString(a.NewStr());
}
return LAutoString();
}
#endif
#endif
bool LPostEvent(OsView Wnd, int Event, LMessage::Param a, LMessage::Param b)
{
#if LGI_SDL
SDL_Event e;
e.type = SDL_USEREVENT;
e.user.code = Event;
e.user.data1 = Wnd;
e.user.data2 = a || b ? new LMessage::EventParams(a, b) : NULL;
/*
printf("LPostEvent Wnd=%p, Event=%i, a/b: %i/%i\n",
Wnd, Event, (int)a, (int)b);
*/
return SDL_PushEvent(&e) == 0;
#elif WINNATIVE
return PostMessage(Wnd, Event, a, b) != 0;
#elif defined(__GTK_H__)
LAssert(Wnd);
LViewI *View = (LViewI*) g_object_get_data(GtkCast(Wnd, g_object, GObject), "LViewI");
if (View)
{
LMessage m(0);
m.Set(Event, a, b);
return m.Send(View);
}
else printf("%s:%i - Error: LPostEvent can't cast OsView to LViewI\n", _FL);
#elif defined(MAC) && !LGI_COCOA
#if 0
int64 Now = LCurrentTime();
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", _FL, (int)e);
}
else
{
EventTargetRef t = GetControlEventTarget(Wnd);
e = SetEventParameter(Ev, kEventParamLgiEvent, typeUInt32, sizeof(Event), &Event);
if (e) printf("%s:%i - error %i\n", _FL, (int)e);
e = SetEventParameter(Ev, kEventParamLgiA, typeUInt32, sizeof(a), &a);
if (e) printf("%s:%i - error %i\n", _FL, (int)e);
e = SetEventParameter(Ev, kEventParamLgiB, typeUInt32, sizeof(b), &b);
if (e) printf("%s:%i - error %i\n", _FL, (int)e);
bool Status = false;
EventQueueRef q = GetMainEventQueue();
e = SetEventParameter(Ev, kEventParamPostTarget, typeEventTargetRef, sizeof(t), &t);
if (e) printf("%s:%i - error %i\n", _FL, (int)e);
e = PostEventToQueue(q, Ev, kEventPriorityStandard);
if (e) printf("%s:%i - error %i\n", _FL, (int)e);
else Status = true;
// printf("PostEventToQueue %i,%i,%i -> %p\n", Event, a, b, q);
ReleaseEvent(Ev);
return Status;
}
#else
LAssert(!"Not impl.");
#endif
return false;
}
void LExitApp()
{
exit(0);
}
//////////////////////////////////////////////////////////////////////////
#ifdef WIN32
bool RegisterActiveXControl(char *Dll)
{
LLibrary 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
LAssert(!"Not impl.");
#endif
}
return false;
}
#endif
//////////////////////////////////////////////////////////////////////////
#ifdef WINDOWS
#include
#pragma comment(lib, "netapi32.lib")
#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 LGetOs
(
/// Returns the version of the OS or NULL if you don't care
LArray *Ver
)
{
#if defined(WIN32) || defined(WIN64)
static int Os = LGI_OS_UNKNOWN;
static int Version = 0, Revision = 0;
if (Os == LGI_OS_UNKNOWN)
{
#if defined(WIN64)
BOOL IsWow64 = TRUE;
#elif defined(WIN32)
BOOL IsWow64 = FALSE;
IsWow64Process(GetCurrentProcess(), &IsWow64);
#endif
SERVER_INFO_101 *v = NULL;
auto r = NetServerGetInfo(NULL, 101, (LPBYTE*)&v);
if (r == NERR_Success)
{
Version = v->sv101_version_major;
Revision = v->sv101_version_minor;
Os = (v->sv101_version_major >= 6)
?
#ifdef WIN32
(IsWow64 ? LGI_OS_WIN64 : LGI_OS_WIN32)
#else
LGI_OS_WIN64
#endif
:
LGI_OS_WIN9X;
NetApiBufferFree(v);
}
else LAssert(0);
}
if (Ver)
{
Ver->Add(Version);
Ver->Add(Revision);
}
return Os;
#elif defined LINUX
if (Ver)
{
utsname Buf;
if (!uname(&Buf))
{
auto t = LString(Buf.release).SplitDelimit(".");
for (int i=0; iAdd(atoi(t[i]));
}
}
}
return LGI_OS_LINUX;
#elif defined MAC
#if !defined(__GTK_H__)
if (Ver)
{
NSOperatingSystemVersion v = [[NSProcessInfo processInfo] operatingSystemVersion];
Ver->Add((int)v.majorVersion);
Ver->Add((int)v.minorVersion);
Ver->Add((int)v.patchVersion);
}
#endif
return LGI_OS_MAC_OS_X;
#elif defined HAIKU
return LGI_OS_HAIKU;
#else
#error "Impl Me"
return LGI_OS_UNKNOWN;
#endif
}
const char *LGetOsName()
{
const char *Str[LGI_OS_MAX] =
{
"Unknown",
"Win9x",
"Win32",
"Win64",
"Haiku",
"Linux",
"MacOSX",
};
auto Os = LGetOs();
if (Os > 0 && Os < CountOf(Str))
return Str[Os];
LAssert(!"Invalid OS index.");
return "error";
}
#ifdef WIN32
#define RecursiveFileSearch_Wildcard "*.*"
#else // unix'ish OS
#define RecursiveFileSearch_Wildcard "*"
#endif
bool LRecursiveFileSearch(const char *Root,
LArray *Ext,
LArray *Files,
uint64 *Size,
uint64 *Count,
std::function Callback,
LCancel *Cancel)
{
// validate args
if (!Root) return false;
// get directory enumerator
LDirectory Dir;
bool Status = true;
// enumerate the directory contents
for (auto Found = Dir.First(Root); Found && (!Cancel || !Cancel->IsCancelled()); Found = Dir.Next())
{
char Name[300];
if (!Dir.Path(Name, sizeof(Name)))
continue;
if (Callback && !Callback(Name, &Dir))
continue;
if (Dir.IsDir())
{
// dir
LRecursiveFileSearch( Name,
Ext,
Files,
Size,
Count,
Callback,
Cancel);
}
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
static LStreamI *_LgiTraceStream = NULL;
void LgiTraceSetStream(LStreamI *stream)
{
_LgiTraceStream = stream;
}
bool LgiTraceGetFilePath(char *LogPath, int BufLen)
{
if (!LogPath)
return false;
auto Exe = LGetExeFile();
if (Exe)
{
#ifdef MAC
char *Dir = strrchr(Exe, DIR_CHAR);
if (Dir)
{
char Part[256];
strcpy_s(Part, sizeof(Part), Dir+1);
LMakePath(LogPath, BufLen, "~/Library/Logs", Dir+1);
strcat_s(LogPath, BufLen, ".txt");
}
else
#endif
{
char *Dot = strrchr(Exe, '.');
if (Dot && !strchr(Dot, DIR_CHAR))
sprintf_s(LogPath, BufLen, "%.*s.txt", (int)(Dot - Exe.Get()), Exe.Get());
else
sprintf_s(LogPath, BufLen, "%s.txt", Exe.Get());
}
LFile f;
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);
LGetSystemPath(LSP_APP_ROOT, LogPath, BufLen);
if (!LDirExists(LogPath))
FileDev->CreateFolder(LogPath);
LMakePath(LogPath, BufLen, LogPath, Leaf);
}
else goto OnError;
}
#if 0
LFile tmp;
if (tmp.Open("c:\\temp\\log.txt", O_WRITE))
{
tmp.SetSize(0);
tmp.Write(LogPath, strlen(LogPath));
}
#endif
}
else
{
// Well what to do now? I give up
OnError:
strcpy_s(LogPath, BufLen, "trace.txt");
return false;
}
return true;
}
void LgiTrace(const char *Msg, ...)
{
#if defined _INC_MALLOC && WINNATIVE
if (_heapchk() != _HEAPOK)
return;
#endif
if (!Msg)
return;
#ifdef WIN32
static LMutex Sem("LgiTrace");
Sem.Lock(_FL, true);
#endif
char Buffer[2049] = "";
#ifdef LGI_TRACE_TO_FILE
static LFile f;
static char LogPath[MAX_PATH_LEN] = "";
if (!_LgiTraceStream && LogPath[0] == 0)
LgiTraceGetFilePath(LogPath, sizeof(LogPath));
#endif
va_list Arg;
va_start(Arg, Msg);
#ifdef LGI_TRACE_TO_FILE
int Ch =
#endif
vsnprintf(Buffer, sizeof(Buffer)-1, Msg, Arg);
va_end(Arg);
#ifdef LGI_TRACE_TO_FILE
LStreamI *Output = NULL;
if (_LgiTraceStream)
Output = _LgiTraceStream;
else
{
if (!f.IsOpen() &&
f.Open(LogPath, O_WRITE))
f.Seek(0, SEEK_END);
Output = &f;
}
if (Output && Ch > 0)
{
Output->ChangeThread();
Output->Write(Buffer, Ch);
}
if (!_LgiTraceStream)
{
#ifdef WINDOWS
// Windows can take AGES to close a file when there is anti-virus on, like 100ms.
// We can't afford to wait here so just keep the file open but flush the
// buffers if we can.
FlushFileBuffers(f.Handle());
#else
f.Close();
#endif
}
#endif
#if defined WIN32
OutputDebugStringA(Buffer);
Sem.Unlock();
#else
printf("%s", Buffer);
#endif
}
#ifndef LGI_STATIC
#define STACK_SIZE 12
void LStackTrace(const char *Msg, ...)
{
#ifndef HAIKU
LSymLookup::Addr Stack[STACK_SIZE];
ZeroObj(Stack);
LSymLookup *Lu = LAppInst ? LAppInst->GetSymLookup() : NULL;
if (!Lu)
{
printf("%s:%i - Failed to get sym lookup object.\n", _FL);
return;
}
int Frames = Lu ? Lu->BackTrace(0, 0, Stack, STACK_SIZE) : 0;
if (Msg)
{
#ifdef LGI_TRACE_TO_FILE
static LFile f;
static char LogPath[MAX_PATH_LEN] = "";
if (!_LgiTraceStream)
{
if (LogPath[0] == 0)
LgiTraceGetFilePath(LogPath, sizeof(LogPath));
f.Open(LogPath, O_WRITE);
}
#endif
va_list Arg;
va_start(Arg, Msg);
char Buffer[2049] = "";
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, (int)strlen(Buffer));
f.Close();
}
#endif
#if defined WIN32
OutputDebugStringA(Buffer);
#else
printf("Trace: %s", Buffer);
#endif
}
#endif
}
#endif
bool LTrimDir(char *Path)
{
if (!Path)
return false;
char *p = strrchr(Path, DIR_CHAR);
if (!p)
return false;
if (p[1] == 0) // Trailing DIR_CHAR doesn't count... do it again.
{
*p = 0;
p = strrchr(Path, DIR_CHAR);
if (!p)
return false;
}
*p = 0;
return true;
}
LString LMakeRelativePath(const char *Base, const char *Path)
{
LStringPipe Status;
if (Base && Path)
{
#ifdef WIN32
bool SameNs = strnicmp(Base, Path, 3) == 0;
#else
bool SameNs = true;
#endif
if (SameNs)
{
auto b = LString(Base + 1).SplitDelimit(":\\/");
auto p = LString(Path + 1).SplitDelimit(":\\/");
int Same = 0;
while (Same < b.Length() && Same < p.Length() && 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 Status.NewGStr();
}
bool LIsRelativePath(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 LMakePath(char *Str, int StrSize, const char *Path, const char *File)
{
if (!Str || StrSize <= 0 || !Path || !File)
{
printf("%s:%i - Invalid LMakePath(%p,%i,%s,%s) param\n", _FL, Str, StrSize, Path, File);
return false;
}
if (StrSize <= 4)
{
printf("%s:%i - LgiMakeFile buf size=%i?\n", _FL, StrSize);
}
if (Str && Path && File)
{
char Dir[] = { '/', '\\', 0 };
if (Path[0] == '~')
{
auto Parts = LString(Path).SplitDelimit(Dir, 2);
char *s = Str, *e = Str + StrSize;
for (auto p: Parts)
{
if (p.Equals("~"))
{
LGetSystemPath(LSP_HOME, s, e - s);
s += strlen(s);
}
else
s += sprintf_s(s, e - s, "%s%s", DIR_STR, p.Get());
}
}
else if (Str != Path)
{
strcpy_s(Str, StrSize, Path);
}
#define EndStr() Str[strlen(Str)-1]
#define EndDir() if (!strchr(Dir, EndStr())) strcat(Str, DIR_STR);
size_t Len = strlen(Str);
char *End = Str + (Len ? Len - 1 : 0);
if (strchr(Dir, *End) && End > Str)
{
*End = 0;
}
auto T = LString(File).SplitDelimit(Dir);
for (int i=0; i= StrSize - 1)
return false;
Str[Len++] = DIR_CHAR;
Str[Len] = 0;
}
size_t SegLen = strlen(T[i]);
if (Len + SegLen + 1 > StrSize)
{
return false;
}
strcpy_s(Str + Len, StrSize - Len, T[i]);
}
}
}
return true;
}
bool LgiGetTempPath(char *Dst, int DstSize)
{
return LGetSystemPath(LSP_TEMP, Dst, DstSize);
}
bool LGetSystemPath(LSystemPath Which, char *Dst, ssize_t DstSize)
{
if (!Dst || DstSize <= 0)
return false;
LFile::Path p;
LString s = p.GetSystem(Which, 0);
if (!s)
return false;
strcpy_s(Dst, DstSize, s);
return true;
}
LString LGetSystemPath(LSystemPath Which, int WordSize)
{
LFile::Path p;
return p.GetSystem(Which, WordSize);
}
LFile::Path::State LFile::Path::Exists()
{
if (Length() == 0)
return TypeNone;
#ifdef WINDOWS
struct _stat64 s;
int r = _stat64(GetFull(), &s);
if (r)
return TypeNone;
if (s.st_mode & _S_IFDIR)
return TypeFolder;
if (s.st_mode & _S_IFREG)
return TypeFile;
#else
struct stat s;
int r = stat(GetFull(), &s);
if (r)
return TypeNone;
if (S_ISDIR(s.st_mode))
return TypeFolder;
if (S_ISREG(s.st_mode))
return TypeFile;
#endif
return TypeNone;
}
LString LFile::Path::PrintAll()
{
LStringPipe p;
#define _(name) \
p.Print(#name ": '%s'\n", GetSystem(name).Get());
_(LSP_OS)
_(LSP_OS_LIB)
_(LSP_TEMP)
_(LSP_COMMON_APP_DATA)
_(LSP_USER_APP_DATA)
_(LSP_LOCAL_APP_DATA)
_(LSP_DESKTOP)
_(LSP_HOME)
_(LSP_USER_APPS)
_(LSP_EXE)
_(LSP_TRASH)
_(LSP_APP_INSTALL)
_(LSP_APP_ROOT)
_(LSP_USER_DOCUMENTS)
_(LSP_USER_MUSIC)
_(LSP_USER_VIDEO)
_(LSP_USER_DOWNLOADS)
_(LSP_USER_LINKS)
_(LSP_USER_PICTURES)
#undef _
#if LGI_COCOA
int Domains[] = {NSUserDomainMask, NSSystemDomainMask};
const char *DomainName[] = {"User", "System"};
for (int i=0; i 0) \
{ \
LString s = [paths objectAtIndex:0]; \
p.Print("%s." #name ": '%s'\n", DomainName[i], s.Get()); \
} \
else p.Print("%s." #name ": null\n", DomainName[i]); \
} \
}
_(NSApplicationDirectory)
_(NSDemoApplicationDirectory)
_(NSDeveloperApplicationDirectory)
_(NSAdminApplicationDirectory)
_(NSLibraryDirectory)
_(NSDeveloperDirectory)
_(NSUserDirectory)
_(NSDocumentationDirectory)
_(NSDocumentDirectory)
_(NSCoreServiceDirectory)
_(NSAutosavedInformationDirectory)
_(NSDesktopDirectory)
_(NSCachesDirectory)
_(NSApplicationSupportDirectory)
_(NSDownloadsDirectory)
_(NSInputMethodsDirectory)
_(NSMoviesDirectory)
_(NSMusicDirectory)
_(NSPicturesDirectory)
_(NSPrinterDescriptionDirectory)
_(NSSharedPublicDirectory)
_(NSPreferencePanesDirectory)
_(NSApplicationScriptsDirectory)
_(NSItemReplacementDirectory)
_(NSAllApplicationsDirectory)
_(NSAllLibrariesDirectory)
_(NSTrashDirectory)
#undef _
}
#endif
return p.NewGStr();
}
LString LFile::Path::GetSystem(LSystemPath Which, int WordSize)
{
LString Path;
#if defined(WIN32)
#ifndef CSIDL_PROFILE
#define CSIDL_PROFILE 0x0028
#endif
#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 defined(LINUX) && !defined(LGI_SDL)
// Ask our window manager add-on if it knows the path
LLibrary *WmLib = LAppInst ? LAppInst->GetWindowManagerLib() : NULL;
if (WmLib)
{
Proc_LgiWmGetPath WmGetPath = (Proc_LgiWmGetPath) WmLib->GetAddress("LgiWmGetPath");
char Buf[MAX_PATH_LEN];
if (WmGetPath && WmGetPath(Which, Buf, sizeof(Buf)))
{
Path = Buf;
return Path;
}
}
#endif
*/
switch (Which)
{
default:
break;
case LSP_USER_DOWNLOADS:
{
#if defined(__GTK_H__)
auto p = Gtk::g_get_user_special_dir(Gtk::G_USER_DIRECTORY_DOWNLOAD);
Path = p;
#elif 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
LLibrary 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))
{
LAutoString u8(WideToUtf8(ptr));
if (u8)
Path = u8;
CoTaskMemFree(ptr);
}
}
if (!Path.Get())
{
LRegKey k(false, "HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders");
char *p = k.GetStr("{374DE290-123F-4565-9164-39C4925E467B}");
if (LDirExists(p))
Path = p;
}
if (!Path.Get())
{
LString MyDoc = WinGetSpecialFolderPath(CSIDL_MYDOCUMENTS);
if (MyDoc)
{
char Buf[MAX_PATH_LEN];
LMakePath(Buf, sizeof(Buf), MyDoc, "Downloads");
if (LDirExists(Buf))
Path = Buf;
}
}
if (!Path.Get())
{
LString Profile = WinGetSpecialFolderPath(CSIDL_PROFILE);
if (Profile)
{
char Buf[MAX_PATH_LEN];
LMakePath(Buf, sizeof(Buf), Profile, "Downloads");
if (LDirExists(Buf))
Path = Buf;
}
}
#elif LGI_COCOA
NSArray *paths = NSSearchPathForDirectoriesInDomains( NSDownloadsDirectory, NSUserDomainMask, YES);
if (paths)
{
if ([paths count])
Path = [paths objectAtIndex:0];
}
#elif defined(HAIKU)
#else
LAssert(!"Not implemented");
#endif
break;
}
case LSP_USER_LINKS:
{
LString Home = LGetSystemPath(LSP_HOME);
#if defined(WIN32)
char p[MAX_PATH_LEN];
if (LMakePath(p, sizeof(p), Home, "Links"))
Path = p;
#elif defined(LINUX)
char p[MAX_PATH_LEN];
if (LMakePath(p, sizeof(p), Home, ".config/gtk-3.0"))
Path = p;
#endif
break;
}
case LSP_USER_PICTURES:
{
#if defined(__GTK_H__)
auto p = Gtk::g_get_user_special_dir(Gtk::G_USER_DIRECTORY_DOCUMENTS);
Path = p;
#elif defined(WIN32)
Path = WinGetSpecialFolderPath(CSIDL_MYPICTURES);
if (Path)
return Path;
#elif defined LGI_COCOA
NSArray *paths = NSSearchPathForDirectoriesInDomains( NSPicturesDirectory, NSUserDomainMask, YES);
if (paths)
{
if ([paths count])
Path = [paths objectAtIndex:0];
}
#endif
// Default to ~/Pictures
char hm[MAX_PATH_LEN];
LString Home = LGetSystemPath(LSP_HOME);
if (LMakePath(hm, sizeof(hm), Home, "Pictures"))
Path = hm;
break;
}
case LSP_USER_DOCUMENTS:
{
+ char path[MAX_PATH_LEN];
+
#if defined(__GTK_H__)
auto p = Gtk::g_get_user_special_dir(Gtk::G_USER_DIRECTORY_DOCUMENTS);
- Path = p;
+ if (p)
+ Path = p;
#elif defined(WIN32) && defined(_MSC_VER)
Path = WinGetSpecialFolderPath(CSIDL_MYDOCUMENTS);
- if (Path)
- return Path;
#elif defined LGI_COCOA
NSArray *paths = NSSearchPathForDirectoriesInDomains( NSDocumentDirectory, NSUserDomainMask, YES);
if (paths)
{
if ([paths count])
Path = [paths objectAtIndex:0];
}
+
+ #elif defined(HAIKU)
+
+ if( find_directory
+ (
+ B_SYSTEM_DOCUMENTATION_DIRECTORY,
+ dev_for_path("/boot"),
+ true,
+ path, sizeof(path)
+ ) == B_OK)
+ Path = path;
#endif
- // Default to ~/Documents
- char hm[MAX_PATH_LEN];
- LString Home = LGetSystemPath(LSP_HOME);
- if (LMakePath(hm, sizeof(hm), Home, "Documents"))
- Path = hm;
+ if (!Path)
+ {
+ // Default to ~/Documents
+ if (LMakePath(path, sizeof(path), LGetSystemPath(LSP_HOME), "Documents"))
+ Path = path;
+ }
break;
}
case LSP_USER_MUSIC:
{
+ char path[MAX_PATH_LEN];
+
#if defined WIN32
Path = WinGetSpecialFolderPath(CSIDL_MYMUSIC);
#elif defined(__GTK_H__)
auto p = Gtk::g_get_user_special_dir(Gtk::G_USER_DIRECTORY_MUSIC);
- Path = p;
+ if (p)
+ Path = p;
#elif defined LGI_CARBON
FSRef Ref;
OSErr e = FSFindFolder(kUserDomain, kMusicDocumentsFolderType, kDontCreateFolder, &Ref);
if (e) printf("%s:%i - FSFindFolder failed e=%i\n", _FL, e);
else
{
LAutoString a = FSRefPath(Ref);
if (a)
Path = a.Get();
}
#elif LGI_COCOA
NSArray *paths = NSSearchPathForDirectoriesInDomains( NSMusicDirectory, NSUserDomainMask, YES);
if (paths)
{
if ([paths count])
Path = [paths objectAtIndex:0];
}
+ #elif defined(HAIKU)
+
+ if( find_directory
+ (
+ B_USER_SOUNDS_DIRECTORY,
+ dev_for_path("/boot"),
+ true,
+ path, sizeof(path)
+ ) == B_OK)
+ Path = path;
+
#endif
if (!Path)
{
// Default to ~/Music
- char p[MAX_PATH_LEN];
- LString Home = LGetSystemPath(LSP_HOME);
- if (LMakePath(p, sizeof(p), Home, "Music"))
- Path = p;
+ if (LMakePath(path, sizeof(path), LGetSystemPath(LSP_HOME), "Music"))
+ Path = path;
}
break;
}
case LSP_USER_VIDEO:
{
+ char path[MAX_PATH_LEN];
+
#if defined WIN32
Path = WinGetSpecialFolderPath(CSIDL_MYVIDEO);
#elif defined(__GTK_H__)
auto p = Gtk::g_get_user_special_dir(Gtk::G_USER_DIRECTORY_VIDEOS);
- Path = p;
+ if (p)
+ Path = p;
#elif defined LGI_CARBON
FSRef Ref;
OSErr e = FSFindFolder(kUserDomain, kMovieDocumentsFolderType, kDontCreateFolder, &Ref);
if (e) printf("%s:%i - FSFindFolder failed e=%i\n", _FL, e);
else
{
LAutoString a = FSRefPath(Ref);
if (a)
Path = a.Get();
}
#elif LGI_COCOA
NSArray *paths = NSSearchPathForDirectoriesInDomains( NSMoviesDirectory, NSUserDomainMask, YES);
if (paths)
{
if ([paths count])
Path = [paths objectAtIndex:0];
}
#endif
if (!Path)
{
// Default to ~/Video
- char p[MAX_PATH_LEN];
- LString Home = LGetSystemPath(LSP_HOME);
- if (LMakePath(p, sizeof(p), Home, "Video"))
- Path = p;
+ if (LMakePath(path, sizeof(path), LGetSystemPath(LSP_HOME), "Video"))
+ Path = path;
}
break;
}
case LSP_USER_APPS:
{
#if defined WIN32
int Id =
#ifdef WIN64
CSIDL_PROGRAM_FILES
#else
CSIDL_PROGRAM_FILESX86
#endif
;
if (WordSize == 32)
Id = CSIDL_PROGRAM_FILESX86;
else if (WordSize == 64)
Id = CSIDL_PROGRAM_FILES;
Path = WinGetSpecialFolderPath(Id);
#elif defined(HAIKU)
- dev_t volume = dev_for_path("/boot");
char path[MAX_PATH_LEN] = "";
- if (find_directory(B_SYSTEM_APPS_DIRECTORY, volume, true, path, sizeof(path)) == B_OK)
+ if (find_directory(B_USER_APPS_DIRECTORY, dev_for_path("/boot"), true, path, sizeof(path)) == B_OK)
Path = path;
#elif LGI_COCOA
NSArray *paths = NSSearchPathForDirectoriesInDomains( NSApplicationDirectory, NSSystemDomainMask, YES);
if (paths)
{
if ([paths count])
Path = [paths objectAtIndex:0];
}
#elif defined MAC
Path = "/Applications";
#elif defined LINUX
Path = "/usr/bin";
#else
LAssert(!"Impl me.");
#endif
break;
}
case LSP_APP_INSTALL:
{
Path = LGetSystemPath(LSP_EXE);
if (Path)
{
size_t Last = Path.RFind(DIR_STR);
if (Last > 0)
{
LString s = Path(Last, -1);
if
(
stristr(s,
#ifdef _DEBUG
"Debug"
#else
"Release"
#endif
)
)
Path = Path(0, Last);
}
}
break;
}
case LSP_APP_ROOT:
{
#ifndef LGI_STATIC
const char *Name = NULL;
// Try and get the configured app name:
if (LAppInst)
Name = LAppInst->LBase::Name();
if (!Name)
{
// Use the exe name?
LString Exe = LGetExeFile();
char *l = LGetLeaf(Exe);
if (l)
{
#ifdef WIN32
char *d = strrchr(l, '.');
*d = NULL;
#endif
Name = l;
// printf("%s:%i - name '%s'\n", _FL, Name);
}
}
if (!Name)
{
LAssert(0);
break;
}
#if defined MAC
#if LGI_COCOA
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES);
if (paths)
Path = [[paths objectAtIndex:0] UTF8String];
#elif LGI_CARBON
FSRef Ref;
OSErr e = FSFindFolder(kUserDomain, kDomainLibraryFolderType, kDontCreateFolder, &Ref);
if (e)
{
printf("%s:%i - FSFindFolder failed e=%i\n", _FL, e);
LAssert(0);
}
else
{
LAutoString Base = FSRefPath(Ref);
Path = Base.Get();
}
#else
struct passwd *pw = getpwuid(getuid());
if (!pw)
return false;
Path.Printf("%s/Library", pw->pw_dir);
#endif
#elif defined WIN32
Path = WinGetSpecialFolderPath(CSIDL_APPDATA);
#elif defined LINUX
char Dot[128];
snprintf(Dot, sizeof(Dot), ".%s", Name);
Name = Dot;
struct passwd *pw = getpwuid(getuid());
if (pw)
Path = pw->pw_dir;
else
LAssert(0);
#elif defined(HAIKU)
dev_t volume = dev_for_path("/boot");
char path[MAX_PATH_LEN] = "";
if (find_directory(B_USER_DIRECTORY , volume, true, path, sizeof(path)) == B_OK)
Path = path;
#else
LAssert(0);
#endif
if (Path)
{
Path += DIR_STR;
Path += Name;
}
#endif
break;
}
case LSP_OS:
{
#if defined WIN32
char16 p[MAX_PATH_LEN];
if (GetWindowsDirectoryW(p, CountOf(p)) > 0)
Path = p;
#elif defined(HAIKU)
dev_t volume = dev_for_path("/boot");
char path[MAX_PATH_LEN] = "";
if (find_directory(B_SYSTEM_DIRECTORY, volume, true, path, sizeof(path)) == B_OK)
Path = path;
#elif defined LGI_CARBON
FSRef Ref;
OSErr e = FSFindFolder(kOnAppropriateDisk, kSystemFolderType, kDontCreateFolder, &Ref);
if (e) printf("%s:%i - FSFindFolder failed e=%i\n", __FILE__, __LINE__, e);
else
{
LAutoString u = FSRefPath(Ref);
if (u)
Path = u.Get();
}
#elif defined LGI_COCOA
NSArray *paths = NSSearchPathForDirectoriesInDomains( NSLibraryDirectory, NSSystemDomainMask, YES);
if (paths)
{
Path = [paths objectAtIndex:0];
LTrimDir(Path);
}
#elif defined LINUX
Path = "/boot"; // it'll do for now...
#endif
break;
}
case LSP_OS_LIB:
{
#if defined WIN32
char16 p[MAX_PATH_LEN];
if (GetSystemDirectoryW(p, CountOf(p)) > 0)
Path = p;
#elif defined(HAIKU)
dev_t volume = dev_for_path("/boot");
char path[MAX_PATH_LEN] = "";
if (find_directory(B_SYSTEM_LIB_DIRECTORY, volume, true, path, sizeof(path)) == B_OK)
Path = path;
#elif defined MAC
Path = "/Library";
#elif defined LINUX
Path = "/lib"; // it'll do for now...
#endif
break;
}
case LSP_TEMP:
{
#if defined WIN32
char16 t[MAX_PATH_LEN];
if (GetTempPathW(CountOf(t), t) > 0)
{
LAutoString utf(WideToUtf8(t));
if (utf)
Path = utf;
}
#elif defined LGI_CARBON
FSRef Ref;
OSErr e = FSFindFolder(kUserDomain, kTemporaryFolderType, kCreateFolder, &Ref);
if (e) LgiTrace("%s:%i - FSFindFolder failed e=%i\n", _FL, e);
else
{
LAutoString u = FSRefPath(Ref);
if (u)
{
Path = u.Get();
}
else LgiTrace("%s:%i - FSRefPath failed.\n", _FL);
}
#elif defined LGI_COCOA
NSString *tempDir = NSTemporaryDirectory();
if (tempDir)
Path = tempDir;
else
LAssert(!"No tmp folder?");
#elif defined LINUX
Path = "/tmp"; // it'll do for now...
#else
LAssert(!"Impl me.");
#endif
break;
}
case LSP_COMMON_APP_DATA:
{
#if defined WIN32
Path = WinGetSpecialFolderPath(CSIDL_COMMON_APPDATA);
#elif defined LGI_CARBON
FSRef Ref;
OSErr e = FSFindFolder(kOnSystemDisk, kDomainLibraryFolderType, kDontCreateFolder, &Ref);
if (e) printf("%s:%i - FSFindFolder failed e=%i\n", _FL, e);
else
{
auto u = FSRefPath(Ref);
if (u)
Path = u.Get();
}
#elif defined LGI_COCOA
NSArray *paths = NSSearchPathForDirectoriesInDomains( NSLibraryDirectory, NSSystemDomainMask, YES);
if (paths)
{
Path = [paths objectAtIndex:0];
}
#elif defined LINUX
Path = "/usr";
#else
LAssert(!"Impl me.");
#endif
break;
}
case LSP_USER_APP_DATA:
{
#if defined WIN32
Path = WinGetSpecialFolderPath(CSIDL_APPDATA);
#elif defined LGI_CARBON
FSRef Ref;
OSErr e = FSFindFolder(kUserDomain, kDomainLibraryFolderType, kDontCreateFolder, &Ref);
if (e) printf("%s:%i - FSFindFolder failed e=%i\n", __FILE__, __LINE__, e);
else
{
auto u = FSRefPath(Ref);
if (u)
Path = u.Get();
}
#elif defined LGI_COCOA
NSArray *paths = NSSearchPathForDirectoriesInDomains( NSLibraryDirectory, NSUserDomainMask, YES);
if (paths)
{
Path = [paths objectAtIndex:0];
}
#elif defined LINUX
Path = "/usr";
#elif defined HAIKU
dev_t volume = dev_for_path("/boot");
char path[MAX_PATH_LEN] = "";
if (find_directory(B_USER_SETTINGS_DIRECTORY, volume, true, path, sizeof(path)) == B_OK)
Path = path;
#else
LAssert(!"Impl me.");
#endif
break;
}
case LSP_LOCAL_APP_DATA:
{
#if defined WIN32
Path = WinGetSpecialFolderPath(CSIDL_LOCAL_APPDATA);
#elif defined LGI_COCOA
NSArray *paths = NSSearchPathForDirectoriesInDomains( NSLibraryDirectory, NSUserDomainMask, YES);
if (paths)
{
Path = [paths objectAtIndex:0];
}
#else
LAssert(!"Impl me.");
#endif
break;
}
case LSP_DESKTOP:
{
#if defined(WINDOWS) && defined(_MSC_VER)
Path = WinGetSpecialFolderPath(CSIDL_DESKTOPDIRECTORY);
#elif defined(HAIKU)
dev_t volume = dev_for_path("/boot");
char path[MAX_PATH_LEN] = "";
if (find_directory(B_DESKTOP_DIRECTORY, volume, true, path, sizeof(path)) == B_OK)
Path = path;
#elif defined(__GTK_H__)
auto p = Gtk::g_get_user_special_dir(Gtk::G_USER_DIRECTORY_DESKTOP);
Path = p;
#elif defined LGI_COCOA
NSArray *paths = NSSearchPathForDirectoriesInDomains( NSDesktopDirectory, NSUserDomainMask, YES);
if (paths)
{
Path = [paths objectAtIndex:0];
}
#elif defined LGI_CARBON
FSRef Ref;
OSErr e = FSFindFolder(kOnAppropriateDisk, kDesktopFolderType, kDontCreateFolder, &Ref);
if (e) printf("%s:%i - FSFindFolder failed e=%i\n", __FILE__, __LINE__, e);
else
{
LAutoString u = FSRefPath(Ref);
if (u)
Path = u.Get();
}
#elif defined POSIX
struct passwd *pw = getpwuid(getuid());
if (pw)
{
#ifdef LINUX
WindowManager wm = LGetWindowManager();
if (wm == WM_Gnome)
{
Path.Printf("%s/.gnome-desktop", pw->pw_dir);
}
#endif
if (!LDirExists(Path))
{
Path.Printf("%s/Desktop", pw->pw_dir);
}
}
#else
#error "Impl me."
#endif
break;
}
case LSP_HOME:
{
#if defined WIN32
Path = WinGetSpecialFolderPath(CSIDL_PROFILE);
#elif defined(HAIKU)
dev_t volume = dev_for_path("/boot");
char path[MAX_PATH_LEN] = "";
if (find_directory(B_USER_DIRECTORY, volume, true, path, sizeof(path)) == B_OK)
Path = path;
#elif defined LGI_COCOA
NSString *home = NSHomeDirectory();
if (home)
Path = home;
else
LAssert(!"No home path?");
#elif defined POSIX
struct passwd *pw = getpwuid(getuid());
if (pw)
Path = pw->pw_dir;
#else
#error "Impl me."
#endif
break;
}
case LSP_EXE:
{
Path = LGetExeFile();
if (!Path)
break;
auto p = Path.RFind(DIR_STR);
if (p > 0)
Path.Length(p);
break;
}
case LSP_TRASH:
{
#if defined LINUX
switch (LGetWindowManager())
{
case WM_Kde:
{
static char KdeTrash[256] = "";
if (!ValidStr(KdeTrash))
{
// Ask KDE where the current trash is...
LStringPipe o;
LSubProcess p("kde-config", "--userpath trash");
if (p.Start())
{
p.Communicate(&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", _FL);
}
}
else
{
printf("%s:%i - Run 'kde-config' failed.\n", _FL);
}
}
if (ValidStr(KdeTrash))
Path = KdeTrash;
break;
}
default:
{
LString Home = LGetSystemPath(LSP_HOME);
if (!Home)
{
LgiTrace("%s:%i - Can't get LSP_HOME.\n", _FL);
break;
}
char p[MAX_PATH_LEN];
if (!LMakePath(p, sizeof(p), Home, ".local/share/Trash/files") ||
!LDirExists(p))
{
LgiTrace("%s:%i - '%s' doesn't exist.\n", _FL, p);
break;
}
Path = p;
break;
}
}
#elif defined(HAIKU)
dev_t volume = dev_for_path("/boot");
char path[MAX_PATH_LEN] = "";
if (find_directory(B_TRASH_DIRECTORY, volume, true, path, sizeof(path)) == B_OK)
Path = path;
#elif defined LGI_CARBON
FSRef Ref;
OSErr e = FSFindFolder(kUserDomain, kTrashFolderType, kDontCreateFolder, &Ref);
if (e) printf("%s:%i - FSFindFolder failed e=%i\n", _FL, e);
else
{
LAutoString u = FSRefPath(Ref);
if (u)
Path = u.Get();
}
#elif defined LGI_COCOA
NSArray *paths = NSSearchPathForDirectoriesInDomains( NSTrashDirectory, NSUserDomainMask, YES);
if (paths)
{
Path = [paths objectAtIndex:0];
}
#elif defined(WIN32)
LAssert(0);
#endif
break;
}
}
return Path;
}
LString LGetExeFile()
{
#if defined WIN32
char16 Exe[MAX_PATH_LEN];
if (GetModuleFileNameW(NULL, Exe, CountOf(Exe)) > 0)
{
LString e = Exe;
if (e)
{
return e;
}
else
{
LgiMsg(0, "LgiFromNativeCp returned 0, ANSI CodePage=%i (%s)", "LgiGetExeFile Error", MB_OK, GetACP(), LAnsiToLgiCp());
return LString();
}
}
LString m;
m.Printf("GetModuleFileName failed err: %08.8X", GetLastError());
MessageBoxA(0, m, "LgiGetExeFile Error", MB_OK);
LExitApp();
#elif defined HAIKU
// Copy the string so as to not allow callers to change it
return LgiArgsAppPath.Get();
#elif defined LINUX
static char ExePathCache[MAX_PATH_LEN] = "";
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 = LFileExists(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());
LStringPipe Ps;
LSubProcess p("ps");
if (p.Start())
{
p.Communicate(&Ps);
char *PsOutput = Ps.NewStr();
if (PsOutput)
{
auto n = LString(PsOutput).SplitDelimit("\r\n");
for (int i=0; !Status && i 7)
{
int LinePid = 0;
for (int i=0; iReset(NewStr(Path));
return;
}
#ifdef WIN32
if (PathLen < sizeof(Path) - 4)
{
strcat(Path, ".lnk");
if (LResolveShortcut(Path, Path, sizeof(Path)))
{
if (GStr)
*GStr = Path;
else if (AStr)
AStr->Reset(NewStr(Path));
return;
}
}
#endif
}
// General search fall back...
LArray Ext;
LArray Files;
Ext.Add((char*)Name);
if (LRecursiveFileSearch(Exe, &Ext, &Files) &&
Files.Length())
{
if (GStr)
*GStr = Files[0];
else
{
AStr->Reset(Files[0]);
Files.DeleteAt(0);
}
}
Files.DeleteArrays();
}
LString LFindFile(const char *Name)
{
LString s;
_LFindFile(Name, &s, NULL);
return s;
}
#if defined WIN32
static LARGE_INTEGER Freq = {0};
static bool CurTimeInit = false;
#endif
uint64_t LCurrentTime()
{
#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 LGI_CARBON
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_t 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 LGI_CARBON
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 LIsReleaseBuild()
{
#ifdef _DEBUG
return 0;
#else
return 1;
#endif
}
bool LIsVolumeRoot(const char *Path)
{
if (!Path)
return false;
#ifdef WIN32
if
(
IsAlpha(Path[0])
&&
Path[1] == ':'
&&
(
(Path[2] == 0)
||
(Path[2] == '\\' && Path[3] == 0)
)
)
{
return true;
}
#else
auto t = LString(Path).SplitDelimit(DIR_STR);
if (t.Length() == 0)
return true;
#ifdef MAC
if (!stricmp(t[0], "Volumes") &&
t.Length() == 2)
return true;
#else
if (!stricmp(t[0], "mnt") &&
t.Length() == 2)
return true;
#endif
#endif
return false;
}
LString LFormatSize(int64_t Size)
{
char Buf[64];
LFormatSize(Buf, sizeof(Buf), Size);
return Buf;
}
void LFormatSize(char *Str, int SLen, int64_t Size)
{
int64_t K = 1024;
int64_t M = K * K;
int64_t G = K * M;
int64_t T = K * G;
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)Size;
sprintf_s(Str, SLen, "%.2f KiB", d / K);
}
else if (Size < M)
{
sprintf_s(Str, SLen, "%u KiB", (int) (Size / K));
}
else if (Size < G)
{
double d = (double)Size;
sprintf_s(Str, SLen, "%.2f MiB", d / M);
}
else if (Size < T)
{
double d = (double)Size;
sprintf_s(Str, SLen, "%.2f GiB", d / G);
}
else
{
double d = (double)Size;
sprintf_s(Str, SLen, "%.2f TiB", d / T);
}
}
char *LTokStr(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;
}
LString LGetEnv(const char *Var)
{
#ifdef _MSC_VER
char *s = NULL;
size_t sz;
errno_t err = _dupenv_s(&s, &sz, Var);
if (err)
return LString();
LString ret(s);
free(s);
return ret;
#else
return getenv("PATH");
#endif
}
LString::Array LGetPath()
{
LString::Array Paths;
#ifdef MAC
// OMG, WHY?! Seriously?
//
// The GUI application path is NOT the same as what is configured for the terminal.
// At least in 10.12. And I don't know how to make them the same. This works around
// that for the time being.
#if 1
LFile EctPaths("/etc/paths", O_READ);
Paths = EctPaths.Read().Split("\n");
#else
LFile::Path Home(LSP_HOME);
Home += ".profile";
if (!Home.Exists())
{
Home--;
Home += ".zprofile";
}
auto Profile = LFile(Home, O_READ).Read().Split("\n");
for (auto Ln : Profile)
{
auto p = Ln.SplitDelimit(" =", 2);
if (p.Length() == 3 &&
p[0].Equals("export") &&
p[1].Equals("PATH"))
{
Paths = p[2].Strip("\"").SplitDelimit(LGI_PATH_SEPARATOR);
Paths.SetFixedLength(false);
for (auto &p : Paths)
{
if (p.Equals("$PATH"))
{
auto SysPath = LGetEnv("PATH").SplitDelimit(LGI_PATH_SEPARATOR);
for (unsigned i=0; i 0)
{
Period = p;
}
}
bool DoEvery::DoNow()
{
int64 Now = LCurrentTime();
if (LastTime + Period < Now)
{
LastTime = Now;
return true;
}
return false;
}
//////////////////////////////////////////////////////////////////////
bool LCapabilityClient::NeedsCapability(const char *Name, const char *Param)
{
for (int i=0; iNeedsCapability(Name, Param);
return Targets.Length() > 0;
}
LCapabilityClient::~LCapabilityClient()
{
for (int i=0; iClients.Delete(this);
}
void LCapabilityClient::Register(LCapabilityTarget *t)
{
if (t && !Targets.HasItem(t))
{
Targets.Add(t);
t->Clients.Add(this);
}
}
LCapabilityTarget::~LCapabilityTarget()
{
for (int i=0; iTargets.Delete(this);
}
/////////////////////////////////////////////////////////////////////
#define BUF_SIZE (4 << 10)
#define PROFILE_MICRO 1
LProfile::LProfile(const char *Name, int HideMs)
{
MinMs = HideMs;
Used = 0;
Buf = NULL;
Add(Name);
}
LProfile::~LProfile()
{
Add("End");
uint64 TotalMs = s.Last().Time - s[0].Time;
if (MinMs > 0)
{
if (TotalMs < MinMs
#if PROFILE_MICRO
* 1000
#endif
)
{
return;
}
}
uint64 accum = 0;
for (int i=0; i BUF_SIZE - 64)
{
LAssert(0);
return;
}
char *Name = Buf + Used;
Used += sprintf_s(Name, BUF_SIZE - Used, "%s:%i", File, Line) + 1;
s.Add(Sample(
#if PROFILE_MICRO
LgiMicroTime(),
#else
LCurrentTime(),
#endif
Name));
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool LIsValidEmail(LString Email)
{
// Local part
char buf[321];
char *o = buf;
char *e = Email;
if (!Email || *e == '.')
return false;
#define OutputChar() \
if (o - buf >= sizeof(buf) - 1) \
return false; \
*o++ = *e++
// Local part
while (*e)
{
if (strchr("!#$%&\'*+-/=?^_`.{|}~", *e) ||
IsAlpha((uchar)*e) ||
IsDigit((uchar)*e))
{
OutputChar();
}
else if (*e == '\"')
{
// Quoted string
OutputChar();
bool quote = false;
while (*e && !quote)
{
quote = *e == '\"';
OutputChar();
}
}
else if (*e == '\\')
{
// Quoted character
e++;
if (*e < ' ' || *e >= 0x7f)
return false;
OutputChar();
}
else if (*e == '@')
{
break;
}
else
{
// Illegal character
return false;
}
}
// Process the '@'
if (*e != '@' || o - buf > 64)
return false;
OutputChar();
// Domain part...
if (*e == '[')
{
// IP addr
OutputChar();
// Initial char must by a number
if (!IsDigit(*e))
return false;
// Check the rest...
char *Start = e;
while (*e)
{
if (IsDigit(*e) ||
*e == '.')
{
OutputChar();
}
else
{
return false;
}
}
// Not a valid IP
if (e - Start > 15)
return false;
if (*e != ']')
return false;
OutputChar();
}
else
{
// Hostname, check initial char
if (!IsAlpha(*e) && !IsDigit(*e))
return false;
// Check the rest.
while (*e)
{
if (IsAlpha(*e) ||
IsDigit(*e) ||
strchr(".-", *e))
{
OutputChar();
}
else
{
return false;
}
}
}
// Remove any trailing dot/dash
while (strchr(".-", o[-1]))
o--;
// Output
*o = 0;
LAssert(o - buf <= sizeof(buf));
if (strcmp(Email, buf))
Email.Set(buf, o - buf);
return true;
}
//////////////////////////////////////////////////////////////////////////
LString LGetAppForProtocol(const char *Protocol)
{
LString App;
if (!Protocol)
return App;
#ifdef WINDOWS
LRegKey k(false, "HKEY_CLASSES_ROOT\\%s\\shell\\open\\command", Protocol);
if (k.IsOk())
{
const char *p = k.GetStr();
if (p)
{
LAutoString a(LTokStr(p));
App = a.Get();
}
}
#elif defined(LINUX)
const char *p = NULL;
if (stricmp(Protocol, "mailto"))
p = "xdg-email";
else
p = "xdg-open";
LString Path = LGetEnv("PATH");
LString::Array a = Path.SplitDelimit(LGI_PATH_SEPARATOR);
for (auto i : a)
{
LFile::Path t(i);
t += p;
if (t.Exists())
{
App = t.GetFull();
break;
}
}
#elif defined(__GTK_H__)
LAssert(!"What to do?");
#elif defined(MAC)
// Get the handler type
LString s;
s.Printf("%s://domain/path", Protocol);
auto str = s.NsStr();
auto type = [NSURL URLWithString:str];
[str release];
auto handlerUrl = [[NSWorkspace sharedWorkspace] URLForApplicationToOpenURL:type];
[type release];
if (handlerUrl)
{
// Convert to app path
s = [handlerUrl absoluteString];
LUri uri(s);
if (uri.sProtocol.Equals("file"))
App = uri.sPath.RStrip("/");
else
LgiTrace("%s:%i - Error: unknown protocol '%s'\n", _FL, uri.sProtocol.Get());
}
[handlerUrl release];
#else
#warning "Impl me."
#endif
return App;
}
diff --git a/src/common/Widgets/ItemContainer.cpp b/src/common/Widgets/ItemContainer.cpp
--- a/src/common/Widgets/ItemContainer.cpp
+++ b/src/common/Widgets/ItemContainer.cpp
@@ -1,1312 +1,1312 @@
#include "lgi/common/Lgi.h"
#include "lgi/common/ItemContainer.h"
#include "lgi/common/DisplayString.h"
#include "lgi/common/SkinEngine.h"
#include "lgi/common/ScrollBar.h"
#include "lgi/common/Edit.h"
#include "lgi/common/CssTools.h"
// Colours
#if defined(__GTK_H__)
#define DOUBLE_BUFFER_COLUMN_DRAWING 1
#else
#define DOUBLE_BUFFER_COLUMN_DRAWING 0
#endif
#if defined(WIN32)
#if !defined(WS_EX_LAYERED)
#define WS_EX_LAYERED 0x80000
#endif
#if !defined(LWA_ALPHA)
#define LWA_ALPHA 2
#endif
typedef BOOL (__stdcall *_SetLayeredWindowAttributes)(HWND hwnd, COLORREF crKey, BYTE bAlpha, DWORD dwFlags);
#endif
class LItemColumnPrivate
{
public:
LRect Pos;
bool Down;
bool Drag;
LItemContainer *Parent;
char *cName;
LDisplayString *Txt;
int cWidth;
int cType;
LSurface *cIcon;
int cImage;
int cMark;
bool OwnIcon;
bool CanResize;
LItemColumnPrivate(LItemContainer *parent)
{
Parent = parent;
Txt = 0;
cName = 0;
cWidth = 0;
cIcon = 0;
cType = GIC_ASK_TEXT;
cImage = -1;
cMark = GLI_MARK_NONE;
Down = false;
OwnIcon = false;
CanResize = true;
Drag = false;
}
~LItemColumnPrivate()
{
DeleteArray(cName);
if (OwnIcon)
{
DeleteObj(cIcon);
}
DeleteObj(Txt);
}
};
static LColour cActiveCol(0x86, 0xba, 0xe9);
static void FillStops(LArray &Stops, LRect &r, bool Active)
{
if (Active)
{
Stops[0].Set(0.0f, LColour(0xd0, 0xe2, 0xf5));
Stops[1].Set(2.0f / (r.Y() - 2), LColour(0x98, 0xc1, 0xe9));
Stops[2].Set(0.5f, LColour(0x86, 0xba, 0xe9));
Stops[3].Set(0.51f, LColour(0x68, 0xaf, 0xea));
Stops[4].Set(1.0f, LColour(0xbb, 0xfc, 0xff));
}
else
{
LColour cMed(L_MED), cWs(L_WORKSPACE);
if (cWs.GetGray() < 96)
{
cMed = cMed.Mix(LColour::White, 0.25f);
cWs = cWs.Mix(LColour::White, 0.25f);
}
Stops[0].Set(0.0f, cWs);
Stops[1].Set(0.5f, cMed.Mix(cWs));
Stops[2].Set(0.51f, cMed);
Stops[3].Set(1.0f, cWs);
}
}
//////////////////////////////////////////////////////////////////////////////////////
LItemContainer::LItemContainer()
{
Flags = 0;
DragMode = 0;
DragCol = NULL;
ColClick = -1;
ColumnHeaders = true;
ColumnHeader.ZOff(-1, -1);
Columns.SetFixedLength(true);
ItemEdit = NULL;
}
LItemContainer::~LItemContainer()
{
DeleteObj(ItemEdit);
DeleteObj(DragCol);
Columns.DeleteObjects();
}
void LItemContainer::PaintColumnHeadings(LSurface *pDC)
{
// Draw column headings
if (!ColumnHeaders || !ColumnHeader.Valid())
return;
LSurface *ColDC = pDC;
LRect cr;
#if DOUBLE_BUFFER_COLUMN_DRAWING
LMemDC Bmp;
if (!pDC->SupportsAlphaCompositing() &&
Bmp.Create(ColumnHeader.X(), ColumnHeader.Y(), System32BitColourSpace))
{
ColDC = &Bmp;
Bmp.Op(GDC_ALPHA);
cr = ColumnHeader.ZeroTranslate();
}
else
#endif
{
cr = ColumnHeader;
pDC->ClipRgn(&cr);
}
// Draw columns
int cx = cr.x1;
if (IconCol)
{
cr.x1 = cx;
cr.x2 = cr.x1 + IconCol->Width() - 1;
IconCol->SetPos(cr);
IconCol->OnPaint(ColDC, cr);
cx += IconCol->Width();
}
// Draw other columns
for (int i=0; iWidth() - 1;
c->SetPos(cr);
c->OnPaint(ColDC, cr);
cx += c->Width();
}
else LAssert(0);
}
// Draw ending piece
cr.x1 = cx;
cr.x2 = ColumnHeader.x2 + 2;
if (cr.Valid())
{
// Draw end section where there are no columns
#ifdef MAC
LArray Stops;
LRect j(cr.x1, cr.y1, cr.x2-1, cr.y2-1);
FillStops(Stops, j, false);
LFillGradient(ColDC, j, true, Stops);
ColDC->Colour(L_LOW);
ColDC->Line(cr.x1, cr.y2, cr.x2, cr.y2);
#else
if (LApp::SkinEngine)
{
LSkinState State;
State.pScreen = ColDC;
State.Rect = cr;
State.Enabled = Enabled();
State.View = this;
LApp::SkinEngine->OnPaint_ListColumn(0, 0, &State);
}
else
{
LWideBorder(ColDC, cr, DefaultRaisedEdge);
ColDC->Colour(LColour(L_MED));
ColDC->Rectangle(&cr);
}
#endif
}
#if DOUBLE_BUFFER_COLUMN_DRAWING
if (!pDC->SupportsAlphaCompositing())
pDC->Blt(ColumnHeader.x1, ColumnHeader.y1, &Bmp);
else
#endif
pDC->ClipRgn(0);
}
LItemColumn *LItemContainer::AddColumn(const char *Name, int Width, int Where)
{
LItemColumn *c = 0;
if (Lock(_FL))
{
c = new LItemColumn(this, Name, Width);
if (c)
{
Columns.SetFixedLength(false);
Columns.AddAt(Where, c);
Columns.SetFixedLength(true);
UpdateAllItems();
SendNotify(LNotifyItemColumnsChanged);
}
Unlock();
}
return c;
}
bool LItemContainer::AddColumn(LItemColumn *Col, int Where)
{
bool Status = false;
if (Col && Lock(_FL))
{
Columns.SetFixedLength(false);
Status = Columns.AddAt(Where, Col);
Columns.SetFixedLength(true);
if (Status)
{
UpdateAllItems();
SendNotify(LNotifyItemColumnsChanged);
}
Unlock();
}
return Status;
}
void LItemContainer::DragColumn(int Index)
{
DeleteObj(DragCol);
if (Index >= 0)
{
DragCol = new LDragColumn(this, Index);
if (DragCol)
{
Capture(true);
DragMode = DRAG_COLUMN;
}
}
}
int LItemContainer::ColumnAtX(int x, LItemColumn **Col, int *Offset)
{
LItemColumn *Column = 0;
if (!Col) Col = &Column;
int Cx = GetImageList() ? 16 : 0;
int c;
for (c=0; c= Cx && x < Cx + (*Col)->Width())
{
if (Offset)
*Offset = Cx;
return c;
}
Cx += (*Col)->Width();
}
return -1;
}
void LItemContainer::EmptyColumns()
{
Columns.DeleteObjects();
Invalidate(&ColumnHeader);
SendNotify(LNotifyItemColumnsChanged);
}
int LItemContainer::HitColumn(int x, int y, LItemColumn *&Resize, LItemColumn *&Over)
{
int Index = -1;
Resize = 0;
Over = 0;
if (ColumnHeaders &&
ColumnHeader.Overlap(x, y))
{
// Clicked on a column heading
int cx = ColumnHeader.x1 + ((IconCol) ? IconCol->Width() : 0);
for (int n = 0; n < Columns.Length(); n++)
{
LItemColumn *c = Columns[n];
cx += c->Width();
if (abs(x-cx) < 5)
{
if (c->Resizable())
{
Resize = c;
Index = n;
break;
}
}
else if (c->d->Pos.Overlap(x, y))
{
Over = c;
Index = n;
break;
}
}
}
return Index;
}
void LItemContainer::OnColumnClick(int Col, LMouse &m)
{
ColClick = Col;
ColMouse = m;
LNotification n(LNotifyItemColumnClicked);
n.Int[0] = Col;
SendNotify(n);
}
bool LItemContainer::GetColumnClickInfo(int &Col, LMouse &m)
{
if (ColClick >= 0)
{
Col = ColClick;
m = ColMouse;
return true;
}
return false;
}
void LItemContainer::GetColumnSizes(ColSizes &cs)
{
// Read in the current sizes
cs.FixedPx = 0;
cs.ResizePx = 0;
for (int i=0; iResizable())
{
ColInfo &Inf = cs.Info.New();
Inf.Col = c;
Inf.Idx = i;
Inf.ContentPx = c->GetContentSize();
Inf.WidthPx = c->Width();
cs.ResizePx += Inf.ContentPx;
}
else
{
cs.FixedPx += c->Width();
}
}
}
LMessage::Result LItemContainer::OnEvent(LMessage *Msg)
{
switch (Msg->Msg())
{
case M_RESIZE_TO_CONTENT:
{
ResizeColumnsToContent((int)Msg->A());
break;
}
default:
break;
}
return LLayout::OnEvent(Msg);
}
void LItemContainer::ResizeColumnsToContent(int Border)
{
if (!InThread())
{
PostEvent(M_RESIZE_TO_CONTENT, Border);
return;
}
if (Lock(_FL))
{
// Read in the current sizes
ColSizes Sizes;
GetColumnSizes(Sizes);
// Allocate space
int AvailablePx = GetClient().X() - 5;
if (VScroll)
AvailablePx -= VScroll->X();
int ExpandPx = AvailablePx - Sizes.FixedPx;
Sizes.Info.Sort([](auto a, auto b)
{
int AGrowPx = a->GrowPx();
int BGrowPx = b->GrowPx();
return AGrowPx - BGrowPx;
});
for (int i=0; iResizable())
{
if (ExpandPx > Sizes.ResizePx)
{
// Everything fits...
Inf.Col->Width(Inf.ContentPx + Border);
}
else
{
int Cx = GetClient().X();
double Ratio = Cx ? (double)Inf.ContentPx / Cx : 1.0;
if (Ratio < 0.25)
{
Inf.Col->Width(Inf.ContentPx + Border);
}
else
{
// Need to scale to fit...
int Px = Inf.ContentPx * ExpandPx / Sizes.ResizePx;
Inf.Col->Width(Px + Border);
}
}
ClearDs(Inf.Idx);
}
}
Unlock();
}
Invalidate();
}
//////////////////////////////////////////////////////////////////////////////
LDragColumn::LDragColumn(LItemContainer *list, int col)
{
List = list;
Index = col;
Offset = 0;
#ifdef LINUX
Back = 0;
#endif
Col = List->ColumnAt(Index);
if (Col)
{
Col->d->Down = false;
Col->d->Drag = true;
LRect r = Col->d->Pos;
r.y1 = 0;
r.y2 = List->Y()-1;
List->Invalidate(&r, true);
#if WINNATIVE
LArray Ver;
bool Layered = (
LGetOs(&Ver) == LGI_OS_WIN32 ||
LGetOs(&Ver) == LGI_OS_WIN64
) &&
Ver[0] >= 5;
SetStyle(WS_POPUP);
SetExStyle(GetExStyle() | WS_EX_TOOLWINDOW);
if (Layered)
{
SetExStyle(GetExStyle() | WS_EX_LAYERED | WS_EX_TRANSPARENT);
}
#endif
Attach(0);
#if WINNATIVE
if (Layered)
{
SetWindowLong(Handle(), GWL_EXSTYLE, GetWindowLong(Handle(), GWL_EXSTYLE) | WS_EX_LAYERED);
LLibrary User32("User32");
_SetLayeredWindowAttributes SetLayeredWindowAttributes = (_SetLayeredWindowAttributes)User32.GetAddress("SetLayeredWindowAttributes");
if (SetLayeredWindowAttributes)
{
if (!SetLayeredWindowAttributes(Handle(), 0, DRAG_COL_ALPHA, LWA_ALPHA))
{
DWORD Err = GetLastError();
}
}
}
#elif defined(__GTK_H__)
Gtk::GtkWindow *w = WindowHandle();
if (w)
{
gtk_window_set_decorated(w, FALSE);
gtk_widget_set_opacity(GtkCast(w, gtk_widget, GtkWidget), DRAG_COL_ALPHA / 255.0);
}
#endif
LMouse m;
List->GetMouse(m);
Offset = m.x - r.x1;
List->PointToScreen(ListScrPos);
r.Offset(ListScrPos.x, ListScrPos.y);
SetPos(r);
Visible(true);
}
}
LDragColumn::~LDragColumn()
{
Visible(false);
if (Col)
{
Col->d->Drag = false;
}
List->Invalidate();
}
#if LINUX_TRANS_COL
void LDragColumn::OnPosChange()
{
Invalidate();
}
#endif
void LDragColumn::OnPaint(LSurface *pScreen)
{
#if LINUX_TRANS_COL
LSurface *Buf = new LMemDC(X(), Y(), GdcD->GetBits());
LSurface *pDC = new LMemDC(X(), Y(), GdcD->GetBits());
#else
LSurface *pDC = pScreen;
#endif
pDC->SetOrigin(Col->d->Pos.x1, 0);
if (Col) Col->d->Drag = false;
List->OnPaint(pDC);
if (Col) Col->d->Drag = true;
pDC->SetOrigin(0, 0);
#if LINUX_TRANS_COL
if (Buf && pDC)
{
LRect p = GetPos();
// Fill the buffer with the background
Buf->Blt(ListScrPos.x - p.x1, 0, Back);
// Draw painted column over the back with alpha
Buf->Op(GDC_ALPHA);
LApplicator *App = Buf->Applicator();
if (App)
{
App->SetVar(GAPP_ALPHA_A, DRAG_COL_ALPHA);
}
Buf->Blt(0, 0, pDC);
// Put result on the screen
pScreen->Blt(0, 0, Buf);
}
DeleteObj(Buf);
DeleteObj(pDC);
#endif
}
///////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////
// List column
LItemColumn::LItemColumn(LItemContainer *parent, const char *name, int width)
: ResObject(Res_Column)
{
d = new LItemColumnPrivate(parent);
d->cWidth = width;
if (name)
Name(name);
}
LItemColumn::~LItemColumn()
{
if (d->Drag)
{
d->Parent->DragColumn(-1);
}
DeleteObj(d);
}
LItemContainer *LItemColumn::GetList()
{
return d->Parent;
}
void LItemColumn::Image(int i)
{
d->cImage = i;
}
int LItemColumn::Image()
{
return d->cImage;
}
bool LItemColumn::Resizable()
{
return d->CanResize;
}
void LItemColumn::Resizable(bool i)
{
d->CanResize = i;
}
bool LItemColumn::InDrag()
{
return d->Drag;
}
LRect LItemColumn::GetPos()
{
return d->Pos;
}
void LItemColumn::SetPos(LRect &r)
{
d->Pos = r;
}
void LItemColumn::Name(const char *n)
{
DeleteArray(d->cName);
DeleteObj(d->Txt);
d->cName = NewStr(n);
LFont *f = d->Parent &&
d->Parent->GetFont() &&
d->Parent->GetFont()->Handle() ?
d->Parent->GetFont() :
LSysFont;
d->Txt = new LDisplayString(f, (char*)n);
if (d->Parent)
{
d->Parent->Invalidate(&d->Parent->ColumnHeader);
}
}
char *LItemColumn::Name()
{
return d->cName;
}
int LItemColumn::GetIndex()
{
if (d->Parent)
{
return (int)d->Parent->Columns.IndexOf(this);
}
return -1;
}
int LItemColumn::GetContentSize()
{
return d->Parent->GetContentSize(GetIndex());
}
void LItemColumn::Width(int i)
{
if (d->cWidth != i)
{
d->cWidth = i;
// If we are attached to a list...
if (d->Parent)
{
/* FIXME
int MyIndex = GetIndex();
// Clear all the cached strings for this column
for (List::I it=d->Parent->Items.Start(); it.In(); it++)
{
DeleteObj((*it)->d->Display[MyIndex]);
}
if (d->Parent->IsAttached())
{
// Update the screen from this column across
LRect Up = d->Parent->GetClient();
Up.x1 = d->Pos.x1;
d->Parent->Invalidate(&Up);
}
*/
}
// Notify listener
auto p = d->Parent;
if (p)
p->SendNotify(LNotifyItemColumnsResized);
}
}
int LItemColumn::Width()
{
return d->cWidth;
}
void LItemColumn::Mark(int i)
{
d->cMark = i;
if (d->Parent)
{
d->Parent->Invalidate(&d->Parent->ColumnHeader);
}
}
int LItemColumn::Mark()
{
return d->cMark;
}
void LItemColumn::Type(int i)
{
d->cType = i;
if (d->Parent)
{
d->Parent->Invalidate(&d->Parent->ColumnHeader);
}
}
int LItemColumn::Type()
{
return d->cType;
}
void LItemColumn::Icon(LSurface *i, bool Own)
{
if (d->OwnIcon)
{
DeleteObj(d->cIcon);
}
d->cIcon = i;
d->OwnIcon = Own;
if (d->Parent)
{
d->Parent->Invalidate(&d->Parent->ColumnHeader);
}
}
LSurface *LItemColumn::Icon()
{
return d->cIcon;
}
bool LItemColumn::Value()
{
return d->Down;
}
void LItemColumn::Value(bool i)
{
d->Down = i;
}
void LItemColumn::OnPaint_Content(LSurface *pDC, LRect &r, bool FillBackground)
{
if (!d->Drag)
{
LCssTools Tools(d->Parent);
auto Fore = Tools.GetFore();
auto cMed = LColour(L_MED);
int Off = d->Down ? 1 : 0;
int Mx = r.x1 + 8, My = r.y1 + ((r.Y() - 8) / 2);
if (d->cIcon)
{
if (FillBackground)
{
pDC->Colour(cMed);
pDC->Rectangle(&r);
}
int x = (r.X()-d->cIcon->X()) / 2;
pDC->Blt( r.x1 + x + Off,
r.y1 + ((r.Y()-d->cIcon->Y())/2) + Off,
d->cIcon);
if (d->cMark)
{
Mx += x + d->cIcon->X() + 4;
}
}
else if (d->cImage >= 0 && d->Parent)
{
LColour Background = cMed;
if (FillBackground)
{
pDC->Colour(Background);
pDC->Rectangle(&r);
}
if (d->Parent->GetImageList())
{
LRect *b = d->Parent->GetImageList()->GetBounds();
int x = r.x1;
int y = r.y1;
if (b)
{
b += d->cImage;
x = r.x1 + ((r.X()-b->X()) / 2) - b->x1;
y = r.y1 + ((r.Y()-b->Y()) / 2) - b->y1;
}
d->Parent->GetImageList()->Draw(pDC,
x + Off,
y + Off,
d->cImage,
Background);
}
if (d->cMark)
{
Mx += d->Parent->GetImageList()->TileX() + 4;
}
}
else if (ValidStr(d->cName) && d->Txt)
{
LFont *f = d->Txt->GetFont();
if (!f)
{
LAssert(0);
return;
}
LColour cText = Fore;
#ifdef MAC
// Contrast check
if (d->cMark && (cText - cActiveCol) < 64)
cText = cText.Invert();
#endif
f->Transparent(!FillBackground);
f->Colour(cText, cMed);
int ty = d->Txt->Y();
int ry = r.Y();
int y = r.y1 + ((ry - ty) >> 1);
d->Txt->Draw(pDC, r.x1 + Off + 3, y + Off, &r);
if (d->cMark)
{
Mx += d->Txt->X();
}
}
else
{
if (FillBackground)
{
pDC->Colour(cMed);
pDC->Rectangle(&r);
}
}
#define ARROW_SIZE 9
pDC->Colour(Fore);
Mx += Off;
My += Off - 1;
switch (d->cMark)
{
case GLI_MARK_UP_ARROW:
{
pDC->Line(Mx + 2, My, Mx + 2, My + ARROW_SIZE - 1);
pDC->Line(Mx, My + 2, Mx + 2, My);
pDC->Line(Mx + 2, My, Mx + 4, My + 2);
break;
}
case GLI_MARK_DOWN_ARROW:
{
pDC->Line(Mx + 2, My, Mx + 2, My + ARROW_SIZE - 1);
pDC->Line( Mx,
My + ARROW_SIZE - 3,
Mx + 2,
My + ARROW_SIZE - 1);
pDC->Line( Mx + 2,
My + ARROW_SIZE - 1,
Mx + 4,
My + ARROW_SIZE - 3);
break;
}
}
}
}
void ColumnPaint(void *UserData, LSurface *pDC, LRect &r, bool FillBackground)
{
((LItemColumn*)UserData)->OnPaint_Content(pDC, r, FillBackground);
}
void LItemColumn::OnPaint(LSurface *pDC, LRect &Rgn)
{
LRect r = Rgn;
if (d->Drag)
{
pDC->Colour(DragColumnColour);
pDC->Rectangle(&r);
}
else
{
#ifdef MAC
LArray Stops;
LRect j(r.x1, r.y1, r.x2-1, r.y2-1);
FillStops(Stops, j, d->cMark != 0);
LFillGradient(pDC, j, true, Stops);
if (d->cMark)
pDC->Colour(Rgb24(0x66, 0x93, 0xc0), 24);
else
pDC->Colour(Rgb24(178, 178, 178), 24);
pDC->Line(r.x1, r.y2, r.x2, r.y2);
pDC->Line(r.x2, r.y1, r.x2, r.y2);
LRect n = r;
n.Inset(2, 2);
OnPaint_Content(pDC, n, false);
#else
if (LApp::SkinEngine)
{
LSkinState State;
- State.pScreen = pDC;
- State.ptrText = &d->Txt;
- State.Rect = Rgn;
- State.Value = Value();
- State.Enabled = GetList()->Enabled();
- State.View = d->Parent;
+ State.pScreen = pDC;
+ State.ptrText = &d->Txt;
+ State.Rect = Rgn;
+ State.Value = Value();
+ State.Enabled = GetList()->Enabled();
+ State.View = d->Parent;
LApp::SkinEngine->OnPaint_ListColumn(ColumnPaint, this, &State);
}
else
{
if (d->Down)
{
LThinBorder(pDC, r, DefaultSunkenEdge);
LFlatBorder(pDC, r, 1);
}
else
{
LWideBorder(pDC, r, DefaultRaisedEdge);
}
OnPaint_Content(pDC, r, true);
}
#endif
}
}
///////////////////////////////////////////////////////////////////////////////////////////
LItem::LItem()
{
SelectionStart = SelectionEnd = -1;
}
LItem::~LItem()
{
}
LView *LItem::EditLabel(int Col)
{
LItemContainer *c = GetContainer();
if (!c)
return NULL;
c->Capture(false);
if (!c->ItemEdit)
{
c->ItemEdit = new LItemEdit(c, this, Col, SelectionStart, SelectionEnd);
SelectionStart = SelectionEnd = -1;
}
return c->ItemEdit;
}
void LItem::OnEditLabelEnd()
{
LItemContainer *c = GetContainer();
if (c)
c->ItemEdit = NULL;
}
void LItem::SetEditLabelSelection(int SelStart, int SelEnd)
{
SelectionStart = SelStart;
SelectionEnd = SelEnd;
}
////////////////////////////////////////////////////////////////////////////////////////////
#define M_END_POPUP (M_USER+0x1500)
#define M_LOSING_FOCUS (M_USER+0x1501)
class LItemEditBox : public LEdit
{
LItemEdit *ItemEdit;
public:
LItemEditBox(LItemEdit *i, int x, int y, const char *s) : LEdit(100, 1, 1, x-3, y-3, s)
{
ItemEdit = i;
Sunken(false);
MultiLine(false);
#ifndef LINUX
SetPos(GetPos());
#endif
}
const char *GetClass() { return "LItemEditBox"; }
void OnCreate()
{
LEdit::OnCreate();
Focus(true);
}
void OnFocus(bool f)
{
if (!f && GetParent())
{
#if DEBUG_EDIT_LABEL
LgiTrace("%s:%i - LItemEditBox posting M_LOSING_FOCUS\n", _FL);
#endif
GetParent()->PostEvent(M_LOSING_FOCUS);
}
LEdit::OnFocus(f);
}
bool OnKey(LKey &k)
{
/* This should be handled by LEdit::OnKey now.
Which will send a LNotifyEscapeKey or LNotifyReturnKey
up to the ItemEdit OnNotify handler.
switch (k.vkey)
{
case LK_RETURN:
case LK_ESCAPE:
{
if (k.Down())
ItemEdit->OnNotify(this, k.c16);
return true;
}
}
*/
return LEdit::OnKey(k);
}
bool SetScrollBars(bool x, bool y)
{
return false;
}
};
//////////////////////////////////////////////////////////////////////////////////////////
class LItemEditPrivate
{
public:
LItem *Item;
LEdit *Edit;
int Index;
bool Esc;
LItemEditPrivate()
{
Esc = false;
Item = 0;
Index = 0;
}
};
LItemEdit::LItemEdit(LView *parent, LItem *item, int index, int SelStart, int SelEnd)
: LPopup(parent)
{
d = new LItemEditPrivate;
d->Item = item;
d->Index = index;
_BorderSize = 0;
Sunken(false);
Raised(false);
#if DEBUG_EDIT_LABEL
LgiTrace("%s:%i - LItemEdit(%p/%s, %i, %i, %i)\n",
_FL,
parent, parent?parent->GetClass():0,
index,
SelStart, SelEnd);
#endif
LPoint p;
SetParent(parent);
GetParent()->PointToScreen(p);
LRect r = d->Item->GetPos(d->Index);
int MinY = 6 + LSysFont->GetHeight();
if (r.Y() < MinY)
r.y2 = r.y1 + MinY - 1;
r.Offset(p.x, p.y);
SetPos(r);
if (Attach(parent))
{
d->Edit = new LItemEditBox(this, r.X(), r.Y(), d->Item->GetText(d->Index));
if (d->Edit)
{
d->Edit->Attach(this);
d->Edit->Focus(true);
if (SelStart >= 0)
{
d->Edit->Select(SelStart, SelEnd-SelStart+1);
}
}
Visible(true);
}
}
LItemEdit::~LItemEdit()
{
if (d->Item)
{
if (d->Edit && !d->Esc)
{
auto Str = d->Edit->Name();
#if DEBUG_EDIT_LABEL
LgiTrace("%s:%i - ~LItemEdit, updating item(%i) with '%s'\n", _FL,
d->Index, Str);
#endif
LItemContainer *c = d->Item->GetContainer();
if (d->Item->SetText(Str, d->Index))
{
d->Item->Update();
}
else
{
// Item is deleting itself...
// Make sure there is no dangling ptr on the container..
if (c) c->ItemEdit = NULL;
// And we don't touch the no longer existant item..
d->Item = NULL;
}
}
#if DEBUG_EDIT_LABEL
else LgiTrace("%s:%i - Edit=%p Esc=%i\n", _FL, d->Edit, d->Esc);
#endif
if (d->Item)
d->Item->OnEditLabelEnd();
}
#if DEBUG_EDIT_LABEL
else LgiTrace("%s:%i - Error: No item?\n", _FL);
#endif
DeleteObj(d);
}
LItem *LItemEdit::GetItem()
{
return d->Item;
}
void LItemEdit::OnPaint(LSurface *pDC)
{
pDC->Colour(L_BLACK);
pDC->Rectangle();
}
int LItemEdit::OnNotify(LViewI *v, LNotification n)
{
switch (v->GetId())
{
case 100:
{
if (n.Type == LNotifyEscapeKey)
{
d->Esc = true;
#if DEBUG_EDIT_LABEL
LgiTrace("%s:%i - LItemEdit got escape\n", _FL);
#endif
}
if (n.Type == LNotifyEscapeKey || n.Type == LNotifyReturnKey)
{
#if DEBUG_EDIT_LABEL
LgiTrace("%s:%i - LItemEdit hiding on esc/enter\n", _FL);
#endif
d->Edit->KeyProcessed();
Visible(false);
}
break;
}
}
return 0;
}
void LItemEdit::Visible(bool i)
{
LPopup::Visible(i);
if (!i)
{
#if DEBUG_EDIT_LABEL
LgiTrace("%s:%i - LItemEdit posting M_END_POPUP\n", _FL);
#endif
PostEvent(M_END_POPUP);
}
}
bool LItemEdit::OnKey(LKey &k)
{
if (d->Edit)
return d->Edit->OnKey(k);
return false;
}
void LItemEdit::OnFocus(bool f)
{
if (f && d->Edit)
d->Edit->Focus(true);
}
LMessage::Result LItemEdit::OnEvent(LMessage *Msg)
{
switch (Msg->Msg())
{
case M_LOSING_FOCUS:
{
#if DEBUG_EDIT_LABEL
LgiTrace("%s:%i - LItemEdit get M_LOSING_FOCUS\n", _FL);
#endif
// One of us has to retain focus... don't care which control.
if (Focus() || d->Edit->Focus())
break;
// else fall thru to end the popup
#if DEBUG_EDIT_LABEL
LgiTrace("%s:%i - LItemEdit falling thru to M_END_POPUP\n", _FL);
#endif
}
case M_END_POPUP:
{
#if DEBUG_EDIT_LABEL
LgiTrace("%s:%i - LItemEdit got M_END_POPUP, quiting\n", _FL);
#endif
if (d->Item &&
d->Item->GetContainer())
{
d->Item->GetContainer()->Focus(true);
}
Quit();
return 0;
}
}
return LPopup::OnEvent(Msg);
}
diff --git a/src/common/Widgets/ToolBar.cpp b/src/common/Widgets/ToolBar.cpp
--- a/src/common/Widgets/ToolBar.cpp
+++ b/src/common/Widgets/ToolBar.cpp
@@ -1,1756 +1,1756 @@
/*
** FILE: GToolbar.cpp
** AUTHOR: Matthew Allen
** DATE: 18/10/2001
** DESCRIPTION: Toolbar classes
**
** Copyright (C) 2001, Matthew Allen
** fret@memecode.com
*/
#include
#include
#include "lgi/common/Lgi.h"
#include "lgi/common/Variant.h"
#include "lgi/common/DisplayString.h"
#include "lgi/common/Palette.h"
#include "lgi/common/Notifications.h"
#include "lgi/common/LgiRes.h"
#include "lgi/common/CssTools.h"
#include "lgi/common/ToolBar.h"
#include "lgi/common/ToolTip.h"
#include "lgi/common/Menu.h"
#define ToolBarHilightColour LC_HIGH
#ifdef WIN32
HPALETTE GetSystemPalette();
bool BltBmpToBmp(HBITMAP hDest, int xDst, int yDst, int cx, int cy, HBITMAP hSrc, int xSrc, int ySrc, DWORD dwRop);
bool BltBmpToDc(HDC DestDC, int xDst, int yDst, int cx, int cy, HBITMAP hSrc, int xSrc, int ySrc, DWORD dwRop);
bool BltDcToBmp(HBITMAP hDest, int xDst, int yDst, int cx, int cy, HDC SrcDC, int xSrc, int ySrc, DWORD dwRop);
#define AttachButton(b) AddView(b);
#else
#define AttachButton(b) b->Attach(this);
#endif
enum IconCacheType
{
IconNormal,
IconHilight,
IconDisabled
};
COLOUR Map(LSurface *pDC, COLOUR c);
////////////////////////////////////////////////////////////////////////
LImageList *LLoadImageList(const char *File, int x, int y)
{
if (!File)
return NULL;
if (x < 0 || y < 0)
{
// Detect dimensions in the filename.
auto leaf = LString(File).Split(DIR_STR).Last();
auto parts = leaf.RSplit(".", 1);
auto last = parts[0].Split("-").Last();
auto dim = last.Split("x");
if (dim.Length() == 1)
{
auto i = dim[0].Strip().Int();
if (i > 0)
x = y = (int)i;
}
else if (dim.Length() == 2)
{
auto X = dim[0].Strip().Int(), Y = dim[1].Strip().Int();
if (X > 0 && Y > 0)
{
x = (int)X;
y = (int)Y;
}
}
}
auto Path = LFileExists(File) ? LString(File) : LFindFile(File);
if (!Path)
{
LgiTrace("%s:%i - Couldn't find '%s'\n", _FL, File);
return NULL;
}
LAutoPtr pDC(GdcD->Load(Path));
if (!pDC)
{
LgiTrace("%s:%i - Couldn't load '%s'\n", _FL, Path.Get());
return NULL;
}
return new LImageList(x, y, pDC);
}
LToolBar *LgiLoadToolbar(LViewI *Parent, const char *File, int x, int y)
{
LToolBar *Toolbar = new LToolBar;
if (!Toolbar)
return NULL;
LString FileName = LFindFile(File);
if (FileName)
{
bool Success = FileName && Toolbar->SetBitmap(FileName, x, y);
if (!Success)
{
LgiMsg(Parent,
"Can't load '%s' for the toolbar.\n"
"This is probably because libpng/libjpeg is missing.",
"LgiLoadToolbar",
MB_OK,
File);
}
}
else
{
LgiMsg(Parent,
"Can't find the graphic '%s' for the toolbar.\n"
"You can find it in this program's archive.",
"LgiLoadToolbar",
MB_OK,
File);
}
return Toolbar;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////
#define ImgLst_Empty 0x40000000
#define IgmLst_Add 0x80000000
class LImageListPriv
{
public:
LImageList *ImgLst;
int Sx, Sy;
uint8_t DisabledAlpha;
struct CacheDC : public LMemDC
{
bool Disabled;
LColour Back;
};
LArray Cache;
LArray Bounds;
CacheDC *GetCache(LColour Back, bool Disabled)
{
if (Back.IsTransparent())
return NULL;
for (int i=0; iBack == Back &&
dc->Disabled == Disabled)
return dc;
}
CacheDC *dc = new CacheDC;
if (dc)
{
dc->Disabled = Disabled;
dc->Back = Back;
bool Status = dc->Create(ImgLst->X(), ImgLst->Y(), GdcD->GetColourSpace());
if (Status)
{
dc->Colour(dc->Back);
dc->Rectangle();
dc->Op(GDC_ALPHA);
if (Disabled)
{
LMemDC tmp(ImgLst->X(), ImgLst->Y(), System32BitColourSpace, LSurface::SurfaceRequireExactCs);
tmp.Colour(0, 32);
tmp.Rectangle();
tmp.Op(GDC_ALPHA);
tmp.Blt(0, 0, ImgLst);
tmp.SetConstantAlpha(DisabledAlpha);
dc->Blt(0, 0, &tmp);
}
else
{
dc->Blt(0, 0, ImgLst);
}
Cache.Add(dc);
}
else
{
delete dc;
LAssert(!"Create memdc failed.");
}
}
return dc;
}
LImageListPriv(LImageList *imglst, int x, int y)
{
ImgLst = imglst;
Sx = x;
Sy = y;
DisabledAlpha = 40;
}
~LImageListPriv()
{
Cache.DeleteObjects();
}
};
static bool HasPad(LColourSpace cs)
{
if (cs == CsRgbx32 ||
cs == CsBgrx32 ||
cs == CsXrgb32 ||
cs == CsXbgr32)
return true;
return false;
}
LImageList::LImageList(int x, int y, LSurface *pDC)
{
d = new LImageListPriv(this, x, y);
uint32_t Transparent = 0;
if (pDC &&
Create(pDC->X(), pDC->Y(), System32BitColourSpace, LSurface::SurfaceRequireExactCs))
{
Colour(Transparent, 32);
Rectangle();
int Old = Op(GDC_ALPHA);
Blt(0, 0, pDC);
Op(Old);
#if 0
printf("Toolbar input image is %s, has_alpha=%i, has_pad=%i\n",
LColourSpaceToString(pDC->GetColourSpace()),
pDC->HasAlpha(),
HasPad(pDC->GetColourSpace()));
#endif
- #if 1
+ #if 0
static int Idx = 0;
char s[256];
sprintf_s(s, sizeof(s), "imglst_%i.bmp", Idx++);
GdcD->Save(s, this);
// sprintf_s(s, sizeof(s), "src_%i.bmp", Idx++);
// GdcD->Save(s, pDC);
#endif
if (pDC->GetBits() < 32 || HasPad(pDC->GetColourSpace()))
{
if (!pDC->HasAlpha())
{
// No source alpha, do colour keying to create the alpha channel
REG uint32_t *p = (uint32_t*)(*this)[0];
if (p)
{
uint32_t key = *p;
for (int y=0; ya == 0)
{
p->r = 0;
p->g = 0;
p->b = 0;
}
p++;
}
}
}
}
}
LImageList::~LImageList()
{
DeleteObj(d);
}
void LImageList::Draw(LSurface *pDC, int Dx, int Dy, int Image, LColour Background, bool Disabled)
{
if (!pDC)
return;
LRect rSrc;
rSrc.ZOff(d->Sx-1, d->Sy-1);
rSrc.Offset(Image * d->Sx, 0);
LImageListPriv::CacheDC *Cache = d->GetCache(Background, Disabled);
if (!Cache && Background.IsValid())
{
LRect rDst;
rDst.ZOff(d->Sx-1, d->Sy-1);
rDst.Offset(Dx, Dy);
pDC->Colour(Background);
pDC->Rectangle(&rDst);
pDC->Colour(LColour(255, 0, 0));
pDC->Line(rDst.x1, rDst.y1, rDst.x2, rDst.y2);
pDC->Line(rDst.x2, rDst.y1, rDst.x1, rDst.y2);
return;
}
if (pDC->SupportsAlphaCompositing())
{
int Old = pDC->Op(GDC_ALPHA, Disabled ? d->DisabledAlpha : -1);
pDC->Blt(Dx, Dy, this, &rSrc);
pDC->Op(Old);
}
else if (Cache)
{
pDC->Blt(Dx, Dy, Cache, &rSrc);
}
else LAssert(!"Impl me.");
}
int LImageList::TileX()
{
return d->Sx;
}
int LImageList::TileY()
{
return d->Sy;
}
int LImageList::GetItems()
{
return X() / d->Sx;
}
void LImageList::Update(int Flags)
{
}
uint8_t LImageList::GetDisabledAlpha()
{
return d->DisabledAlpha;
}
void LImageList::SetDisabledAlpha(uint8_t alpha)
{
d->DisabledAlpha = alpha;
}
LRect LImageList::GetIconRect(int Idx)
{
LRect r(0, 0, -1, -1);
if (Idx >= 0 && Idx < GetItems())
{
r.ZOff(d->Sx-1, d->Sy-1);
r.Offset(Idx * d->Sx, 0);
}
return r;
}
LRect *LImageList::GetBounds()
{
if (!d->Bounds.Length() && (*this)[0])
{
int Items = GetItems();
if (d->Bounds.Length(Items))
{
for (int i=0; iBounds[i].ZOff(d->Sx - 1, d->Sy - 1);
d->Bounds[i].Offset(i * d->Sx, 0);
LFindBounds(this, &d->Bounds[i]);
d->Bounds[i].Offset(-i * d->Sx, 0);
}
}
}
return &d->Bounds[0];
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////
class LToolBarPrivate
{
public:
int Bx, By;
int Sx, Sy;
bool Vertical;
bool Text;
int LastIndex;
bool OwnImgList;
LImageList *ImgList;
LFont *Font;
LToolTip *Tip;
// Customization menu
LDom *CustomDom;
const char *CustomProp;
// bitmap cache
LAutoPtr IconCache;
LToolBarPrivate()
{
Bx = By = 16;
Sx = Sy = 10;
Vertical = false;
Text = false;
Font = 0;
Tip = 0;
CustomProp = 0;
CustomDom = 0;
}
bool ShowTextLabels()
{
return (Text || !ImgList) && Font;
}
void FixSeparators(LToolBar *Tb)
{
// Fix up separators so that no 2 separators are next to each other. I.e.
// all the buttons between them are switched off.
LToolButton *Last = 0;
bool HasVis = false;
for (LViewI *v: Tb->IterateViews())
{
LToolButton *Btn = dynamic_cast(v);
if (Btn)
{
if (Btn->Separator())
{
Btn->Visible(HasVis);
if (HasVis)
{
Last = Btn;
}
HasVis = false;
}
else
{
HasVis |= Btn->Visible();
}
}
}
if (Last)
{
Last->Visible(HasVis);
}
}
void Customizable(LToolBar *Tb)
{
LVariant v;
if (CustomDom)
{
CustomDom->GetValue(CustomProp, v);
}
char *o;
if ((o = v.Str()))
{
auto t = LString(o).SplitDelimit(",");
if (t.Length() >= 1)
{
Text = stricmp(t[0], "text") == 0;
// Make all controls not visible.
for (auto v: Tb->IterateViews())
{
LToolButton *Btn = dynamic_cast(v);
if (Btn) v->Visible(false);
}
// Set sub-set of ctrls visible according to saved ID list
for (int i=1; i 0)
Tb->SetCtrlVisible(Id, true);
}
FixSeparators(Tb);
}
}
}
};
/////////////////////////////////////////////////////////////////////////////////////////////////////////
struct LToolButtonPriv
{
LArray Text;
};
LToolButton::LToolButton(int Bx, int By)
{
d = new LToolButtonPriv;
Type = TBT_PUSH;
SetId(IDM_NONE);
Down = false;
Clicked = false;
Over = false;
ImgIndex = -1;
NeedsRightClick = false;
LRect r(0, 0, Bx+1, By+1);
SetPos(r);
SetParent(0);
TipId = -1;
_BorderSize = 0;
LResources::StyleElement(this);
}
LToolButton::~LToolButton()
{
d->Text.DeleteObjects();
delete d;
}
bool LToolButton::Name(const char *n)
{
bool s = LView::Name(n);
d->Text.DeleteObjects();
return s;
}
void LToolButton::Layout()
{
auto Parent = GetParent();
LToolBar *ToolBar = dynamic_cast(Parent);
if (!ToolBar)
return;
// Text
auto s = Name();
if (!ToolBar->d->ShowTextLabels() || !s)
return;
// Write each word centered on a different line
char Buf[256];
strcpy_s(Buf, sizeof(Buf), s);
auto t = LString(Buf).SplitDelimit(" ");
if (t.Length() < 3)
{
if (t.Length() > 0)
d->Text.Add(new LDisplayString(ToolBar->d->Font, t[0]));
if (t.Length() > 1)
d->Text.Add(new LDisplayString(ToolBar->d->Font, t[1]));
}
else if (t.Length() == 3)
{
sprintf_s(Buf, sizeof(Buf), "%s %s", t[0].Get(), t[1].Get());
LDisplayString *d1 = new LDisplayString(ToolBar->d->Font, Buf);
sprintf_s(Buf, sizeof(Buf), "%s %s", t[1].Get(), t[2].Get());
LDisplayString *d2 = new LDisplayString(ToolBar->d->Font, Buf);
if (d1 && d2)
{
if (d1->X() < d2->X())
{
DeleteObj(d2);
d->Text.Add(d1);
d->Text.Add(new LDisplayString(ToolBar->d->Font, t[2]));
}
else
{
DeleteObj(d1);
d->Text.Add(new LDisplayString(ToolBar->d->Font, t[0]));
d->Text.Add(d2);
}
}
}
}
void LToolButton::OnPaint(LSurface *pDC)
{
LToolBar *Par = dynamic_cast(GetParent());
bool e = Enabled();
if (Par)
{
LRect p = GetClient();
#if 0 // def _DEBUG
pDC->Colour(LColour(255, 0, 255));
pDC->Rectangle();
#endif
LCssTools Tools(this);
LColour cBack = Tools.GetBack();
auto BackImg = Tools.GetBackImage();
bool Hilight = e && Over;
if (Hilight)
cBack = cBack.Mix(LColour::White);
// Draw Background
if (GetId() >= 0)
{
// Draw border
LColour Background;
if (Down) // Sunken if the button is pressed
LThinBorder(pDC, p, DefaultSunkenEdge);
if (BackImg)
{
LDoubleBuffer Buf(pDC);
Tools.PaintContent(pDC, p);
if (Hilight)
{
// Draw translucent white over image...
pDC->Op(GDC_ALPHA);
pDC->Colour(LColour(255, 255, 255, 128));
pDC->Rectangle(&p);
}
}
else
{
Background = cBack;
pDC->Colour(Background);
pDC->Box(&p);
p.Inset(1, 1);
}
LRect IconPos;
if (Par->d->ImgList)
IconPos.Set(0, 0, Par->d->ImgList->TileX()-1, Par->d->ImgList->TileY()-1);
else
IconPos.ZOff(Par->d->Bx-1, Par->d->By-1);
LRegion Unpainted(p);
// Center the icon
if (IconPos.X() < p.X() - 1)
IconPos.Offset((p.X() - IconPos.X()) >> 1, 0);
// Offset it if the button is pressed
if (Down)
IconPos.Offset(2, 2);
// Draw any icon.
if (ImgIndex >= 0)
{
if (Par->d->ImgList)
{
// Draw cached
if (BackImg)
{
Par->d->ImgList->SetDisabledAlpha(0x60);
}
else if (pDC->SupportsAlphaCompositing())
{
pDC->Colour(Background);
pDC->Rectangle(&IconPos);
}
Par->d->ImgList->Draw(pDC, IconPos.x1, IconPos.y1, ImgIndex, Background, !e);
Unpainted.Subtract(&IconPos);
if (!BackImg)
{
// Fill in the rest of the area
pDC->Colour(Background);
for (LRect *r = Unpainted.First(); r; r = Unpainted.Next())
pDC->Rectangle(r);
}
}
else
{
// Draw a red cross indicating no icons.
pDC->Colour(Background);
pDC->Rectangle(&p);
pDC->Colour(LColour::Red);
pDC->Line(IconPos.x1, IconPos.y1, IconPos.x2, IconPos.y2);
pDC->Line(IconPos.x2, IconPos.y1, IconPos.x1, IconPos.y2);
}
}
else if (!BackImg)
{
Tools.PaintContent(pDC, p);
}
// Text
if (Par->d->ShowTextLabels())
{
if (Name() && !d->Text.Length())
{
Layout();
}
if (d->Text.Length())
{
// Write each word centered on a different line
int Ty = Down + Par->d->By + 2;
LColour a = Tools.GetFore();
LColour b = Tools.GetBack();
if (!e)
a = b.Mix(a);
Par->d->Font->Colour(a, b);
for (int i=0; iText.Length(); i++)
{
LDisplayString *Ds = d->Text[i];
Ds->Draw(pDC, Down + ((X()-Ds->X())/2), Ty);
Ty += Ds->Y();
}
}
}
}
else
{
// Separator
int Px = X()-1;
int Py = Y()-1;
if (BackImg)
Tools.PaintContent(pDC, p);
else
{
pDC->Colour(cBack);
pDC->Rectangle();
}
LColour cLow = cBack.Mix(LColour::Black);
LColour cHigh = cBack.Mix(LColour::White, 0.8f);
if (X() > Y())
{
int c = Y()/2-1;
pDC->Colour(cLow);
pDC->Line(2, c, Px-2, c);
pDC->Colour(cHigh);
pDC->Line(2, c+1, Px-2, c+1);
}
else
{
int c = X()/2-1;
pDC->Colour(cLow);
pDC->Line(c, 2, c, Py-2);
pDC->Colour(cHigh);
pDC->Line(c+1, 2, c+1, Py-2);
}
}
}
#if 0 // def _DEBUG
pDC->Colour(LColour(255, 0, 255));
pDC->Box();
#endif
}
void LToolButton::Image(int i)
{
if (ImgIndex != i)
{
ImgIndex = i;
Invalidate();
}
}
void LToolButton::Value(int64 b)
{
switch (Type)
{
case TBT_PUSH:
{
// do nothing... can't set value
break;
}
case TBT_TOGGLE:
{
if (Value() != b)
{
Down = b != 0;
Invalidate();
SendNotify(LNotifyValueChanged);
}
break;
}
case TBT_RADIO:
{
if (GetParent() && b)
{
// Clear any other radio buttons that are down
auto it = GetParent()->IterateViews();
ssize_t CurIdx = it.IndexOf(this);
if (CurIdx >= 0)
{
for (ssize_t i=CurIdx-1; i>=0; i--)
{
LToolButton *But = dynamic_cast(it[i]);
if (But->Separator())
break;
if (But->Type == TBT_RADIO && But->Down)
But->Value(false);
}
for (size_t i=CurIdx+1; i(it[i]);
if (But->Separator())
break;
if (But->Type == TBT_RADIO && But->Down)
But->Value(false);
}
}
}
Down = b != 0;
if (GetParent())
{
GetParent()->Invalidate();
SendNotify(LNotifyValueChanged);
}
break;
}
}
}
void LToolButton::SendCommand()
{
LToolBar *t = dynamic_cast(GetParent());
if (t)
t->OnButtonClick(this);
else
printf("%s:%i - Error: parent not toolbar.\n", _FL);
}
void LToolButton::OnMouseClick(LMouse &m)
{
LToolBar *ToolBar = dynamic_cast(GetParent());
#if 0
printf("tool button click %i,%i down=%i, left=%i right=%i middle=%i, ctrl=%i alt=%i shift=%i Double=%i\n",
m.x, m.y,
m.Down(), m.Left(), m.Right(), m.Middle(),
m.Ctrl(), m.Alt(), m.Shift(), m.Double());
#endif
if (m.IsContextMenu())
{
if (!NeedsRightClick &&
ToolBar &&
ToolBar->IsCustomizable())
{
m.ToScreen();
ToolBar->ContextMenu(m);
}
else
{
SendNotify(LNotification(m));
}
}
else if (m.Left())
{
// left click action...
if (GetId() >= 0 && Enabled())
{
switch (Type)
{
case TBT_PUSH:
{
bool Old = Down;
Clicked = m.Down();
Capture(m.Down());
if (Old && IsOver(m))
{
SendCommand();
SendNotify(LNotifyActivate);
}
Down = m.Down();
if (Old != Down)
{
Invalidate();
}
break;
}
case TBT_TOGGLE:
{
if (m.Down())
{
if (m.Left())
{
Value(!Down);
SendCommand();
}
SendNotify(LNotifyActivate);
}
break;
}
case TBT_RADIO:
{
if (m.Down())
{
if (!Down && m.Left())
{
Value(true);
SendCommand();
}
SendNotify(LNotifyActivate);
}
break;
}
}
}
}
}
void LToolButton::OnMouseEnter(LMouse &m)
{
if (!Separator() && Enabled())
{
Over = true;
Invalidate();
}
if (Clicked)
{
Value(true);
Invalidate();
}
else
{
LToolBar *Bar = dynamic_cast(GetParent());
if (Bar)
{
Bar->OnMouseEnter(m);
if (!Bar->TextLabels() &&
Bar->d->Tip &&
TipId < 0)
{
TipId = Bar->d->Tip->NewTip(Name(), GetPos());
}
}
if (GetParent())
{
LToolBar *ToolBar = dynamic_cast(GetParent());
if (ToolBar) ToolBar->PostDescription(this, Name());
}
}
}
void LToolButton::OnMouseMove(LMouse &m)
{
}
void LToolButton::OnMouseExit(LMouse &m)
{
if (Over)
{
Over = false;
Invalidate();
}
if (Clicked)
{
Value(false);
Invalidate();
}
else if (GetParent())
{
LToolBar *ToolBar = dynamic_cast(GetParent());
if (ToolBar) ToolBar->PostDescription(this, "");
}
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////
LToolBar::LToolBar()
{
d = new LToolBarPrivate;
Name("LGI_Toolbar");
_BorderSize = 1;
_IsToolBar = 1;
// Setup tool button font
LFontType SysFontType;
if (SysFontType.GetSystemFont("Small"))
{
d->Font = SysFontType.Create();
if (d->Font)
{
d->Font->PointSize(MIN(d->Font->PointSize(), LSysFont->PointSize()));
d->Font->Colour(L_TEXT);
d->Font->Bold(false);
d->Font->Transparent(true);
}
}
d->LastIndex = 0;
d->OwnImgList = false;
d->ImgList = 0;
GetCss(true)->BackgroundColor(LColour(L_MED).Mix(LColour::Black, 0.05f));
LResources::StyleElement(this);
}
LToolBar::~LToolBar()
{
DeleteObj(d->Tip);
if (d->OwnImgList)
DeleteObj(d->ImgList);
DeleteObj(d->Font);
DeleteObj(d);
}
void LToolBar::OnCreate()
{
#ifndef WIN32
AttachChildren();
#endif
}
int LToolBar::GetBx()
{
return d->Bx;
}
int LToolBar::GetBy()
{
return d->By;
}
void LToolBar::ContextMenu(LMouse &m)
{
if (IsCustomizable())
{
LSubMenu *Sub = new LSubMenu;
if (Sub)
{
int n = 1;
for (auto it = Children.begin(); it != Children.end(); it++, n++)
{
LViewI *v = *it;
LToolButton *Btn = dynamic_cast(v);
if (Btn && Btn->Separator())
{
Sub->AppendSeparator();
}
else
{
auto Item = Sub->AppendItem(v->Name(), n, true);
if (Item)
{
Item->Checked(v->Visible());
}
}
}
Sub->AppendSeparator();
auto Txt = Sub->AppendItem(LLoadString(L_TOOLBAR_SHOW_TEXT, "Show Text Labels"), 1000, true);
Txt->Checked(d->Text);
bool Save = false;
int Pick = Sub->Float(this, m);
switch (Pick)
{
case 1000:
{
d->Text = !d->Text;
Save = true;
SendNotify(LNotifyTableLayoutRefresh);
break;
}
default:
{
LViewI *Ctrl = Children[Pick - 1];
if (Ctrl)
{
Ctrl->Visible(!Ctrl->Visible());
Save = true;
}
break;
}
}
DeleteObj(Sub);
if (Save)
{
LStringPipe p(256);
p.Push((char*) (d->Text ? "text" : "no"));
for (auto v: Children)
{
if (v->Visible())
{
p.Print(",%i", v->GetId());
}
}
char *o = p.NewStr();
if (o)
{
if (d->CustomDom)
{
LVariant v(o);
d->CustomDom->SetValue(d->CustomProp, v);
}
DeleteArray(o);
}
d->FixSeparators(this);
for (auto v: Children)
{
LToolButton *b = dynamic_cast(v);
if (b && b->TipId >= 0)
{
d->Tip->DeleteTip(b->TipId);
b->TipId = -1;
}
}
GetWindow()->PourAll();
}
}
}
}
bool LToolBar::IsCustomizable()
{
return d->CustomDom != 0 && d->CustomProp;
}
void LToolBar::Customizable(LDom *Store, const char *Option)
{
d->CustomDom = Store;
d->CustomProp = Option;
d->Customizable(this);
}
bool LToolBar::IsVertical()
{
return d->Vertical;
}
void LToolBar::IsVertical(bool v)
{
d->Vertical = v;
}
bool LToolBar::TextLabels()
{
return d->Text;
}
void LToolBar::TextLabels(bool i)
{
d->Text = i;
}
LFont *LToolBar::GetFont()
{
return d->Font;
}
bool LToolBar::OnLayout(LViewLayoutInfo &Inf)
{
if (Inf.Width.Min == 0)
{
// Calc width
LRegion r(0, 0, 10000, 10000);
Pour(r);
Inf.Width.Min = X();
Inf.Width.Max = X();
}
else
{
// Calc height
Inf.Height.Min = Y();
Inf.Height.Max = Y();
}
return true;
}
#define GetBorderSpacing() GetCss() && GetCss()->BorderSpacing().IsValid() ? \
GetCss()->BorderSpacing().ToPx(X(), GetFont()) : \
1
bool LToolBar::Pour(LRegion &r)
{
int BorderSpacing = GetBorderSpacing();
int EndX = 0;
int EndY = 0;
int MaxDim = 0;
LCssTools Tools(this);
LRect Border = Tools.GetBorder(r);
LRect Padding = Tools.GetPadding(r);
int PosX = BorderSpacing + Border.x1 + Padding.x1;
int PosY = BorderSpacing + Border.y1 + Padding.y1;
LRect ButPos;
for (auto But: Children)
{
if (But->Visible())
{
int Tx = 0, Ty = 0;
LToolButton *Btn = dynamic_cast(But);
if (d->ShowTextLabels())
{
if (Btn)
{
if (Btn->d->Text.Length() == 0)
{
Btn->Layout();
}
for (int i=0; id->Text.Length(); i++)
{
LDisplayString *Ds = Btn->d->Text[i];
Tx = MAX(Ds->X() + 4, Tx);
Ty += Ds->Y();
}
}
}
ButPos = But->GetPos();
if (Btn)
{
if (Btn->Separator())
{
// This will be stretched out later by the code that makes
// everything the same height.
ButPos.ZOff(BORDER_SEPARATOR+1, BORDER_SEPARATOR+1);
}
else
{
if (Btn->Image() >= 0)
{
// Set initial size to the icon size
ButPos.ZOff(d->Bx + 2, d->By + 2);
}
else
{
// Otherwise default to text size
if (d->Vertical)
ButPos.ZOff(0, 7);
else
ButPos.ZOff(7, 0);
}
Tx += 4;
if (ButPos.X() < Tx)
{
// Make button wider for text label
ButPos.x2 = Tx - 1;
}
ButPos.y2 += Ty;
}
}
if (d->Vertical)
MaxDim = MAX(MaxDim, ButPos.X());
else
MaxDim = MAX(MaxDim, ButPos.Y());
ButPos.Offset(PosX - ButPos.x1, PosY - ButPos.y1);
if (But->GetId() == IDM_BREAK)
{
ButPos.ZOff(0, 0);
if (d->Vertical)
{
PosX = MaxDim;
PosY = BORDER_SHADE + BorderSpacing;
}
else
{
PosX = BORDER_SHADE + BorderSpacing;
PosY = MaxDim;
}
}
else
{
if (d->Vertical)
PosY = ButPos.y2 + BorderSpacing;
else
PosX = ButPos.x2 + BorderSpacing;
}
But->SetPos(ButPos);
}
else
{
LRect p(-100, -100, -90, -90);
But->SetPos(p);
}
}
for (auto w: Children)
{
LRect p = w->GetPos();
if (d->Vertical)
{
if (w->X() < MaxDim)
{
p.x2 = p.x1 + MaxDim - 1;
w->SetPos(p);
}
}
else
{
if (w->Y() < MaxDim)
{
p.y2 = p.y1 + MaxDim - 1;
w->SetPos(p);
}
}
EndX = MAX(EndX, p.x2);
EndY = MAX(EndY, p.y2);
}
d->Sx = EndX + BorderSpacing;
d->Sy = EndY + BorderSpacing;
d->Sx += Border.x2 + Padding.x2;
d->Sy += Border.y2 + Padding.y2;
LRect n;
n.ZOff(MAX(7, d->Sx), MAX(7, d->Sy));
LRect *Best = FindLargestEdge(r, GV_EDGE_TOP);
if (Best)
{
n.Offset(Best->x1, Best->y1);
n.Bound(Best);
SetPos(n, true);
// _Dump();
return true;
}
else LgiTrace("%s:%i - No best pos.\n", _FL);
return false;
}
void LToolBar::OnButtonClick(LToolButton *Btn)
{
LViewI *v = GetNotify() ? GetNotify() : GetParent();
if (v && Btn)
{
int Id = Btn->GetId();
if (v->PostEvent(M_COMMAND, (LMessage::Param) Id
#if LGI_VIEW_HANDLE
, (LMessage::Param) Handle()
#endif
))
; //printf("Send M_COMMAND(%i)\n", Id);
else
printf("%s:%i - Failed to send M_COMMAND.\n", _FL);
}
else printf("%s:%i - Ptr error: %p %p\n", _FL, v, Btn);
}
int LToolBar::PostDescription(LView *Ctrl, const char *Text)
{
if (GetParent())
{
return GetParent()->PostEvent(M_DESCRIBE, (LMessage::Param) Ctrl, (LMessage::Param) Text);
}
return 0;
}
LMessage::Result LToolBar::OnEvent(LMessage *Msg)
{
switch (Msg->Msg())
{
case M_CHANGE:
{
if (GetParent())
return GetParent()->OnEvent(Msg);
LAutoPtr note((LNotification*)Msg->B());
break;
}
}
return LView::OnEvent(Msg);
}
void LToolBar::OnPaint(LSurface *pDC)
{
LRect c = GetClient();
LCssTools Tools(this);
Tools.PaintBorder(pDC, c);
Tools.PaintPadding(pDC, c);
Tools.PaintContent(pDC, c);
}
void LToolBar::OnMouseClick(LMouse &m)
{
}
void LToolBar::OnMouseEnter(LMouse &m)
{
if (!d->Tip)
{
d->Tip = new LToolTip;
if (d->Tip)
{
d->Tip->Attach(this);
}
}
}
void LToolBar::OnMouseExit(LMouse &m)
{
}
void LToolBar::OnMouseMove(LMouse &m)
{
}
bool LToolBar::SetBitmap(char *File, int bx, int by)
{
LAutoPtr pDC(GdcD->Load(File));
return pDC ? SetDC(pDC, bx, by) : false;
}
bool LToolBar::SetDC(LSurface *pNewDC, int bx, int by)
{
if (d->OwnImgList)
{
DeleteObj(d->ImgList);
}
d->Bx = bx;
d->By = by;
if (pNewDC)
{
d->ImgList = new LImageList(bx, by, pNewDC);
if (d->ImgList)
{
d->OwnImgList = true;
return true;
}
}
return false;
}
LImageList *LToolBar::GetImageList()
{
return d->ImgList;
}
bool LToolBar::SetImageList(LImageList *l, int bx, int by, bool Own)
{
if (d->OwnImgList)
DeleteObj(d->ImgList);
d->OwnImgList = Own;
d->Bx = bx;
d->By = by;
d->ImgList = l;
return d->ImgList != 0;
}
LToolButton *LToolBar::AppendButton(const char *Tip, int Id, int Type, int Enabled, int IconId)
{
// bool HasIcon = IconId != TOOL_ICO_NONE;
LToolButton *But = new LToolButton(d->Bx, d->By);
if (But)
{
But->Name(Tip);
But->SetId(Id);
But->Type = Type;
But->Enabled(Enabled != 0);
if (IconId >= 0)
{
But->ImgIndex = IconId;
}
else if (IconId == TOOL_ICO_NEXT)
{
But->ImgIndex = d->LastIndex++;
}
else if (IconId == TOOL_ICO_NONE)
{
But->ImgIndex = -1;
}
AttachButton(But);
}
return But;
}
bool LToolBar::AppendSeparator()
{
LToolButton *But = new LToolButton(d->Bx, d->By);
if (But)
{
But->SetId(IDM_SEPARATOR);
AttachButton(But);
return true;
}
return false;
}
bool LToolBar::AppendBreak()
{
LToolButton *But = new LToolButton(d->Bx, d->By);
if (But)
{
But->SetId(IDM_BREAK);
But->SetParent(this);
AttachButton(But);
return true;
}
return false;
}
bool LToolBar::AppendControl(LView *Ctrl)
{
bool Status = false;
if (Ctrl)
{
Ctrl->SetParent(this);
AttachButton(Ctrl);
Status = true;
}
return Status;
}
void LToolBar::Empty()
{
for (auto But: Children)
{
DeleteObj(But);
}
}
#ifdef MAC
bool LToolBar::Attach(LViewI *parent)
{
return LLayout::Attach(parent);
}
#endif
///////////////////////////////////////////////////////////////////////
COLOUR Map(LSurface *pDC, COLOUR c)
{
if (pDC && pDC->GetBits() <= 8)
{
if (pDC->IsScreen())
{
c = CBit(24, c);
}
#ifdef WIN32
else
{
HPALETTE hPal = GetSystemPalette();
if (hPal)
{
c = GetNearestPaletteIndex(hPal, c);
DeleteObject(hPal);
}
}
#endif
}
return c;
}
#ifdef WIN32
HPALETTE GetSystemPalette()
{
HPALETTE hPal = 0;
LOGPALETTE *Log = (LOGPALETTE*) new uchar[sizeof(LOGPALETTE) + (sizeof(PALETTEENTRY) * 255)];
if (Log)
{
Log->palVersion = 0x300;
Log->palNumEntries = 256;
HDC hDC = CreateCompatibleDC(0);
GetSystemPaletteEntries(hDC, 0, 256, Log->palPalEntry);
DeleteDC(hDC);
hPal = CreatePalette(Log);
}
return hPal;
}
bool BltBmpToBmp(HBITMAP hDest,
int xDst,
int yDst,
int cx,
int cy,
HBITMAP hSrc,
int xSrc,
int ySrc,
DWORD dwRop)
{
bool Status = false;
HDC DestDC = CreateCompatibleDC(0);
HDC SrcDC = CreateCompatibleDC(0);
if (DestDC && SrcDC)
{
hDest = (HBITMAP) SelectObject(DestDC, hDest);
hSrc = (HBITMAP) SelectObject(SrcDC, hSrc);
Status = BitBlt(DestDC,
xDst,
yDst,
cx,
cy,
SrcDC,
xSrc,
ySrc,
dwRop) != 0;
hDest = (HBITMAP) SelectObject(DestDC, hDest);
hSrc = (HBITMAP) SelectObject(SrcDC, hSrc);
}
if (DestDC)
{
DeleteDC(DestDC);
}
if (SrcDC)
{
DeleteDC(SrcDC);
}
return Status;
}
bool BltBmpToDc(HDC DestDC,
int xDst,
int yDst,
int cx,
int cy,
HBITMAP hSrc,
int xSrc,
int ySrc,
DWORD dwRop)
{
bool Status = false;
HDC SrcDC = CreateCompatibleDC(0);
if (DestDC && SrcDC)
{
hSrc = (HBITMAP) SelectObject(SrcDC, hSrc);
Status = BitBlt(DestDC,
xDst,
yDst,
cx,
cy,
SrcDC,
xSrc,
ySrc,
dwRop) != 0;
hSrc = (HBITMAP) SelectObject(SrcDC, hSrc);
}
if (SrcDC)
{
DeleteDC(SrcDC);
}
return Status;
}
bool BltDcToBmp(HBITMAP hDest,
int xDst,
int yDst,
int cx,
int cy,
HDC SrcDC,
int xSrc,
int ySrc,
DWORD dwRop)
{
bool Status = false;
HDC DestDC = CreateCompatibleDC(0);
if (DestDC && SrcDC)
{
hDest = (HBITMAP) SelectObject(DestDC, hDest);
Status = BitBlt(DestDC,
xDst,
yDst,
cx,
cy,
SrcDC,
xSrc,
ySrc,
dwRop) != 0;
hDest = (HBITMAP) SelectObject(DestDC, hDest);
}
if (DestDC)
{
DeleteDC(DestDC);
}
return Status;
}
#endif
diff --git a/src/common/Widgets/Tree.cpp b/src/common/Widgets/Tree.cpp
--- a/src/common/Widgets/Tree.cpp
+++ b/src/common/Widgets/Tree.cpp
@@ -1,2261 +1,2261 @@
#include
#include "lgi/common/Lgi.h"
#include "lgi/common/Tree.h"
#include "lgi/common/ScrollBar.h"
#include "lgi/common/DisplayString.h"
#include "lgi/common/Palette.h"
#include "lgi/common/LgiRes.h"
#include "lgi/common/CssTools.h"
#define TREE_BLOCK 16
#define DRAG_THRESHOLD 4
#define DRAG_SCROLL_EDGE 20
#define DRAG_SCROLL_X 8
#define DRAG_SCROLL_Y 1
#define TreeUpdateNow false
-#define TREELOCK LMutex::Auto Lck(d, _FL);
#define ForAll(Items) for (auto c : Items)
+struct LTreeLocker
+{
+ LTree *t = NULL;
+ bool status = false;
+ LTreeLocker(LTree *tree, const char *file, int line) : t(tree)
+ {
+ if (t)
+ status = t->Lock(file, line);
+ }
+ ~LTreeLocker()
+ {
+ if (status && t)
+ t->Unlock();
+ }
+};
+
+#define TREELOCK(ptr) LTreeLocker _lock(ptr, _FL);
+
//////////////////////////////////////////////////////////////////////////////
// Private class definitions for binary compatibility
-class LTreePrivate : public LMutex
+class LTreePrivate
{
public:
// Private data
int LineFlags[4];
bool LayoutDirty;
LPoint Limit;
LPoint LastClick;
LPoint DragStart;
int DragData;
LMemDC *IconCache;
bool InPour;
int64 DropSelectTime;
int8 IconTextGap;
int LastLayoutPx;
LMouse *CurrentClick;
LTreeItem *ScrollTo;
// Visual style
LTree::ThumbStyle Btns;
bool JoiningLines;
// Pointers into items... be careful to clear when deleting items...
LTreeItem *LastHit;
List Selection;
LTreeItem *DropTarget;
- LTreePrivate() : LMutex("LTreePrivate")
+ LTreePrivate()
{
CurrentClick = NULL;
LastLayoutPx = -1;
DropSelectTime = 0;
InPour = false;
LastHit = 0;
DropTarget = 0;
IconCache = 0;
LayoutDirty = true;
IconTextGap = 0;
ScrollTo = NULL;
Btns = LTree::TreeTriangle;
JoiningLines = false;
}
~LTreePrivate()
{
DeleteObj(IconCache);
}
};
class LTreeItemPrivate
{
LArray Ds;
LArray ColPx;
public:
LTreeItem *Item;
LRect Pos;
LRect Thumb;
LRect Text;
LRect Icon;
bool Open;
bool Selected;
bool Visible;
bool Last;
int Depth;
LTreeItemPrivate(LTreeItem *it)
{
Item = it;
Ds = NULL;
Pos.ZOff(-1, -1);
Open = false;
Selected = false;
Visible = false;
Last = false;
Depth = 0;
Text.ZOff(-1, -1);
Icon.ZOff(-1, -1);
}
~LTreeItemPrivate()
{
Ds.DeleteObjects();
}
LDisplayString *GetDs(int Col, int FixPx)
{
if (!Ds[Col])
{
LFont *f = Item->GetTree() ? Item->GetTree()->GetFont() : LSysFont;
auto txt = Item->GetText(Col);
if (txt)
{
Ds[Col] = new LDisplayString(f, Item->GetText(Col));
if (Ds[Col])
{
ColPx[Col] = Ds[Col]->X();
if (FixPx > 0)
{
Ds[Col]->TruncateWithDots(FixPx);
}
}
}
}
return Ds[Col];
}
void ClearDs(int Col = -1)
{
if (Col >= 0)
{
delete Ds[Col];
Ds[Col] = NULL;
}
else
{
Ds.DeleteObjects();
}
}
int GetColumnPx(int Col)
{
int BasePx = 0;
GetDs(Col, 0);
if (Col == 0)
{
BasePx = (Depth + 1) * TREE_BLOCK;
}
return ColPx[Col] + BasePx;
}
};
//////////////////////////////////////////////////////////////////////////////
LTreeNode::LTreeNode()
{
Parent = NULL;
Tree = NULL;
}
LTreeNode::~LTreeNode()
{
}
void LTreeNode::SetLayoutDirty()
{
Tree->d->LayoutDirty = true;
}
void LTreeNode::_Visible(bool v)
{
for (LTreeItem *i=GetChild(); i; i=i->GetNext())
{
LAssert(i != this);
i->OnVisible(v);
i->_Visible(v);
}
}
void LTreeNode::_ClearDs(int Col)
{
List::I it = Items.begin();
for (LTreeItem *c = *it; c; c = *++it)
{
c->_ClearDs(Col);
}
}
LItemContainer *LTreeItem::GetContainer()
{
return Tree;
}
LTreeItem *LTreeNode::Insert(LTreeItem *Obj, ssize_t Idx)
{
LAssert(Obj != this);
if (Obj && Obj->Tree)
Obj->Remove();
LTreeItem *NewObj = Obj ? Obj : new LTreeItem;
if (NewObj)
{
NewObj->Parent = Item();
NewObj->_SetTreePtr(Tree);
Items.Delete(NewObj);
Items.Insert(NewObj, Idx);
if (Tree)
{
Tree->d->LayoutDirty = true;
if (Pos() && Pos()->Y() > 0)
Tree->_UpdateBelow(Pos()->y1);
else
Tree->Invalidate();
}
}
return NewObj;
}
void LTreeNode::Detach()
{
if (Parent)
{
LTreeItem *It = Item();
if (It)
{
LAssert(Parent->Items.HasItem(It));
Parent->Items.Delete(It);
}
Parent = 0;
}
if (Tree)
{
Tree->d->LayoutDirty = true;
Tree->Invalidate();
}
if (Item())
Item()->_SetTreePtr(0);
}
void LTreeNode::Remove()
{
int y = 0;
if (Parent)
{
LTreeItem *i = Item();
if (i && i->IsRoot())
{
LRect *p = Pos();
LTreeItem *Prev = GetPrev();
if (Prev)
{
y = Prev->d->Pos.y1;
}
else
{
y = p->y1;
}
}
else
{
y = Parent->d->Pos.y1;
}
}
LTree *t = Tree;
if (Item())
Item()->_Remove();
if (t)
{
t->_UpdateBelow(y);
}
}
bool LTreeNode::IsRoot()
{
return Parent == 0 || (LTreeNode*)Parent == (LTreeNode*)Tree;
}
size_t LTreeNode::Length()
{
return Items.Length();
}
bool LTreeNode::HasItem(LTreeItem *obj, bool recurse)
{
if (!obj)
return false;
if (this == (LTreeNode*)obj)
return true;
for (auto i: Items)
{
if (i == obj)
return true;
if (recurse && i->HasItem(obj, recurse))
return true;
}
return false;
}
int LTreeNode::ForEach(std::function Fn)
{
int Count = 0;
for (auto t : Items)
{
Fn(t);
Count += t->ForEach(Fn);
}
return Count + 1;
}
ssize_t LTreeNode::IndexOf()
{
if (Parent)
{
return Parent->Items.IndexOf(Item());
}
else if (Tree)
{
return Tree->Items.IndexOf(Item());
}
return -1;
}
LTreeItem *LTreeNode::GetChild()
{
return Items.Length() ? Items[0] : NULL;
}
LTreeItem *LTreeNode::GetPrev()
{
List *l = (Parent) ? &Parent->Items : (Tree) ? &Tree->Items : 0;
if (l)
{
ssize_t Index = l->IndexOf(Item());
if (Index >= 0)
{
return l->ItemAt(Index-1);
}
}
return 0;
}
LTreeItem *LTreeNode::GetNext()
{
List *l = (Parent) ? &Parent->Items : (Tree) ? &Tree->Items : 0;
if (l)
{
ssize_t Index = l->IndexOf(Item());
if (Index >= 0)
{
return l->ItemAt(Index+1);
}
}
return 0;
}
//////////////////////////////////////////////////////////////////////////////
LTreeItem::LTreeItem()
{
d = new LTreeItemPrivate(this);
}
LTreeItem::~LTreeItem()
{
if (Tree)
{
if (Tree->d->DropTarget == this)
Tree->d->DropTarget = 0;
if (Tree->d->LastHit == this)
Tree->d->LastHit = 0;
if (Tree->IsCapturing())
Tree->Capture(false);
}
int y = 0;
LTree *t = 0;
if (Parent && (LTreeNode*)Parent != (LTreeNode*)Tree)
{
t = Tree;
y = Parent->d->Pos.y1;
}
else if ((LTreeNode*)this != (LTreeNode*)Tree)
{
t = Tree;
LTreeItem *p = GetPrev();
if (p)
y = p->d->Pos.y1;
else
y = d->Pos.y1;
}
_Remove();
while (Items.Length())
{
auto It = Items.begin();
delete *It;
}
DeleteObj(d);
if (t)
t->_UpdateBelow(y);
}
int LTreeItem::GetColumnSize(int Col)
{
int Px = d->GetColumnPx(Col);
if (Expanded())
{
ForAll(Items)
{
int ChildPx = c->GetColumnSize(Col);
Px = MAX(ChildPx, Px);
}
}
return Px;
}
LRect *LTreeItem::Pos()
{
return &d->Pos;
}
LPoint LTreeItem::_ScrollPos()
{
LPoint p;
if (Tree) p = Tree->_ScrollPos();
return p;
}
LRect *LTreeItem::_GetRect(LTreeItemRect Which)
{
switch (Which)
{
case TreeItemPos: return &d->Pos;
case TreeItemThumb: return &d->Thumb;
case TreeItemText: return &d->Text;
case TreeItemIcon: return &d->Icon;
}
return 0;
}
bool LTreeItem::IsDropTarget()
{
LTree *t = GetTree();
if (t && t->d && t->d->DropTarget == this)
return true;
return false;
}
LRect *LTreeItem::GetPos(int Col)
{
if (!d->Pos.Valid() && Tree)
Tree->_Pour();
static LRect r;
r = d->Pos;
if (Col >= 0)
{
LItemColumn *Column = 0;
int Cx = Tree->GetImageList() ? 16 : 0;
for (int c=0; cColumnAt(c);
if (Column)
{
Cx += Column->Width();
}
}
Column = Tree->ColumnAt(Col);
if (Column)
{
r.x1 = Cx;
r.x2 = Cx + Column->Width() - 1;
}
}
return &r;
}
void LTreeItem::_RePour()
{
if (Tree)
Tree->_Pour();
}
void LTreeItem::ScrollTo()
{
if (!Tree)
return;
if (Tree->VScroll)
{
LRect c = Tree->GetClient();
LRect p = d->Pos;
int y = d->Pos.Y() ? d->Pos.Y() : 16;
p.Offset(0, (int) (-Tree->VScroll->Value() * y));
if (p.y1 < c.y1)
{
int Lines = (c.y1 - p.y1 + y - 1) / y;
Tree->VScroll->Value(Tree->VScroll->Value() - Lines);
}
else if (p.y2 > c.y2)
{
int Lines = (p.y2 - c.y2 + y - 1) / y;
Tree->VScroll->Value(Tree->VScroll->Value() + Lines);
}
}
else
{
Tree->d->ScrollTo = this;
if (Tree->IsAttached())
Tree->PostEvent(M_SCROLL_TO);
}
}
void LTreeItem::_SetTreePtr(LTree *t)
{
if (Tree && !t)
{
// Clearing tree pointer, must remove all references to this item that
// the tree might still have.
if (d->Selected)
{
Tree->d->Selection.Delete(this);
d->Selected = false;
}
if (Tree->d->LastHit == this)
{
Tree->d->LastHit = 0;
}
if (Tree->d->DropTarget == this)
{
Tree->d->DropTarget = 0;
}
}
Tree = t;
List::I it = Items.begin();
for (LTreeItem *i=*it; i; i=*++it)
{
i->_SetTreePtr(t);
}
}
void LTreeItem::_Remove()
{
if ((LTreeNode*)this != (LTreeNode*)Tree)
{
if (Parent)
{
LAssert(Parent->Items.HasItem(this));
Parent->Items.Delete(this);
}
else if (Tree)
{
LAssert(Tree->Items.HasItem(this));
Tree->Items.Delete(this);
}
if (Tree)
{
LAssert(Tree->d != NULL);
Tree->d->LayoutDirty = true;
if (Tree->IsCapturing())
Tree->Capture(false);
}
}
Parent = 0;
_SetTreePtr(0);
}
void LTreeItem::_PourText(LPoint &Size)
{
LFont *f = Tree ? Tree->GetFont() : LSysFont;
auto *Txt = GetText();
#if defined(_WIN64) && defined(_DEBUG)
if ((void*)Txt == (void*)0xfeeefeeefeeefeee ||
(void*)Txt == (void*)0xcdcdcdcdcdcdcdcd)
{
LAssert(!"Yeah nah...");
}
#endif
LDisplayString ds(f, Txt);
Size.x = ds.X() + 4;
Size.y = 0;
}
void LTreeItem::_PaintText(LItem::ItemPaintCtx &Ctx)
{
const char *Text = GetText();
if (Text)
{
LDisplayString *Ds = d->GetDs(0, d->Text.X());
LFont *f = Tree ? Tree->GetFont() : LSysFont;
int Tab = f->TabSize();
f->TabSize(0);
f->Transparent(false);
f->Colour(Ctx.Fore, Ctx.TxtBack);
if (Ds)
{
Ds->Draw(Ctx.pDC, d->Text.x1 + 2, d->Text.y1 + 1, &d->Text);
if (Ctx.x2 > d->Text.x2)
{
LRect r = Ctx;
r.x1 = d->Text.x2 + 1;
Ctx.pDC->Colour(Ctx.Back);
Ctx.pDC->Rectangle(&r);
}
}
f->TabSize(Tab);
}
else
{
Ctx.pDC->Colour(Ctx.Back);
Ctx.pDC->Rectangle(&Ctx);
}
}
void LTreeItem::_Pour(LPoint *Limit, int ColumnPx, int Depth, bool Visible)
{
auto css = GetCss(false);
auto display = css ? css->Display() != LCss::DispNone : true;
d->Visible = display && Visible;
d->Depth = Depth;
if (d->Visible)
{
LPoint TextSize;
_PourText(TextSize);
LImageList *ImgLst = Tree->GetImageList();
// int IconX = (ImgLst && GetImage() >= 0) ? ImgLst->TileX() + Tree->d->IconTextGap : 0;
int IconY = (ImgLst && GetImage() >= 0) ? ImgLst->TileY() : 0;
int Height = MAX(TextSize.y, IconY);
if (!Height)
Height = 16;
LDisplayString *Ds = d->GetDs(0, 0);
d->Pos.ZOff(ColumnPx - 1, (Ds ? MAX(Height, Ds->Y()) : Height) - 1);
d->Pos.Offset(0, Limit->y);
if (!d->Pos.Valid())
{
printf("Invalid pos: %s, ColumnPx=%i\n", d->Pos.GetStr(), ColumnPx);
}
Limit->x = MAX(Limit->x, d->Pos.x2 + 1);
Limit->y = MAX(Limit->y, d->Pos.y2 + 1);
}
else
{
d->Pos.ZOff(-1, -1);
}
LTreeItem *n;
List::I it = Items.begin();
for (LTreeItem *i=*it; i; i=n)
{
n = *++it;
i->d->Last = n == 0;
i->_Pour(Limit, ColumnPx, Depth+1, d->Open && d->Visible);
}
}
void LTreeItem::_ClearDs(int Col)
{
d->ClearDs(Col);
LTreeNode::_ClearDs(Col);
}
const char *LTreeItem::GetText(int i)
{
return Str[i];
}
bool LTreeItem::SetText(const char *s, int i)
{
- LAutoPtr Lck;
-
- if (Tree)
- Lck.Reset(new LMutex::Auto(Tree->d, -1, _FL));
+ TREELOCK(Tree);
Str[i] = s;
-
if (Tree)
Update();
return true;
}
int LTreeItem::GetImage(int Flags)
{
return Sys_Image;
}
void LTreeItem::SetImage(int i)
{
Sys_Image = i;
}
void LTreeItem::Update()
{
if (Tree)
{
LRect p = d->Pos;
p.x2 = 10000;
d->ClearDs();
Tree->_Update(&p, TreeUpdateNow);
}
}
bool LTreeItem::Select()
{
return d->Selected;
}
void LTreeItem::Select(bool b)
{
if (d->Selected != b)
{
d->Selected = b;
if (b)
{
LTreeItem *p = this;
while ((p = p->GetParent()))
{
p->Expanded(true);
}
}
Update();
if (b && Tree)
{
Tree->_OnSelect(this);
Tree->OnItemSelect(this);
}
}
}
bool LTreeItem::Expanded()
{
return d->Open;
}
void LTreeItem::Expanded(bool b)
{
if (d->Open != b)
{
d->Open = b;
if (Items.Length() > 0)
{
if (Tree)
{
Tree->d->LayoutDirty = true;
Tree->_UpdateBelow(d->Pos.y1);
}
OnExpand(b);
}
}
}
void LTreeItem::OnExpand(bool b)
{
_Visible(b);
}
LTreeItem *LTreeItem::_HitTest(int x, int y, bool Debug)
{
LTreeItem *Status = 0;
if (d->Pos.Overlap(x, y) &&
x > (d->Depth*TREE_BLOCK))
{
Status = this;
}
if (d->Open)
{
List::I it = Items.begin();
for (LTreeItem *i=*it; i && !Status; i=*++it)
{
Status = i->_HitTest(x, y, Debug);
}
}
return Status;
}
void LTreeItem::_MouseClick(LMouse &m)
{
if (m.Down())
{
if ((Items.Length() > 0 &&
d->Thumb.Overlap(m.x, m.y)) ||
m.Double())
{
Expanded(!Expanded());
}
LRect rText = d->Text;
if (Tree && Tree->Columns.Length() > 0)
rText.x2 = Tree->X();
if (rText.Overlap(m.x, m.y) ||
d->Icon.Overlap(m.x, m.y))
{
Select(true);
if (Tree)
Tree->OnItemClick(this, m);
}
}
}
void LTreeItem::OnPaint(ItemPaintCtx &Ctx)
{
LAssert(Tree != NULL);
if (!d->Visible)
return;
// background up to text
LSurface *&pDC = Ctx.pDC;
pDC->Colour(Ctx.Back);
pDC->Rectangle(0, d->Pos.y1, (d->Depth*TREE_BLOCK)+TREE_BLOCK, d->Pos.y2);
// draw trunk
LRect Pos = d->Pos;
Pos.x2 = Pos.x1 + Ctx.ColPx[0] - 1;
int x = 0;
LColour Ws(L_WORKSPACE);
LColour Lines = Ws.Invert().Mix(Ws);
pDC->Colour(Lines);
if (Tree->d->JoiningLines)
{
for (int i=0; iDepth; i++)
{
if (Tree->d->LineFlags[0] & (1 << i))
pDC->Line(x + 8, Pos.y1, x + 8, Pos.y2);
x += TREE_BLOCK;
}
}
else
{
x += TREE_BLOCK * d->Depth;
}
// draw node
int cy = Pos.y1 + (Pos.Y() >> 1);
if (Items.Length() > 0)
{
d->Thumb.ZOff(8, 8);
d->Thumb.Offset(x + 4, cy - 4);
switch (Tree->d->Btns)
{
case LTree::TreePlus:
{
// plus/minus symbol
pDC->Colour(L_LOW);
pDC->Box(&d->Thumb);
pDC->Colour(L_WHITE);
pDC->Rectangle(d->Thumb.x1+1, d->Thumb.y1+1, d->Thumb.x2-1, d->Thumb.y2-1);
pDC->Colour(L_SHADOW);
pDC->Line( d->Thumb.x1+2,
d->Thumb.y1+4,
d->Thumb.x1+6,
d->Thumb.y1+4);
if (!d->Open)
{
// not open, so draw the cross bar making the '-' into a '+'
pDC->Colour(L_SHADOW);
pDC->Line( d->Thumb.x1+4,
d->Thumb.y1+2,
d->Thumb.x1+4,
d->Thumb.y1+6);
}
break;
}
case LTree::TreeTriangle:
{
// Triangle style expander
pDC->Colour(Lines);
int Off = 2;
if (d->Open)
{
for (int y=0; yThumb.Y(); y++)
{
int x1 = d->Thumb.x1 + y;
int x2 = d->Thumb.x2 - y;
if (x2 < x1)
break;
pDC->HLine(x1, x2, d->Thumb.y1 + y + Off);
}
}
else
{
for (int x=0; xThumb.X(); x++)
{
int y1 = d->Thumb.y1 + x;
int y2 = d->Thumb.y2 - x;
if (y2 < y1)
break;
pDC->VLine(d->Thumb.x1 + x + Off, y1, y2);
}
}
break;
}
}
pDC->Colour(Lines);
if (Tree->d->JoiningLines)
{
if (Parent || IndexOf() > 0)
// draw line to item above
pDC->Line(x + 8, Pos.y1, x + 8, d->Thumb.y1-1);
// draw line to leaf beside
pDC->Line(d->Thumb.x2+1, cy, x + (TREE_BLOCK-1), cy);
if (!d->Last)
// draw line to item below
pDC->Line(x + 8, d->Thumb.y2+1, x + 8, Pos.y2);
}
}
else if (Tree->d->JoiningLines)
{
// leaf node
pDC->Colour(L_MED);
if (d->Last)
pDC->Rectangle(x + 8, Pos.y1, x + 8, cy);
else
pDC->Rectangle(x + 8, Pos.y1, x + 8, Pos.y2);
pDC->Rectangle(x + 8, cy, x + (TREE_BLOCK-1), cy);
}
x += TREE_BLOCK;
// draw icon
int Image = GetImage(Select());
LImageList *Lst = Tree->GetImageList();
if (Image >= 0 && Lst)
{
d->Icon.ZOff(Lst->TileX() + Tree->d->IconTextGap - 1, Pos.Y() - 1);
d->Icon.Offset(x, Pos.y1);
pDC->Colour(Ctx.Back);
if (Tree->d->IconCache)
{
// no flicker
LRect From;
From.ZOff(Lst->TileX()-1, Tree->d->IconCache->Y()-1);
From.Offset(Lst->TileX()*Image, 0);
pDC->Blt(d->Icon.x1, d->Icon.y1, Tree->d->IconCache, &From);
pDC->Rectangle(d->Icon.x1 + Lst->TileX(), d->Icon.y1, d->Icon.x2, d->Icon.y2);
}
else
{
// flickers...
int Px = d->Icon.y1 + ((Lst->TileY()-Pos.Y()) >> 1);
pDC->Rectangle(&d->Icon);
Tree->GetImageList()->Draw(pDC, d->Icon.x1, Px, Image, Ctx.Back);
}
x += d->Icon.X();
}
LColour SelFore(Tree->Focus() ? L_FOCUS_SEL_FORE : L_NON_FOCUS_SEL_FORE);
LColour SelBack(Tree->Focus() ? L_FOCUS_SEL_BACK : L_NON_FOCUS_SEL_BACK);
bool IsSelected = (Tree->d->DropTarget == this) || (Tree->d->DropTarget == NULL && Select());
LColour Fore = Ctx.Fore;
LColour TxtBack = Ctx.TxtBack;
auto Css = GetCss();
LCss::ColorDef f, b;
if (Css)
{
f = Css->Color();
b = Css->BackgroundColor();
}
// text: first column
Ctx.Fore = f.Type == LCss::ColorRgb ? (LColour)f : (IsSelected ? SelFore : Fore);
Ctx.TxtBack = b.Type == LCss::ColorRgb ? (LColour)b : (IsSelected ? SelBack : Ctx.Back);
auto ColourDiff = abs(Ctx.Fore.GetGray() - Ctx.TxtBack.GetGray());
if (ColourDiff < 32) // Check if the colours are too similar and then disambiguate...
{
// LgiTrace("%s %s are too similar %i\n", Ctx.Fore.GetStr(), Ctx.TxtBack.GetStr(), (int)ColourDiff);
Ctx.TxtBack = Ctx.TxtBack.Mix(L_WORKSPACE);
}
LPoint TextSize;
_PourText(TextSize);
d->Text.ZOff(TextSize.x-1, Pos.Y()-1);
d->Text.Offset(x, Pos.y1);
(LRect&)Ctx = d->Text;
Ctx.x2 = Ctx.ColPx[0] - 1;
_PaintText(Ctx);
x = Pos.x2 + 1;
// text: other columns
Ctx.Fore = f.Type == LCss::ColorRgb ? (LColour)f : Fore;
Ctx.TxtBack = b.Type == LCss::ColorRgb ? (LColour)b : Ctx.Back;
for (int i=1; iColumns[i]);
x = Ctx.x2 + 1;
}
Ctx.Fore = Fore;
Ctx.TxtBack = TxtBack;
// background after text
pDC->Colour(Ctx.Back);
pDC->Rectangle(x, Pos.y1, MAX(Tree->X(), Tree->d->Limit.x), Pos.y2);
// children
if (d->Open)
{
if (!d->Last)
Tree->d->LineFlags[0] |= 1 << d->Depth;
List::I it = Items.begin();
for (LTreeItem *i=*it; i; i=*++it)
i->OnPaint(Ctx);
Tree->d->LineFlags[0] &= ~(1 << d->Depth);
}
}
void LTreeItem::OnPaintColumn(LItem::ItemPaintCtx &Ctx, int i, LItemColumn *c)
{
LDisplayString *ds = d->GetDs(i, Ctx.ColPx[i]);
if (ds)
{
// Draw the text in the context area:
LFont *f = ds->GetFont();
f->Colour(Ctx.Fore, Ctx.TxtBack);
ds->Draw(Ctx.pDC, Ctx.x1 + 2, Ctx.y1 + 1, &Ctx);
}
else
{
// No string, fill the space with background
Ctx.pDC->Colour(Ctx.Back);
Ctx.pDC->Rectangle(&Ctx);
}
}
//////////////////////////////////////////////////////////////////////////////
LTree::LTree(int id, int x, int y, int cx, int cy, const char *name) :
ResObject(Res_TreeView)
{
d = new LTreePrivate;
SetId(id);
LRect e(x, y, x+cx, y+cy);
SetPos(e);
if (name) Name(name);
else Name("LGI.LTree");
Sunken(true);
Tree = this;
Lines = true;
Buttons = true;
LinesAtRoot = true;
EditLabels = false;
ColumnHeaders = false;
rItems.ZOff(-1, -1);
#if WINNATIVE
SetStyle(GetStyle() | WS_CHILD | WS_VISIBLE | WS_TABSTOP);
#endif
SetTabStop(true);
LResources::StyleElement(this);
}
LTree::~LTree()
{
Empty();
DeleteObj(d);
}
-bool LTree::Lock(const char *file, int line, int TimeOut)
-{
- if (TimeOut > 0)
- return d->LockWithTimeout(TimeOut, file, line);
-
- return d->Lock(file, line);
-}
-
-void LTree::Unlock()
-{
- return d->Unlock();
-}
-
// Internal tree methods
List *LTree::GetSelLst()
{
return &d->Selection;
}
void LTree::_Update(LRect *r, bool Now)
{
- TREELOCK
+ TREELOCK(this)
if (r)
{
LRect u = *r;
LPoint s = _ScrollPos();
LRect c = GetClient();
u.Offset(c.x1-s.x, c.y1-s.y);
Invalidate(&u, Now && !d->InPour);
}
else
{
Invalidate((LRect*)0, Now && !d->InPour);
}
}
void LTree::_UpdateBelow(int y, bool Now)
{
- TREELOCK
+ TREELOCK(this)
LPoint s = _ScrollPos();
LRect c = GetClient();
LRect u(c.x1, y - s.y + c.y1, X()-1, Y()-1);
Invalidate(&u, Now);
}
void LTree::ClearDs(int Col)
{
- TREELOCK
+ TREELOCK(this)
List::I it = Items.begin();
for (LTreeItem *i=*it; i; i=*++it)
i->_ClearDs(Col);
}
LPoint LTree::_ScrollPos()
{
- TREELOCK
+ TREELOCK(this)
LPoint Status;
Status.x = (HScroll) ? (int)HScroll->Value() : 0;
Status.y = (VScroll) ? (int)VScroll->Value() * TREE_BLOCK : 0;
return Status;
}
void LTree::_UpdateScrollBars()
{
static bool Processing = false;
if (!Processing)
{
Processing = true;
{
- TREELOCK
+ TREELOCK(this)
LPoint Old = _ScrollPos();
LRect Client = GetClient();
bool x = d->Limit.x > Client.X();
bool y = d->Limit.y > Client.Y();
SetScrollBars(x, y);
Client = GetClient();
// x scroll... in pixels
if (HScroll)
{
HScroll->SetRange(d->Limit.x);
HScroll->SetPage(Client.X());
int Max = d->Limit.x - Client.X();
if (HScroll->Value() > Max)
{
HScroll->Value(Max+1);
}
}
// y scroll... in items
if (VScroll)
{
int All = (d->Limit.y + TREE_BLOCK - 1) / TREE_BLOCK;
int Visible = Client.Y() / TREE_BLOCK;
VScroll->SetRange(All);
VScroll->SetPage(Visible);
/* Why is this commented out? -fret Dec2018
int Max = All - Visible + 1;
if (VScroll->Value() > Max)
VScroll->Value(Max);
*/
}
LPoint New = _ScrollPos();
if (Old.x != New.x ||
Old.y != New.y)
{
Invalidate();
}
}
Processing = false;
}
}
void LTree::_OnSelect(LTreeItem *Item)
{
- TREELOCK
+ TREELOCK(this)
if
(
!MultiSelect()
||
!d->CurrentClick
||
(
d->CurrentClick
&&
!d->CurrentClick->Ctrl()
)
)
{
for (auto i: d->Selection)
{
if (i != Item)
i->Select(false);
}
d->Selection.Empty();
}
else
{
d->Selection.Delete(Item);
}
d->Selection.Insert(Item);
}
void LTree::_Pour()
{
- TREELOCK
+ TREELOCK(this)
d->InPour = true;
d->Limit.x = rItems.x1;
d->Limit.y = rItems.y1;
int ColumnPx = 0;
if (Columns.Length())
{
for (int i=0; iWidth();
}
}
else
{
ColumnPx = d->LastLayoutPx = GetClient().X();
if (ColumnPx < 16)
ColumnPx = 16;
}
LTreeItem *n;
List::I it = Items.begin();
for (LTreeItem *i=*it; i; i=n)
{
n = *++it;
i->d->Last = n == 0;
i->_Pour(&d->Limit, ColumnPx, 0, true);
}
_UpdateScrollBars();
d->LayoutDirty = false;
d->InPour = false;
}
// External methods and events
void LTree::OnItemSelect(LTreeItem *Item)
{
if (!Item)
return;
- TREELOCK
+ TREELOCK(this)
Item->OnSelect();
SendNotify(LNotifyItemSelect);
}
void LTree::OnItemExpand(LTreeItem *Item, bool Expand)
{
- TREELOCK
+ TREELOCK(this)
if (Item)
Item->OnExpand(Expand);
}
LTreeItem *LTree::GetAdjacent(LTreeItem *i, bool Down)
{
- TREELOCK
+ TREELOCK(this)
LTreeItem *Ret = NULL;
if (i)
{
if (Down)
{
LTreeItem *n = i->GetChild();
if (!n ||
!n->d->Visible)
{
for (n = i; n; )
{
LTreeItem *p = n->GetParent();
if (p)
{
ssize_t Index = n->IndexOf();
if (Index < (ssize_t)p->Items.Length()-1)
{
n = n->GetNext();
break;
}
else
{
n = p;
}
}
else
{
n = n->GetNext();
break;
}
}
}
Ret = n;
}
else
{
LTreeItem *p = i->GetParent() ? i->GetParent() : 0;
ssize_t Index = i->IndexOf();
if (p)
{
LTreeItem *n = p;
if (Index > 0)
{
n = i->GetPrev();
while ( n->GetChild() &&
n->GetChild()->d->Visible)
{
n = n->Items.ItemAt(n->Items.Length()-1);
}
}
Ret = n;
}
else if (Index > 0)
{
p = i->GetTree()->ItemAt(Index - 1);
while (p->GetChild() &&
p->GetChild()->d->Visible)
{
if (p->Items.Length())
{
p = p->Items.ItemAt(p->Items.Length()-1);
}
else break;
}
Ret = p;
}
}
}
return Ret;
}
bool LTree::OnKey(LKey &k)
{
if (!Lock(_FL))
return false;
bool Status = false;
LTreeItem *i = d->Selection[0];
if (!i)
{
i = Items[0];
if (i)
i->Select();
}
if (k.Down())
{
switch (k.vkey)
{
case LK_PAGEUP:
case LK_PAGEDOWN:
{
if (i && i->d->Pos.Y() > 0)
{
int Page = GetClient().Y() / i->d->Pos.Y();
for (int j=0; jSelect(true);
i->ScrollTo();
}
}
Status = true;
break;
}
case LK_HOME:
{
LTreeItem *i;
if ((i = Items[0]))
{
i->Select(true);
i->ScrollTo();
}
Status = true;
break;
}
case LK_END:
{
LTreeItem *n = i, *p = 0;
while ((n = GetAdjacent(n, true)))
{
p = n;
}
if (p)
{
p->Select(true);
p->ScrollTo();
}
Status = true;
break;
}
case LK_LEFT:
{
if (i)
{
if (i->Items.Length() && i->Expanded())
{
i->Expanded(false);
break;
}
else
{
LTreeItem *p = i->GetParent();
if (p)
{
p->Select(true);
p->Expanded(false);
_Pour();
break;
}
}
}
// fall thru
}
case LK_UP:
{
LTreeItem *n = GetAdjacent(i, false);
if (n)
{
n->Select(true);
n->ScrollTo();
}
Status = true;
break;
}
case LK_RIGHT:
{
if (i)
{
i->Expanded(true);
if (d->LayoutDirty)
{
_Pour();
break;
}
}
// fall thru
}
case LK_DOWN:
{
LTreeItem *n = GetAdjacent(i, true);
if (n)
{
n->Select(true);
n->ScrollTo();
}
Status = true;
break;
}
case LK_DELETE:
{
if (k.Down())
{
Unlock(); // before potentially being deleted...?
SendNotify(LNotification(k));
// This might delete the item... so just return here.
return true;
}
break;
}
#ifdef VK_APPS
case VK_APPS:
{
LTreeItem *s = Selection();
if (s)
{
LRect *r = &s->d->Text;
if (r)
{
LMouse m;
m.x = r->x1 + (r->X() >> 1);
m.y = r->y1 + (r->Y() >> 1);
m.Target = this;
m.ViewCoords = true;
m.Down(true);
m.Right(true);
s->OnMouseClick(m);
}
}
break;
}
#endif
default:
{
switch (k.c16)
{
case 'F':
case 'f':
{
if (k.Ctrl())
SendNotify(LNotifyContainerFind);
break;
}
}
break;
}
}
}
if (i && i != (LTreeItem*)this)
{
i->OnKey(k);
}
Unlock();
return Status;
}
LTreeItem *LTree::ItemAtPoint(int x, int y, bool Debug)
{
- TREELOCK
+ TREELOCK(this)
LPoint s = _ScrollPos();
List::I it = Items.begin();
LTreeItem *Hit = NULL;
for (LTreeItem *i = *it; i; i=*++it)
{
Hit = i->_HitTest(s.x + x, s.y + y, Debug);
if (Hit)
break;
}
return Hit;
}
bool LTree::OnMouseWheel(double Lines)
{
- TREELOCK
+ TREELOCK(this)
if (VScroll)
VScroll->Value(VScroll->Value() + (int)Lines);
return true;
}
void LTree::OnMouseClick(LMouse &m)
{
- TREELOCK
+ TREELOCK(this)
d->CurrentClick = &m;
if (m.Down())
{
DragMode = DRAG_NONE;
if (ColumnHeaders &&
ColumnHeader.Overlap(m.x, m.y))
{
d->DragStart.x = m.x;
d->DragStart.y = m.y;
// Clicked on a column heading
LItemColumn *Resize;
LItemColumn *Over = NULL;
HitColumn(m.x, m.y, Resize, Over);
if (Resize)
{
if (m.Double())
{
Resize->Width(Resize->GetContentSize() + DEFAULT_COLUMN_SPACING);
Invalidate();
}
else
{
DragMode = RESIZE_COLUMN;
d->DragData = (int)Columns.IndexOf(Resize);
Capture(true);
}
}
/*
else
{
DragMode = CLICK_COLUMN;
d->DragData = Columns.IndexOf(Over);
if (Over)
{
Over->Value(true);
LRect r = Over->GetPos();
Invalidate(&r);
Capture(true);
}
}
*/
}
else if (rItems.Overlap(m.x, m.y))
{
Focus(true);
Capture(true);
d->LastClick.x = m.x;
d->LastClick.y = m.y;
d->LastHit = ItemAtPoint(m.x, m.y, true);
if (d->LastHit)
{
LPoint c = _ScrollPos();
m.x += c.x;
m.y += c.y;
d->LastHit->_MouseClick(m);
}
else
{
SendNotify(LNotification(m, LNotifyContainerClick));
}
}
}
else if (IsCapturing())
{
Capture(false);
if (rItems.Overlap(m.x, m.y))
{
d->LastClick.x = m.x;
d->LastClick.y = m.y;
d->LastHit = ItemAtPoint(m.x, m.y);
if (d->LastHit)
{
LPoint c = _ScrollPos();
m.x += c.x;
m.y += c.y;
d->LastHit->_MouseClick(m);
}
}
}
d->CurrentClick = NULL;
}
void LTree::OnMouseMove(LMouse &m)
{
if (!IsCapturing())
return;
- TREELOCK
+ TREELOCK(this)
switch (DragMode)
{
/*
case DRAG_COLUMN:
{
if (DragCol)
{
LPoint p;
PointToScreen(p);
LRect r = DragCol->GetPos();
r.Offset(-p.x, -p.y); // to view co-ord
r.Offset(m.x - DragCol->GetOffset() - r.x1, 0);
if (r.x1 < 0) r.Offset(-r.x1, 0);
if (r.x2 > X()-1) r.Offset((X()-1)-r.x2, 0);
r.Offset(p.x, p.y); // back to screen co-ord
DragCol->SetPos(r, true);
r = DragCol->GetPos();
}
break;
}
*/
case RESIZE_COLUMN:
{
LItemColumn *c = Columns[d->DragData];
if (c)
{
// int OldWidth = c->Width();
int NewWidth = m.x - c->GetPos().x1;
c->Width(MAX(NewWidth, 4));
_ClearDs(d->DragData);
Invalidate();
}
break;
}
default:
{
if (rItems.Overlap(m.x, m.y))
{
if (abs(d->LastClick.x - m.x) > DRAG_THRESHOLD ||
abs(d->LastClick.y - m.y) > DRAG_THRESHOLD)
{
OnItemBeginDrag(d->LastHit, m);
Capture(false);
}
}
break;
}
}
}
void LTree::OnPosChange()
{
- TREELOCK
+ TREELOCK(this)
if (Columns.Length() == 0 &&
d->LastLayoutPx != GetClient().X())
d->LayoutDirty = true;
LLayout::OnPosChange();
_UpdateScrollBars();
}
void LTree::OnPaint(LSurface *pDC)
{
- TREELOCK
+ TREELOCK(this)
LCssTools Tools(this);
#if 0 // coverage testing...
pDC->Colour(LColour(255, 0, 255));
pDC->Rectangle();
#endif
rItems = GetClient();
LFont *f = GetFont();
if (ShowColumnHeader())
{
ColumnHeader.ZOff(rItems.X()-1, f->GetHeight() + 4);
PaintColumnHeadings(pDC);
rItems.y1 = ColumnHeader.y2 + 1;
}
else
{
ColumnHeader.ZOff(-1, -1);
}
d->IconTextGap = GetFont()->GetHeight() / 6;
auto cText = LColour(L_TEXT);
auto cWs = LColour(L_WORKSPACE);
LColour Fore = Tools.GetFore(&cText);
LColour Background = Tools.GetBack(&cWs, 0);
// icon cache
if (GetImageList() &&
!d->IconCache)
{
int CacheHeight = MAX(LSysFont->GetHeight(), GetImageList()->Y());
d->IconCache = new LMemDC;
if (d->IconCache &&
d->IconCache->Create(GetImageList()->X(), CacheHeight, GdcD->GetColourSpace()))
{
if (d->IconCache->GetColourSpace() == CsIndex8)
{
d->IconCache->Palette(new LPalette(GdcD->GetGlobalColour()->GetPalette()));
}
d->IconCache->Colour(Background);
d->IconCache->Rectangle();
d->IconCache->Op(GDC_ALPHA);
GetImageList()->Lock();
int DrawY = (CacheHeight - GetImageList()->TileY()) >> 1;
LAssert(DrawY >= 0);
for (int i=0; iGetItems(); i++)
{
GetImageList()->Draw(d->IconCache, i * GetImageList()->TileX(), DrawY, i, Background);
}
GetImageList()->Unlock();
d->IconCache->Unlock();
}
}
// scroll
LPoint s = _ScrollPos();
int Ox, Oy;
pDC->GetOrigin(Ox, Oy);
pDC->SetOrigin(Ox + s.x, Oy + s.y);
// selection colour
LArray ColPx;
LItem::ItemPaintCtx Ctx;
Ctx.pDC = pDC;
if (Columns.Length() > 0)
{
Ctx.Columns = (int)Columns.Length();
for (int i=0; iWidth();
}
else
{
Ctx.Columns = 1;
ColPx[0] = rItems.X();
}
Ctx.ColPx = &ColPx[0];
Ctx.Fore = Fore;
Ctx.Back = Background;
Ctx.TxtBack = Background;
LColour SelFore(Focus() ? L_FOCUS_SEL_FORE : L_NON_FOCUS_SEL_FORE);
LColour SelBack(Focus() ? L_FOCUS_SEL_BACK : L_NON_FOCUS_SEL_BACK);
// layout items
if (d->LayoutDirty)
{
_Pour();
}
// paint items
ZeroObj(d->LineFlags);
List::I it = Items.begin();
for (LTreeItem *i = *it; i; i=*++it)
i->OnPaint(Ctx);
pDC->SetOrigin(Ox, Oy);
if (d->Limit.y-s.y < rItems.Y())
{
// paint after items
pDC->Colour(Background);
pDC->Rectangle(rItems.x1, d->Limit.y - s.y, rItems.x2, rItems.y2);
}
}
int LTree::OnNotify(LViewI *Ctrl, LNotification n)
{
switch (Ctrl->GetId())
{
case IDC_HSCROLL:
case IDC_VSCROLL:
{
- TREELOCK
+ TREELOCK(this)
if (n.Type == LNotifyScrollBarCreate)
{
_UpdateScrollBars();
if (VScroll)
{
if (HasItem(d->ScrollTo))
d->ScrollTo->ScrollTo();
d->ScrollTo = NULL;
}
}
Invalidate();
break;
}
}
return LLayout::OnNotify(Ctrl, n);
}
LMessage::Result LTree::OnEvent(LMessage *Msg)
{
switch (Msg->Msg())
{
case M_SCROLL_TO:
{
LTreeItem *Item = (LTreeItem*)Msg->A();
if (!HasItem(Item))
break;
if (VScroll)
Item->ScrollTo();
break;
}
}
return LItemContainer::OnEvent(Msg);
}
LTreeItem *LTree::Insert(LTreeItem *Obj, ssize_t Pos)
{
- TREELOCK
+ TREELOCK(this)
LTreeItem *NewObj = LTreeNode::Insert(Obj, Pos);
if (NewObj)
NewObj->_SetTreePtr(this);
return NewObj;
}
bool LTree::HasItem(LTreeItem *Obj, bool Recurse)
{
- TREELOCK
+ TREELOCK(this)
if (!Obj)
return false;
return LTreeNode::HasItem(Obj, Recurse);
}
bool LTree::Remove(LTreeItem *Obj)
{
- TREELOCK
+ TREELOCK(this)
bool Status = false;
if (Obj && Obj->Tree == this)
{
Obj->Remove();
Status = true;
}
return Status;
}
void LTree::RemoveAll()
{
- TREELOCK
+ TREELOCK(this)
List::I it = Items.begin();
for (LTreeItem *i=*it; i; i=*++it)
i->_Remove();
Invalidate();
}
void LTree::Empty()
{
- TREELOCK
+ TREELOCK(this)
LTreeItem *i;
while ((i = Items[0]))
Delete(i);
}
bool LTree::Delete(LTreeItem *Obj)
{
bool Status = false;
- TREELOCK
+ TREELOCK(this)
if (Obj)
{
LTreeItem *i;
while ((i = Obj->Items[0]))
{
Delete(i);
}
Obj->Remove();
DeleteObj(Obj);
Status = true;
}
return Status;
}
void LTree::OnPulse()
{
- TREELOCK
+ TREELOCK(this)
if (d->DropTarget)
{
int64 p = LCurrentTime() - d->DropSelectTime;
if (p >= 1000)
{
SetPulse();
if (!d->DropTarget->Expanded() &&
d->DropTarget->GetChild())
{
d->DropTarget->Expanded(true);
}
}
}
if (InsideDragOp())
{
LMouse m;
if (GetMouse(m))
{
if (!m.Left() && !m.Right() && !m.Middle())
{
// Be robust against missing drag exit events (Mac specific?)
InsideDragOp(false);
}
else
{
LRect c = GetClient();
if (VScroll)
{
if (m.y < DRAG_SCROLL_EDGE)
{
// Scroll up...
VScroll->Value(VScroll->Value() - DRAG_SCROLL_Y);
}
else if (m.y > c.Y() - DRAG_SCROLL_EDGE)
{
// Scroll down...
VScroll->Value(VScroll->Value() + DRAG_SCROLL_Y);
}
}
if (HScroll)
{
if (m.x < DRAG_SCROLL_EDGE)
{
// Scroll left...
HScroll->Value(HScroll->Value() - DRAG_SCROLL_X);
}
else if (m.x > c.X() - DRAG_SCROLL_EDGE)
{
// Scroll right...
HScroll->Value(HScroll->Value() + DRAG_SCROLL_X);
}
}
}
}
}
}
int LTree::GetContentSize(int ColumnIdx)
{
- TREELOCK
+ TREELOCK(this)
int MaxPx = 0;
List::I it = Items.begin();
for (LTreeItem *i = *it; i; i=*++it)
{
int ItemPx = i->GetColumnSize(ColumnIdx);
MaxPx = MAX(ItemPx, MaxPx);
}
return MaxPx;
}
LCursor LTree::GetCursor(int x, int y)
{
- TREELOCK
+ TREELOCK(this)
LItemColumn *Resize = NULL, *Over = NULL;
HitColumn(x, y, Resize, Over);
return (Resize) ? LCUR_SizeHor : LCUR_Normal;
}
void LTree::OnDragEnter()
{
- TREELOCK
+ TREELOCK(this)
InsideDragOp(true);
SetPulse(120);
}
void LTree::OnDragExit()
{
- TREELOCK
+ TREELOCK(this)
InsideDragOp(false);
SetPulse();
SelectDropTarget(0);
}
void LTree::SelectDropTarget(LTreeItem *Item)
{
- TREELOCK
+ TREELOCK(this)
if (Item != d->DropTarget)
{
bool Update = (d->DropTarget != 0) ^ (Item != 0);
LTreeItem *Old = d->DropTarget;
d->DropTarget = Item;
if (Old)
{
Old->Update();
}
if (d->DropTarget)
{
d->DropTarget->Update();
d->DropSelectTime = LCurrentTime();
}
if (Update)
{
OnFocus(true);
}
}
}
bool LTree::Select(LTreeItem *Obj)
{
- TREELOCK
+ TREELOCK(this)
bool Status = false;
if (Obj && IsAttached())
{
Obj->Select(true);
Status = true;
}
else if (d->Selection.Length())
{
d->Selection.Empty();
OnItemSelect(0);
Status = true;
}
return Status;
}
LTreeItem *LTree::Selection()
{
- TREELOCK
+ TREELOCK(this)
return d->Selection[0];
}
bool LTree::ForAllItems(std::function Callback)
{
- TREELOCK
+ TREELOCK(this)
return ForEach(Callback) > 0;
}
void LTree::OnItemClick(LTreeItem *Item, LMouse &m)
{
if (!Item)
return;
- TREELOCK
+ TREELOCK(this)
Item->OnMouseClick(m);
if (!m.Ctrl() && !m.Shift())
SendNotify(LNotification(m));
}
void LTree::OnItemBeginDrag(LTreeItem *Item, LMouse &m)
{
if (!Item)
return;
- TREELOCK
+ TREELOCK(this)
Item->OnBeginDrag(m);
}
void LTree::OnFocus(bool b)
{
- TREELOCK
+ TREELOCK(this)
// errors during deletion of the control can cause
// this to be called after the destructor
if (d)
{
List::I it = d->Selection.begin();
for (LTreeItem *i=*it; i; i=*++it)
i->Update();
}
}
static void LTreeItemUpdateAll(LTreeNode *n)
{
for (LTreeItem *i=n->GetChild(); i; i=i->GetNext())
{
i->Update();
LTreeItemUpdateAll(i);
}
}
void LTree::UpdateAllItems()
{
- TREELOCK
+ TREELOCK(this)
d->LayoutDirty = true;
LTreeItemUpdateAll(this);
}
void LTree::SetVisualStyle(ThumbStyle Btns, bool JoiningLines)
{
- TREELOCK
+ TREELOCK(this)
d->Btns = Btns;
d->JoiningLines = JoiningLines;
Invalidate();
}
diff --git a/src/haiku/App.cpp b/src/haiku/App.cpp
--- a/src/haiku/App.cpp
+++ b/src/haiku/App.cpp
@@ -1,1057 +1,1062 @@
#include
#include
#include
#include
#include
#include
#include
#include "lgi/common/Lgi.h"
#include "lgi/common/SkinEngine.h"
#include "lgi/common/Array.h"
#include "lgi/common/Variant.h"
#include "lgi/common/Token.h"
#include "lgi/common/FontCache.h"
#include "AppPriv.h"
#include "MimeType.h"
#define DEBUG_MSG_TYPES 0
#define DEBUG_HND_WARNINGS 0
#define IDLE_ALWAYS 0
LString LgiArgsAppPath;
////////////////////////////////////////////////////////////////
struct OsAppArgumentsPriv
{
::LArray Ptr;
~OsAppArgumentsPriv()
{
Ptr.DeleteArrays();
}
};
OsAppArguments::OsAppArguments(int args, const char **arg)
{
d = new OsAppArgumentsPriv;
for (int i=0; iPtr.Add(NewStr(arg[i]));
}
Args = d->Ptr.Length();
Arg = &d->Ptr[0];
}
OsAppArguments::~OsAppArguments()
{
DeleteObj(d);
}
bool OsAppArguments::Get(const char *Name, const char **Val)
{
if (!Name)
return false;
for (int i=0; iPtr.DeleteArrays();
if (!CmdLine)
return;
for (char *s = CmdLine; *s; )
{
while (*s && strchr(WhiteSpace, *s)) s++;
if (*s == '\'' || *s == '\"')
{
char delim = *s++;
char *e = strchr(s, delim);
if (e)
d->Ptr.Add(NewStr(s, e - s));
else
break;
s = e + 1;
}
else
{
char *e = s;
while (*e && !strchr(WhiteSpace, *e))
e++;
d->Ptr.Add(NewStr(s, e-s));
s = e;
}
}
Args = d->Ptr.Length();
Arg = &d->Ptr[0];
}
OsAppArguments &OsAppArguments::operator =(OsAppArguments &a)
{
d->Ptr.DeleteArrays();
for (int i=0; iPtr.Add(NewStr(a.Arg[i]));
}
Args = d->Ptr.Length();
Arg = &d->Ptr[0];
return *this;
}
////////////////////////////////////////////////////////////////
#if HAS_SHARED_MIME
#include "GFilterUtils.h"
#include "mime-types.h"
class LSharedMime : public LLibrary
{
public:
LSharedMime() :
#ifdef _DEBUG
LLibrary("libsharedmime1d")
#else
LLibrary("libsharedmime1")
#endif
{
}
DynFunc0(int, mimetypes_init);
DynFunc1(const char*, mimetypes_set_default_type, const char *, default_type);
DynFunc2(const char*, mimetypes_get_file_type, const char*, pathname, mimetypes_flags, flags);
DynFunc2(const char*, mimetypes_get_data_type, const void*, data, int, length);
DynFunc3(bool, mimetypes_decode, const char *, type, char **, media_type, char **, sub_type);
DynFunc2(char *, mimetypes_convert_filename, const char *, pathname, const char *, mime_type);
DynFunc3(bool, mimetypes_add_mime_dir, const char *, path, bool, high_priority, bool, rescan);
DynFunc2(const char *, mimetypes_get_type_info, const char *, mime_type, const char *, lang);
};
#endif
#if 1
/////////////////////////////////////////////////////////////////////////////
//
// Attempts to cleanup and call drkonqi to process the crash
//
void LgiCrashHandler(int Sig)
{
// Don't get into an infinite loop
signal(SIGSEGV, SIG_DFL);
#ifndef _MSC_VER
// Our pid
int MyPid = getpid();
printf("LgiCrashHandler trigger MyPid=%i\n", MyPid);
#endif
exit(-1);
}
#endif
/////////////////////////////////////////////////////////////////////////////
#ifndef XK_Num_Lock
#define XK_Num_Lock 0xff7f
#endif
#ifndef XK_Shift_Lock
#define XK_Shift_Lock 0xffe6
#endif
#ifndef XK_Caps_Lock
#define XK_Caps_Lock 0xffe5
#endif
struct Msg
{
LViewI *v;
int m;
LMessage::Param a, b;
void Set(LViewI *V, int M, LMessage::Param A, LMessage::Param B)
{
v = V;
m = M;
a = A;
b = B;
}
};
// Out of thread messages... must lock before access.
class LMessageQue : public LMutex
{
public:
typedef ::LArray MsgArray;
LMessageQue() : LMutex("LMessageQue")
{
}
MsgArray *Lock(const char *file, int line)
{
if (!LMutex::Lock(file, line))
return NULL;
return &q;
}
operator bool()
{
return q.Length() > 0;
}
private:
MsgArray q;
} MsgQue;
/////////////////////////////////////////////////////////////////////////////
LApp *TheApp = NULL;
LSkinEngine *LApp::SkinEngine;
LMouseHook *LApp::MouseHook;
LApp::LApp(OsAppArguments &AppArgs, const char *name, LAppArguments *Args) :
OsApplication(AppArgs.Args, AppArgs.Arg)
{
TheApp = this;
LgiArgsAppPath = AppArgs.Arg[0];
Name(name);
d = new LAppPrivate(this);
if (LIsRelativePath(LgiArgsAppPath))
{
char Cwd[MAX_PATH_LEN];
getcwd(Cwd, sizeof(Cwd));
LMakePath(Cwd, sizeof(Cwd), Cwd, LgiArgsAppPath);
LgiArgsAppPath = Cwd;
}
char AppPathLnk[MAX_PATH_LEN];
if (LResolveShortcut(LgiArgsAppPath, AppPathLnk, sizeof(AppPathLnk)))
LgiArgsAppPath = AppPathLnk;
int WCharSz = sizeof(wchar_t);
#if defined(_MSC_VER)
LAssert(WCharSz == 2);
::LFile::Path Dlls(LgiArgsAppPath);
Dlls--;
SetDllDirectoryA(Dlls);
#else
LAssert(WCharSz == 4);
#endif
#ifdef _MSC_VER
SetEnvironmentVariable(_T("GTK_CSD"), _T("0"));
#else
setenv("GTK_CSD", "0", true);
#endif
// We want our printf's NOW!
setvbuf(stdout,(char *)NULL,_IONBF,0); // print mesgs immediately.
// Setup the file and graphics sub-systems
d->FileSystem = new LFileSystem;
d->GdcSystem = new GdcDevice;
SetAppArgs(AppArgs);
srand(LCurrentTime());
LColour::OnChange();
MouseHook = new LMouseHook;
d->GetConfig();
// System font setup
LFontType SysFontType;
if (SysFontType.GetSystemFont("System"))
{
SystemNormal = SysFontType.Create();
if (SystemNormal)
SystemNormal->Transparent(true);
SystemBold = SysFontType.Create();
if (SystemBold)
{
SystemBold->Bold(true);
SystemBold->Transparent(true);
SystemBold->Create();
}
}
else
{
printf("%s:%i - Couldn't get system font setting.\n", __FILE__, __LINE__);
}
if (!SystemNormal)
{
LgiMsg(0, "Error: Couldn't create system font.", "Lgi Error: LApp::LApp", MB_OK);
LExitApp();
}
if (!GetOption("noskin"))
{
extern LSkinEngine *CreateSkinEngine(LApp *App);
SkinEngine = CreateSkinEngine(this);
}
}
LApp::~LApp()
{
DeleteObj(AppWnd);
DeleteObj(SystemNormal);
DeleteObj(SystemBold);
DeleteObj(SkinEngine);
DeleteObj(MouseHook);
DeleteObj(d->FileSystem);
DeleteObj(d->GdcSystem);
DeleteObj(LFontSystem::Me);
DeleteObj(d);
TheApp = 0;
}
LApp *LApp::ObjInstance()
{
return TheApp;
}
bool LApp::IsOk()
{
bool Status =
#ifndef __clang__
(this != 0) &&
#endif
(d != 0);
LAssert(Status);
return Status;
}
LMouseHook *LApp::GetMouseHook()
{
return MouseHook;
}
int LApp::GetMetric(LSystemMetric Metric)
{
switch (Metric)
{
case LGI_MET_DECOR_X:
return 8;
case LGI_MET_DECOR_Y:
return 8 + 19;
case LGI_MET_DECOR_CAPTION:
return 19;
default:
break;
}
return 0;
}
LViewI *LApp::GetFocus()
{
// GtkWidget *w = gtk_window_get_focus(GtkWindow *window);
return NULL;
}
OsThread LApp::_GetGuiThread()
{
return d->GuiThread;
}
OsThreadId LApp::GetGuiThreadId()
{
return d->GuiThreadId;
}
OsProcessId LApp::GetProcessId()
{
#ifdef WIN32
return GetCurrentProcessId();
#else
return getpid();
#endif
}
OsAppArguments *LApp::GetAppArgs()
{
return IsOk() ? &d->Args : 0;
}
void LApp::SetAppArgs(OsAppArguments &AppArgs)
{
if (IsOk())
{
d->Args = AppArgs;
}
}
bool LApp::InThread()
{
OsThreadId Me = GetCurrentThreadId();
OsThreadId Gui = GetGuiThreadId();
// printf("Me=%i Gui=%i\n", Me, Gui);
return Gui == Me;
}
bool LApp::Run(OnIdleProc IdleCallback, void *IdleParam)
{
if (!InThread())
{
printf("%s:%i - Error: Out of thread.\n", _FL);
return false;
}
printf("Running main loop...\n");
d->Run();
printf("Main loop finished.\n");
return true;
}
bool LApp::Yield()
{
return false;
}
void LApp::Exit(int Code)
{
if (Code)
{
// hard exit
::exit(Code);
}
else
{
// soft exit
printf("Quitting main loop...\n");
if (d->Lock())
{
d->Quit();
d->Unlock();
}
}
}
void LApp::OnUrl(const char *Url)
{
if (AppWnd)
AppWnd->OnUrl(Url);
}
void LApp::OnReceiveFiles(::LArray &Files)
{
if (AppWnd)
AppWnd->OnReceiveFiles(Files);
else
LAssert(!"You probably want to set 'AppWnd' before calling LApp::Run... maybe.");
}
const char *LApp::GetArgumentAt(int n)
{
return n >= 0 && n < d->Args.Args ? NewStr(d->Args.Arg[n]) : 0;
}
bool LApp::GetOption(const char *Option, char *Dest, int DestLen)
{
::LString Buf;
if (GetOption(Option, Buf))
{
if (Dest)
{
if (DestLen > 0)
{
strcpy_s(Dest, DestLen, Buf);
}
else return false;
}
return true;
}
return false;
}
bool LApp::GetOption(const char *Option, ::LString &Buf)
{
if (IsOk() && Option)
{
int OptLen = strlen(Option);
for (int i=1; iArgs.Args; i++)
{
auto *a = d->Args.Arg[i];
if (!a)
continue;
if (strchr("-/\\", a[0]))
{
if (strnicmp(a+1, Option, OptLen) == 0)
{
const char *Arg = NULL;
if (strlen(a+1+OptLen) > 0)
{
Arg = a + 1 + OptLen;
}
else if (i < d->Args.Args - 1)
{
Arg = d->Args.Arg[i + 1];
}
if (Arg)
{
if (strchr("\'\"", *Arg))
{
char Delim = *Arg++;
char *End = strchr(Arg, Delim);
if (End)
{
auto Len = End-Arg;
if (Len > 0)
Buf.Set(Arg, Len);
else
return false;
}
else return false;
}
else
{
Buf = Arg;
}
}
return true;
}
}
}
}
return false;
}
void LApp::OnCommandLine()
{
::LArray Files;
for (int i=1; iArgs; i++)
{
auto a = GetAppArgs()->Arg[i];
if (LFileExists(a))
{
Files.Add(NewStr(a));
}
}
// call app
if (Files.Length() > 0)
{
OnReceiveFiles(Files);
}
// clear up
Files.DeleteArrays();
}
LString LApp::GetFileMimeType(const char *File)
{
BMimeType mt;
auto r = BMimeType::GuessMimeType(File, &mt);
if (r != B_OK)
{
LgiTrace("%s:%i - GuessMimeType(%s) failed: %i\n", _FL, File, r);
return LString();
}
return mt.Type();
}
bool LApp::GetAppsForMimeType(const char *Mime, LArray &Apps)
{
// Find alternative version of the MIME type (e.g. x-type and type).
char AltMime[256];
strcpy(AltMime, Mime);
char *s = strchr(AltMime, '/');
if (s)
{
s++;
int Len = strlen(s) + 1;
if (strnicmp(s, "x-", 2) == 0)
{
memmove(s, s+2, Len - 2);
}
else
{
memmove(s+2, s, Len);
s[0] = 'x';
s[1] = '-';
}
}
LGetAppsForMimeType(Mime, Apps);
LGetAppsForMimeType(AltMime, Apps);
return Apps.Length() > 0;
}
#if defined(LINUX)
LLibrary *LApp::GetWindowManagerLib()
{
if (this != NULL && !d->WmLib)
{
char Lib[32];
WindowManager Wm = LGetWindowManager();
switch (Wm)
{
case WM_Kde:
strcpy(Lib, "liblgikde3");
break;
case WM_Gnome:
strcpy(Lib, "liblgignome2");
break;
default:
strcpy(Lib, "liblgiother");
break;
}
#ifdef _DEBUG
strcat(Lib, "d");
#endif
d->WmLib = new LLibrary(Lib, true);
if (d->WmLib)
{
if (d->WmLib->IsLoaded())
{
Proc_LgiWmInit WmInit = (Proc_LgiWmInit) d->WmLib->GetAddress("LgiWmInit");
if (WmInit)
{
WmInitParams Params;
// Params.Dsp = XObject::XDisplay();
Params.Args = d->Args.Args;
Params.Arg = d->Args.Arg;
WmInit(&Params);
}
// else printf("%s:%i - Failed to find method 'LgiWmInit' in WmLib.\n", __FILE__, __LINE__);
}
// else printf("%s:%i - couldn't load '%s.so'\n", __FILE__, __LINE__, Lib);
}
// else printf("%s:%i - alloc error\n", __FILE__, __LINE__);
}
return d->WmLib && d->WmLib->IsLoaded() ? d->WmLib : 0;
}
void LApp::DeleteMeLater(LViewI *v)
{
d->DeleteLater.Add(v);
}
void LApp::SetClipBoardContent(OsView Hnd, ::LVariant &v)
{
// Store the clipboard data we will serve
d->ClipData = v;
}
bool LApp::GetClipBoardContent(OsView Hnd, ::LVariant &v, ::LArray &Types)
{
return false;
}
#endif
LSymLookup *LApp::GetSymLookup()
{
return NULL;
}
bool LApp::IsElevated()
{
#ifdef WIN32
LAssert(!"What API works here?");
return false;
#else
return geteuid() == 0;
#endif
}
int LApp::GetCpuCount()
{
return 1;
}
LFontCache *LApp::GetFontCache()
{
if (!d->FontCache)
d->FontCache.Reset(new LFontCache(SystemNormal));
return d->FontCache;
}
#ifdef LINUX
LApp::DesktopInfo::DesktopInfo(const char *file)
{
File = file;
Dirty = false;
if (File)
Serialize(false);
}
bool LApp::DesktopInfo::Serialize(bool Write)
{
::LFile f;
if (Write)
{
::LFile::Path p(File);
p--;
if (!p.Exists())
return false;
}
else if (!LFileExists(File))
return false;
if (!f.Open(File, Write?O_WRITE:O_READ))
{
LgiTrace("%s:%i - Failed to open '%s'\n", _FL, File.Get());
return false;
}
if (Write)
{
f.SetSize(0);
for (unsigned i=0; i= 0)
{
int e = l.Find("]", ++s);
if (e >= 0)
{
Cur = &Data.New();
Cur->Name = l(s, e - s + 1);
}
}
else if ((s = l.Find("=")) >= 0)
{
if (!Cur)
Cur = &Data.New();
KeyPair &kp = Cur->Values.New();
kp.Key = l(0, s).Strip();
kp.Value = l(++s, -1).Strip();
// printf("Read '%s': '%s'='%s'\n", Cur->Name.Get(), kp.Key.Get(), kp.Value.Get());
}
}
}
return true;
}
LApp::DesktopInfo::Section *LApp::DesktopInfo::GetSection(const char *Name, bool Create)
{
for (unsigned i=0; iGet(Field, false, Dirty);
if (kp)
{
return kp->Value;
}
}
}
return ::LString();
}
bool LApp::DesktopInfo::Set(const char *Field, const char *Value, const char *Sect)
{
if (!Field)
return false;
Section *s = GetSection(Sect ? Sect : DefaultSection, true);
if (!s)
return false;
KeyPair *kp = s->Get(Field, true, Dirty);
if (!kp)
return false;
if (kp->Value != Value)
{
kp->Value = Value;
Dirty = true;
}
return true;
}
LApp::DesktopInfo *LApp::GetDesktopInfo()
{
auto sExe = LGetExeFile();
::LFile::Path Exe(sExe);
::LFile::Path Desktop(LSP_HOME);
::LString Leaf;
Leaf.Printf("%s.desktop", Exe.Last().Get());
Desktop += ".local/share/applications";
Desktop += Leaf;
const char *Ex = Exe;
const char *Fn = Desktop;
if (d->DesktopInfo.Reset(new DesktopInfo(Desktop)))
{
// Do a sanity check...
::LString s = d->DesktopInfo->Get("Name");
if (!s && Name())
d->DesktopInfo->Set("Name", Name());
s = d->DesktopInfo->Get("Exec");
if (!s || s != (const char*)sExe)
d->DesktopInfo->Set("Exec", sExe);
s = d->DesktopInfo->Get("Type");
if (!s) d->DesktopInfo->Set("Type", "Application");
s = d->DesktopInfo->Get("Categories");
if (!s) d->DesktopInfo->Set("Categories", "Application;");
s = d->DesktopInfo->Get("Terminal");
if (!s) d->DesktopInfo->Set("Terminal", "false");
d->DesktopInfo->Update();
}
return d->DesktopInfo;
}
bool LApp::SetApplicationIcon(const char *FileName)
{
DesktopInfo *di = GetDesktopInfo();
if (!di)
return false;
::LString IcoPath = di->Get("Icon");
if (IcoPath == FileName)
return true;
di->Set("Icon", FileName);
return di->Update();
}
#endif
////////////////////////////////////////////////////////////////
OsApplication *OsApplication::Inst = 0;
class OsApplicationPriv
{
public:
OsApplicationPriv()
{
}
};
OsApplication::OsApplication(int Args, const char **Arg)
{
Inst = this;
d = new OsApplicationPriv;
}
OsApplication::~OsApplication()
{
DeleteObj(d);
Inst = 0;
}
////////////////////////////////////////////////////////////////
int LMessage::Msg()
{
return what;
}
LMessage::Param LMessage::A()
{
int64 a = 0;
if (FindInt64(PropA, &a) != B_OK)
{
int32 c = CountNames(B_ANY_TYPE);
printf("%s:%i - Failed to find PropA (%i names)\n", _FL, c);
for (int32 i=0; iPostEvent(what, A(), B());
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
LLocker::LLocker(BHandler *h, const char *File, int Line)
{
hnd = h;
file = File;
line = Line;
}
LLocker::~LLocker()
{
Unlock();
}
bool LLocker::Lock()
{
if (locked)
{
printf("%s:%i - Locker already locked.\n", file, line);
LAssert(!"Locker already locked.");
return false;
}
if (!hnd)
{
// printf("%s:%i - Locker hnd is NULL.\n", file, line);
return false;
}
while (!locked)
{
status_t result = hnd->LockLooperWithTimeout(1000 * 1000);
if (result == B_OK)
{
locked = true;
break;
}
else if (result == B_TIMED_OUT)
{
// Warn about failure to lock...
- thread_id Thread = hnd->Looper()->LockingThread();
- printf("%s:%i - Warning: can't lock. Myself=%i\n", _FL, LGetCurrentThread(), Thread);
+ auto cur = GetCurrentThreadId();
+ auto locking = hnd->Looper()->LockingThread();
+
+ printf("%s:%i - Warning: can't lock. cur=%i locking=%i\n",
+ _FL,
+ cur,
+ locking);
}
else if (result == B_BAD_VALUE)
{
break;
}
else
{
// Warn about error
printf("%s:%i - Error from LockLooperWithTimeout = 0x%x\n", _FL, result);
}
}
return locked;
}
status_t LLocker::LockWithTimeout(int64 time)
{
LAssert(!locked);
LAssert(hnd != NULL);
status_t result = hnd->LockLooperWithTimeout(time);
if (result == B_OK)
locked = true;
return result;
}
void LLocker::Unlock()
{
if (locked)
{
hnd->UnlockLooper();
locked = false;
}
}
diff --git a/src/haiku/File.cpp b/src/haiku/File.cpp
--- a/src/haiku/File.cpp
+++ b/src/haiku/File.cpp
@@ -1,1513 +1,1452 @@
/*hdr
** FILE: File.cpp
** AUTHOR: Matthew Allen
** DATE: 8/10/2000
-** DESCRIPTION: The new file subsystem
+** DESCRIPTION: The file subsystem
**
** Copyright (C) 2000, Matthew Allen
** fret@memecode.com
*/
/****************************** Includes **********************************/
-
-#include
-#include
-#include
-#include
-#include
-
-#include
-#include
-#include
-#include
+#include
#include "Path.h"
+#include "VolumeRoster.h"
+#include "Directory.h"
#include "lgi/common/LgiDefs.h"
#include "lgi/common/File.h"
#include "lgi/common/Containers.h"
-#include "lgi/common/Token.h"
#include "lgi/common/Gdc2.h"
#include "lgi/common/LgiCommon.h"
-#include "lgi/common/LgiString.h"
#include "lgi/common/DateTime.h"
-/****************************** Globals ***********************************/
-LString LFile::Path::Sep(DIR_STR);
+/****************************** Helper Functions **************************/
+LString BGetFullPath(BEntry &entry)
+{
+ BPath path;
+ auto r = entry.GetPath(&path);
+ if (r != B_OK)
+ return LString();
+ return path.Path();
+}
-/****************************** Helper Functions **************************/
+LString BGetFullPath(BDirectory &dir)
+{
+ BEntry entry;
+ auto r = dir.GetEntry(&entry);
+ if (r != B_OK)
+ return LString();
+ return BGetFullPath(entry);
+}
+
+LString BGetFullPath(BVolume &volume)
+{
+ BDirectory directory;
+ auto r = volume.GetRootDirectory(&directory);
+ if (r != B_OK)
+ return LString();
+ return BGetFullPath(directory);
+}
+
char *LReadTextFile(const char *File)
{
char *s = 0;
LFile f;
if (File && f.Open(File, O_READ))
{
int Len = f.GetSize();
s = new char[Len+1];
if (s)
{
int Read = f.Read(s, Len);
s[Read] = 0;
}
}
return s;
}
int64 LFileSize(const char *FileName)
{
- struct stat s;
- if (FileName &&
- stat(FileName, &s) == 0)
- {
- return s.st_size;
- }
-
- return 0;
+ BEntry e(FileName);
+ if (e.InitCheck() != B_OK)
+ return 0;
+
+ off_t size = 0;
+ if (e.GetSize(&size) != B_OK)
+ return 0;
+
+ return size;
}
bool LDirExists(const char *FileName, char *CorrectCase)
{
- bool Status = false;
-
- if (FileName)
+ if (!FileName)
+ return false;
+
+ struct stat s;
+ auto r = lstat(FileName, &s);
+ if (r == 0)
{
- struct stat s;
-
- // Check for exact match...
- int r = lstat(FileName, &s);
+ return S_ISDIR(s.st_mode) ||
+ S_ISLNK(s.st_mode);
+ }
+ else
+ {
+ r = stat(FileName, &s);
if (r == 0)
{
- Status = S_ISDIR(s.st_mode) ||
- S_ISLNK(s.st_mode);
- // printf("DirStatus(%s) lstat = %i, %i\n", FileName, Status, s.st_mode);
- }
- else
- {
- r = stat(FileName, &s);
- if (r == 0)
- {
- Status = S_ISDIR(s.st_mode) ||
- S_ISLNK(s.st_mode);
- // printf("DirStatus(%s) stat ok = %i, %i\n", FileName, Status, s.st_mode);
- }
- else
- {
- // printf("DirStatus(%s) lstat and stat failed, r=%i, errno=%i\n", FileName, r, errno);
- }
+ return S_ISDIR(s.st_mode) ||
+ S_ISLNK(s.st_mode);
}
}
- return Status;
+ return false;
}
bool LFileExists(const char *FileName, char *CorrectCase)
{
bool Status = false;
if (FileName)
{
struct stat s;
// Check for exact match...
if (stat(FileName, &s) == 0)
{
Status = !S_ISDIR(s.st_mode);
}
else if (CorrectCase)
{
// Look for altenate case by enumerating the directory
char d[256];
strcpy(d, FileName);
char *e = strrchr(d, DIR_CHAR);
if (e)
{
*e++ = 0;
DIR *Dir = opendir(d);
if (Dir)
{
dirent *De;
while ((De = readdir(Dir)))
{
if (stricmp(De->d_name, e) == 0)
{
try
{
// Tell the calling program the actual case of the file...
e = (char*) strrchr(FileName, DIR_CHAR);
// If this crashes because the argument is read only then we get caught by the try catch
strcpy(e+1, De->d_name);
// It worked!
Status = true;
}
catch (...)
{
// It didn't work :(
#ifdef _DEBUG
printf("%s,%i - LFileExists(%s) found an alternate case version but couldn't return it to the caller.\n",
__FILE__, __LINE__, FileName);
#endif
}
break;
}
}
closedir(Dir);
}
}
}
}
return Status;
}
bool LResolveShortcut(const char *LinkFile, char *Path, ssize_t Len)
{
if (!LinkFile || !Path || Len < 1)
return false;
BEntry e(LinkFile);
auto r = e.InitCheck();
if (r != B_OK)
{
printf("%s:%i - LResolveShortcut: %i\n", _FL, r);
return false;
}
if (!e.IsSymLink())
{
return false;
}
r = e.SetTo(LinkFile, true);
if (r != B_OK)
{
printf("%s:%i - LResolveShortcut: %i\n", _FL, r);
return false;
}
- BPath p;
- r = e.GetPath(&p);
- if (r != B_OK)
- {
- printf("%s:%i - LResolveShortcut: %i\n", _FL, r);
- return false;
- }
-
- strcpy_s(Path, Len, p.Path());
+ auto p = BGetFullPath(e);
+ strcpy_s(Path, Len, p);
return true;
}
void WriteStr(LFile &f, const char *s)
{
uint32_t Len = (s) ? strlen(s) : 0;
f << Len;
if (Len > 0)
{
f.Write(s, Len);
}
}
char *ReadStr(LFile &f DeclDebugArgs)
{
char *s = 0;
// read the strings length...
uint32_t Len;
f >> Len;
if (Len > 0)
{
// 16mb sanity check.... anything over this
// is _probably_ an error
if (Len >= (16 << 20))
{
// LAssert(0);
return 0;
}
// allocate the memory buffer
#if defined(_DEBUG) && defined MEMORY_DEBUG
s = new(_file, _line) char[Len+1];
#else
s = new char[Len+1];
#endif
if (s)
{
// read the bytes from disk
f.Read(s, Len);
s[Len] = 0;
}
else
{
// memory allocation error, skip the data
// on disk so the caller is where they think
// they are in the file.
f.Seek(Len, SEEK_CUR);
}
}
return s;
}
ssize_t SizeofStr(const char *s)
{
return sizeof(ulong) + ((s) ? strlen(s) : 0);
}
bool LGetDriveInfo
(
char *Path,
uint64 *Free,
uint64 *Size,
uint64 *Available
)
{
bool Status = false;
if (Path)
{
struct stat s;
if (lstat(Path, &s) == 0)
{
// printf("LGetDriveInfo dev=%i\n", s.st_dev);
}
}
return Status;
}
/////////////////////////////////////////////////////////////////////////
-#include
-#include
-
struct LVolumePriv
{
- int64 _Size, _Free;
- int _Type, _Flags;
- LSystemPath SysPath;
- LString _Name, _Path;
- List _Sub;
- List::I _It;
+ LVolume *Owner = NULL;
+ int64 Size = 0, Free = 0;
+ int Type = VT_NONE, Flags = 0;
+ LSystemPath SysPath = LSP_ROOT;
+ LString Name, Path;
+ LVolume *NextVol = NULL, *ChildVol = NULL;
- void Init()
+ LVolumePriv(LVolume *owner, const char *path) : Owner(owner)
{
- SysPath = LSP_ROOT;
- _Type = VT_NONE;
- _Flags = 0;
- _Size = 0;
- _Free = 0;
+ Path = path;
+ Name = LGetLeaf(path);
+ Type = VT_FOLDER;
}
- LVolumePriv(const char *path) : _It(_Sub.end())
+ LVolumePriv(LVolume *owner, LSystemPath sys, const char *name) : Owner(owner)
{
- Init();
-
- _Path = path;
- _Name = LGetLeaf(path);
- _Type = VT_FOLDER;
- }
-
- LVolumePriv(LSystemPath sys, const char *name) : _It(_Sub.end())
- {
- Init();
SysPath = sys;
+ Name = name;
if (SysPath == LSP_ROOT)
- _Path = "/";
+ Path = "/";
else
- _Path = LGetSystemPath(SysPath);
- if (_Path)
- {
- _Name = name;
- _Type = sys == LSP_DESKTOP ? VT_DESKTOP : VT_FOLDER;
- }
+ Path = LGetSystemPath(SysPath);
+
+ if (Path)
+ Type = sys == LSP_DESKTOP ? VT_DESKTOP : VT_FOLDER;
}
~LVolumePriv()
{
- _Sub.DeleteObjects();
+ DeleteObj(NextVol);
+ DeleteObj(ChildVol);
}
+ void Insert(LVolume *vol, LVolume *newVol)
+ {
+ if (!vol || !newVol)
+ return;
+
+ if (vol->d->ChildVol)
+ {
+ for (auto v = vol->d->ChildVol; v; v = v->d->NextVol)
+ {
+ if (!v->d->NextVol)
+ {
+ LAssert(newVol != v->d->Owner);
+ v->d->NextVol = newVol;
+ // printf("Insert %p:%s into %p:%s\n", newVol, newVol->Name(), vol, vol->Name());
+ break;
+ }
+ }
+ }
+ else
+ {
+ LAssert(newVol != vol->d->Owner);
+ vol->d->ChildVol = newVol;
+ // printf("Insert %p:%s into %p:%s\n", newVol, newVol->Name(), vol, vol->Name());
+ }
+ }
+
LVolume *First()
{
- if (SysPath == LSP_DESKTOP && !_Sub.Length())
+ if (SysPath == LSP_DESKTOP && !ChildVol)
{
// Get various shortcuts to points of interest
- LVolume *v = new LVolume(LSP_ROOT, "Root");
- if (v)
- _Sub.Insert(v);
-
+ Insert(Owner, new LVolume(LSP_ROOT, "Root"));
+
struct passwd *pw = getpwuid(getuid());
if (pw)
- {
- v = new LVolume(LSP_HOME, "Home");
- if (v)
- _Sub.Insert(v);
- }
-
- // Get mount list
- // this is just a hack at this stage to establish some base
- // functionality. I would appreciate someone telling me how
- // to do this properly. Till then...
- LFile f;
- auto fstab = "/etc/fstab";
- if (LFileExists(fstab) && f.Open(fstab, O_READ))
- {
- auto Buf= f.Read();
- f.Close();
+ Insert(Owner, new LVolume(LSP_HOME, "Home"));
- auto Lines = Buf.SplitDelimit("\r\n");
- for (auto ln : Lines)
- {
- auto M = ln.Strip().SplitDelimit(" \t");
- if (M[0](0) != '#' && M.Length() > 2)
- {
- auto &Device = M[0];
- auto &Mount = M[1];
- auto &FileSys = M[2];
-
- if
- (
- (Device.Find("/dev/") == 0 || Mount.Find("/mnt/") == 0)
- &&
- Device.Lower().Find("/by-uuid/") < 0
- &&
- Mount.Length() > 1
- &&
- !FileSys.Equals("swap")
- )
- {
- v = new LVolume(0);
- if (v)
- {
- char *MountName = strrchr(Mount, '/');
- v->d->_Name = (MountName ? MountName + 1 : Mount.Get());
- v->d->_Path = Mount;
- v->d->_Type = VT_HARDDISK;
-
- char *Device = M[0];
- // char *FileSys = M[2];
- if (stristr(Device, "fd"))
- {
- v->d->_Type = VT_FLOPPY;
- }
- else if (stristr(Device, "cdrom"))
- {
- v->d->_Type = VT_CDROM;
- }
-
- _Sub.Insert(v);
- }
- }
- }
- }
- }
-
- LSystemPath p[] = {LSP_USER_DOCUMENTS,
+ LSystemPath p[] = { LSP_USER_DOCUMENTS,
LSP_USER_MUSIC,
LSP_USER_VIDEO,
LSP_USER_DOWNLOADS,
- LSP_USER_PICTURES};
+ LSP_USER_PICTURES };
for (int i=0; id->_Path = Path;
- v->d->_Name = *Parts.rbegin();
- v->d->_Type = VT_FOLDER;
- _Sub.Insert(v);
+ auto v = new LVolume(0);
+ if (Path && v)
+ {
+ auto Parts = Path.Split("/");
+ v->d->Path = Path;
+ v->d->Name = *Parts.rbegin();
+ v->d->Type = VT_FOLDER;
+ Insert(Owner, v);
+ }
}
}
}
- _It = _Sub.begin();
- return *_It;
+ return ChildVol;
}
LVolume *Next()
{
- return *(++_It);
+ if (SysPath == LSP_DESKTOP && !NextVol)
+ {
+ NextVol = new LVolume(LSP_MOUNT_POINT, "Mounts");
+
+ LHashTbl, bool> Map;
+
+ BVolumeRoster roster;
+ BVolume volume;
+ for (auto r = roster.GetBootVolume(&volume);
+ r == B_OK;
+ r = roster.GetNextVolume(&volume))
+ {
+ auto Path = BGetFullPath(volume);
+ if (!Path)
+ continue;
+
+ if (Stricmp(Path.Get(), "/") == 0 ||
+ Stricmp(Path.Get(), "/dev") == 0 ||
+ Stricmp(Path.Get(), "/boot/system/var/shared_memory") == 0)
+ continue;
+
+ if (volume.Capacity() == (4 << 10))
+ continue;
+
+ if (!volume.IsPersistent())
+ continue;
+
+ char Name[B_FILE_NAME_LENGTH];
+ if (volume.GetName(Name) != B_OK)
+ continue;
+
+ auto Done = Map.Find(Name);
+ if (Done)
+ continue;
+ Map.Add(Name, true);
+
+ auto v = new LVolume(0);
+ if (v)
+ {
+ v->d->Name = Name;
+ v->d->Path = Path;
+ v->d->Type = VT_HARDDISK;
+ v->d->Size = volume.Capacity();
+ v->d->Free = volume.FreeBytes();
+
+ // printf("%s, %s\n", Name, v->d->Path.Get());
+
+ Insert(NextVol, v);
+ }
+ }
+ }
+
+ return NextVol;
}
};
-LVolume::LVolume(const char *Path)
+LVolume::LVolume(const char *Path = NULL)
{
- d = new LVolumePriv(Path);
+ d = new LVolumePriv(this, Path);
}
LVolume::LVolume(LSystemPath SysPath, const char *Name)
{
- d = new LVolumePriv(SysPath, Name);
+ d = new LVolumePriv(this, SysPath, Name);
}
LVolume::~LVolume()
{
DeleteObj(d);
}
const char *LVolume::Name() const
{
- return d->_Name;
+ return d->Name;
}
const char *LVolume::Path() const
{
- return d->_Path;
+ return d->Path;
}
int LVolume::Type() const
{
- return d->_Type;
+ return d->Type;
}
int LVolume::Flags() const
{
- return d->_Flags;
+ return d->Flags;
}
uint64 LVolume::Size() const
{
- return d->_Size;
+ return d->Size;
}
uint64 LVolume::Free() const
{
- return d->_Free;
+ return d->Free;
}
LSurface *LVolume::Icon() const
{
return NULL;
}
bool LVolume::IsMounted() const
{
return true;
}
bool LVolume::SetMounted(bool Mount)
{
return Mount;
}
LVolume *LVolume::First()
{
return d->First();
}
LVolume *LVolume::Next()
{
return d->Next();
}
void LVolume::Insert(LAutoPtr v)
{
- d->_Sub.Insert(v.Release());
+ d->Insert(this, v.Release());
}
LDirectory *LVolume::GetContents()
{
LDirectory *Dir = 0;
- if (d->_Path)
+ if (d->Path)
{
Dir = new LDirectory;
- if (Dir && !Dir->First(d->_Path))
+ if (Dir && !Dir->First(d->Path))
DeleteObj(Dir);
}
return Dir;
}
///////////////////////////////////////////////////////////////////////////////
LFileSystem *LFileSystem::Instance = 0;
LFileSystem::LFileSystem()
{
Instance = this;
Root = 0;
}
LFileSystem::~LFileSystem()
{
DeleteObj(Root);
}
void LFileSystem::OnDeviceChange(char *Reserved)
{
}
LVolume *LFileSystem::GetRootVolume()
{
if (!Root)
Root = new LVolume(LSP_DESKTOP, "Desktop");
return Root;
}
bool LFileSystem::Copy(const char *From, const char *To, LError *Status, CopyFileCallback Callback, void *Token)
{
LArray Buf;
if (Status)
*Status = 0;
if (Buf.Length(2 << 20))
{
LFile In, Out;
if (!In.Open(From, O_READ))
{
if (Status)
*Status = In.GetError();
return false;
}
if (!Out.Open(To, O_WRITE))
{
if (Status)
*Status = Out.GetError();
return false;
}
int64 Size = In.GetSize(), Done = 0;
for (int64 i=0; i 0)
{
int w = Out.Write(&Buf[0], r);
if (w <= 0)
break;
r -= w;
Done += w;
if (Callback)
Callback(Token, Done, Size);
}
if (r > 0)
break;
}
return Done == Size;
}
return false;
}
bool LFileSystem::Delete(LArray &Files, LArray *Status, bool ToTrash)
{
bool Error = false;
if (ToTrash)
{
char p[MAX_PATH_LEN];
if (LGetSystemPath(LSP_TRASH, p, sizeof(p)))
{
for (int i=0; i f;
f.Add(FileName);
return Delete(f, 0, ToTrash);
}
return false;
}
bool LFileSystem::CreateFolder(const char *PathName, bool CreateParentTree, LError *ErrorCode)
{
int r = mkdir(PathName, S_IRWXU | S_IXGRP | S_IXOTH);
if (r)
{
if (ErrorCode)
*ErrorCode = errno;
if (CreateParentTree)
{
LFile::Path p(PathName);
LString dir = DIR_STR;
for (size_t i=1; i<=p.Length(); i++)
{
LFile::Path Par(dir + dir.Join(p.Slice(0, i)));
if (!Par.Exists())
{
const char *ParDir = Par;
r = mkdir(ParDir, S_IRWXU | S_IXGRP | S_IXOTH);
if (r)
{
if (ErrorCode)
*ErrorCode = errno;
break;
}
LgiTrace("%s:%i - Created '%s'\n", _FL, Par.GetFull().Get());
}
}
if (p.Exists())
return true;
}
LgiTrace("%s:%i - mkdir('%s') failed with %i, errno=%i\n", _FL, PathName, r, errno);
}
return r == 0;
}
bool LFileSystem::RemoveFolder(const char *PathName, bool Recurse)
{
if (Recurse)
{
- LDirectory *Dir = new LDirectory;
- if (Dir && Dir->First(PathName))
+ LDirectory Dir;
+ if (Dir.First(PathName))
{
do
{
- char Str[256];
- Dir->Path(Str, sizeof(Str));
+ char Str[MAX_PATH_LEN];
+ Dir.Path(Str, sizeof(Str));
- if (Dir->IsDir())
- {
+ if (Dir.IsDir())
RemoveFolder(Str, Recurse);
- }
else
- {
Delete(Str, false);
- }
}
- while (Dir->Next());
+ while (Dir.Next());
}
- DeleteObj(Dir);
}
return rmdir(PathName) == 0;
}
bool LFileSystem::Move(const char *OldName, const char *NewName, LError *Err)
{
if (rename(OldName, NewName))
{
printf("%s:%i - rename failed, error: %s(%i)\n",
_FL, LErrorCodeToString(errno), errno);
return false;
}
return true;
}
-
-/*
-bool Match(char *Name, char *Mask)
-{
- strupr(Name);
- strupr(Mask);
-
- while (*Name && *Mask)
- {
- if (*Mask == '*')
- {
- if (*Name == *(Mask+1))
- {
- Mask++;
- }
- else
- {
- Name++;
- }
- }
- else if (*Mask == '?' || *Mask == *Name)
- {
- Mask++;
- Name++;
- }
- else
- {
- return false;
- }
- }
-
- while (*Mask && ((*Mask == '*') || (*Mask == '.'))) Mask++;
-
- return (*Name == 0 && *Mask == 0);
-}
-*/
-
-short DaysInMonth[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
-int LeapYear(int year)
-{
- if (year & 3)
- {
- return 0;
- }
- if ((year % 100 == 0) && !(year % 400 == 0))
- {
- return 0;
- }
-
- return 1;
-}
-
-/////////////////////////////////////////////////////////////////////////////////
-bool LDirectory::ConvertToTime(char *Str, int SLen, uint64 Time) const
-{
- time_t k = Time;
- struct tm *t = localtime(&k);
- if (t)
- {
- strftime(Str, SLen, "%I:%M:%S", t);
- return true;
- }
- return false;
-}
-
-bool LDirectory::ConvertToDate(char *Str, int SLen, uint64 Time) const
-{
- time_t k = Time;
- struct tm *t = localtime(&k);
- if (t)
- {
- strftime(Str, SLen, "%d/%m/%y", t);
- return true;
- }
- return false;
-}
-
-/////////////////////////////////////////////////////////////////////////////////
-//////////////////////////// Directory //////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////
struct LDirectoryPriv
{
char BasePath[MAX_PATH_LEN];
char *BaseEnd = NULL;
DIR *Dir = NULL;
struct dirent *De = NULL;
struct stat Stat;
LString Pattern;
LDirectoryPriv()
{
BasePath[0] = 0;
}
bool Ignore()
{
return De
&&
(
strcmp(De->d_name, ".") == 0
||
strcmp(De->d_name, "..") == 0
||
(
Pattern
&&
!MatchStr(Pattern, De->d_name)
)
);
}
};
LDirectory::LDirectory()
{
d = new LDirectoryPriv;
}
LDirectory::~LDirectory()
{
Close();
DeleteObj(d);
}
LDirectory *LDirectory::Clone()
{
return new LDirectory;
}
int LDirectory::First(const char *Name, const char *Pattern)
{
Close();
if (!Name)
return 0;
strcpy_s(d->BasePath, sizeof(d->BasePath), Name);
+ d->BaseEnd = NULL;
+
if (!Pattern || stricmp(Pattern, LGI_ALL_FILES) == 0)
{
struct stat S;
if (lstat(Name, &S) == 0)
{
if (S_ISREG(S.st_mode))
{
char *Dir = strrchr(d->BasePath, DIR_CHAR);
if (Dir)
{
*Dir++ = 0;
d->Pattern = Dir;
}
}
}
}
else
{
d->Pattern = Pattern;
}
d->Dir = opendir(d->BasePath);
if (d->Dir)
{
d->De = readdir(d->Dir);
if (d->De)
{
char s[MaxPathLen];
LMakePath(s, sizeof(s), d->BasePath, GetName());
lstat(s, &d->Stat);
if (d->Ignore() && !Next())
return false;
}
}
return d->Dir != NULL && d->De != NULL;
}
int LDirectory::Next()
{
int Status = false;
while (d->Dir && d->De)
{
if ((d->De = readdir(d->Dir)))
{
char s[MaxPathLen];
LMakePath(s, sizeof(s), d->BasePath, GetName());
lstat(s, &d->Stat);
if (!d->Ignore())
{
Status = true;
break;
}
}
}
return Status;
}
int LDirectory::Close()
{
if (d->Dir)
{
closedir(d->Dir);
d->Dir = NULL;
}
d->De = NULL;
d->BaseEnd = NULL;
d->BasePath[0] = 0;
return true;
}
const char *LDirectory::FullPath()
{
auto nm = GetName();
if (!nm)
return NULL;
if (!d->BaseEnd)
{
d->BaseEnd = d->BasePath + strlen(d->BasePath);
if (d->BaseEnd > d->BasePath &&
d->BaseEnd[-1] != DIR_CHAR)
{
*d->BaseEnd++ = DIR_CHAR;
}
}
auto used = d->BaseEnd - d->BasePath;
auto remaining = sizeof(d->BasePath) - used;
strcpy_s(d->BaseEnd, remaining, nm);
return d->BasePath;
}
LString LDirectory::FileName() const
{
return GetName();
}
bool LDirectory::Path(char *s, int BufLen) const
{
if (!s)
{
return false;
}
return LMakePath(s, BufLen, d->BasePath, GetName());
}
int LDirectory::GetType() const
{
return IsDir() ? VT_FOLDER : VT_FILE;
}
int LDirectory::GetUser(bool Group) const
{
if (Group)
{
return d->Stat.st_gid;
}
else
{
return d->Stat.st_uid;
}
}
bool LDirectory::IsReadOnly() const
{
if (getuid() == d->Stat.st_uid)
{
// Check user perms
return !TestFlag(GetAttributes(), S_IWUSR);
}
else if (getgid() == d->Stat.st_gid)
{
// Check group perms
return !TestFlag(GetAttributes(), S_IWGRP);
}
// Check global perms
return !TestFlag(GetAttributes(), S_IWOTH);
}
bool LDirectory::IsHidden() const
{
return GetName() && GetName()[0] == '.';
}
bool LDirectory::IsDir() const
{
int a = GetAttributes();
return !S_ISLNK(a) && S_ISDIR(a);
}
bool LDirectory::IsSymLink() const
{
int a = GetAttributes();
return S_ISLNK(a);
}
long LDirectory::GetAttributes() const
{
return d->Stat.st_mode;
}
char *LDirectory::GetName() const
{
return (d->De) ? d->De->d_name : NULL;
}
uint64 LDirectory::GetCreationTime() const
{
return (uint64) d->Stat.st_ctime * LDateTime::Second64Bit;
}
uint64 LDirectory::GetLastAccessTime() const
{
return (uint64) d->Stat.st_atime * LDateTime::Second64Bit;
}
uint64 LDirectory::GetLastWriteTime() const
{
return (uint64) d->Stat.st_mtime * LDateTime::Second64Bit;
}
uint64 LDirectory::GetSize() const
{
return d->Stat.st_size;
}
int64 LDirectory::GetSizeOnDisk()
{
return d->Stat.st_size;
}
+bool LDirectory::ConvertToTime(char *Str, int SLen, uint64 Time) const
+{
+ time_t k = Time;
+ struct tm *t = localtime(&k);
+ if (t)
+ {
+ strftime(Str, SLen, "%I:%M:%S", t);
+ return true;
+ }
+ return false;
+}
+
+bool LDirectory::ConvertToDate(char *Str, int SLen, uint64 Time) const
+{
+ time_t k = Time;
+ struct tm *t = localtime(&k);
+ if (t)
+ {
+ strftime(Str, SLen, "%d/%m/%y", t);
+ return true;
+ }
+ return false;
+}
+
/////////////////////////////////////////////////////////////////////////////////
class LFilePrivate
{
public:
int hFile;
char *Name;
bool Swap;
int Status;
int Attributes;
int ErrorCode;
LFilePrivate()
{
hFile = INVALID_HANDLE;
Name = 0;
Swap = false;
Status = true;
Attributes = 0;
ErrorCode = 0;
}
~LFilePrivate()
{
DeleteArray(Name);
}
};
LFile::LFile(const char *Path, int Mode)
{
d = new LFilePrivate;
if (Path)
Open(Path, Mode);
}
LFile::~LFile()
{
if (d && ValidHandle(d->hFile))
{
Close();
}
DeleteObj(d);
}
OsFile LFile::Handle()
{
return d->hFile;
}
int LFile::GetError()
{
return d->ErrorCode;
}
bool LFile::IsOpen()
{
return ValidHandle(d->hFile);
}
#define DEBUG_OPEN_FILES 0
#if DEBUG_OPEN_FILES
LMutex Lck;
LArray OpenFiles;
#endif
int LFile::Open(const char *File, int Mode)
{
int Status = false;
if (File)
{
if (TestFlag(Mode, O_WRITE) ||
TestFlag(Mode, O_READWRITE))
{
Mode |= O_CREAT;
}
Close();
d->hFile = open(File, Mode
#ifdef O_LARGEFILE
| O_LARGEFILE
#endif
, S_IRUSR | S_IWUSR);
if (ValidHandle(d->hFile))
{
d->Attributes = Mode;
d->Name = new char[strlen(File)+1];
if (d->Name)
{
strcpy(d->Name, File);
}
Status = true;
d->Status = true;
#if DEBUG_OPEN_FILES
if (Lck.Lock(_FL))
{
if (!OpenFiles.HasItem(this))
OpenFiles.Add(this);
Lck.Unlock();
}
#endif
}
else
{
d->ErrorCode = errno;
#if DEBUG_OPEN_FILES
if (Lck.Lock(_FL))
{
for (unsigned i=0; iGetName());
Lck.Unlock();
}
#endif
printf("LFile::Open failed\n\topen(%s,%08.8x) = %i\n\terrno=%s\n",
File,
Mode,
d->hFile,
LErrorCodeToString(d->ErrorCode));
}
}
return Status;
}
int LFile::Close()
{
if (ValidHandle(d->hFile))
{
close(d->hFile);
d->hFile = INVALID_HANDLE;
DeleteArray(d->Name);
#if DEBUG_OPEN_FILES
if (Lck.Lock(_FL))
{
OpenFiles.Delete(this);
Lck.Unlock();
}
#endif
}
return true;
}
uint64_t LFile::GetModifiedTime()
{
struct stat s;
stat(d->Name, &s);
return s.st_mtime;
}
bool LFile::SetModifiedTime(uint64_t dt)
{
struct stat s;
stat(d->Name, &s);
struct timeval times[2] = {};
times[0].tv_sec = s.st_atime;
times[1].tv_sec = dt;
int r = utimes(d->Name, times);
return r == 0;
}
void LFile::ChangeThread()
{
}
#define CHUNK 0xFFF0
ssize_t LFile::Read(void *Buffer, ssize_t Size, int Flags)
{
int Red = 0;
if (Buffer && Size > 0)
{
Red = read(d->hFile, Buffer, Size);
}
d->Status = Red == Size;
return MAX(Red, 0);
}
ssize_t LFile::Write(const void *Buffer, ssize_t Size, int Flags)
{
int Written = 0;
if (Buffer && Size > 0)
{
Written = write(d->hFile, Buffer, Size);
}
d->Status = Written == Size;
return MAX(Written, 0);
}
#ifdef LINUX
#define LINUX64 1
#endif
int64 LFile::Seek(int64 To, int Whence)
{
#if LINUX64
return lseek64(d->hFile, To, Whence); // If this doesn't compile, switch off LINUX64
#else
return lseek(d->hFile, To, Whence);
#endif
}
int64 LFile::SetPos(int64 Pos)
{
#if LINUX64
int64 p = lseek64(d->hFile, Pos, SEEK_SET);
if (p < 0)
{
int e = errno;
printf("%s:%i - lseek64(%Lx) failed (error %i: %s).\n",
__FILE__, __LINE__,
Pos, e, GetErrorName(e));
}
#else
return lseek(d->hFile, Pos, SEEK_SET);
#endif
}
int64 LFile::GetPos()
{
#if LINUX64
int64 p = lseek64(d->hFile, 0, SEEK_CUR);
if (p < 0)
{
int e = errno;
printf("%s:%i - lseek64 failed (error %i: %s).\n",
__FILE__, __LINE__,
e, GetErrorName(e));
}
return p;
#else
return lseek(d->hFile, 0, SEEK_CUR);
#endif
}
int64 LFile::GetSize()
{
int64 Here = GetPos();
#if LINUX64
int64 Ret = lseek64(d->hFile, 0, SEEK_END);
#else
int64 Ret = lseek(d->hFile, 0, SEEK_END);
#endif
SetPos(Here);
return Ret;
}
int64 LFile::SetSize(int64 Size)
{
if (ValidHandle(d->hFile))
{
int64 Pos = GetPos();
#if LINUX64
ftruncate64(d->hFile, Size);
#else
ftruncate(d->hFile, Size);
#endif
if (d->hFile)
{
SetPos(Pos);
}
}
return GetSize();
}
bool LFile::Eof()
{
return GetPos() >= GetSize();
}
ssize_t LFile::SwapRead(uchar *Buf, ssize_t Size)
{
ssize_t i = 0;
ssize_t r = 0;
Buf = &Buf[Size-1];
while (Size--)
{
r = read(d->hFile, Buf--, 1);
i += r;
}
return i;
}
ssize_t LFile::SwapWrite(uchar *Buf, ssize_t Size)
{
ssize_t i = 0;
ssize_t w = 0;
Buf = &Buf[Size-1];
while (Size--)
{
w = write(d->hFile, Buf--, 1);
i += w;
}
return i;
}
ssize_t LFile::ReadStr(char *Buf, ssize_t Size)
{
ssize_t i = 0;
ssize_t r = 0;
if (Buf && Size > 0)
{
char c;
Size--;
do
{
r = read(d->hFile, &c, 1);
if (Eof())
{
break;
}
*Buf++ = c;
i++;
} while (i < Size - 1 && c != '\n');
*Buf = 0;
}
return i;
}
ssize_t LFile::WriteStr(char *Buf, ssize_t Size)
{
ssize_t i = 0;
ssize_t w;
while (i <= Size)
{
w = write(d->hFile, Buf, 1);
Buf++;
i++;
if (*Buf == '\n') break;
}
return i;
}
void LFile::SetStatus(bool s)
{
d->Status = s;
}
bool LFile::GetStatus()
{
return d->Status;
}
void LFile::SetSwap(bool s)
{
d->Swap = s;
}
bool LFile::GetSwap()
{
return d->Swap;
}
int LFile::GetOpenMode()
{
return d->Attributes;
}
const char *LFile::GetName()
{
return d->Name;
}
#define GFileOp(type) LFile &LFile::operator >> (type &i) { d->Status |= ((d->Swap) ? SwapRead((uchar*) &i, sizeof(i)) : Read(&i, sizeof(i))) != sizeof(i); return *this; }
GFileOps();
#undef GFileOp
#define GFileOp(type) LFile &LFile::operator << (type i) { d->Status |= ((d->Swap) ? SwapWrite((uchar*) &i, sizeof(i)) : Write(&i, sizeof(i))) != sizeof(i); return *this; }
GFileOps();
#undef GFileOp
//////////////////////////////////////////////////////////////////////////////
+LString LFile::Path::Sep(DIR_STR);
+
bool LFile::Path::FixCase()
{
LString Prev;
bool Changed = false;
// printf("FixCase '%s'\n", GetFull().Get());
for (size_t i=0; i %s\n", part.Get(), name);
Part = Name;
Next = (Prev ? Prev : "") + Sep + Part;
Changed = true;
}
}
}
Prev = Next;
}
return Changed;
}
diff --git a/src/haiku/ScreenDC.cpp b/src/haiku/ScreenDC.cpp
--- a/src/haiku/ScreenDC.cpp
+++ b/src/haiku/ScreenDC.cpp
@@ -1,479 +1,481 @@
/*hdr
** FILE: LScreenDC.cpp
** AUTHOR: Matthew Allen
** DATE: 29/11/2021
** DESCRIPTION: Haiku screen DC
**
** Copyright (C) 2021, Matthew Allen
** fret@memecode.com
*/
#include
#include
#include "lgi/common/Lgi.h"
#include
#include
#define LOGGING 0
#define VIEW_CHECK(...) if (!d->v) return __VA_ARGS__;
class LScreenPrivate
{
public:
int x = 0, y = 0, Bits = 32;
bool Own = false;
LColour Col;
LRect Client;
LView *View = NULL;
OsView v = NULL;
LScreenPrivate()
{
Client.ZOff(-1, -1);
}
~LScreenPrivate()
{
}
};
// Translates are cumulative... so we undo the last one before resetting it.
/////////////////////////////////////////////////////////////////////////////////////////////////////
LScreenDC::LScreenDC()
{
d = new LScreenPrivate;
d->x = GdcD->X();
d->y = GdcD->Y();
d->Bits = GdcD->GetBits();
}
LScreenDC::LScreenDC(LView *view, void *param)
{
d = new LScreenPrivate;
if (d->View = view)
d->v = view->Handle();
d->Bits = GdcD->GetBits();
/*
if (d->v)
d->Client = d->v->Frame();
else
LgiTrace("%s:%i - LScreenDC::LScreenDC - No view?\n", _FL);
*/
}
LScreenDC::~LScreenDC()
{
DeleteObj(d);
}
OsPainter LScreenDC::Handle()
{
return d->v;
}
::LString LScreenDC::Dump()
{
::LString s;
s.Printf("LScreenDC size=%i,%i\n", d->x, d->y);
return s;
}
bool LScreenDC::SupportsAlphaCompositing()
{
return true;
}
LPoint LScreenDC::GetDpi()
{
return LScreenDpi();
}
bool LScreenDC::GetClient(LRect *c)
{
if (!c)
return false;
*c = d->Client;
return true;
}
LString GetClip(BView *v)
{
BRegion r;
v->GetClippingRegion(&r);
LRect lr = r.Frame();
return lr.GetStr();
}
void LScreenDC::GetOrigin(int &x, int &y)
{
x = OriginX;
y = OriginY;
#if LOGGING
printf("%p.GetOrigin=%i+%i=%i, %i+%i=%i\n",
this,
OriginX, d->Client.x1, x,
OriginY, d->Client.y1, y);
#endif
}
void LScreenDC::SetOrigin(int x, int y)
{
VIEW_CHECK()
if (d->Client.Valid())
{
// The clipping region is relative to the offset.
// Remove it here and reinstate it after setting the origin.
d->v->ConstrainClippingRegion(NULL);
}
OriginX = x;
OriginY = y;
#if LOGGING
printf("%p.SetOrigin=%i,%i (%i,%i) = %i,%i\n",
this,
x, y,
d->Client.x1, d->Client.y1,
d->Client.x1 - OriginX,
d->Client.y1 - OriginY);
#endif
d->v->SetOrigin( d->Client.x1 - OriginX,
d->Client.y1 - OriginY);
if (d->Client.Valid())
{
// Reset the clipping region related to the origin.
auto clp = d->Client.ZeroTranslate();
clp.Offset(OriginX, OriginY);
d->v->ClipToRect(clp);
}
}
void LScreenDC::SetClient(LRect *c)
{
VIEW_CHECK()
if (c)
{
d->Client = *c;
+ OriginX += d->Client.x1;
+ OriginY += d->Client.y1;
#if LOGGING
printf("SetClient(%s)\n", d->Client.GetStr());
#endif
d->v->SetOrigin(OriginX, OriginY);
auto clp = d->Client.ZeroTranslate();
- clp.Offset(OriginX, OriginY);
+ // clp.Offset(OriginX, OriginY);
d->v->ClipToRect(clp);
/*
BRegion r;
d->v->GetClippingRegion(&r);
LRect lr = r.Frame();
printf("SetClient %s = %s (%i,%i)\n", c->GetStr(), lr.GetStr(), OriginX, OriginY);
*/
}
else
{
d->v->ConstrainClippingRegion(NULL);
d->v->SetOrigin(OriginX = 0, OriginX = 0);
d->Client.ZOff(-1, -1);
#if LOGGING
printf("SetClient()\n");
#endif
}
}
LRect *LScreenDC::GetClient()
{
return &d->Client;
}
uint LScreenDC::LineStyle()
{
return LSurface::LineStyle();
}
uint LScreenDC::LineStyle(uint32_t Bits, uint32_t Reset)
{
return LSurface::LineStyle(Bits);
}
int LScreenDC::GetFlags()
{
return 0;
}
LRect LScreenDC::ClipRgn()
{
return Clip;
}
LRect LScreenDC::ClipRgn(LRect *c)
{
LRect Prev = Clip;
if (c)
{
Clip = *c;
d->v->ClipToRect(Clip);
}
else
{
Clip.ZOff(-1, -1);
d->v->ConstrainClippingRegion(NULL);
}
return Prev;
}
COLOUR LScreenDC::Colour()
{
return d->Col.Get(GetBits());
}
COLOUR LScreenDC::Colour(COLOUR c, int Bits)
{
auto b = Bits ? Bits : GetBits();
d->Col.Set(c, b);
return Colour(d->Col).Get(b);
}
LColour LScreenDC::Colour(LColour c)
{
LColour Prev = d->Col;
d->Col = c;
if (d->v)
{
d->v->SetLowColor(c);
d->v->SetHighColor(c);
}
else LgiTrace("%s:%i - No view.\n", _FL);
return Prev;
}
int LScreenDC::Op(int ROP, NativeInt Param)
{
return GDC_SET; // not supported..
}
int LScreenDC::Op()
{
return GDC_SET; // not supported..
}
int LScreenDC::X()
{
return d->Client.Valid() ? d->Client.X() : d->x;
}
int LScreenDC::Y()
{
return d->Client.Valid() ? d->Client.Y() : d->y;
}
int LScreenDC::GetBits()
{
return d->Bits;
}
LPalette *LScreenDC::Palette()
{
return NULL; // not supported.
}
void LScreenDC::Palette(LPalette *pPal, bool bOwnIt)
{
}
void LScreenDC::Set(int x, int y)
{
VIEW_CHECK()
d->v->StrokeLine(BPoint(x, y), BPoint(x, y));
}
COLOUR LScreenDC::Get(int x, int y)
{
return 0;
}
void LScreenDC::HLine(int x1, int x2, int y)
{
VIEW_CHECK()
d->v->StrokeLine(BPoint(x1, y), BPoint(x2, y));
}
void LScreenDC::VLine(int x, int y1, int y2)
{
VIEW_CHECK()
d->v->StrokeLine(BPoint(x, y1), BPoint(x, y2));
}
void LScreenDC::Line(int x1, int y1, int x2, int y2)
{
VIEW_CHECK()
d->v->StrokeLine(BPoint(x1, y1), BPoint(x2, y2));
}
void LScreenDC::Circle(double cx, double cy, double radius)
{
VIEW_CHECK()
d->v->StrokeArc(BPoint(cx, cy), radius, radius, 0, 360);
}
void LScreenDC::FilledCircle(double cx, double cy, double radius)
{
VIEW_CHECK()
d->v->FillArc(BPoint(cx, cy), radius, radius, 0, 360);
}
void LScreenDC::Arc(double cx, double cy, double radius, double start, double end)
{
VIEW_CHECK()
d->v->StrokeArc(BPoint(cx, cy), radius, radius, start, end);
}
void LScreenDC::FilledArc(double cx, double cy, double radius, double start, double end)
{
VIEW_CHECK()
d->v->FillArc(BPoint(cx, cy), radius, radius, start, end);
}
void LScreenDC::Ellipse(double cx, double cy, double x, double y)
{
VIEW_CHECK()
d->v->StrokeArc(BPoint(cx, cy), x, y, 0, 360);
}
void LScreenDC::FilledEllipse(double cx, double cy, double x, double y)
{
VIEW_CHECK()
d->v->FillArc(BPoint(cx, cy), x, y, 0, 360);
}
void LScreenDC::Box(int x1, int y1, int x2, int y2)
{
VIEW_CHECK()
d->v->StrokeRect(LRect(x1, y1, x2, y2));
}
void LScreenDC::Box(LRect *a)
{
VIEW_CHECK()
if (a)
d->v->StrokeRect(*a);
else
Box(0, 0, X()-1, Y()-1);
}
void LScreenDC::Rectangle(int x1, int y1, int x2, int y2)
{
VIEW_CHECK()
if (x2 >= x1 && y2 >= y1)
{
// auto o = d->v->Origin();
// printf("Rect %i,%i,%i,%i %g,%g\n", x1, y1, x2, y2, o.x, o.y);
d->v->FillRect(LRect(x1, y1, x2, y2));
}
}
void LScreenDC::Rectangle(LRect *a)
{
VIEW_CHECK()
LRect r = a ? *a : Bounds();
BRect br(r.x1, r.y1, r.x2, r.y2);
d->v->FillRect(br);
}
void LScreenDC::Polygon(int Points, LPoint *Data)
{
VIEW_CHECK()
if (!Data || Points <= 0)
return;
}
void LScreenDC::Blt(int x, int y, LSurface *Src, LRect *a)
{
VIEW_CHECK()
if (!Src)
{
LgiTrace("%s:%i - No source.\n", _FL);
return;
}
if (Src->IsScreen())
{
LgiTrace("%s:%i - Can't do screen->screen blt.\n", _FL);
return;
}
BBitmap *bmp = Src->GetBitmap();
if (!bmp)
{
LAssert(!"no bmp.");
LgiTrace("%s:%i - No bitmap.\n", _FL);
return;
}
d->v->SetDrawingMode(B_OP_ALPHA);
d->v->SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_COMPOSITE);
if (a)
{
BRect bitmapRect = *a;
BRect viewRect(x, y, x + a->X() - 1, y + a->Y() - 1);
d->v->DrawBitmap(bmp, bitmapRect, viewRect);
#if 0
printf("ScreenDC::Blt %g,%g/%gx%g -> %g,%g/%gx%g %i,%i\n",
bitmapRect.left, bitmapRect.top, bitmapRect.Width(), bitmapRect.Height(),
viewRect.left, viewRect.top, viewRect.Width(), viewRect.Height(),
a->X(), a->Y());
#endif
}
else
{
BRect bitmapRect = bmp->Bounds();
BRect viewRect(x, y, x + Src->X() - 1, y + Src->Y() - 1);
d->v->DrawBitmap(bmp, bitmapRect, viewRect);
#if 0
printf("ScreenDC::Blt %g,%g/%gx%g -> %g,%g/%gx%g %i,%i\n",
bitmapRect.left, bitmapRect.top, bitmapRect.Width(), bitmapRect.Height(),
viewRect.left, viewRect.top, viewRect.Width(), viewRect.Height(),
x, y);
#endif
}
d->v->SetDrawingMode(B_OP_COPY);
}
void LScreenDC::StretchBlt(LRect *Dest, LSurface *Src, LRect *s)
{
VIEW_CHECK()
LAssert(0);
}
void LScreenDC::Bezier(int Threshold, LPoint *Pt)
{
VIEW_CHECK()
LAssert(0);
}
void LScreenDC::FloodFill(int x, int y, int Mode, COLOUR Border, LRect *Bounds)
{
VIEW_CHECK()
LAssert(0);
}
diff --git a/src/haiku/View.cpp b/src/haiku/View.cpp
--- a/src/haiku/View.cpp
+++ b/src/haiku/View.cpp
@@ -1,1129 +1,1130 @@
/*hdr
** FILE: LView.cpp
** AUTHOR: Matthew Allen
** DATE: 29/11/2021
** DESCRIPTION: Haiku LView Implementation
**
** Copyright (C) 2021, Matthew Allen
** fret@memecode.com
*/
#include
#include
#include "lgi/common/Lgi.h"
#include "lgi/common/DragAndDrop.h"
#include "lgi/common/Edit.h"
#include "lgi/common/Popup.h"
#include "lgi/common/Css.h"
#include "ViewPriv.h"
#include
#define DEBUG_MOUSE_EVENTS 0
#if 0
#define DEBUG_INVALIDATE(...) printf(__VA_ARGS__)
#else
#define DEBUG_INVALIDATE(...)
#endif
struct CursorInfo
{
public:
LRect Pos;
LPoint HotSpot;
}
CursorMetrics[] =
{
// up arrow
{ LRect(0, 0, 8, 15), LPoint(4, 0) },
// cross hair
{ LRect(20, 0, 38, 18), LPoint(29, 9) },
// hourglass
{ LRect(40, 0, 51, 15), LPoint(45, 8) },
// I beam
{ LRect(60, 0, 66, 17), LPoint(63, 8) },
// N-S arrow
{ LRect(80, 0, 91, 16), LPoint(85, 8) },
// E-W arrow
{ LRect(100, 0, 116, 11), LPoint(108, 5) },
// NW-SE arrow
{ LRect(120, 0, 132, 12), LPoint(126, 6) },
// NE-SW arrow
{ LRect(140, 0, 152, 12), LPoint(146, 6) },
// 4 way arrow
{ LRect(160, 0, 178, 18), LPoint(169, 9) },
// Blank
{ LRect(0, 0, 0, 0), LPoint(0, 0) },
// Vertical split
{ LRect(180, 0, 197, 16), LPoint(188, 8) },
// Horizontal split
{ LRect(200, 0, 216, 17), LPoint(208, 8) },
// Hand
{ LRect(220, 0, 233, 13), LPoint(225, 0) },
// No drop
{ LRect(240, 0, 258, 18), LPoint(249, 9) },
// Copy drop
{ LRect(260, 0, 279, 19), LPoint(260, 0) },
// Move drop
{ LRect(280, 0, 299, 19), LPoint(280, 0) },
};
// CursorData is a bitmap in an array of uint32's. This is generated from a graphics file:
// ./Code/cursors.png
//
// The pixel values are turned into C code by a program called i.Mage:
// http://www.memecode.com/image.php
//
// Load the graphic into i.Mage and then go Edit->CopyAsCode
// Then paste the text into the CursorData variable at the bottom of this file.
//
// This saves a lot of time finding and loading an external resouce, and even having to
// bundle extra files with your application. Which have a tendancy to get lost along the
// way etc.
extern uint32_t CursorData[];
LInlineBmp Cursors =
{
300, 20, 8, CursorData
};
////////////////////////////////////////////////////////////////////////////
void _lgi_yield()
{
LAppInst->Yield();
}
void *IsAttached(BView *v)
{
auto pview = v->Parent();
auto pwnd = v->Window();
return pwnd ? (void*)pwnd : (void*)pview;
}
bool LgiIsKeyDown(int Key)
{
LAssert(0);
return false;
}
LKey::LKey(int Vkey, uint32_t flags)
{
vkey = Vkey;
Flags = flags;
IsChar = false;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
template
struct LBView : public Parent
{
LViewPrivate *d = NULL;
static uint32 MouseButtons;
LBView(LViewPrivate *priv) :
d(priv),
Parent
(
"",
B_FULL_UPDATE_ON_RESIZE |
B_WILL_DRAW |
B_NAVIGABLE |
B_FRAME_EVENTS
)
{
Parent::SetName(d->View->GetClass());
}
~LBView()
{
if (d)
d->Hnd = NULL;
}
void AttachedToWindow()
{
if (d)
d->View->OnCreate();
}
LKey ConvertKey(const char *bytes, int32 numBytes)
{
LKey k;
uint8_t *utf = (uint8_t*)bytes;
ssize_t len = numBytes;
auto w = LgiUtf8To32(utf, len);
key_info KeyInfo;
if (get_key_info(&KeyInfo) == B_OK)
{
k.Ctrl(TestFlag(KeyInfo.modifiers, B_CONTROL_KEY));
k.Alt(TestFlag(KeyInfo.modifiers, B_MENU_KEY));
k.Shift(TestFlag(KeyInfo.modifiers, B_SHIFT_KEY));
k.System(TestFlag(KeyInfo.modifiers, B_COMMAND_KEY));
}
#if 0
LString::Array a;
for (int i=0; iCurrentMessage();
if (bmsg)
{
int32 key = 0;
if (bmsg->FindInt32("key", &key) == B_OK)
{
// Translate the function keys into the LGI address space...
switch (key)
{
case B_F1_KEY: w = LK_F1; break;
case B_F2_KEY: w = LK_F2; break;
case B_F3_KEY: w = LK_F3; break;
case B_F4_KEY: w = LK_F4; break;
case B_F5_KEY: w = LK_F5; break;
case B_F6_KEY: w = LK_F6; break;
case B_F7_KEY: w = LK_F7; break;
case B_F8_KEY: w = LK_F8; break;
case B_F9_KEY: w = LK_F9; break;
case B_F10_KEY: w = LK_F10; break;
case B_F11_KEY: w = LK_F11; break;
case B_F12_KEY: w = LK_F12; break;
default:
printf("%s:%i - Upsupported key %i.\n", _FL, key);
break;
}
}
else printf("%s:%i - No 'key' in BMessage.\n", _FL);
}
else printf("%s:%i - No BMessage.\n", _FL);
}
k.c16 = k.vkey = w;
}
k.IsChar =
!(
k.System()
||
k.Alt()
)
&&
(
(k.c16 >= ' ' && k.c16 < LK_DELETE)
||
k.c16 == LK_BACKSPACE
||
k.c16 == LK_TAB
||
k.c16 == LK_RETURN
);
return k;
}
void KeyDown(const char *bytes, int32 numBytes)
{
if (!d) return;
auto k = ConvertKey(bytes, numBytes);
k.Down(true);
auto wnd = d->View->GetWindow();
if (wnd)
wnd->HandleViewKey(d->View, k);
else
d->View->OnKey(k);
}
void KeyUp(const char *bytes, int32 numBytes)
{
if (!d) return;
auto k = ConvertKey(bytes, numBytes);
auto wnd = d->View->GetWindow();
if (wnd)
wnd->HandleViewKey(d->View, k);
else
d->View->OnKey(k);
}
void FrameMoved(BPoint newPosition)
{
if (!d) return;
d->View->Pos = Parent::Frame();
d->View->OnPosChange();
}
void FrameResized(float newWidth, float newHeight)
{
if (!d) return;
d->View->Pos = Parent::Frame();
d->View->OnPosChange();
}
void MessageReceived(BMessage *message)
{
if (!d) return;
void *v = NULL;
if (message->FindPointer(LMessage::PropView, &v) == B_OK)
{
// Proxy'd event for child view...
((LView*)v)->OnEvent((LMessage*)message);
return;
}
else d->View->OnEvent((LMessage*)message);
if (message->what == B_MOUSE_WHEEL_CHANGED)
{
float x = 0.0f, y = 0.0f;
message->FindFloat("be:wheel_delta_x", &x);
message->FindFloat("be:wheel_delta_y", &y);
d->View->OnMouseWheel(y * 3.0f);
}
else if (message->what == M_SET_SCROLL)
{
return;
}
Parent::MessageReceived(message);
}
void Draw(BRect updateRect)
{
if (!d) return;
LScreenDC dc(d->View);
LPoint off(0,0);
d->View->_Paint(&dc, &off, NULL);
}
LMouse ConvertMouse(BPoint where, bool down = false)
{
LMouse m;
BPoint loc;
uint32 buttons = 0;
m.Target = d->View;
m.x = where.x;
m.y = where.y;
if (down)
{
m.Down(true);
Parent::GetMouse(&loc, &buttons, false);
MouseButtons = buttons; // save for up click
}
else
{
buttons = MouseButtons;
}
if (buttons & B_PRIMARY_MOUSE_BUTTON) m.Left(true);
if (buttons & B_TERTIARY_MOUSE_BUTTON) m.Middle(true);
if (buttons & B_SECONDARY_MOUSE_BUTTON) m.Right(true);
uint32 mod = modifiers();
if (mod & B_SHIFT_KEY) m.Shift(true);
if (mod & B_OPTION_KEY) m.Alt(true);
if (mod & B_CONTROL_KEY) m.Ctrl(true);
if (mod & B_COMMAND_KEY) m.System(true);
return m;
}
void MouseDown(BPoint where)
{
if (!d) return;
static uint64_t lastClick = 0;
bigtime_t interval = 0;
status_t r = get_click_speed(&interval);
auto now = LCurrentTime();
bool doubleClick = now-lastClick < (interval/1000);
lastClick = now;
LMouse m = ConvertMouse(where, true);
m.Double(doubleClick);
d->View->_Mouse(m, false);
}
void MouseUp(BPoint where)
{
if (!d) return;
LMouse m = ConvertMouse(where);
m.Down(false);
d->View->_Mouse(m, false);
}
void MouseMoved(BPoint where, uint32 code, const BMessage *dragMessage)
{
if (!d) return;
LMouse m = ConvertMouse(where);
m.Down( m.Left() ||
m.Middle() ||
m.Right());
m.IsMove(true);
d->View->_Mouse(m, true);
}
void MakeFocus(bool focus=true)
{
if (!d) return;
Parent::MakeFocus(focus);
d->View->OnFocus(focus);
}
};
template
uint32 LBView::MouseButtons = 0;
////////////////////////////////////////////////////////////////////////////////////////////////////
LViewPrivate::LViewPrivate(LView *view) :
View(view),
Hnd(new LBView(this))
{
}
LViewPrivate::~LViewPrivate()
{
View->d = NULL;
MsgQue.DeleteObjects();
if (Font && FontOwnType == GV_FontOwned)
DeleteObj(Font);
if (Hnd)
{
auto *bv = dynamic_cast*>(Hnd);
// printf("%p::~LViewPrivate View=%p bv=%p th=%u\n", this, View, bv, GetCurrentThreadId());
if (bv)
bv->d = NULL;
auto Wnd = Hnd->Window();
+ bool locked = false;
if (Wnd)
- Wnd->LockLooper();
+ locked = Wnd->LockLooper();
if (Hnd->Parent())
Hnd->RemoveSelf();
DeleteObj(Hnd);
- if (Wnd)
+ if (Wnd && locked)
Wnd->UnlockLooper();
}
}
void LView::_Focus(bool f)
{
ThreadCheck();
if (f)
SetFlag(WndFlags, GWF_FOCUS);
else
ClearFlag(WndFlags, GWF_FOCUS);
LLocker lck(d->Hnd, _FL);
if (lck.Lock())
{
d->Hnd->MakeFocus(f);
lck.Unlock();
}
// OnFocus will be called by the LBview handler...
Invalidate();
}
void LView::_Delete()
{
SetPulse();
// Remove static references to myself
if (_Over == this)
_Over = 0;
if (_Capturing == this)
_Capturing = 0;
auto *Wnd = GetWindow();
if (Wnd && Wnd->GetFocus() == static_cast(this))
Wnd->SetFocus(this, LWindow::ViewDelete);
if (LAppInst && LAppInst->AppWnd == this)
{
LAppInst->AppWnd = 0;
}
// Hierarchy
LViewI *c;
while ((c = Children[0]))
{
if (c->GetParent() != (LViewI*)this)
{
printf("%s:%i - ~LView error, %s not attached correctly: %p(%s) Parent: %p(%s)\n",
_FL, c->GetClass(), c, c->Name(), c->GetParent(), c->GetParent() ? c->GetParent()->Name() : "");
Children.Delete(c);
}
DeleteObj(c);
}
Detach();
// Misc
Pos.ZOff(-1, -1);
}
LView *&LView::PopupChild()
{
return d->Popup;
}
BCursorID LgiToHaiku(LCursor c)
{
switch (c)
{
#define _(l,h) case l: return h;
_(LCUR_Blank, B_CURSOR_ID_NO_CURSOR)
_(LCUR_Normal, B_CURSOR_ID_SYSTEM_DEFAULT)
_(LCUR_UpArrow, B_CURSOR_ID_RESIZE_NORTH)
_(LCUR_DownArrow, B_CURSOR_ID_RESIZE_SOUTH)
_(LCUR_LeftArrow, B_CURSOR_ID_RESIZE_WEST)
_(LCUR_RightArrow, B_CURSOR_ID_RESIZE_EAST)
_(LCUR_Cross, B_CURSOR_ID_CROSS_HAIR)
_(LCUR_Wait, B_CURSOR_ID_PROGRESS)
_(LCUR_Ibeam, B_CURSOR_ID_I_BEAM)
_(LCUR_SizeVer, B_CURSOR_ID_RESIZE_NORTH_SOUTH)
_(LCUR_SizeHor, B_CURSOR_ID_RESIZE_EAST_WEST)
_(LCUR_SizeBDiag, B_CURSOR_ID_RESIZE_NORTH_WEST_SOUTH_EAST)
_(LCUR_SizeFDiag, B_CURSOR_ID_RESIZE_NORTH_EAST_SOUTH_WEST)
_(LCUR_PointingHand, B_CURSOR_ID_GRAB)
_(LCUR_Forbidden, B_CURSOR_ID_NOT_ALLOWED)
_(LCUR_DropCopy, B_CURSOR_ID_COPY)
_(LCUR_DropMove, B_CURSOR_ID_MOVE)
// _(LCUR_SizeAll,
// _(LCUR_SplitV,
// _(LCUR_SplitH,
#undef _
}
return B_CURSOR_ID_SYSTEM_DEFAULT;
}
bool LView::_Mouse(LMouse &m, bool Move)
{
ThreadCheck();
#if DEBUG_MOUSE_EVENTS
if (!Move)
LgiTrace("%s:%i - %s\n", _FL, m.ToString().Get());
#endif
if
(
GetWindow()
&&
!GetWindow()->HandleViewMouse(this, m)
)
{
#if DEBUG_MOUSE_EVENTS
LgiTrace("%s:%i - HandleViewMouse consumed event, cls=%s\n", _FL, GetClass());
#endif
return false;
}
#if 0 //DEBUG_MOUSE_EVENTS
if (!Move)
LgiTrace("%s:%i - _Capturing=%p/%s\n", _FL, _Capturing, _Capturing ? _Capturing->GetClass() : NULL);
#endif
if (Move)
{
auto *o = m.Target;
if (_Over != o)
{
#if DEBUG_MOUSE_EVENTS
// if (!o) WindowFromPoint(m.x, m.y, true);
LgiTrace("%s:%i - _Over changing from %p/%s to %p/%s\n", _FL,
_Over, _Over ? _Over->GetClass() : NULL,
o, o ? o->GetClass() : NULL);
#endif
if (_Over)
_Over->OnMouseExit(lgi_adjust_click(m, _Over));
_Over = o;
if (_Over)
_Over->OnMouseEnter(lgi_adjust_click(m, _Over));
}
int cursor = GetCursor(m.x, m.y);
if (cursor >= 0)
{
BCursorID haikuId = LgiToHaiku((LCursor)cursor);
static BCursorID curId = B_CURSOR_ID_SYSTEM_DEFAULT;
if (curId != haikuId)
{
curId = haikuId;
LLocker lck(Handle(), _FL);
if (lck.Lock())
{
Handle()->SetViewCursor(new BCursor(curId));
lck.Unlock();
}
}
}
}
LView *Target = NULL;
if (_Capturing)
Target = dynamic_cast(_Capturing);
else
Target = dynamic_cast(_Over ? _Over : this);
if (!Target)
return false;
LRect Client = Target->LView::GetClient(false);
m = lgi_adjust_click(m, Target, !Move);
if (!Client.Valid() || Client.Overlap(m.x, m.y) || _Capturing)
{
if (Move)
Target->OnMouseMove(m);
else
Target->OnMouseClick(m);
}
else if (!Move)
{
#if DEBUG_MOUSE_EVENTS
LgiTrace("%s:%i - Click outside %s %s %i,%i\n", _FL, Target->GetClass(), Client.GetStr(), m.x, m.y);
#endif
}
return true;
}
LRect &LView::GetClient(bool ClientSpace)
{
int Edge = (Sunken() || Raised()) ? _BorderSize : 0;
static LRect c;
if (ClientSpace)
{
c.ZOff(Pos.X() - 1 - (Edge<<1), Pos.Y() - 1 - (Edge<<1));
}
else
{
c.ZOff(Pos.X()-1, Pos.Y()-1);
c.Inset(Edge, Edge);
}
return c;
}
LViewI *LView::FindControl(OsView hCtrl)
{
if (d->Hnd == hCtrl)
{
return this;
}
for (auto i : Children)
{
LViewI *Ctrl = i->FindControl(hCtrl);
if (Ctrl)
{
return Ctrl;
}
}
return 0;
}
void LView::Quit(bool DontDelete)
{
ThreadCheck();
if (DontDelete)
{
Visible(false);
}
else
{
delete this;
}
}
bool LView::SetPos(LRect &p, bool Repaint)
{
if (Pos != p)
{
Pos = p;
LLocker lck(d->Hnd, _FL);
if (lck.Lock())
{
d->Hnd->ResizeTo(Pos.X(), Pos.Y());
d->Hnd->MoveTo(Pos.x1, Pos.y1);
lck.Unlock();
}
OnPosChange();
}
return true;
}
bool LView::Invalidate(LRect *rc, bool Repaint, bool Frame)
{
auto *ParWnd = GetWindow();
if (!ParWnd)
return false; // Nothing we can do till we attach
if (!InThread())
{
DEBUG_INVALIDATE("%s::Invalidate out of thread\n", GetClass());
return PostEvent(M_INVALIDATE, NULL, (LMessage::Param)this);
}
LRect r;
if (rc)
{
r = *rc;
}
else
{
if (Frame)
r = Pos.ZeroTranslate();
else
r = GetClient().ZeroTranslate();
}
DEBUG_INVALIDATE("%s::Invalidate r=%s frame=%i\n", GetClass(), r.GetStr(), Frame);
if (!Frame)
r.Offset(_BorderSize, _BorderSize);
LPoint Offset;
WindowVirtualOffset(&Offset);
r.Offset(Offset.x, Offset.y);
DEBUG_INVALIDATE(" voffset=%i,%i = %s\n", Offset.x, Offset.y, r.GetStr());
if (!r.Valid())
{
DEBUG_INVALIDATE(" error: invalid\n");
return false;
}
static bool Repainting = false;
if (!Repainting)
{
Repainting = true;
if (d->Hnd)
{
LLocker lck(d->Hnd, _FL);
if (lck.Lock())
d->Hnd->Invalidate();
}
Repainting = false;
}
else
{
DEBUG_INVALIDATE(" error: repainting\n");
}
return true;
}
void LView::SetPulse(int Length)
{
DeleteObj(d->PulseThread);
if (Length > 0)
d->PulseThread = new LPulseThread(this, Length);
}
LMessage::Param LView::OnEvent(LMessage *Msg)
{
ThreadCheck();
int Id;
switch (Id = Msg->Msg())
{
case M_HANDLE_IN_THREAD:
{
LMessage::InThreadCb *Cb = NULL;
if (Msg->FindPointer(LMessage::PropCallback, (void**)&Cb) == B_OK)
{
// printf("M_HANDLE_IN_THREAD before call..\n");
(*Cb)();
// printf("M_HANDLE_IN_THREAD after call..\n");
delete Cb;
// printf("M_HANDLE_IN_THREAD after delete..\n");
}
else printf("%s:%i - No Callback.\n", _FL);
break;
}
case M_INVALIDATE:
{
if ((LView*)this == (LView*)Msg->B())
{
LAutoPtr r((LRect*)Msg->A());
Invalidate(r);
}
break;
}
case M_PULSE:
{
OnPulse();
break;
}
case M_CHANGE:
{
LViewI *Ctrl = NULL;
if (GetViewById(Msg->A(), Ctrl))
{
LNotification n((LNotifyType)Msg->B());
return OnNotify(Ctrl, n);
}
break;
}
case M_COMMAND:
{
// printf("M_COMMAND %i\n", (int)Msg->A());
return OnCommand(Msg->A(), 0, 0);
}
case M_THREAD_COMPLETED:
{
auto Th = (LThread*)Msg->A();
if (!Th)
break;
Th->OnComplete();
if (Th->GetDeleteOnExit())
delete Th;
return true;
}
}
return 0;
}
OsView LView::Handle() const
{
if (!d)
{
printf("%s:%i - No priv?\n", _FL);
return NULL;
}
return d->Hnd;
}
bool LView::PointToScreen(LPoint &p)
{
if (!Handle())
{
LgiTrace("%s:%i - No handle.\n", _FL);
return false;
}
LPoint Offset;
WindowVirtualOffset(&Offset);
// printf("p=%i,%i offset=%i,%i\n", p.x, p.y, Offset.x, Offset.y);
p += Offset;
// printf("p=%i,%i\n", p.x, p.y);
LLocker lck(Handle(), _FL);
if (!lck.Lock())
{
LgiTrace("%s:%i - Can't lock.\n", _FL);
return false;
}
BPoint pt = Handle()->ConvertToScreen(BPoint(p.x, p.y));
// printf("pt=%g,%g\n", pt.x, pt.y);
p.x = pt.x;
p.y = pt.y;
// printf("p=%i,%i\n\n", p.x, p.y);
return true;
}
bool LView::PointToView(LPoint &p)
{
if (!Handle())
{
LgiTrace("%s:%i - No handle.\n", _FL);
return false;
}
LPoint Offset;
WindowVirtualOffset(&Offset);
p -= Offset;
LLocker lck(Handle(), _FL);
if (!lck.Lock())
{
LgiTrace("%s:%i - Can't lock.\n", _FL);
return false;
}
BPoint pt = Handle()->ConvertFromScreen(BPoint(Offset.x, Offset.y));
Offset.x = pt.x;
Offset.y = pt.y;
return true;
}
bool LView::GetMouse(LMouse &m, bool ScreenCoords)
{
LLocker Locker(d->Hnd, _FL);
if (!Locker.Lock())
return false;
// get mouse state
BPoint Cursor;
uint32 Buttons;
d->Hnd->GetMouse(&Cursor, &Buttons);
if (ScreenCoords)
d->Hnd->ConvertToScreen(&Cursor);
// position
m.x = Cursor.x;
m.y = Cursor.y;
// buttons
m.Left(TestFlag(Buttons, B_PRIMARY_MOUSE_BUTTON));
m.Middle(TestFlag(Buttons, B_TERTIARY_MOUSE_BUTTON));
m.Right(TestFlag(Buttons, B_SECONDARY_MOUSE_BUTTON));
// key states
key_info KeyInfo;
if (get_key_info(&KeyInfo) == B_OK)
{
m.Ctrl(TestFlag(KeyInfo.modifiers, B_CONTROL_KEY));
m.Alt(TestFlag(KeyInfo.modifiers, B_MENU_KEY));
m.Shift(TestFlag(KeyInfo.modifiers, B_SHIFT_KEY));
}
return true;
}
bool LView::IsAttached()
{
bool attached = false;
LLocker lck(d->Hnd, _FL);
if (lck.Lock())
{
auto pview = d->Hnd->Parent();
auto pwnd = d->Hnd->Window();
attached = pview != NULL || pwnd != NULL;
}
return attached;
}
const char *LView::GetClass()
{
return "LView";
}
bool LView::Attach(LViewI *parent)
{
bool Status = false;
bool Debug = false; // !Stricmp(GetClass(), "LScrollBar");
LView *Parent = d->GetParent();
LAssert(Parent == NULL || Parent == parent);
SetParent(parent);
Parent = d->GetParent();
auto WndNull = _Window == NULL;
_Window = Parent ? Parent->_Window : this;
if (!parent)
{
LgiTrace("%s:%i - No parent window.\n", _FL);
}
else
{
auto w = GetWindow();
if (w && TestFlag(WndFlags, GWF_FOCUS))
w->SetFocus(this, LWindow::GainFocus);
auto bview = parent->Handle();
if (bview)
{
LLocker lck(bview, _FL);
if (lck.Lock())
{
if (Debug)
LgiTrace("%s:%i - Attaching %s to view %s\n",
_FL, GetClass(), parent->GetClass());
d->Hnd->SetName(GetClass());
if (::IsAttached(d->Hnd))
d->Hnd->RemoveSelf();
bview->AddChild(d->Hnd);
d->Hnd->ResizeTo(Pos.X(), Pos.Y());
d->Hnd->MoveTo(Pos.x1, Pos.y1);
bool ShowView = TestFlag(GViewFlags, GWF_VISIBLE) && d->Hnd->IsHidden();
if (Debug)
LgiTrace("%s:%i - IsHidden=%i ShowView=%i\n", _FL, d->Hnd->IsHidden(), ShowView);
if (ShowView)
d->Hnd->Show();
Status = true;
if (d->MsgQue.Length())
{
printf("%s:%i - %s.Attach msgQue=%i\n", _FL, GetClass(), (int)d->MsgQue.Length());
for (auto bmsg: d->MsgQue)
d->Hnd->Window()->PostMessage(bmsg);
d->MsgQue.DeleteObjects();
}
if (Debug)
LgiTrace("%s:%i - Attached %s/%p to view %s/%p, success\n",
_FL,
GetClass(), d->Hnd,
parent->GetClass(), bview);
}
else
{
if (Debug)
LgiTrace("%s:%i - Error attaching %s to view %s, can't lock.\n",
_FL, GetClass(), parent->GetClass());
}
}
if (!Parent->HasView(this))
{
OnAttach();
if (!d->Parent->HasView(this))
d->Parent->AddView(this);
d->Parent->OnChildrenChanged(this, true);
}
}
return Status;
}
bool LView::Detach()
{
ThreadCheck();
// Detach view
if (_Window)
{
auto *Wnd = dynamic_cast(_Window);
if (Wnd)
Wnd->SetFocus(this, LWindow::ViewDelete);
_Window = NULL;
}
LViewI *Par = GetParent();
if (Par)
{
// Events
Par->DelView(this);
Par->OnChildrenChanged(this, false);
Par->Invalidate(&Pos);
}
d->Parent = 0;
d->ParentI = 0;
#if 0 // Windows is not doing a deep detach... so we shouldn't either?
{
int Count = Children.Length();
if (Count)
{
int Detached = 0;
LViewI *c, *prev = NULL;
while ((c = Children[0]))
{
LAssert(!prev || c != prev);
if (c->GetParent())
c->Detach();
else
Children.Delete(c);
Detached++;
prev = c;
}
LAssert(Count == Detached);
}
}
#endif
return true;
}
LCursor LView::GetCursor(int x, int y)
{
return LCUR_Normal;
}
///////////////////////////////////////////////////////////////////
bool LgiIsMounted(char *Name)
{
return false;
}
bool LgiMountVolume(char *Name)
{
return false;
}
////////////////////////////////
uint32_t CursorData[] = {
0x02020202, 0x02020201, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020102, 0x02020202, 0x02020202, 0x01010101, 0x01010101, 0x01010101, 0x02020202, 0x02020202, 0x02010101, 0x02010101, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02010102, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020102, 0x01020202, 0x02020202, 0x02020202, 0x02020202, 0x01010101, 0x01010101, 0x02020201, 0x02020202, 0x01010101, 0x01010101, 0x02020201, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020102, 0x02020202, 0x02020202, 0x02020202, 0x01020202, 0x02010101, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020201, 0x02020202, 0x02020202, 0x02020202, 0x02010102, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x01020202, 0x01010101, 0x02020202, 0x02020202, 0x02020101, 0x02020202,
0x02020202, 0x02020202, 0x02020202, 0x02020101, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x01020202, 0x02020100, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02010001, 0x02020202, 0x02020202, 0x00000001, 0x00000000, 0x01000000, 0x02020202, 0x02020202, 0x01000001, 0x02010000, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x01000001, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020101, 0x01020202, 0x02020201, 0x02020202, 0x02020202, 0x00000102, 0x00000000, 0x02020201, 0x02020202, 0x00000001, 0x01000000, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02010001, 0x02020202, 0x02020202, 0x02020202, 0x01020202, 0x02010000, 0x02020202, 0x02020202, 0x02020202, 0x01020202, 0x02020100, 0x02020202, 0x02020202, 0x02020202, 0x01000001, 0x02020202, 0x02020202,
0x02020202, 0x02020202, 0x00010102, 0x00000000, 0x02020101, 0x02020202, 0x02010001, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02010001, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x01020202, 0x02020100, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02010001, 0x02020202, 0x02020202, 0x00010001, 0x00010001, 0x01000001, 0x02020202, 0x02020202, 0x00010102, 0x02020101, 0x02020202, 0x02020202, 0x02020202, 0x01020202, 0x00000000, 0x02020201, 0x02020202, 0x02020202, 0x01020202, 0x02020100, 0x01020202, 0x02020100, 0x02020202, 0x02020202, 0x00010202, 0x00000000, 0x02020201, 0x02020202, 0x00000001, 0x02010000, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x01020202, 0x01000000, 0x02020202, 0x02020202, 0x02020202, 0x01020202, 0x02010000, 0x02020202, 0x02020202, 0x02020202,
0x00010202, 0x02010000, 0x02020202, 0x02020202, 0x02020202, 0x01000001, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x00000001, 0x00000000, 0x02010000, 0x02020202, 0x01000001, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x01000001, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x00010202, 0x02010000, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02010001, 0x02020202, 0x02020202, 0x01000102, 0x00000100, 0x02010000, 0x02020202, 0x02020202, 0x00010202, 0x02020201, 0x02020202, 0x02020202, 0x02020202, 0x00010202, 0x00000000, 0x02020100, 0x02020202, 0x02020202, 0x00010202, 0x02020100, 0x01020202, 0x02010000, 0x02020202, 0x02020202, 0x01020202, 0x00000000, 0x02020201, 0x02020202, 0x00000001, 0x02020100, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x00010202, 0x00000000,
0x02020201, 0x02020202, 0x02020202, 0x01020202, 0x02010000, 0x02020202, 0x02020202, 0x02020202, 0x00000102, 0x01000000, 0x02020202, 0x02020202, 0x02020202, 0x01000001, 0x02020202, 0x02020202, 0x02020202, 0x01020202, 0x00000000, 0x00010101, 0x01000000, 0x02020202, 0x00000001, 0x02020201, 0x02020202, 0x02020202, 0x02020202, 0x00000001, 0x02020201, 0x02020202, 0x02020202, 0x02020202, 0x00010202, 0x02010000, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02010001, 0x02020202, 0x02020202, 0x00010202, 0x00000001, 0x02020100, 0x02020202, 0x02020202, 0x00010202, 0x02020201, 0x02020202, 0x02020202, 0x02020202, 0x00000102, 0x00000000, 0x02010000, 0x02020202, 0x02020202, 0x00000102, 0x01010100, 0x01010101, 0x01000000, 0x02020202, 0x02020201, 0x00010202, 0x00000000, 0x02020201, 0x02020202,
0x00000001, 0x02010000, 0x02020202, 0x02020201, 0x02020202, 0x02020202, 0x01010102, 0x01010001, 0x02020101, 0x02020202, 0x02020202, 0x01020201, 0x02010000, 0x02020102, 0x02020202, 0x02020202, 0x01010101, 0x01010100, 0x02020201, 0x02020202, 0x02020202, 0x01000001, 0x02020101, 0x02020202, 0x02020202, 0x00010202, 0x01010000, 0x01020202, 0x00000001, 0x02020201, 0x00000001, 0x02020100, 0x02020202, 0x02020202, 0x02020202, 0x00000001, 0x02020100, 0x02020202, 0x02020202, 0x02020202, 0x00000102, 0x01000000, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02010001, 0x02020202, 0x02020202, 0x01020202, 0x00000000, 0x02020201, 0x02020202, 0x02020202, 0x00010202, 0x02020201, 0x02020202, 0x02020202, 0x02020202, 0x01010101, 0x01000001, 0x01010101, 0x02020202, 0x02020202, 0x00000001, 0x00000000,
0x00000000, 0x00000000, 0x02020201, 0x02020101, 0x00000102, 0x00000100, 0x02020201, 0x02020202, 0x01000001, 0x01000000, 0x01020202, 0x02020201, 0x02020202, 0x02020202, 0x02020201, 0x02010001, 0x02010202, 0x02020202, 0x01020202, 0x01020201, 0x02010000, 0x02010102, 0x02020202, 0x02020202, 0x01020202, 0x02020100, 0x02020202, 0x02020202, 0x02020202, 0x00000001, 0x01010000, 0x02020101, 0x02020202, 0x00000102, 0x01000000, 0x02020202, 0x00000102, 0x02020100, 0x00000001, 0x02010000, 0x02020202, 0x02020202, 0x02020202, 0x00000001, 0x02010000, 0x02020202, 0x02020202, 0x02020202, 0x00000102, 0x01000000, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02010001, 0x02020202, 0x02020202, 0x02020202, 0x01000001, 0x02020202, 0x02020202, 0x02020202, 0x00010202, 0x02020201, 0x02020202, 0x02020202,
0x02020202, 0x02020202, 0x01000001, 0x02020202, 0x02020202, 0x02020202, 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x02020201, 0x02010001, 0x00000001, 0x00010201, 0x02020201, 0x02020202, 0x02010001, 0x00000001, 0x00010201, 0x02020201, 0x02020202, 0x01020202, 0x02020201, 0x02010001, 0x01010202, 0x02020202, 0x00010202, 0x01020201, 0x02010000, 0x01000102, 0x02020202, 0x02020202, 0x01020202, 0x02020100, 0x02020202, 0x02020202, 0x02010102, 0x00000001, 0x00000000, 0x02010000, 0x02020202, 0x00000102, 0x00000001, 0x02020201, 0x00010202, 0x02020100, 0x00000001, 0x01000000, 0x02020202, 0x02020202, 0x02020202, 0x00000001, 0x01000000, 0x02020202, 0x02020202, 0x02020202, 0x00000001, 0x00000000, 0x02020201, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02010001, 0x02020202, 0x02020202, 0x02020202,
0x01000001, 0x02020202, 0x02020202, 0x02020202, 0x00010202, 0x02020201, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x01000001, 0x02020202, 0x02020202, 0x02020202, 0x00000102, 0x01010100, 0x01010101, 0x01000000, 0x02020202, 0x01000001, 0x01000000, 0x01020202, 0x02020201, 0x02020202, 0x02020101, 0x00000102, 0x00000100, 0x02020201, 0x02020202, 0x00010202, 0x02020201, 0x02010001, 0x00010202, 0x02020201, 0x00000102, 0x01010101, 0x01010000, 0x00000101, 0x02020201, 0x01010101, 0x01010101, 0x01010100, 0x01010101, 0x02020201, 0x01000001, 0x00000001, 0x00000000, 0x02010000, 0x02020202, 0x00000001, 0x00000101, 0x02020100, 0x00010202, 0x02010000, 0x00000001, 0x00000000, 0x02020201, 0x02020202, 0x02020202, 0x00000001, 0x00000000, 0x02020201, 0x02020202, 0x02020202, 0x00000001, 0x00000000, 0x02020201,
0x02020202, 0x02020202, 0x01010102, 0x01010101, 0x01010001, 0x01010101, 0x02020101, 0x02020202, 0x01000001, 0x02020202, 0x02020202, 0x02020202, 0x00010202, 0x02020201, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x01000001, 0x02020202, 0x02020202, 0x02020202, 0x00010202, 0x02020100, 0x01020202, 0x02010000, 0x02020202, 0x00000001, 0x02010000, 0x02020202, 0x02020201, 0x02020202, 0x02020201, 0x00010202, 0x00000000, 0x02020201, 0x02020202, 0x00000102, 0x01010101, 0x01010001, 0x00010101, 0x02020100, 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x02020100, 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x02020201, 0x00000001, 0x00000000, 0x00000000, 0x02010000, 0x02020202, 0x01000001, 0x00010202, 0x02010000, 0x01020202, 0x02010000, 0x00000001, 0x00000000, 0x02020100, 0x02020202, 0x02020202,
0x00000001, 0x00000000, 0x02020100, 0x02020202, 0x02020202, 0x01010101, 0x01010100, 0x02020201, 0x02020202, 0x02020202, 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x02010000, 0x02020202, 0x01000001, 0x02020202, 0x02020202, 0x02020202, 0x00010202, 0x02020201, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x01000001, 0x02020202, 0x02020202, 0x02020202, 0x01020202, 0x02020100, 0x01020202, 0x02020100, 0x02020202, 0x00000001, 0x02020100, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x01020202, 0x00000000, 0x02020201, 0x02020202, 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x02010000, 0x00000102, 0x01010101, 0x01010000, 0x00000101, 0x02020201, 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x02020201, 0x00000102, 0x00000000, 0x00000000, 0x02010000, 0x02020202, 0x01000001, 0x01020202,
0x01000000, 0x01020202, 0x02010000, 0x00000001, 0x00000000, 0x02010000, 0x02020202, 0x02020202, 0x00000001, 0x00000000, 0x02010000, 0x02020202, 0x02020202, 0x01020202, 0x02020100, 0x02020202, 0x02020202, 0x02020202, 0x01010102, 0x01010101, 0x01010001, 0x01010101, 0x02020101, 0x01020202, 0x00000000, 0x02020201, 0x02020202, 0x02020202, 0x00010202, 0x02020201, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x01000001, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020101, 0x01020202, 0x02020201, 0x02020202, 0x00000001, 0x02010000, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x00010202, 0x00000000, 0x02020201, 0x02020202, 0x00000102, 0x01010101, 0x01010001, 0x00010101, 0x02020100, 0x00010202, 0x01020201, 0x02010000, 0x01000102, 0x02020202, 0x01010101, 0x01010101, 0x01010100, 0x01010101,
0x02020201, 0x00010202, 0x00000000, 0x00000000, 0x02010000, 0x02020202, 0x01000001, 0x02020202, 0x00000001, 0x01020201, 0x02010000, 0x00000001, 0x01000000, 0x02010101, 0x02020202, 0x02020202, 0x00000001, 0x01000000, 0x02010101, 0x02020202, 0x02020202, 0x01020202, 0x02020100, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02010001, 0x02020202, 0x02020202, 0x00010202, 0x01000001, 0x02020100, 0x02020202, 0x02020202, 0x00010202, 0x02020201, 0x02020202, 0x02020202, 0x02020202, 0x01010101, 0x01000001, 0x01010101, 0x02020202, 0x02020202, 0x02020202, 0x02020102, 0x01020202, 0x02020202, 0x02020202, 0x00000001, 0x01000000, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x00000102, 0x00000000, 0x02020201, 0x02020202, 0x00010202, 0x02020201, 0x02010001, 0x00010202, 0x02020201, 0x01020202,
0x01020201, 0x02010000, 0x02010102, 0x02020202, 0x02020202, 0x01020202, 0x02020100, 0x02020202, 0x02020202, 0x01020202, 0x00000000, 0x00000000, 0x02010000, 0x02020202, 0x00000001, 0x02020201, 0x00000102, 0x00010100, 0x02010000, 0x00000001, 0x01000001, 0x01020202, 0x01010101, 0x01010101, 0x00000001, 0x01000001, 0x01020202, 0x01010101, 0x01010101, 0x01020202, 0x02020100, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02010001, 0x02020202, 0x02020202, 0x01000102, 0x01000001, 0x02010001, 0x02020202, 0x02020202, 0x00010202, 0x02020201, 0x02020202, 0x02020202, 0x02020202, 0x00000102, 0x00000000, 0x02010000, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x01010101, 0x01010101, 0x02020201, 0x02020202, 0x02020202, 0x02020202, 0x01010101, 0x01010101,
0x02020201, 0x02020202, 0x01020202, 0x02020201, 0x02010001, 0x01010202, 0x02020202, 0x02020202, 0x01020201, 0x02010000, 0x02020102, 0x02020202, 0x02020202, 0x01020202, 0x02020100, 0x02020202, 0x02020202, 0x02020202, 0x00000001, 0x00000000, 0x02020100, 0x02020202, 0x00000102, 0x02020201, 0x00010202, 0x00010000, 0x02020100, 0x01000001, 0x01000001, 0x01020202, 0x00000000, 0x01000000, 0x01000001, 0x01000001, 0x01020202, 0x00000000, 0x01000000, 0x01020202, 0x02020100, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02010001, 0x02020202, 0x02020202, 0x00010001, 0x00000000, 0x01000100, 0x02020202, 0x02020202, 0x00010202, 0x02020201, 0x02020202, 0x02020202, 0x02020202, 0x00010202, 0x00000000, 0x02020100, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202,
0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020201, 0x02010001, 0x02010202, 0x02020202, 0x02020202, 0x01020202, 0x02010000, 0x02020202, 0x02020202, 0x02020202, 0x01010101, 0x01010100, 0x02020201, 0x02020202, 0x02020202, 0x00000102, 0x00000000, 0x02020201, 0x02020202, 0x00000102, 0x02020100, 0x01020202, 0x00000000, 0x02020100, 0x02010001, 0x00000102, 0x01020201, 0x01010000, 0x01000001, 0x02010001, 0x00000102, 0x01020201, 0x01010100, 0x01000001, 0x01020202, 0x02020100, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02010001, 0x02020202, 0x02020202, 0x00000001, 0x00000000, 0x01000000, 0x02020202, 0x02020202, 0x00010202, 0x02020201, 0x02020202, 0x02020202, 0x02020202, 0x01020202, 0x00000000,
0x02020201, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x01010102, 0x01010001, 0x02020101, 0x02020202, 0x02020202, 0x01020202, 0x02010000, 0x02020202, 0x02020202, 0x02020202, 0x00000102, 0x01000000, 0x02020202, 0x02020202, 0x02020202, 0x01010202, 0x01010101, 0x02020202, 0x02020202, 0x00010202, 0x01010000, 0x01020202, 0x00000001, 0x02020201, 0x02020101, 0x00000102, 0x01020201, 0x00000100, 0x01000100, 0x02020101, 0x00000102, 0x01020201, 0x01000100, 0x01000100, 0x01020202, 0x02020101, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02010001, 0x02020202, 0x02020202, 0x01010101, 0x01010101, 0x01010101, 0x02020202,
0x02020202, 0x00010102, 0x02020101, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x01000001, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x00010202, 0x00000000, 0x02020201, 0x02020202, 0x02020202, 0x01020202, 0x02010000, 0x02020202, 0x02020202, 0x02020202, 0x00010202, 0x02010000, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x01020202, 0x00000000, 0x00010101, 0x01000000, 0x02020202, 0x02020202, 0x00010202, 0x01020100, 0x00000100, 0x01000000, 0x02020202, 0x00010202, 0x01020100, 0x01000100, 0x01000100, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202,
0x02020202, 0x02010001, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x01000001, 0x02010000, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02010102, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x01020202, 0x01000000, 0x02020202, 0x02020202, 0x02020202, 0x01020202, 0x02010101, 0x02020202, 0x02020202, 0x02020202, 0x01020202, 0x02020100, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x00000001, 0x00000000, 0x02010000, 0x02020202, 0x02020202, 0x00010202, 0x01020100, 0x00000100, 0x01000100, 0x02020202, 0x00010202, 0x01020100,
0x01000100, 0x01000100, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02010001, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02010101, 0x02010101, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02010001, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020201, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x00010102, 0x00000000, 0x02020101, 0x02020202,
0x02020202, 0x01020202, 0x01020201, 0x01010000, 0x01000001, 0x02020202, 0x01020202, 0x01020201, 0x01000100, 0x01000100, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020102, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020102, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202,
0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x01020202, 0x01010101, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x01020202, 0x00000000, 0x01000000, 0x02020202, 0x02020202, 0x01020202, 0x00000000, 0x01000000, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202,
0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x01020202, 0x01010101, 0x01010101, 0x02020202, 0x02020202, 0x01020202, 0x01010101, 0x01010101,
};
diff --git a/src/haiku/Window.cpp b/src/haiku/Window.cpp
--- a/src/haiku/Window.cpp
+++ b/src/haiku/Window.cpp
@@ -1,1010 +1,1017 @@
#include
#include "lgi/common/Lgi.h"
#include "lgi/common/DragAndDrop.h"
#include "lgi/common/Token.h"
#include "lgi/common/Popup.h"
#include "lgi/common/Panel.h"
#include "lgi/common/Notifications.h"
#include "lgi/common/Menu.h"
#include "ViewPriv.h"
#include "MenuBar.h"
#define DEBUG_SETFOCUS 0
#define DEBUG_HANDLEVIEWKEY 0
+#define DEBUG_WAIT_THREAD 1
+
+#if DEBUG_WAIT_THREAD
+ #define WAIT_LOG(...) LgiTrace(__VA_ARGS__)
+#else
+ #define WAIT_LOG(...)
+#endif
LString ToString(BRect &r)
{
LString s;
s.Printf("%g,%g,%g,%g", r.left, r.top, r.right, r.bottom);
return s;
}
///////////////////////////////////////////////////////////////////////
class HookInfo
{
public:
LWindowHookType Flags;
LView *Target;
};
enum LAttachState
{
LUnattached,
LAttaching,
LAttached,
LDetaching,
};
class LWindowPrivate : public BWindow
{
public:
LWindow *Wnd;
bool SnapToEdge = false;
LArray Hooks;
LWindowPrivate(LWindow *wnd) :
Wnd(wnd),
BWindow(BRect(100,100,400,400),
"...loading...",
B_DOCUMENT_WINDOW_LOOK,
B_NORMAL_WINDOW_FEEL,
0)
{
}
~LWindowPrivate()
{
DeleteObj(Wnd->Menu);
if (IsMinimized())
Wnd->_PrevZoom = LZoomMin;
Wnd->d = NULL;
}
int GetHookIndex(LView *Target, bool Create = false)
{
for (int i=0; iTarget = Target;
n->Flags = LNoEvents;
return Hooks.Length() - 1;
}
}
return -1;
}
void FrameMoved(BPoint newPosition)
{
auto Pos = Frame();
if (Pos != Wnd->Pos)
{
Wnd->Pos = Pos;
Wnd->OnPosChange();
}
BWindow::FrameMoved(newPosition);
}
void FrameResized(float newWidth, float newHeight)
{
auto Pos = Frame();
if (Pos != Wnd->Pos)
{
Wnd->Pos.SetSize(newWidth, newHeight);
// printf("Wnd->Pos=%s %i,%i %g,%g\n", Wnd->Pos.GetStr(), Wnd->Pos.X(), Wnd->Pos.Y(), newWidth, newHeight);
Wnd->OnPosChange();
}
BWindow::FrameResized(newWidth, newHeight);
}
bool QuitRequested()
{
// printf("%s::QuitRequested() starting..\n", Wnd->GetClass());
auto r = Wnd->OnRequestClose(false);
// printf("%s::QuitRequested()=%i\n", Wnd->GetClass(), r);
return r;
}
void MessageReceived(BMessage *message)
{
if (message->what == M_LWINDOW_DELETE)
{
// printf("Processing M_LWINDOW_DELETE th=%u\n", GetCurrentThreadId());
Wnd->Handle()->RemoveSelf();
Quit();
// printf("Processed M_LWINDOW_DELETE\n");
}
else
{
BWindow::MessageReceived(message);
LView *view = NULL;
auto r = message->FindPointer(LMessage::PropView, &view);
if (r == B_OK)
view->OnEvent((LMessage*)message);
else
Wnd->OnEvent((LMessage*)message);
}
}
};
///////////////////////////////////////////////////////////////////////
LWindow::LWindow() : LView(0)
{
d = new LWindowPrivate(this);
_QuitOnClose = false;
Menu = NULL;
_Default = 0;
_Window = this;
ClearFlag(WndFlags, GWF_VISIBLE);
}
LWindow::~LWindow()
{
if (LAppInst->AppWnd == this)
LAppInst->AppWnd = NULL;
LAssert(!Menu);
WaitThread();
}
int LWindow::WaitThread()
{
if (!d)
return -1;
thread_id id = d->Thread();
bool thisThread = id == GetCurrentThreadId();
- // printf("%s::~LWindow thread=%u lock=%u\n", Name(), GetCurrentThreadId(), d->LockingThread());
+ WAIT_LOG("%s::~LWindow thread=%u lock=%u\n", Name(), GetCurrentThreadId(), d->LockingThread());
if (thisThread)
{
// We are in thread... can delete easily.
if (d->Lock())
{
// printf("%s::~LWindow Quiting\n", Name());
Handle()->RemoveSelf();
d->Quit();
// printf("%s::~LWindow Quit finished\n", Name());
}
d = NULL;
return 0; // If we're in thread... no need to wait.
}
// Post event to the window's thread to delete itself...
- // printf("%s::~LWindow posting M_LWINDOW_DELETE from th=%u\n", Name(), GetCurrentThreadId());
+ WAIT_LOG("%s::~LWindow posting M_LWINDOW_DELETE from th=%u\n", Name(), GetCurrentThreadId());
d->PostMessage(new BMessage(M_LWINDOW_DELETE));
status_t value = 0;
- // printf("wait_for_thread(%u) start..\n", id);
+ WAIT_LOG("wait_for_thread(%u) start..\n", id);
wait_for_thread(id, &value);
- // printf("wait_for_thread(%u) end=%i\n", id, value);
+ WAIT_LOG("wait_for_thread(%u) end=%i\n", id, value);
d = NULL;
return value;
}
OsWindow LWindow::WindowHandle()
{
return d;
}
bool LWindow::SetIcon(const char *FileName)
{
LString a;
if (!LFileExists(FileName))
{
if (a = LFindFile(FileName))
FileName = a;
}
return false;
}
bool LWindow::GetSnapToEdge()
{
return d->SnapToEdge;
}
void LWindow::SetSnapToEdge(bool s)
{
d->SnapToEdge = s;
}
bool LWindow::IsActive()
{
LLocker lck(d, _FL);
if (!lck.Lock())
return false;
return d->IsActive();
}
bool LWindow::SetActive()
{
LLocker lck(d, _FL);
if (!lck.Lock())
return false;
d->Activate();
return true;
}
bool LWindow::Visible()
{
LLocker lck(d, _FL);
if (!lck.Lock())
return false;
return !d->IsHidden();
}
void LWindow::Visible(bool i)
{
LLocker lck(d, _FL);
if (!lck.Lock())
{
printf("%s:%i - Can't lock.\n", _FL);
return;
}
if (i)
{
if (d->IsHidden())
d->Show();
}
else
{
if (!d->IsHidden())
d->Hide();
}
}
bool LWindow::Obscured()
{
return false;
}
bool DndPointMap(LViewI *&v, LPoint &p, LDragDropTarget *&t, LWindow *Wnd, int x, int y)
{
LRect cli = Wnd->GetClient();
t = NULL;
v = Wnd->WindowFromPoint(x - cli.x1, y - cli.y1, false);
if (!v)
{
LgiTrace("%s:%i - @ %i,%i\n", _FL, x, y);
return false;
}
v->WindowVirtualOffset(&p);
p.x = x - p.x;
p.y = y - p.y;
for (LViewI *view = v; !t && view; view = view->GetParent())
t = view->DropTarget();
if (t)
return true;
LgiTrace("%s:%i - No target for %s\n", _FL, v->GetClass());
return false;
}
bool LWindow::Attach(LViewI *p)
{
LLocker lck(d, _FL);
if (!lck.Lock())
return false;
auto rootView = Handle();
auto wnd = WindowHandle();
// printf("%s:%i attach %p to %p\n", _FL, Handle(), WindowHandle());
if (rootView && wnd)
{
wnd->AddChild(rootView);
if (rootView->IsHidden())
rootView->Show();
auto menu = wnd->KeyMenuBar();
BRect menuPos = menu ? menu->Frame() : BRect(0, 0, 0, 0);
auto f = wnd->Frame();
rootView->ResizeTo(f.Width(), f.Height() - menuPos.Height());
if (menu)
rootView->MoveTo(0, menuPos.Height());
rootView->SetResizingMode(B_FOLLOW_ALL_SIDES);
}
// Setup default button...
if (!_Default)
_Default = FindControl(IDOK);
// Do a rough layout of child windows
PourAll();
return true;
}
bool LWindow::OnRequestClose(bool OsShuttingDown)
{
if (GetQuitOnClose())
LCloseApp();
return LView::OnRequestClose(OsShuttingDown);
}
bool LWindow::HandleViewMouse(LView *v, LMouse &m)
{
if (m.Down() && !m.IsMove())
{
bool InPopup = false;
for (LViewI *p = v; p; p = p->GetParent())
{
if (dynamic_cast(p))
{
InPopup = true;
break;
}
}
if (!InPopup && LPopup::CurrentPopups.Length())
{
for (int i=0; iVisible())
p->Visible(false);
}
}
}
for (int i=0; iHooks.Length(); i++)
{
if (d->Hooks[i].Flags & LMouseEvents)
{
if (!d->Hooks[i].Target->OnViewMouse(v, m))
{
return false;
}
}
}
return true;
}
bool LWindow::HandleViewKey(LView *v, LKey &k)
{
bool Status = false;
LViewI *Ctrl = 0;
#if DEBUG_HANDLEVIEWKEY
bool Debug = 1; // k.vkey == LK_RETURN;
char SafePrint = k.c16 < ' ' ? ' ' : k.c16;
// if (Debug)
{
LgiTrace("%s/%p::HandleViewKey=%i ischar=%i %s%s%s%s\n",
v->GetClass(), v,
k.c16,
k.IsChar,
(char*)(k.Down()?" Down":" Up"),
(char*)(k.Shift()?" Shift":""),
(char*)(k.Alt()?" Alt":""),
(char*)(k.Ctrl()?" Ctrl":""));
}
#endif
// Any window in a popup always gets the key...
LViewI *p;
for (p = v->GetParent(); p; p = p->GetParent())
{
if (dynamic_cast(p))
{
#if DEBUG_HANDLEVIEWKEY
if (Debug)
LgiTrace("\tSending key to popup\n");
#endif
return v->OnKey(k);
}
}
// Give key to popups
if (LAppInst &&
LAppInst->GetMouseHook() &&
LAppInst->GetMouseHook()->OnViewKey(v, k))
{
#if DEBUG_HANDLEVIEWKEY
if (Debug)
LgiTrace("\tMouseHook got key\n");
#endif
goto AllDone;
}
// Allow any hooks to see the key...
for (int i=0; iHooks.Length(); i++)
{
#if DEBUG_HANDLEVIEWKEY
// if (Debug) LgiTrace("\tHook[%i]\n", i);
#endif
if (d->Hooks[i].Flags & LKeyEvents)
{
LView *Target = d->Hooks[i].Target;
#if DEBUG_HANDLEVIEWKEY
if (Debug)
LgiTrace("\tHook[%i].Target=%p %s\n", i, Target, Target->GetClass());
#endif
if (Target->OnViewKey(v, k))
{
Status = true;
#if DEBUG_HANDLEVIEWKEY
if (Debug)
LgiTrace("\tHook[%i] ate '%c'(%i) down=%i alt=%i ctrl=%i sh=%i\n", i, SafePrint, k.c16, k.Down(), k.Alt(), k.Ctrl(), k.Shift());
#endif
goto AllDone;
}
}
}
// Give the key to the window...
if (v->OnKey(k))
{
#if DEBUG_HANDLEVIEWKEY
if (Debug)
LgiTrace("\tView ate '%c'(%i) down=%i alt=%i ctrl=%i sh=%i\n",
SafePrint, k.c16, k.Down(), k.Alt(), k.Ctrl(), k.Shift());
#endif
Status = true;
goto AllDone;
}
#if DEBUG_HANDLEVIEWKEY
else if (Debug)
LgiTrace("\t%s didn't eat '%c'(%i) down=%i alt=%i ctrl=%i sh=%i\n",
v->GetClass(), SafePrint, k.c16, k.Down(), k.Alt(), k.Ctrl(), k.Shift());
#endif
// Window didn't want the key...
switch (k.vkey)
{
case LK_RETURN:
#ifdef LK_KEYPADENTER
case LK_KEYPADENTER:
#endif
{
Ctrl = _Default;
break;
}
case LK_ESCAPE:
{
Ctrl = FindControl(IDCANCEL);
break;
}
}
// printf("Ctrl=%p\n", Ctrl);
if (Ctrl)
{
if (Ctrl->Enabled())
{
if (Ctrl->OnKey(k))
{
Status = true;
#if DEBUG_HANDLEVIEWKEY
if (Debug)
LgiTrace("\tDefault Button ate '%c'(%i) down=%i alt=%i ctrl=%i sh=%i\n",
SafePrint, k.c16, k.Down(), k.Alt(), k.Ctrl(), k.Shift());
#endif
goto AllDone;
}
// else printf("OnKey()=false\n");
}
// else printf("Ctrl=disabled\n");
}
#if DEBUG_HANDLEVIEWKEY
else if (Debug)
LgiTrace("\tNo default ctrl to handle key.\n");
#endif
if (Menu)
{
Status = Menu->OnKey(v, k);
if (Status)
{
#if DEBUG_HANDLEVIEWKEY
if (Debug)
LgiTrace("\tMenu ate '%c' down=%i alt=%i ctrl=%i sh=%i\n", k.c16, k.Down(), k.Alt(), k.Ctrl(), k.Shift());
#endif
}
}
// Tab through controls
if (k.vkey == LK_TAB && k.Down() && !k.IsChar)
{
LViewI *Wnd = GetNextTabStop(v, k.Shift());
#if DEBUG_HANDLEVIEWKEY
if (Debug)
LgiTrace("\tTab moving focus shift=%i Wnd=%p\n", k.Shift(), Wnd);
#endif
if (Wnd)
Wnd->Focus(true);
}
// Control shortcut?
if (k.Down() && k.Alt() && k.c16 > ' ')
{
ShortcutMap Map;
BuildShortcuts(Map);
LViewI *c = Map.Find(ToUpper(k.c16));
if (c)
{
c->OnNotify(c, LNotifyActivate);
return true;
}
}
AllDone:
return Status;
}
void LWindow::Raise()
{
}
LWindowZoom LWindow::GetZoom()
{
if (!d)
return _PrevZoom;
LLocker lck(d, _FL);
if (!IsAttached() || lck.Lock())
{
if (d->IsMinimized())
return LZoomMin;
}
return LZoomNormal;
}
void LWindow::SetZoom(LWindowZoom i)
{
LLocker lck(d, _FL);
if (lck.Lock())
{
d->Minimize(i == LZoomMin);
}
}
LViewI *LWindow::GetDefault()
{
return _Default;
}
void LWindow::SetDefault(LViewI *v)
{
if (v &&
v->GetWindow() == this)
{
if (_Default != v)
{
LViewI *Old = _Default;
_Default = v;
if (Old) Old->Invalidate();
if (_Default) _Default->Invalidate();
}
}
else
{
_Default = 0;
}
}
bool LWindow::Name(const char *n)
{
LLocker lck(d, _FL);
if (lck.Lock())
d->SetTitle(n);
return LBase::Name(n);
}
const char *LWindow::Name()
{
return LBase::Name();
}
LPoint LWindow::GetDpi()
{
return LPoint(96, 96);
}
LPointF LWindow::GetDpiScale()
{
auto Dpi = GetDpi();
return LPointF((double)Dpi.x/96.0, (double)Dpi.y/96.0);
}
LRect &LWindow::GetClient(bool ClientSpace)
{
static LRect r;
r = Pos.ZeroTranslate();
LLocker lck(WindowHandle(), _FL);
if (lck.Lock())
{
auto br = Handle()->Bounds();
r = br;
auto frm = d->Frame();
frm.OffsetBy(-frm.left, -frm.top);
// printf("Frame=%s Bounds=%s r=%s\n", ToString(frm).Get(), ToString(br).Get(), r.GetStr());
lck.Unlock();
}
return r;
}
bool LWindow::SerializeState(LDom *Store, const char *FieldName, bool Load)
{
if (!Store || !FieldName)
return false;
if (Load)
{
::LVariant v;
if (Store->GetValue(FieldName, v) && v.Str())
{
LRect Position(0, 0, -1, -1);
LWindowZoom State = LZoomNormal;
// printf("SerializeState load %s\n", v.Str());
LToken t(v.Str(), ";");
for (int i=0; iSetValue(FieldName, v))
return false;
}
return true;
}
LRect &LWindow::GetPos()
{
return Pos;
}
bool LWindow::SetPos(LRect &p, bool Repaint)
{
Pos = p;
LLocker lck(d, _FL);
if (lck.Lock())
{
d->MoveTo(Pos.x1, Pos.y1);
d->ResizeTo(Pos.X(), Pos.Y());
}
return true;
}
void LWindow::OnChildrenChanged(LViewI *Wnd, bool Attaching)
{
// Force repour
}
void LWindow::OnCreate()
{
AttachChildren();
}
void LWindow::OnPaint(LSurface *pDC)
{
pDC->Colour(L_MED);
pDC->Rectangle();
}
void LWindow::OnPosChange()
{
LLocker lck(WindowHandle(), _FL);
if (lck.Lock())
{
auto frame = WindowHandle()->Bounds();
auto menu = WindowHandle()->KeyMenuBar();
auto menuPos = menu ? menu->Frame() : BRect(0, 0, 0, 0);
auto rootPos = Handle()->Frame();
if (menu)
{
if (menu->IsHidden()) // Why?
{
menu->Show();
if (menu->IsHidden())
{
// printf("Can't show menu?\n");
for (auto p = menu->Parent(); p; p = p->Parent())
printf(" par=%s %i\n", p->Name(), p->IsHidden());
}
}
if (menuPos.Width() < 1) // Again... WHHHHY? FFS
{
float x = 0.0f, y = 0.0f;
menu->GetPreferredSize(&x, &y);
// printf("Pref=%g,%g\n", x, y);
if (y > 0.0f)
menu->ResizeTo(frame.Width(), y);
}
}
#if 0
printf("frame=%s menu=%p,%i,%s rootpos=%s\n",
ToString(frame).Get(), menu, menu?menu->IsHidden():0, ToString(menuPos).Get(), ToString(rootPos).Get());
#endif
int rootTop = menu ? menuPos.bottom + 1 : 0;
if (rootPos.top != rootTop)
{
Handle()->MoveTo(0, rootTop);
Handle()->ResizeTo(rootPos.Width(), frame.Height() - menuPos.Height());
}
lck.Unlock();
}
LView::OnPosChange();
PourAll();
}
#define IsTool(v) \
( \
dynamic_cast(v) \
&& \
dynamic_cast(v)->_IsToolBar \
)
void LWindow::PourAll()
{
LRect c = GetClient();
LRegion Client(c);
LViewI *MenuView = 0;
LRegion Update(Client);
bool HasTools = false;
LViewI *v;
List::I Lst = Children.begin();
{
LRegion Tools;
for (v = *Lst; v; v = *++Lst)
{
bool IsMenu = MenuView == v;
if (!IsMenu && IsTool(v))
{
LRect OldPos = v->GetPos();
if (OldPos.Valid())
Update.Union(&OldPos);
if (HasTools)
{
// 2nd and later toolbars
if (v->Pour(Tools))
{
if (!v->Visible())
{
v->Visible(true);
}
auto vpos = v->GetPos();
if (OldPos != vpos)
{
// position has changed update...
v->Invalidate();
}
// Has it increased the size of the toolbar area?
auto b = Tools.Bound();
if (vpos.y2 >= b.y2)
{
LRect Bar = Client;
Bar.y2 = vpos.y2;
Client.Subtract(&Bar);
// LgiTrace("IncreaseToolbar=%s\n", Bar.GetStr());
}
Tools.Subtract(&vpos);
Update.Subtract(&vpos);
// LgiTrace("vpos=%s\n", vpos.GetStr());
}
}
else
{
// First toolbar
if (v->Pour(Client))
{
HasTools = true;
if (!v->Visible())
{
v->Visible(true);
}
if (OldPos != v->GetPos())
{
v->Invalidate();
}
LRect Bar(v->GetPos());
// LgiTrace("%s = %s\n", v->GetClass(), Bar.GetStr());
Bar.x2 = GetClient().x2;
Tools = Bar;
Tools.Subtract(&v->GetPos());
Client.Subtract(&Bar);
Update.Subtract(&Bar);
}
}
}
}
}
Lst = Children.begin();
for (LViewI *v = *Lst; v; v = *++Lst)
{
bool IsMenu = MenuView == v;
if (!IsMenu && !IsTool(v))
{
LRect OldPos = v->GetPos();
if (OldPos.Valid())
Update.Union(&OldPos);
if (v->Pour(Client))
{
// LRect p = v->GetPos();
// LgiTrace("%s = %s\n", v->GetClass(), p.GetStr());
if (!v->Visible())
v->Visible(true);
v->Invalidate();
Client.Subtract(&v->GetPos());
Update.Subtract(&v->GetPos());
}
else
{
// non-pourable
}
}
}
for (int i=0; iMsg())
{
case M_CLOSE:
{
if (OnRequestClose(false))
{
Quit();
return 0;
}
break;
}
}
return LView::OnEvent(m);
}
bool LWindow::RegisterHook(LView *Target, LWindowHookType EventType, int Priority)
{
bool Status = false;
if (Target && EventType)
{
int i = d->GetHookIndex(Target, true);
if (i >= 0)
{
d->Hooks[i].Flags = EventType;
Status = true;
}
}
return Status;
}
bool LWindow::UnregisterHook(LView *Target)
{
int i = d->GetHookIndex(Target);
if (i >= 0)
{
d->Hooks.DeleteAt(i);
return true;
}
return false;
}
void LWindow::OnFrontSwitch(bool b)
{
}
LViewI *LWindow::GetFocus()
{
return NULL;
}
void LWindow::SetFocus(LViewI *ctrl, FocusType type)
{
}
void LWindow::SetDragHandlers(bool On)
{
}
void LWindow::OnTrayClick(LMouse &m)
{
if (m.Down() || m.IsContextMenu())
{
LSubMenu RClick;
OnTrayMenu(RClick);
if (GetMouse(m, true))
{
int Result = RClick.Float(this, m.x, m.y);
OnTrayMenuResult(Result);
}
}
}
void LWindow::SetAlwaysOnTop(bool b)
{
}