diff --git a/include/common/GMenu.h b/include/common/GMenu.h --- a/include/common/GMenu.h +++ b/include/common/GMenu.h @@ -1,490 +1,489 @@ /** \file \author Matthew Allen */ #ifndef __GMENU_H #define __GMENU_H // Os specific declarations #if defined __GTK_H__ typedef Gtk::GtkMenuShell *OsSubMenu; typedef Gtk::GtkMenuItem *OsMenuItem; #elif defined WIN32 typedef HMENU OsSubMenu; typedef MENUITEMINFO OsMenuItem; #ifndef COLOR_MENUHILIGHT #define COLOR_MENUHILIGHT 29 #endif #ifndef COLOR_MENUBAR #define COLOR_MENUBAR 30 #endif #elif defined BEOS typedef BMenu *OsSubMenu; typedef BMenuItem *OsMenuItem; #elif defined ATHEOS #include typedef os::Menu *OsSubMenu; typedef os::MenuItem *OsMenuItem; #elif defined MAC #if defined(COCOA) typedef void *OsSubMenu; typedef void *OsMenuItem; #else typedef MenuRef OsSubMenu; typedef MenuItemIndex OsMenuItem; #endif #else #error "Not impl." #endif #include "GXmlTree.h" #include "Res.h" /////////////////////////////////////////////////////////////////////////////////////////////// // Menu wrappers class LgiClass GMenuLoader { friend class GMenuItem; friend class GMenu; friend class GSubMenu; friend class MenuImpl; friend class SubMenuImplPrivate; protected: #ifdef WIN32 OsSubMenu Info; #endif List Items; public: GMenuLoader() { #ifdef WIN32 Info = 0; #endif } bool Load( class LgiMenuRes *MenuRes, GXmlTag *Tag, ResFileFormat Format, class TagHash *TagList); virtual GMenuItem *AppendItem(const char *Str, int Id, bool Enabled, int Where = -1, const char *Shortcut = 0) = 0; virtual GSubMenu *AppendSub(const char *Str, int Where = -1) = 0; virtual GMenuItem *AppendSeparator(int Where = -1) = 0; }; /// Sub menu. class LgiClass GSubMenu : public GBase, public GTarget, // public GFlags, public GMenuLoader, public GItemContainer { friend class GMenuItem; friend class GMenu; friend class SubMenuImpl; friend class MenuItemImpl; friend class MenuImpl; #if !WIN32NATIVE OsSubMenu Info; #endif #if defined(__GTK_H__) friend void MenuItemCallback(class GMenuItem *Item); friend void GSubMenuDeactivate(Gtk::GtkMenuShell *widget, GSubMenu *Sub); int *_ContextMenuId; bool IsContext(GMenuItem *Item); void OnDeactivate(); #elif defined(WIN32NATIVE) HWND TrackHandle; #elif defined(BEOS) void _CopyMenu(BMenu *To, GSubMenu *From); GMenuItem *MatchShortcut(GKey &k); #else bool OnKey(GKey &k); #endif protected: /// The parent menu item or NULL if the root menu GMenuItem *Parent; /// The top level window this sub menu belongs to or NULL GMenu *Menu; /// The window that the menu belongs to or NULL. GViewI *Window; void OnAttach(bool Attach); void ClearHandle(); public: /// Constructor GSubMenu ( /// Name of the menu const char *name = "", /// True if it's popup bool Popup = true ); virtual ~GSubMenu(); /// Returns the OS handle OsSubMenu Handle() { return Info; } /// Detachs the OS handle and returns it OsSubMenu Release() { OsSubMenu Hnd = Info; Info = 0; return Hnd; } /// Add a new item GMenuItem *AppendItem ( /// The text of the item. /// /// If you put a tab control in the text, anything after the tab is considered /// to be the keyboard shortcut for the menu item. The shortcut can be a combination /// of keys added together with '+'. /// /// e.g. /// const char *Str, /// Command ID to post to the OnCommand() handler int Id, /// True if the item should be enabled bool Enabled = true, /// The index into the list to insert at, or -1 to insert at the end int Where = -1, // Shortcut if not embeded in "Str" const char *Shortcut = 0 ); /// Add a submenu GSubMenu *AppendSub ( /// The text of the item const char *Str, /// The index to insert the item, or -1 to insert on the end int Where = -1 ); /// Add a separator GMenuItem *AppendSeparator(int Where = -1); /// Delete all items void Empty(); /// Detachs an item from the sub menu but doesn't delete it bool RemoveItem ( /// The index of the item to remove int i ); /// Detachs an item from the sub menu but doesn't delete it bool RemoveItem ( /// Pointer of the item to remove GMenuItem *Item ); /// Returns numbers of items in menu int Length(); /// Return a pointer to an item GMenuItem *ItemAt ( /// The index of the item to return int i ); /// Returns a pointer to an item GMenuItem *FindItem ( /// The ID of the item to return int Id ); /// Returns a pointer to an sub menu GSubMenu *FindSubMenu ( /// The ID of the sub menu to return int Id ); /// Floats the submenu anywhere on the screen int Float ( /// The parent view GView *Parent, /// The x coord of the top-left corner int x, /// The y coord of the top-left corner int y, /// True if the menu is tracking the left button, else it tracks the right button bool Left = false ); /// Returns the parent menu item GMenuItem *GetParent() { return Parent; } /// Returns the menu that this belongs to GMenu *GetMenu() { return Menu; } }; /// An item an a menu class LgiClass GMenuItem : public GBase, public GTarget // public GFlags { friend class GSubMenu; friend class GMenu; friend class GView; friend class LgiMenuItem; friend class SubMenuImpl; friend class MenuItemImpl; friend class MenuImpl; friend class SubMenuImplPrivate; private: #ifdef WIN32 bool Insert(int Pos); bool Update(); #endif #if defined(__GTK_H__) || defined(BEOS) GAutoString ShortCut; #endif protected: GMenu *Menu; GSubMenu *Parent; GSubMenu *Child; int Position; int _Icon; OsMenuItem Info; class GMenuItemPrivate *d; #if defined BEOS BMessage *Msg; bool UnsupportedShortcut; int ShortcutKey; int ShortcutMod; GMenuItem *MatchShortcut(GKey &k); #else int _Id; - bool _Check; - bool _Enabled; + int _Flags; #endif virtual void _Measure(GdcPt2 &Size); virtual void _Paint(GSurface *pDC, int Flags); virtual void _PaintText(GSurface *pDC, int x, int y, int Width); void OnAttach(bool Attach); void ClearHandle(); public: GMenuItem(); #if defined BEOS GMenuItem(BMenuItem *item); GMenuItem(GSubMenu *p); #endif GMenuItem(GMenu *m, GSubMenu *p, const char *txt, int Pos, const char *Shortcut = 0); virtual ~GMenuItem(); GMenuItem &operator =(const GMenuItem &m) { LgiAssert(!"This shouldn't be used anywhere"); return *this; } /// Creates a sub menu off the item GSubMenu *Create(); /// Removes the item from it's place in the menu but doesn't delete it bool Remove(); /// Returns the parent sub menu GSubMenu *GetParent(); /// Returns the parent sub menu GMenu *GetMenu() { return Menu; } /// Scans the text of the item for a keyboard shortcut bool ScanForAccel(); /// Returns the OS handle for the menuitem OsMenuItem Handle() { return Info; } /// Set the id void Id(int i); /// Turn the item into a separator void Separator(bool s); /// Put a check mark on the item void Checked(bool c); /// \brief Set the text of the item /// \sa GSubMenu::AppendItem() bool Name(const char *n); /// Enable or disable the item void Enabled(bool e); void Visible(bool v); void Focus(bool f); /// Attach a sub menu to the item void Sub(GSubMenu *s); /// Set the icon for the item. The icon is stored in the GMenu's image list. void Icon(int i); /// Get the id int Id(); /// Get the text of the item char *Name(); /// Return whether this item is a separator bool Separator(); /// Return whether this item has a check mark bool Checked(); /// Return whether this item is enabled bool Enabled(); bool Visible(); bool Focus(); /// Return whether this item's submenu GSubMenu *Sub(); /// Return the icon of this this int Icon(); }; /// Encapsulates a keyboard shortcut class LgiClass GAccelerator { int Flags; int Key; int Id; public: GAccelerator(int flags, int key, int id); int GetId() { return Id; } /// See if the accelerator matchs a keyboard event bool Match(GKey &k); }; /** \brief Top level window menu This class contains GMenuItem's and GSubMenu's. A basic menu can be constructed inside a GWindow like this: \code Menu = new GMenu; if (Menu) { Menu->Attach(this); GSubMenu *File = Menu->AppendSub("&File"); if (File) { File->AppendItem("&Open\tCtrl+O", IDM_OPEN, true); File->AppendItem("&Save All\tCtrl+S", IDM_SAVE_ALL, true); File->AppendItem("Save &As", IDM_SAVEAS, true); File->AppendSeparator(); File->AppendItem("&Options", IDM_OPTIONS, true); File->AppendSeparator(); File->AppendItem("E&xit", IDM_EXIT, true); } GSubMenu *Help = Menu->AppendSub("&Help"); if (Help) { Help->AppendItem("&Help", IDM_HELP, true); Help->AppendItem("&About", IDM_ABOUT, true); } } \endcode Or you can load a menu from a resource like this: \code Menu = new GMenu; if (Menu) { Menu->Attach(this); Menu->Load(this, "IDM_MENU"); } \endcode */ class LgiClass GMenu : public GSubMenu { friend class GSubMenu; friend class GMenuItem; friend class GWindow; static GFont *_Font; class GMenuPrivate *d; #if defined WIN32 void OnChange(); #else void OnChange() {} #endif protected: /// List of keyboard shortcuts in the menu items attached List Accel; #ifdef __GTK_H__ Gtk::GtkAccelGroup *AccelGrp; #endif public: /// Constructor GMenu(); /// Destructor virtual ~GMenu(); /// Returns the font used by the menu items static GFont *GetFont(); /// Returns the top level window that this menu is attached to GViewI *WindowHandle() { return Window; } /// Attach the menu to a window bool Attach(GViewI *p); /// Detact the menu from the window bool Detach(); /// Load the menu from a resource file bool Load ( /// The parent view for any error message boxes GView *p, /// The resource to load. Will probably change to an int sometime. const char *Res, /// Optional list of comma or space separated tags const char *Tags = 0 ); /// \brief See if any of the accelerators match the key event /// \return true if none of the accelerators match bool OnKey ( /// The view that will eventually receive the key event GView *v, /// The keyboard event details GKey &k ); #if defined(WIN32) static int _OnEvent(GMessage *Msg); #elif defined(BEOS) GRect GetPos(); #endif }; #endif diff --git a/include/mac/carbon/LgiOsDefs.h b/include/mac/carbon/LgiOsDefs.h --- a/include/mac/carbon/LgiOsDefs.h +++ b/include/mac/carbon/LgiOsDefs.h @@ -1,340 +1,340 @@ // // FILE: LgiOsDefs.h (Mac) // AUTHOR: Matthew Allen // DATE: 1/12/2005 // DESCRIPTION: Lgi Mac OS X defines // // Copyright (C) 2005, Matthew Allen // fret@memecode.com // #ifndef __LGI_MAC_OS_DEFS_H #define __LGI_MAC_OS_DEFS_H #include #include #include #include #include "LgiInc.h" #include "LgiDefs.h" #include "GAutoPtr.h" #include "LgiClass.h" #include "pthread.h" ////////////////////////////////////////////////////////////////// // Includes #include "LgiInc.h" ////////////////////////////////////////////////////////////////// // Typedefs typedef WindowRef OsWindow; typedef ControlRef OsView; typedef pthread_t OsThread; typedef UniChar OsChar; typedef ATSUStyle OsFont; typedef CGContextRef OsPainter; typedef CGContextRef OsBitmap; typedef int OsProcessId; class LgiClass GMessage { public: typedef NativeInt Param; typedef NativeInt Result; int m; Param a; Param b; GMessage() { m = a = b = 0; } GMessage(int M, Param A = 0, Param B = 0) { m = M; a = A; b = B; } }; class OsAppArguments { struct OsAppArgumentsPriv *d; public: int Args; char **Arg; OsAppArguments(int args = 0, char **arg = 0); ~OsAppArguments(); void Set(char *CmdLine); OsAppArguments &operator =(OsAppArguments &a); }; ////////////////////////////////////////////////////////////////// // Defines #define MsgCode(msg) (msg->m) #define MsgA(msg) (msg->a) #define MsgB(msg) (msg->b) extern GMessage CreateMsg(int m, int a = 0, int b = 0); #define _stricmp strcasecmp #define _strnicmp strncasecmp // Posix system #define POSIX 1 // Process typedef int OsProcess; typedef int OsProcessId; // Threads typedef pthread_t OsThreadId; typedef pthread_mutex_t OsSemaphore; #define LgiGetCurrentThread() pthread_self() // Sockets #define ValidSocket(s) ((s)>=0) #define INVALID_SOCKET -1 typedef int OsSocket; // Sleep the current thread LgiFunc void LgiSleep(int i); // Run the message loop to process any pending messages #define LgiYield() GApp::ObjInstance()->Run(false) #define LGI_GViewMagic 0x14412662 #define LGI_FileDropFormat "furl" // typeFileURL #define LGI_LgiDropFormat "lgi " -#define LGI_WideCharset "utf-32" +#define LGI_WideCharset "utf-16" #define LGI_PrintfInt64 "%lli" #define atoi64 atoll #define sprintf_s snprintf #define vsprintf_s vsnprintf #define swprintf_s swprintf #define LGI_IllegalFileNameChars "/" // FIXME: what other characters should be in here? // 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 #define ODS_SELECTED 0x1 #define ODS_DISABLED 0x2 #define ODS_CHECKED 0x4 /// Edge type: Sunken #define SUNKEN 1 /// Edge type: Raised #define RAISED 2 /// Edge type: Chiseled #define CHISEL 3 /// Edge type: Flat #define FLAT 4 /// 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 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)=='\'')) /// 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 "dylib" // Carbon user events #define GViewThisPtr 'gvtp' #define kEventClassUser 'user' #define kEventUser 1 #define kEventParamLgiEvent 'Lgie' #define kEventParamLgiA 'Lgia' #define kEventParamLgiB 'Lgib' /// Base point for system messages. #define M_SYSTEM 0 /// Message that indicates the user is trying to close a top level window. #define M_CLOSE (M_SYSTEM+92) /// Minimum value for application defined message ID's #define M_USER (M_SYSTEM+1000) /// \brief Mouse enter event /// /// a = bool Inside; // is the mouse inside the client area?\n /// b = MAKELONG(x, y); // mouse location #define M_MOUSEENTER (M_USER+100) /// \brief Mouse exit event /// /// a = bool Inside; // is the mouse inside the client area?\n /// b = MAKELONG(x, y); // mouse location #define M_MOUSEEXIT (M_USER+101) /// \brief GView change notification /// /// a = (GView*) Wnd;\n /// b = (int) Flags; // Specific to each GView #define M_CHANGE (M_USER+102) /// \brief Pass a text message up to the UI to descibe whats happening /// /// a = (GView*) Wnd;\n /// b = (char*) Text; // description from window #define M_DESCRIBE (M_USER+103) // return (bool) #define M_WANT_DIALOG_PROC (M_USER+104) #define M_MENU (M_USER+105) #define M_COMMAND (M_USER+106) #define M_DRAG_DROP (M_USER+107) #define M_TRAY_NOTIFY (M_USER+108) #define M_CUT (M_USER+109) #define M_COPY (M_USER+110) #define M_PASTE (M_USER+111) #define M_PULSE (M_USER+112) #define M_DELETE (M_USER+113) /// GThreadWork object completed /// /// MsgA = (GThreadOwner*) Owner; /// MsgB = (GThreadWork*) WorkUnit; #define M_GTHREADWORK_COMPELTE (M_USER+114) /// Standard ID for an "Ok" button. /// \sa LgiMsg #define IDOK 1 /// Standard ID for a "Cancel" button. /// \sa LgiMsg #define IDCANCEL 2 /// Standard ID for a "Yes" button. /// \sa LgiMsg #define IDYES 3 /// Standard ID for a "No" button. /// \sa LgiMsg #define IDNO 4 /// Standard message box with an Ok button. /// \sa LgiMsg #define MB_OK 5 /// Standard message box with Ok and Cancel buttons. /// \sa LgiMsg #define MB_OKCANCEL 6 /// Standard message box with Yes and No buttons. /// \sa LgiMsg #define MB_YESNO 7 /// Standard message box with Yes, No and Cancel buttons. /// \sa LgiMsg #define MB_YESNOCANCEL 8 #define MB_SYSTEMMODAL 0x1000 /// The CTRL key is pressed /// \sa GKey #define LGI_VKEY_CTRL 0x001 /// The ALT key is pressed /// \sa GKey #define LGI_VKEY_ALT 0x002 /// The SHIFT key is pressed /// \sa GKey #define LGI_VKEY_SHIFT 0x004 /// The left mouse button is pressed /// \sa GMouse #define LGI_VMOUSE_LEFT 0x008 /// The middle mouse button is pressed /// \sa GMouse #define LGI_VMOUSE_MIDDLE 0x010 /// The right mouse button is pressed /// \sa GMouse #define LGI_VMOUSE_RIGHT 0x020 /// The ctrl key is pressed /// \sa GMouse #define LGI_VMOUSE_CTRL 0x040 /// The alt key is pressed /// \sa GMouse #define LGI_VMOUSE_ALT 0x080 /// The shift key is pressed /// \sa GMouse #define LGI_VMOUSE_SHIFT 0x100 /// The mouse event is a down click /// \sa GMouse #define LGI_VMOUSE_DOWN 0x200 /// The mouse event is a double click /// \sa GMouse #define LGI_VMOUSE_DOUBLE 0x400 // Keys #define VK_F1 1 #define VK_F2 2 #define VK_ENTER 3 #define VK_F3 4 #define VK_F4 5 #define VK_F5 6 #define VK_F6 7 #define VK_BACKSPACE 8 #define VK_TAB 9 #define VK_F7 11 #define VK_F8 12 #define VK_RETURN 13 #define VK_F9 14 #define VK_F10 15 #define VK_F11 16 #define VK_F12 17 #define VK_SHIFT 18 #define VK_PAGEUP 19 #define VK_PAGEDOWN 20 #define VK_HOME 21 #define VK_END 22 #define VK_INSERT 23 #define VK_DELETE 24 #define VK_APPS 25 #define VK_ESCAPE 27 #define VK_LEFT 28 #define VK_RIGHT 29 #define VK_UP 30 #define VK_DOWN 31 ///////////////////////////////////////////////////////////////////////////////////// // Externs LgiFunc GView *GWindowFromHandle(OsView hWnd); LgiFunc int GetMouseWheelLines(); LgiFunc int WinPointToHeight(int Pt); LgiFunc int WinHeightToPoint(int Ht); LgiFunc int stricmp(const char *a, const char *b); LgiFunc char *strlwr(char *a); LgiFunc char *strupr(char *a); LgiFunc char *p2c(unsigned char *s); LgiFunc void c2p255(Str255 &d, char *s); LgiFunc char *CFStringToUtf8(CFStringRef r); LgiFunc CFStringRef Utf8ToCFString(char *s, int len = -1); /// 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 diff --git a/src/common/Gdc2/Font/GFontSystem.cpp b/src/common/Gdc2/Font/GFontSystem.cpp --- a/src/common/Gdc2/Font/GFontSystem.cpp +++ b/src/common/Gdc2/Font/GFontSystem.cpp @@ -1,767 +1,767 @@ #include #include #include "Lgi.h" #include "GToken.h" #include "GLibrary.h" #include "GLibraryUtils.h" #if defined(LGI_STATIC) #undef HAS_ICONV #endif #if HAS_ICONV // // Get 'iconv.h' from http://www.gnu.org/software/libiconv // Current download at time of writing: // http://ftp.gnu.org/pub/gnu/libiconv/libiconv-1.8.tar.gz // // Then add whatever include path to your project or development // settings. Otherwise you can build without extended charset // support by changing the HAS_ICONV define in Lgi.h to '0' // // Linux should always build with iconv, on windows you may or // may not want to bother depending on what sort of app your // writing. // #include "iconv.h" #if defined(_WINDOWS) typedef const char IconvChar; #else typedef char IconvChar; #endif #endif ///////////////////////////////////////////////////////////////////// // Private growable class for binary compatability class GFontSystemPrivate : public GLibrary { public: bool DefaultGlyphSub; int Used, Refs; bool FontTableLoaded; bool SubSupport; bool CheckedConfig; #ifdef __GTK_H__ Gtk::PangoFontMap *Map; Gtk::PangoContext *Ctx; GFontSystemPrivate() { Map = Gtk::pango_cairo_font_map_get_default(); if (!Map) LgiAssert(!"pango_cairo_font_map_get_default failed.\n"); Ctx = Gtk::pango_cairo_font_map_create_context((Gtk::PangoCairoFontMap*)Map); if (!Ctx) LgiAssert(!"pango_cairo_font_map_create_context failed.\n"); } #endif #if HAS_ICONV #ifdef _WINDOWS DynFunc2(iconv_t, libiconv_open, const char*, tocode, const char*, fromcode); DynFunc5(size_t, libiconv, iconv_t, cd, IconvChar**, inbuf, size_t*, inbytesleft, char**, outbuf, size_t*, outbytesleft); DynFunc1(int, libiconv_close, iconv_t, cd); #elif !defined(MAC) // Use glibc I guess iconv_t libiconv_open(const char *tocode, const char *fromcode) { return ::iconv_open(tocode, fromcode); } size_t libiconv(iconv_t cd, IconvChar** inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft) { return ::iconv(cd, inbuf, inbytesleft, outbuf, outbytesleft); } int libiconv_close(iconv_t cd) { return ::iconv_close(cd); } bool IsLoaded() { return true; } #endif #endif // HAS_ICONV }; ///////////////////////////////////////////////////////////////////// static bool FontSystemDone = false; GFontSystem *GFontSystem::Me = 0; GFontSystem *GFontSystem::Inst() { if (!Me && !FontSystemDone) new GFontSystem; return Me; } GFontSystem::GFontSystem() { Me = this; d = new GFontSystemPrivate; // Glyph sub setup int Os = LgiGetOs(); d->SubSupport = (Os == LGI_OS_LINUX) || (Os == LGI_OS_WIN64) || (Os == LGI_OS_WIN32); // && Rev == 0); // WinXP does it's own glyph substitution d->DefaultGlyphSub = d->SubSupport; d->CheckedConfig = false; d->FontTableLoaded = false; d->Used = 1; // the '0th' font spot isn't used // because Lut[Char] == 0 means no font // available d->Refs = 0; ZeroObj(Lut); // Clear the table to 'no font' ZeroObj(Font); // Initialize the list of fonts to empty } GFontSystem::~GFontSystem() { // Clean up all our resources for (int i=0; iUsed; i++) { DeleteObj(Font[i]); } AllFonts.DeleteArrays(); SubFonts.DeleteArrays(); DeleteObj(d); Me = 0; FontSystemDone = true; } #ifdef __GTK_H__ Gtk::PangoFontMap *GFontSystem::GetFontMap() { return d->Map; } Gtk::PangoContext *GFontSystem::GetContext() { return d->Ctx; } #endif bool GFontSystem::GetGlyphSubSupport() { return d->SubSupport; } bool GFontSystem::GetDefaultGlyphSub() { if (!d->CheckedConfig && LgiApp) { GXmlTag *FontSys = LgiApp->GetConfig("font_system"); if (FontSys) { char *GlyphSub; if ((GlyphSub = FontSys->GetAttr("glyph_sub"))) { d->DefaultGlyphSub = atoi(GlyphSub); } } d->CheckedConfig = true; } return d->DefaultGlyphSub; } void GFontSystem::SetDefaultGlyphSub(bool i) { if (d->SubSupport) d->DefaultGlyphSub = i; } #ifdef _WINDOWS int CALLBACK _EnumFonts(ENUMLOGFONT FAR *lpelf, NEWTEXTMETRIC FAR *lpntm, int FontType, LPARAM lParam) { List *p = (List*) lParam; if (p) { p->Insert(LgiFromNativeCp(lpelf->elfLogFont.lfFaceName)); } return true; } #endif int StringSort(const char *a, const char *b, NativeInt Data) { if (a && b) return stricmp(a, b); return 0; } bool GFontSystem::EnumerateFonts(List &Fonts) { if (!AllFonts.First()) { #if defined _WINDOWS HDC hDC = CreateCompatibleDC(NULL); if (hDC) { EnumFontFamilies( hDC, NULL, (FONTENUMPROC) _EnumFonts, (LPARAM) &AllFonts); DeleteDC(hDC); } #elif defined BEOS int32 numFamilies = count_font_families(); for (int32 i = 0; i < numFamilies; i++) { font_family Temp; uint32 flags; if (get_font_family(i, &Temp, &flags) == B_OK) { AllFonts.Insert(NewStr(Temp)); } } #elif defined __GTK_H__ #elif defined XWIN XObject o; XftFontSet *Set = XftListFonts( o.XDisplay(), 0, 0, XFT_FAMILY, 0); if (Set) { for (int i=0; infont; i++) { char s[256]; if (XftNameUnparse(Set->fonts[i], s, sizeof(s))) { AllFonts.Insert(NewStr(s)); } } XftFontSetDestroy(Set); } #elif defined MAC && !defined COCOA // ATSFontFilter myFontFilter; ATSFontFamilyIterator myFamilyIterator; OSStatus status = ATSFontFamilyIteratorCreate ( kATSFontContextLocal, NULL, NULL, kATSOptionFlagsUnRestrictedScope, &myFamilyIterator); while (status == noErr) { ATSFontFamilyRef myFamilyRef; status = ATSFontFamilyIteratorNext (myFamilyIterator, &myFamilyRef); if (status == noErr) { CFStringRef Name; status = ATSFontFamilyGetName(myFamilyRef, kATSOptionFlagsUnRestrictedScope, &Name); char n[256]; if (CFStringGetCString(Name, n, sizeof(n), kCFStringEncodingUTF8)) { AllFonts.Insert(NewStr(n)); } } else if (status == kATSIterationScopeModified) { status = ATSFontFamilyIteratorReset ( kATSFontContextLocal, NULL, NULL, kATSOptionFlagsUnRestrictedScope, &myFamilyIterator); // Add your code here to take any actions needed because of the // reset operation. } } status = ATSFontFamilyIteratorRelease(&myFamilyIterator); #endif AllFonts.Sort(StringSort, 0); } if (AllFonts.First() && &AllFonts != &Fonts) { for (const char *s=AllFonts.First(); s; s=AllFonts.Next()) { Fonts.Insert(NewStr(s)); } return true; } return false; } bool GFontSystem::HasIconv(bool Quiet) { if (d->IsLoaded()) return true; - bool Status = d->Load("libiconv-2"); + bool Status = d->Load("libiconv-1.9.1."LGI_LIBRARY_EXT); if (!Status && !Quiet) { if (!NeedsCapability("libiconv")) { static bool Warn = true; if (Warn) { Warn = false; LgiAssert(!"Iconv is not available"); } } } return Status; } #if defined MAC && !defined COCOA // This converts a normal charset to an Apple encoding ID static CFStringEncoding CharsetToEncoding(const char *cs) { CFStringRef InputCs = CFStringCreateWithCString(0, cs, kCFStringEncodingUTF8); CFStringEncoding enc = CFStringConvertIANACharSetNameToEncoding(InputCs); CFRelease(InputCs); return enc; } #endif int GFontSystem::IconvConvert(const char *OutCs, GStreamI *Out, const char *InCs, const char *&In, int InLen) { char Buf[2 << 10]; LgiAssert(InLen > 0); if (!Out || !In) return 0; #if defined(MAC) #if !defined COCOA CFStringEncoding InEnc = CharsetToEncoding(InCs); CFStringEncoding OutEnc = CharsetToEncoding(OutCs); if (InEnc != kCFStringEncodingInvalidId && OutEnc != kCFStringEncodingInvalidId) { CFStringRef r = CFStringCreateWithBytes(0, (const UInt8 *)In, InLen, InEnc, false); if (r) { CFRange g = { 0, CFStringGetLength(r) }; CFIndex used = 0; CFIndex ret; while ((ret = CFStringGetBytes(r, g, OutEnc, '?', false, (UInt8*)Buf, sizeof(Buf), &used)) > 0 && g.length > 0) { Out->Write(Buf, used); g.location += ret; g.length -= ret; } CFRelease(r); } else return 0; } else return 0; #endif #elif HAS_ICONV if (!HasIconv(false)) { return 0; } iconv_t Conv; if ((NativeInt)(Conv = d->libiconv_open(OutCs, InCs)) >= 0) { char *i = (char*)In; LgiAssert((NativeInt)Conv != 0xffffffff); while (InLen) { char *o = (char*)Buf; int OutLen = sizeof(Buf); int OldInLen = InLen; int s = d->libiconv(Conv, (IconvChar**)&i, (size_t*)&InLen, &o, (size_t*)&OutLen); Out->Write((uchar*)Buf, sizeof(Buf) - OutLen); if (OldInLen == InLen) break; } d->libiconv_close(Conv); } else { LgiTrace("Iconv won't load.\n"); return 0; } #endif return 1; } int GFontSystem::IconvConvert(const char *OutCs, char *Out, int OutLen, const char *InCs, const char *&In, int InLen) { int Status = 0; if (!Out || !In || !HasIconv(false)) return 0; #if defined(MAC) #if !defined COCOA CFStringEncoding InEnc = CharsetToEncoding(InCs); CFStringEncoding OutEnc = CharsetToEncoding(OutCs); if (InEnc != kCFStringEncodingInvalidId && OutEnc != kCFStringEncodingInvalidId) { CFStringRef r = CFStringCreateWithBytes(0, (const UInt8 *)In, InLen, InEnc, false); if (r) { CFRange g = { 0, CFStringGetLength(r) }; CFIndex ret = CFStringGetBytes(r, g, OutEnc, '?', false, (UInt8*)Out, OutLen, 0); CFRelease(r); return ret; } } #endif #elif HAS_ICONV // Set locale yet? static bool sl = false; if (!sl) { sl = true; setlocale(LC_ALL, ""); } // Iconv conversion // const char *InCs = InInfo->GetIconvName(); // const char *OutCs = OutInfo->GetIconvName(); iconv_t Conv; if ((Conv = d->libiconv_open(OutCs, InCs)) >= 0) { int InLength = InLen; char *o = Out; char *i = (char*)In; // Convert char *Start = o; int s = d->libiconv(Conv, (IconvChar**)&i, (size_t*)&InLen, &o, (size_t*)&OutLen); d->libiconv_close(Conv); In = (const char*)i; Status = (NativeInt)o-(NativeInt)Out; } else { LgiTrace("Iconv not present/won't load.\n"); } #endif return Status; } GFont *GFontSystem::GetBestFont(char *Str) { GFont *MatchingFont = 0; if (d->SubSupport) { char16 *s = LgiNewUtf8To16(Str); if (s) { // Make list of possible fonts List Possibles; char16 *i; for (i = s; *i; i++) { GFont *Font = GetGlyph(*i, SysFont); if (Font) { bool Has = false; for (GFont *h=Possibles.First(); h; h=Possibles.Next()) { if (h == Font) { Has = true; break; } } if (!Has) { Possibles.Insert(Font); } } } // Choose best match amongst possibles int MatchingChars = 0; for (GFont *h=Possibles.First(); h; h=Possibles.Next()) { int Chars = 0; for (i = s; *i; i++) { if (h->GetGlyphMap() && _HasUnicodeGlyph(h->GetGlyphMap(), *i)) { Chars++; } } if (!MatchingFont || Chars > MatchingChars) { MatchingFont = h; } } DeleteArray(s); } } return MatchingFont; } GFont *GFontSystem::GetGlyph(int u, GFont *UserFont) { if (u > MAX_UNICODE || !UserFont) { return 0; } // Check app font if (!d->SubSupport || (UserFont->GetGlyphMap() && _HasUnicodeGlyph(UserFont->GetGlyphMap(), u))) { return UserFont; } // Check LUT GFont *Has = 0; if (Lut[u]) { Has = Font[Lut[u]]; LgiAssert(Has); if (!Has) { LgiTrace("%s:%i - Font table missing pointer. u=%i Lut[u]=%i\n", __FILE__, __LINE__, u, Lut[u]); Has = UserFont; } } else if (d->Used < 255 && !d->FontTableLoaded) { // Add fonts to Lut... if (!SubFonts.First()) { #if LGI_EXCEPTIONS try { #endif if (GFontSystem::Inst()->EnumerateFonts(SubFonts)) { // Reorder font list to prefer certain known as good fonts or // avoid certain bad fonts. List Ascend, Descend; if (LgiGetOs() == LGI_OS_WIN32 || LgiGetOs() == LGI_OS_WIN64) { Ascend.Insert("Microsoft Sans Serif"); Ascend.Insert("Arial Unicode MS"); Ascend.Insert("Verdana"); Ascend.Insert("Tahoma"); Descend.Insert("Bookworm"); Descend.Insert("Christmas Tree"); Descend.Insert("MingLiU"); } if (LgiGetOs() == LGI_OS_LINUX) { // Windows fonts are much better than anything Linux // has to offer. Ascend.Insert("Verdana"); Ascend.Insert("Tahoma"); Ascend.Insert("Arial Unicode MS"); // Most linux fonts suck... and the rest aren't much // good either Descend.Insert("AR PL *"); Descend.Insert("Baekmuk *"); Descend.Insert("console8*"); Descend.Insert("Courier*"); Descend.Insert("Fangsong*"); Descend.Insert("Kochi*"); Descend.Insert("MiscFixed"); Descend.Insert("Serto*"); Descend.Insert("Standard Symbols*"); Descend.Insert("Nimbus*"); } // Prefer these fonts... List Temp; const char *p; for (p=Ascend.First(); p; p=Ascend.Next()) { for (const char *f=SubFonts.First(); f; ) { if (MatchStr(p, f)) { SubFonts.Delete(f); Temp.Insert(f); f = SubFonts.Current(); } else { f = SubFonts.Next(); } } } for (p=Temp.First(); p; p=Temp.Next()) { SubFonts.Insert(p, 0); } // Avoid these fonts... Temp.Empty(); for (p=Descend.First(); p; p=Descend.Next()) { for (const char *f=SubFonts.First(); f; ) { if (MatchStr(p, f)) { SubFonts.Delete(f); Temp.Insert(f); f = SubFonts.Current(); } else { f = SubFonts.Next(); } } } for (p=Temp.First(); p; p=Temp.Next()) { SubFonts.Insert(p); } // Delete fonts prefixed with '@' to the end, as they are for // vertical rendering... and aren't suitable for what LGI uses // fonts for. for (const char *f=SubFonts.First(); f; ) { if (*f == '@') { SubFonts.Delete(f); DeleteObj((char*&)f); f = SubFonts.Current(); } else { f = SubFonts.Next(); } } } #if LGI_EXCEPTIONS } catch (...) { LgiTrace("%s:%i - Font enumeration crashed.\n", __FILE__, __LINE__); } #endif } #if LGI_EXCEPTIONS try { #endif const char *s; while ( (d->Used < CountOf(Font) - 1) && (s = SubFonts.First())) { SubFonts.Delete(s); GFont *n = new GFont; if (n) { int LutIndex = d->Used; *n = *UserFont; n->Face(s); DeleteArray((char*&)s); n->Create(); Font[d->Used++] = n; if (n->GetGlyphMap()) { // Insert all the characters of this font into the LUT // so that we can map from a character back to the font for (int k=0; k<=MAX_UNICODE; k++) { if (!Lut[k] && _HasUnicodeGlyph(n->GetGlyphMap(), k)) { Lut[k] = LutIndex; } } if (_HasUnicodeGlyph(n->GetGlyphMap(), u)) { Has = n; LgiAssert(Has); break; } } } else { DeleteArray((char*&)s); } } #if LGI_EXCEPTIONS } catch (...) { LgiTrace("%s:%i - Glyph search crashed.\n", __FILE__, __LINE__); } #endif if (!SubFonts.First()) { d->FontTableLoaded = true; } } return Has; } diff --git a/src/mac/carbon/Lgi/GMenu.cpp b/src/mac/carbon/Lgi/GMenu.cpp --- a/src/mac/carbon/Lgi/GMenu.cpp +++ b/src/mac/carbon/Lgi/GMenu.cpp @@ -1,1362 +1,1410 @@ /*hdr ** FILE: GuiMenu.cpp ** AUTHOR: Matthew Allen ** DATE: 18/7/98 ** DESCRIPTION: Gui menu system ** ** Copyright (C) 1998, Matthew Allen ** fret@memecode.com */ #include #include #include #include "Lgi.h" #include "GToken.h" #include "GUtf8.h" #include "GDisplayString.h" static int NextId = 0; #define DEBUG_INFO 0 /////////////////////////////////////////////////////////////////////////////////////////////// GSubMenu::GSubMenu(const char *name, bool Popup) { Menu = 0; Parent = 0; Info = 0; GBase::Name(name); OSStatus e = CreateNewMenu( NextId++, // MenuId 0, // MenuAttributes &Info); if (e) printf("%s:%i - can't create menu (e=%i)\n", __FILE__, __LINE__, (int)e); #if DEBUG_INFO else printf("CreateNewMenu()=%p\n", Info); #endif } GSubMenu::~GSubMenu() { - Items.DeleteObjects(); + while (Items.Length()) + { + GMenuItem *i = Items.First(); + if (i->Parent != this) + { + i->Parent = NULL; + Items.Delete(i); + } + delete i; + } + if (Info) { DisposeMenu(Info); } } void GSubMenu::OnAttach(bool Attach) { for (GMenuItem *i = Items.First(); i; i = Items.Next()) { i->OnAttach(Attach); } if (Attach && this != Menu && Parent && Parent->Parent) { #if 0 GSubMenu *k = Parent->Parent; if (Parent->Info == 0) { Parent->Info = k->Items.IndexOf(Parent) + 1; char *Str = Parent->Name(); CFStringRef s = CFStringCreateWithBytes(kCFAllocatorDefault, (UInt8*)Str, strlen(Str), kCFStringEncodingUTF8, false); OSStatus e = InsertMenuItemTextWithCFString(Parent->Parent->Info, s, Parent->Info - 1, 0, 0); CFRelease(s); if (e) printf("%s:%i - Error: AppendMenuItemTextWithCFString(%p)=%i\n", __FILE__, __LINE__, Parent->Parent->Info, Parent->Info); else { e = SetMenuItemHierarchicalMenu(Parent->Parent->Info, Parent->Info, Info); if (e) printf("%s:%i - Error: SetMenuItemHierarchicalMenu(%p, %i, %p) = %i\n", __FILE__, __LINE__, Parent->Parent->Info, Parent->Info, Info, e); } } #endif } } int GSubMenu::Length() { return Items.Length(); } GMenuItem *GSubMenu::ItemAt(int Id) { return Items.ItemAt(Id); } GMenuItem *GSubMenu::AppendItem(const char *Str, int Id, bool Enabled, int Where, const char *Shortcut) { GMenuItem *i = new GMenuItem(Menu, this, Str, Where, Shortcut); if (i) { if (Info) { Items.Insert(i, Where); Str = i->Name(); - CFStringRef s = CFStringCreateWithBytes(kCFAllocatorDefault, (UInt8*)Str, strlen(Str), kCFStringEncodingUTF8, false); - OSStatus e = AppendMenuItemTextWithCFString(Info, s, 0, 0, &i->Info); - if (e) printf("%s:%i - AppendMenuItemTextWithCFString failed (e=%i)\n", __FILE__, __LINE__, (int)e); - #if DEBUG_INFO - else printf("AppendMenuItemTextWithCFString(%p, %s)=%p\n", Info, Str, i->Info); - #endif - CFRelease(s); + CFStringRef s = CFStringCreateWithBytes(kCFAllocatorDefault, + (UInt8*)Str, strlen(Str), + kCFStringEncodingUTF8, + false); + if (!s) + s = CFStringCreateWithBytes(kCFAllocatorDefault, (UInt8*)"#error", 6, kCFStringEncodingUTF8, false); + if (s) + { + OSStatus e = AppendMenuItemTextWithCFString(Info, s, 0, 0, &i->Info); + if (e) printf("%s:%i - AppendMenuItemTextWithCFString failed (e=%i)\n", __FILE__, __LINE__, (int)e); + #if DEBUG_INFO + else printf("AppendMenuItemTextWithCFString(%p, %s)=%p\n", Info, Str, i->Info); + #endif + CFRelease(s); + } i->Id(Id); i->Enabled(Enabled); i->ScanForAccel(); return i; } else { printf("%s:%i - No menu to attach item to.\n", __FILE__, __LINE__); DeleteObj(i); } } return 0; } GMenuItem *GSubMenu::AppendSeparator(int Where) { GMenuItem *i = new GMenuItem; if (i) { i->Parent = this; i->Menu = Menu; i->Id(-2); Items.Insert(i, Where); if (Info) { OSStatus e = AppendMenuItemTextWithCFString(Info, 0, kMenuItemAttrSeparator, 0, &i->Info); if (e) printf("%s:%i - AppendMenuItemTextWithCFString failed (e=%i)\n", __FILE__, __LINE__, (int)e); #if DEBUG_INFO else printf("AppendMenuItemTextWithCFString(%p, ---)=%p\n", Info, i->Info); #endif } else { printf("%s:%i - No menu to attach item to.\n", __FILE__, __LINE__); } return i; } return 0; } GSubMenu *GSubMenu::AppendSub(const char *Str, int Where) { GMenuItem *i = new GMenuItem; if (i && Str) { i->Name(Str); i->Parent = this; i->Menu = Menu; i->Id(-1); Items.Insert(i, Where); if (Info) { i->Child = new GSubMenu(Str); if (i->Child) { i->Child->Parent = i; i->Child->Menu = Menu; i->Child->Window = Window; CFStringRef s; OSStatus e; Str = i->Name(); s = CFStringCreateWithBytes(kCFAllocatorDefault, (UInt8*)Str, strlen(Str), kCFStringEncodingUTF8, false); - e = SetMenuTitleWithCFString(i->Child->Info, s); - if (e) printf("%s:%i - SetMenuTitleWithCFString failed (e=%i)\n", __FILE__, __LINE__, (int)e); - #if DEBUG_INFO - else printf("SetMenuTitleWithCFString(%p, %s)\n", i->Child->Info, Str); - #endif - CFRelease(s); + if (s) + { + e = SetMenuTitleWithCFString(i->Child->Info, s); + if (e) printf("%s:%i - SetMenuTitleWithCFString failed (e=%i)\n", __FILE__, __LINE__, (int)e); + #if DEBUG_INFO + else printf("SetMenuTitleWithCFString(%p, %s)\n", i->Child->Info, Str); + #endif + CFRelease(s); + } i->Info = Items.IndexOf(i) + 1; s = CFStringCreateWithBytes(kCFAllocatorDefault, (UInt8*)Str, strlen(Str), kCFStringEncodingUTF8, false); - e = InsertMenuItemTextWithCFString(Info, s, i->Info - 1, 0, 0); - CFRelease(s); - if (e) printf("%s:%i - Error: AppendMenuItemTextWithCFString(%p)=%i\n", __FILE__, __LINE__, Parent->Parent->Info, Parent->Info); + if (s) + { + e = InsertMenuItemTextWithCFString(Info, s, i->Info - 1, 0, 0); + CFRelease(s); + } + if (e) + printf("%s:%i - Error: AppendMenuItemTextWithCFString(%p)=%i\n", + _FL, + Parent && Parent->Parent ? Parent->Parent->Info : NULL, + Parent ? Parent->Info : NULL); else { e = SetMenuItemHierarchicalMenu(Info, i->Info, i->Child->Info); - if (e) printf("%s:%i - Error: SetMenuItemHierarchicalMenu(%p, %i, %p) = %i\n", __FILE__, __LINE__, Parent->Parent->Info, Parent->Info, Info, (int)e); + if (e) + printf("%s:%i - Error: SetMenuItemHierarchicalMenu(%p, %i, %p) = %i\n", + _FL, + Parent && Parent->Parent ? Parent->Parent->Info : NULL, + Parent ? Parent->Info : NULL, + Info, + (int)e); } } } else { printf("%s:%i - No menu to attach item to.\n", __FILE__, __LINE__); } return i->Child; } return 0; } void GSubMenu::Empty() { while (Items.First()) { RemoveItem(Items.First()); } } bool GSubMenu::RemoveItem(int i) { GMenuItem *Item = Items[i]; if (Item) { return Item->Remove(); } return false; } bool GSubMenu::RemoveItem(GMenuItem *Item) { if (Item && Items.HasItem(Item)) { return Item->Remove(); } return false; } bool GSubMenu::OnKey(GKey &k) { return false; } #if 0 bool IsOverMenu(XEvent *e) { if (e->xany.type == ButtonPress OR e->xany.type == ButtonRelease) { QWidget *q = QWidget::Find(e->xany.window); if (q) { QPopup *m = 0; for (; q; q = q->parentWidget()) { if (m = dynamic_cast(q)) { break; } } if (m) { GRect gr = m->geometry(); return gr.Overlap ( e->xbutton.x_root, e->xbutton.y_root ); } } } return false; } #endif MenuCommand *ReturnFloatCommand = 0; int GSubMenu::Float(GView *From, int x, int y, bool Left) { static int Depth = 0; MenuCommand Cmd = 0; if (From && Depth == 0) { Depth++; UInt32 UserSelectionType; SInt16 MenuID; MenuItemIndex MenuItem; Point Pt = { y, x }; From->Capture(false); OnAttach(true); ReturnFloatCommand = &Cmd; OSStatus e = ContextualMenuSelect( Info, Pt, false, kCMHelpItemRemoveHelp, 0, 0, // AEDesc *inSelection, &UserSelectionType, &MenuID, &MenuItem); ReturnFloatCommand = 0; if (e == userCanceledErr) { // Success } else { printf("%s:%i - ContextualMenuSelect failed (e=%i)\n", __FILE__, __LINE__, (int)e); Cmd = 0; } Depth--; } else printf("%s:%i - Recursive limit.\n", __FILE__, __LINE__); return Cmd; } GSubMenu *GSubMenu::FindSubMenu(int Id) { for (GMenuItem *i = Items.First(); i; i = Items.Next()) { GSubMenu *Sub = i->Sub(); if (i->Id() == Id) { return Sub; } else if (Sub) { GSubMenu *m = Sub->FindSubMenu(Id); if (m) { return m; } } } return 0; } GMenuItem *GSubMenu::FindItem(int Id) { for (GMenuItem *i = Items.First(); i; i = Items.Next()) { GSubMenu *Sub = i->Sub(); if (i->Id() == Id) { return i; } else if (Sub) { i = Sub->FindItem(Id); if (i) { return i; } } } return 0; } /////////////////////////////////////////////////////////////////////////////////////////////// class GMenuItemPrivate { public: GAutoString Shortcut; }; GMenuItem::GMenuItem() { d = new GMenuItemPrivate(); Menu = NULL; Info = NULL; Child = NULL; Parent = NULL; _Icon = -1; _Id = 0; - _Check = false; + _Flags = 0; } GMenuItem::GMenuItem(GMenu *m, GSubMenu *p, const char *Str, int Pos, const char *Shortcut) { d = new GMenuItemPrivate(); GBase::Name(Str); Menu = m; Parent = p; Info = NULL; Child = NULL; _Icon = -1; _Id = 0; - _Check = false; + _Flags = 0; d->Shortcut.Reset(NewStr(Shortcut)); Name(Str); } GMenuItem::~GMenuItem() { + if (Parent) + { + Parent->Items.Delete(this); + Parent = NULL; + } DeleteObj(Child); DeleteObj(d); } void GMenuItem::OnAttach(bool Attach) { if (Attach) { if (_Icon >= 0) { Icon(_Icon); } if (Sub()) { Sub()->OnAttach(Attach); } } } // the following 3 functions paint the menus according the to // windows standard. but also allow for correct drawing of menuitem // icons. some implementations of windows force the program back // to the 8-bit palette when specifying the icon graphic, thus removing // control over the colours displayed. these functions remove that // limitation and also provide the application the ability to override // the default painting behaviour if desired. void GMenuItem::_Measure(GdcPt2 &Size) { GFont *Font = Menu && Menu->GetFont() ? Menu->GetFont() : SysFont; bool BaseMenu = Parent == Menu; // true if attached to a windows menu // else is a submenu int Ht = Font->GetHeight(); // int IconX = BaseMenu ? ((24-Ht)/2)-Ht : 20; int IconX = BaseMenu ? 2 : 16; if (Separator()) { Size.x = 8; Size.y = 8; } else { // remove '&' chars for string measurement char Str[256]; char *n = Name(), *i = n, *o = Str; while (i && *i) { if (*i == '&') { if (i[1] == '&') { *o++ = *i++; } } else { *o++ = *i; } i++; } *o++ = 0; // check for accelerators char *Tab = strchr(Str, '\t'); if (Tab) { // string with accel int Mx, Tx; GDisplayString ds(Font, Str, (int)Tab-(int)Str); Mx = ds.X(); GDisplayString ds2(Font, Tab + 1); Tx = ds2.X(); Size.x = IconX + 32 + Mx + Tx; } else { // normal string GDisplayString ds(Font, Str); Size.x = IconX + ds.X() + 4; } if (!BaseMenu) { // leave room for child pointer Size.x += Child ? 8 : 0; } Size.y = max(IconX, Ht+2); } } #define Time(a, b) ((double)(b - a) / 1000) void GMenuItem::_PaintText(GSurface *pDC, int x, int y, int Width) { char *n = Name(); if (n) { GFont *Font = Menu && Menu->GetFont() ? Menu->GetFont() : SysFont; bool Underline = false; char *e = 0; for (char *s=n; s && *s; s = *e ? e : 0) { switch (*s) { case '&': { if (s[1] == '&') { e = s + 2; GDisplayString d(Font, "&"); d.Draw(pDC, x, y, 0); x += d.X(); } else { Underline = true; e = s + 1; } break; } case '\t': { GDisplayString ds(Font, e + 1); x = Width - ds.X() - 8; e = s + 1; break; } default: { if (Underline) { LgiNextUtf8(e); } else { for (e = s; *e; e++) { if (*e == '\t') break; if (*e == '&') break; } } int Len = e - s; if (Len > 0) { // paint text till that point GDisplayString d(Font, s, Len); d.Draw(pDC, x, y, 0); if (Underline) { GDisplayString ds(Font, s, 1); int UnderX = ds.X(); int Ascent = (int)ceil(Font->Ascent()); pDC->Colour(Font->Fore()); pDC->Line(x, y+Ascent+1, x+max(UnderX-2, 1), y+Ascent+1); Underline = false; } x += d.X(); } break; } } } } } void GMenuItem::_Paint(GSurface *pDC, int Flags) { bool BaseMenu = Parent == Menu; int IconX = BaseMenu ? 5 : 20; bool Selected = TestFlag(Flags, ODS_SELECTED); bool Disabled = TestFlag(Flags, ODS_DISABLED); bool Checked = TestFlag(Flags, ODS_CHECKED); #if defined(WIN32) || defined(MAC) GRect r(0, 0, pDC->X()-1, pDC->Y()-1); #else GRect r = Info->GetClient(); #endif if (Separator()) { // Paint a separator int Cy = r.Y() / 2; pDC->Colour(LC_MED, 24); pDC->Rectangle(); pDC->Colour(LC_LOW, 24); pDC->Line(0, Cy-1, pDC->X()-1, Cy-1); pDC->Colour(LC_LIGHT, 24); pDC->Line(0, Cy, pDC->X()-1, Cy); } else { // Paint a text menu item COLOUR Fore = LC_TEXT; // Selected ? LC_SEL_TEXT : LC_TEXT; COLOUR Back = Selected ? LC_HIGH : LC_MED; // Selected ? LC_SELECTION : LC_MED; int x = IconX; int y = 1; // For a submenu pDC->Colour(Back, 24); pDC->Rectangle(); // Draw the text on top GFont *Font = Menu && Menu->GetFont() ? Menu->GetFont() : SysFont; Font->Transparent(true); if (Disabled) { // Disabled text if (!Selected) { Font->Colour(LC_LIGHT, 0); _PaintText(pDC, x+1, y+1, r.X()); } // Else selected... don't draw the hilight // "greyed" text... Font->Colour(LC_LOW, 0); _PaintText(pDC, x, y, r.X()); } else { // Normal coloured text Font->Colour(Fore, 0); _PaintText(pDC, x, y, r.X()); } GImageList *ImgLst = (Menu && Menu->GetImageList()) ? Menu->GetImageList() : Parent ? Parent->GetImageList() : 0; // Draw icon/check mark if (Checked && IconX > 0) { // it's a check! int x = 4; int y = 6; pDC->Colour(Fore, 24); pDC->Line(x, y, x+2, y+2); pDC->Line(x+2, y+2, x+6, y-2); y++; pDC->Line(x, y, x+2, y+2); pDC->Line(x+2, y+2, x+6, y-2); y++; pDC->Line(x, y, x+2, y+2); pDC->Line(x+2, y+2, x+6, y-2); } else if (ImgLst && _Icon >= 0) { // it's an icon! GColour Bk(LC_MED, 24); ImgLst->Draw(pDC, 0, 0, _Icon, Bk); } // Sub menu arrow if (Child && !dynamic_cast(Parent)) { pDC->Colour(LC_TEXT, 24); int x = r.x2 - 4; int y = r.y1 + (r.Y()/2); for (int i=0; i<4; i++) { pDC->Line(x, y-i, x, y+i); x--; } } } } bool GMenuItem::ScanForAccel() { if (!d->Shortcut) return false; GToken Keys(d->Shortcut, "+-"); if (Keys.Length() <= 0) return false; int Flags = 0; uchar Key = 0; for (int i=0; i= 1 && idx <= 12) { Key = F[idx-1]; } } else if (isalpha(k[0])) { Key = toupper(k[0]); } else if (isdigit(k[0])) { Key = k[0]; } else if (strchr(",", k[0])) { Key = k[0]; } else { printf("%s:%i - Unhandled shortcut token '%s'\n", _FL, k); } } if (Key == ' ') { Menu->Accel.Insert( new GAccelerator(Flags, Key, Id()) ); } else if (Key) { OSStatus e; int ModMask = (TestFlag(Flags, LGI_EF_CTRL) ? 0 : kMenuNoCommandModifier) | (TestFlag(Flags, LGI_EF_ALT) ? kMenuOptionModifier : 0) | (TestFlag(Flags, LGI_EF_SHIFT) ? kMenuShiftModifier : 0); e = SetMenuItemModifiers(Parent->Info, Info, ModMask); if (e) printf("%s:%i - SetMenuItemModifiers() failed with %i\n", __FILE__, __LINE__, (int)e); switch (Key) { #define Map(k, g) \ case k: \ SetMenuItemKeyGlyph(Parent->Info, Info, g); \ break Map(VK_F1, kMenuF1Glyph); Map(VK_F2, kMenuF2Glyph); Map(VK_F3, kMenuF3Glyph); Map(VK_F4, kMenuF4Glyph); Map(VK_F5, kMenuF5Glyph); Map(VK_F6, kMenuF6Glyph); Map(VK_F7, kMenuF7Glyph); Map(VK_F8, kMenuF8Glyph); Map(VK_F9, kMenuF9Glyph); Map(VK_F10, kMenuF10Glyph); Map(VK_F11, kMenuF11Glyph); Map(VK_F12, kMenuF12Glyph); Map(' ', kMenuSpaceGlyph); Map(VK_DELETE, kMenuDeleteRightGlyph); Map(VK_BACKSPACE, kMenuDeleteLeftGlyph); Map(VK_UP, kMenuUpArrowGlyph); Map(VK_DOWN, kMenuDownArrowGlyph); Map(VK_LEFT, kMenuLeftArrowGlyph); Map(VK_RIGHT, kMenuRightArrowGlyph); default: { e = SetMenuItemCommandKey( Parent->Info, Info, false, Key); if (e) printf("%s:%i - SetMenuItemCommandKey(%i/%c) failed with %i\n", - __FILE__, __LINE__, Key, Key, (int)e); + _FL, Key, Key, (int)e); break; } } } return true; } GSubMenu *GMenuItem::GetParent() { return Parent; } bool GMenuItem::Remove() { if (Parent) { if (Parent->Info && Info) { int Index = Parent->Items.IndexOf(this); LgiAssert(Index + 1 == Info); DeleteMenuItem(Parent->Info, Info); Parent->Items.Delete(this); // Re-index all the following items GMenuItem *mi; for (int i = Index; (mi = Parent->Items.ItemAt(i)); i++) { mi->Info = i + 1; } Info = NULL; } else { Parent->Items.Delete(this); } return true; } return false; } void GMenuItem::Id(int i) { _Id = i; if (Parent && Parent->Info && Info) { SetMenuItemCommandID(Parent->Info, Info, _Id); } } void GMenuItem::Separator(bool s) { if (s) { _Id = -2; } if (Parent) { if (s) ChangeMenuItemAttributes(Parent->Info, Info, kMenuItemAttrSeparator, 0); else ChangeMenuItemAttributes(Parent->Info, Info, 0, kMenuItemAttrSeparator); } } void GMenuItem::Checked(bool c) { - _Check = c; + if (c) + SetFlag(_Flags, ODS_CHECKED); + else + ClearFlag(_Flags, ODS_CHECKED); if (Parent) { - CheckMenuItem(Parent->Info, Info, _Check); + CheckMenuItem(Parent->Info, Info, c); } } bool GMenuItem::Name(const char *n) { char *Tmp = NewStr(n); if (Tmp) { char *in = Tmp, *out = Tmp; while (*in) { if (*in != '&') *out++ = *in; in++; } *out++ = 0; } bool Status = GBase::Name(Tmp); if (Status && Parent) { CFStringRef s = CFStringCreateWithBytes(kCFAllocatorDefault, (UInt8*)Tmp, strlen(Tmp), kCFStringEncodingUTF8, false); - SetMenuItemTextWithCFString(Parent->Info, Info, s); - // if (e) printf("%s:%i - SetMenuItemTextWithCFString(%p, %s) failed with %i.\n", _FL, Parent->Info, Tmp, e); + if (!s) + s = CFStringCreateWithBytes(kCFAllocatorDefault, (UInt8*)"#error", 6, kCFStringEncodingUTF8, false); - CFRelease(s); + if (s) + { + SetMenuItemTextWithCFString(Parent->Info, Info, s); + // if (e) printf("%s:%i - SetMenuItemTextWithCFString(%p, %s) failed with %i.\n", _FL, Parent->Info, Tmp, e); + + CFRelease(s); + } } DeleteArray(Tmp); return Status; } void GMenuItem::Enabled(bool e) { if (Parent) { if (e) { EnableMenuItem(Parent->Info, Info); } else { DisableMenuItem(Parent->Info, Info); } } } void GMenuItem::Focus(bool f) { } void GMenuItem::Sub(GSubMenu *s) { Child = s; } void releaseData(void *info, const void *data, size_t size) { uchar *i = (uchar*)info; DeleteArray(i); } void GMenuItem::Icon(int i) { _Icon = i; if (Parent && Parent->Info && Info) { GImageList *Lst = Menu ? Menu->GetImageList() : Parent->GetImageList(); if (!Lst) return; #if 0 int Bpp = Lst->GetBits() / 8; int Off = _Icon * Lst->TileX() * Bpp; int Line = (*Lst)[1] - (*Lst)[0]; uchar *Base = (*Lst)[0]; if (!Base) return; int TempSize = Lst->TileX() * Lst->TileY() * Bpp; uchar *Temp = new uchar[TempSize]; if (!Temp) return; uchar *d = Temp; for (int y=0; yTileY(); y++) { uchar *s = Base + Off + (Line * (Lst->TileY() - y - 1)); uchar *e = s + (Lst->TileX() * Bpp); while (s < e) { if (memcmp(Base, s, Bpp) == 0) memset(d, 0, Bpp); else memcpy(d, s, Bpp); d += Bpp; s += Bpp; } } CGDataProviderRef Provider = CGDataProviderCreateWithData(0, Temp, TempSize, releaseData); if (Provider) { // CGColorSpaceRef Cs = CGColorSpaceCreateWithName(kCGColorSpaceUserRGB); CGColorSpaceRef Cs = CGColorSpaceCreateDeviceRGB(); CGImageRef Ico = CGImageCreate( Lst->TileX(), Lst->TileX(), 8, Lst->GetBits(), Lst->TileX() * Bpp, Cs, kCGImageAlphaPremultipliedLast, Provider, NULL, false, kCGRenderingIntentDefault); if (Ico) { OSErr e = SetMenuItemIconHandle(Parent->Info, Info, kMenuCGImageRefType, (char**)Ico); if (e) printf("%s:%i - SetMenuItemIconHandle failed with %i\n", __FILE__, __LINE__, e); } else printf("%s:%i - CGImageCreate failed.\n", __FILE__, __LINE__); // CGColorSpaceRelease(Cs); // CGDataProviderRelease(Provider); } #endif } else { printf("Can't set icon.\n"); } } void GMenuItem::Visible(bool i) { } int GMenuItem::Id() { return _Id; } char *GMenuItem::Name() { return GBase::Name(); } bool GMenuItem::Separator() { return _Id == -2; } bool GMenuItem::Checked() { - return _Check; + return TestFlag(_Flags, ODS_CHECKED); } bool GMenuItem::Enabled() { if (Parent) { return IsMenuItemEnabled(Parent->Info, Info); } return true; } bool GMenuItem::Visible() { return true; } bool GMenuItem::Focus() { return 0; } GSubMenu *GMenuItem::Sub() { return Child; } int GMenuItem::Icon() { return _Icon; } /////////////////////////////////////////////////////////////////////////////////////////////// GFont *GMenu::_Font = 0; GMenu::GMenu() : GSubMenu("", false) { Menu = this; } GMenu::~GMenu() { Accel.DeleteObjects(); } GFont *GMenu::GetFont() { if (!_Font) { GFontType Type; if (Type.GetSystemFont("Menu")) { _Font = Type.Create(); if (_Font) { #ifndef MAC _Font->CodePage(SysFont->CodePage()); #endif } else { printf("GMenu::GetFont Couldn't create menu font.\n"); } } else { printf("GMenu::GetFont Couldn't get menu typeface.\n"); } if (!_Font) { _Font = new GFont; if (_Font) { *_Font = *SysFont; } } } return _Font ? _Font : SysFont; } bool GMenu::Attach(GViewI *p) { bool Status = false; GWindow *w = dynamic_cast(p); if (w) { Window = p; if (Info) { OnAttach(true); Status = true; } else { printf("%s:%i - No menu\n", __FILE__, __LINE__); } } return Status; } bool GMenu::Detach() { bool Status = false; return Status; } bool GMenu::OnKey(GView *v, GKey &k) { if (k.Down()) { for (GAccelerator *a = Accel.First(); a; a = Accel.Next()) { if (a->Match(k)) { Window->OnCommand(a->GetId(), 0, 0); return true; } } if (k.Alt() && !dynamic_cast(v) && !dynamic_cast(v)) { bool Hide = false; for (GMenuItem *s=Items.First(); s; s=Items.Next()) { if (!s->Separator()) { if (Hide) { // s->Info->HideSub(); } else { char *n = s->Name(); if (ValidStr(n)) { char *Amp = strchr(n, '&'); while (Amp && Amp[1] == '&') { Amp = strchr(Amp + 2, '&'); } if (Amp) { char Accel = tolower(Amp[1]); char Press = tolower(k.c16); if (Accel == Press) { Hide = true; } } } if (Hide) { // s->Info->ShowSub(); } else { // s->Info->HideSub(); } } } } if (Hide) { return true; } } } return false; } //////////////////////////////////////////////////////////////////////////// GAccelerator::GAccelerator(int flags, int key, int id) { Flags = flags; Key = key; Id = id; } bool GAccelerator::Match(GKey &k) { int Press = (uint) k.c16; - #if 1 + #if 0 printf("GAccelerator::Match %i(%c)%s%s%s = %i(%c)%s%s%s\n", Press, Press>=' '?Press:'.', k.Ctrl()?" ctrl":"", k.Alt()?" alt":"", k.Shift()?" shift":"", Key, Key>=' '?Key:'.', TestFlag(Flags, LGI_EF_CTRL)?" ctrl":"", TestFlag(Flags, LGI_EF_ALT)?" alt":"", TestFlag(Flags, LGI_EF_SHIFT)?" shift":"" ); #endif if (toupper(Press) == (uint)Key) { if ( ((TestFlag(Flags, LGI_EF_CTRL) ^ k.Ctrl()) == 0) && ((TestFlag(Flags, LGI_EF_ALT) ^ k.Alt()) == 0) && ((TestFlag(Flags, LGI_EF_SHIFT) ^ k.Shift()) == 0) ) { return true; } } return false; } //////////////////////////////////////////////////////////////////////////// GCommand::GCommand() { Flags = GWF_VISIBLE; Id = 0; ToolButton = 0; MenuItem = 0; Accelerator = 0; TipHelp = 0; PrevValue = false; } GCommand::~GCommand() { DeleteArray(Accelerator); DeleteArray(TipHelp); } bool GCommand::Enabled() { if (ToolButton) return ToolButton->Enabled(); if (MenuItem) return MenuItem->Enabled(); return false; } void GCommand::Enabled(bool e) { if (ToolButton) { ToolButton->Enabled(e); } if (MenuItem) { MenuItem->Enabled(e); } } bool GCommand::Value() { bool HasChanged = false; if (ToolButton) { HasChanged |= (ToolButton->Value() != 0) ^ PrevValue; } if (MenuItem) { HasChanged |= (MenuItem->Checked() != 0) ^ PrevValue; } if (HasChanged) { Value(!PrevValue); } return PrevValue; } void GCommand::Value(bool v) { if (ToolButton) { ToolButton->Value(v); } if (MenuItem) { MenuItem->Checked(v); } PrevValue = v; }