/* -*- c++ -*- * Copyright (C) 2007-2017 Andreas Persson * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with program; see the file COPYING. If not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA. */ #ifndef GIGEDIT_GLOBAL_H #define GIGEDIT_GLOBAL_H #if HAVE_CONFIG_H # include #endif #include #include // threads.h must be included first to be able to build with // G_DISABLE_DEPRECATED #if (GLIBMM_MAJOR_VERSION == 2 && GLIBMM_MINOR_VERSION == 31 && GLIBMM_MICRO_VERSION >= 2) || \ (GLIBMM_MAJOR_VERSION == 2 && GLIBMM_MINOR_VERSION > 31) || GLIBMM_MAJOR_VERSION > 2 #include #endif #if !defined(WIN32) # include # include #endif #include #include #ifdef LIBGIG_HEADER_FILE # include LIBGIG_HEADER_FILE(gig.h) # include LIBGIG_HEADER_FILE(Serialization.h) #else # include # include #endif //FIXME: for some reason AC GETTEXT check fails on the Mac cross compiler? #if (HAVE_GETTEXT || defined(__APPLE__)) # include # define _(String) gettext(String) #else # define _(String) String #endif #if defined(WIN32) && !HAVE_CONFIG_H # include "../../win32/libgigedit_private.h" // like config.h, automatically generated by Dev-C++ # define PACKAGE "gigedit" # define VERSION VER_STRING // VER_STRING defined in libgig_private.h #endif // WIN32 #define UNICODE_RIGHT_ARROW Glib::ustring(1, gunichar(0x2192)) #define UNICODE_LEFT_ARROW Glib::ustring(1, gunichar(0x2190)) #define UNICODE_SHIFT_KEY_SYMBOL Glib::ustring("\xe2\x87\xa7") #if defined(__APPLE__) # define UNICODE_ALT_KEY_SYMBOL Glib::ustring("\xe2\x8c\xa5") #else # define UNICODE_ALT_KEY_SYMBOL Glib::ustring("Alt") #endif #define UNICODE_ERASE_KEY_SYMBOL Glib::ustring("\xe2\x8c\xab") #define UNICODE_CTRL_KEY_SYMBOL Glib::ustring("Ctrl") #define UNICODE_CMD_KEY_SYMBOL Glib::ustring(1, gunichar(0x2318)) #if defined(__APPLE__) # define UNICODE_PRIMARY_KEY_SYMBOL UNICODE_CMD_KEY_SYMBOL #else # define UNICODE_PRIMARY_KEY_SYMBOL UNICODE_CTRL_KEY_SYMBOL #endif // taken from gdk/gdkkeysyms.h // (define on demand, to avoid unnecessary dev lib package build dependency) #ifndef GDK_KEY_Control_L # define GDK_KEY_Control_L 0xffe3 #endif #ifndef GDK_KEY_Control_R # define GDK_KEY_Control_R 0xffe4 #endif #ifndef GDK_KEY_Left # define GDK_KEY_Left 0xff51 #endif #ifndef GDK_KEY_Right # define GDK_KEY_Right 0xff53 #endif #ifndef GDK_KEY_Up # define GDK_KEY_Up 0xff52 #endif #ifndef GDK_KEY_Down # define GDK_KEY_Down 0xff54 #endif #include #define GIG_STR_ENCODING "CP1252" static inline Glib::ustring gig_to_utf8(const gig::String& gig_string) { return Glib::convert_with_fallback(gig_string, "UTF-8", GIG_STR_ENCODING, "?"); } static inline gig::String gig_from_utf8(const Glib::ustring& utf8_string) { return Glib::convert_with_fallback(utf8_string, GIG_STR_ENCODING, "UTF-8", "?"); } template inline std::string ToString(T o) { std::stringstream ss; ss << o; return ss.str(); } inline static bool endsWith(const std::string& haystack, const std::string& needle, bool caseSensitive) { if (haystack.size() < needle.size()) return false; const std::string sub = haystack.substr(haystack.size() - needle.size(), needle.size()); return (caseSensitive) ? (sub == needle) : (!strcasecmp(sub.c_str(), needle.c_str())); } inline int getDimensionIndex(gig::dimension_t type, gig::Region* rgn) { for (uint i = 0; i < rgn->Dimensions; ++i) if (rgn->pDimensionDefinitions[i].dimension == type) return i; return -1; } inline int getDimensionRegionIndex(gig::DimensionRegion* dr) { if (!dr) return -1; gig::Region* rgn = (gig::Region*)dr->GetParent(); for (uint i = 0; i < 256; ++i) if (rgn->pDimensionRegions[i] == dr) return i; return -1; } /// Find the number of bits required to hold the specified amount of zones. inline int zoneCountToBits(int nZones) { if (!nZones) return 0; int iFinalBits = 0; int zoneBits = nZones - 1; for (; zoneBits > 1; iFinalBits += 2, zoneBits >>= 2); iFinalBits += zoneBits; return iFinalBits; } /** * Returns the sum of all bits of all dimensions defined before the given * dimensions (@a type). This allows to access cases of that particular * dimension directly. If the supplied dimension @a type does not exist in the * the supplied @a region, then this function returns -1 instead! * * @param type - dimension that shall be used * @param rgn - parent region of that dimension */ inline int baseBits(gig::dimension_t type, gig::Region* rgn) { int previousBits = 0; for (uint i = 0; i < rgn->Dimensions; ++i) { if (rgn->pDimensionDefinitions[i].dimension == type) return previousBits; previousBits += rgn->pDimensionDefinitions[i].bits; } return -1; } // key: dimension type, value: dimension's zone index class DimensionCase : public std::map { public: bool isViolating(const DimensionCase& c) const { for (DimensionCase::const_iterator it = begin(); it != end(); ++it) { if (c.find(it->first) == c.end()) continue; if (c.find(it->first)->second != it->second) return true; } return false; } // prevent passing gig::dimension_none from creating a new pair // (TODO: other invalid gig::dimension_t values should be filtered here as well) int& operator[](const gig::dimension_t& k) { static int unused = 0; if (k == gig::dimension_none) { unused = 0; return unused; } return std::map::operator[](k); } }; //TODO: this function and caseOfDimRegion() from dimregionchooser.h are duplicates, eliminate either one of them! inline DimensionCase dimensionCaseOf(gig::DimensionRegion* dr) { DimensionCase dimCase; int idr = getDimensionRegionIndex(dr); if (idr < 0) return dimCase; gig::Region* rgn = (gig::Region*)dr->GetParent(); int bitpos = 0; for (int d = 0; d < rgn->Dimensions; ++d) { const gig::dimension_def_t& dimdef = rgn->pDimensionDefinitions[d]; const int zone = (idr >> bitpos) & ((1 << dimdef.bits) - 1); dimCase[dimdef.dimension] = zone; bitpos += rgn->pDimensionDefinitions[d].bits; } return dimCase; } /** * Checks whether the passed dimension zones are within the boundaries of the * defined dimensions. This is especially relevant if there are dimensions * defined with an amount not equal to a power of two, in that case there are * unused dimensions regions which should be ignored. */ inline bool isUsedCase(const DimensionCase& c, gig::Region* rgn) { for (int d = 0; d < rgn->Dimensions; ++d) { gig::dimension_t type = rgn->pDimensionDefinitions[d].dimension; if (c.find(type) == c.end()) continue; int zone = c.find(type)->second; if (zone < 0 || zone >= rgn->pDimensionDefinitions[d].zones) return false; } return true; } inline std::vector dimensionRegionsMatching( const DimensionCase& dimCase, gig::Region* rgn, bool skipUnusedZones = false) { std::vector v; for (int idr = 0; idr < 256; ++idr) { if (!rgn->pDimensionRegions[idr]) continue; DimensionCase c = dimensionCaseOf(rgn->pDimensionRegions[idr]); if (dimCase.isViolating(c)) continue; if (skipUnusedZones && !isUsedCase(c, rgn)) continue; v.push_back(rgn->pDimensionRegions[idr]); } return v; } inline gig::DimensionRegion* dimensionRegionMatching(const DimensionCase& dimCase, gig::Region* rgn) { for (int idr = 0; idr < 256; ++idr) { if (!rgn->pDimensionRegions[idr]) continue; DimensionCase c = dimensionCaseOf(rgn->pDimensionRegions[idr]); if (c == dimCase) return rgn->pDimensionRegions[idr]; } return NULL; } template class SignalGuard { public: SignalGuard(sigc::signal& start, sigc::signal& end, T_Message message) : m_end(end), m_message(message) { if (message) start.emit(message); } virtual ~SignalGuard() { if (m_message) m_end.emit(m_message); } protected: sigc::signal& m_end; T_Message m_message; }; #endif // GIGEDIT_GLOBAL_H