From fce514266eb23f396cb6ec24b7929de9b1a54d40 Mon Sep 17 00:00:00 2001 From: jules Date: Sun, 25 Sep 2011 21:33:38 +0100 Subject: [PATCH] Refactored the linux Freetype font code and native file chooser (via zenity). --- .../native/juce_linux_Messaging.cpp | 55 +- .../juce_graphics/native/juce_linux_Fonts.cpp | 529 ++++++++---------- .../filebrowser/juce_FileChooser.cpp | 11 +- .../filebrowser/juce_FileChooser.h | 1 + .../native/juce_android_FileChooser.cpp | 5 + .../native/juce_linux_Clipboard.cpp | 6 +- .../native/juce_linux_FileChooser.cpp | 62 +- .../native/juce_mac_FileChooser.mm | 10 + .../native/juce_win32_FileChooser.cpp | 5 + 9 files changed, 301 insertions(+), 383 deletions(-) diff --git a/modules/juce_events/native/juce_linux_Messaging.cpp b/modules/juce_events/native/juce_linux_Messaging.cpp index 2242e5e6e9..8f8a8344d1 100644 --- a/modules/juce_events/native/juce_linux_Messaging.cpp +++ b/modules/juce_events/native/juce_linux_Messaging.cpp @@ -48,9 +48,6 @@ public: { int ret = ::socketpair (AF_LOCAL, SOCK_STREAM, 0, fd); (void) ret; jassert (ret == 0); - - //setNonBlocking (fd[0]); - //setNonBlocking (fd[1]); } ~InternalMessageQueue() @@ -131,18 +128,6 @@ public: return (ret > 0); // ret <= 0 if error or timeout } - //============================================================================== - struct MessageThreadFuncCall - { - enum { uniqueID = 0x73774623 }; - - MessageCallbackFunction* func; - void* parameter; - void* result; - CriticalSection lock; - WaitableEvent event; - }; - //============================================================================== juce_DeclareSingleton_SingleThreaded_Minimal (InternalMessageQueue); @@ -212,20 +197,7 @@ private: if (msg == nullptr) return false; - if (msg->intParameter1 == MessageThreadFuncCall::uniqueID) - { - // Handle callback message - MessageThreadFuncCall* const call = (MessageThreadFuncCall*) msg->pointerParameter; - - call->result = (*(call->func)) (call->parameter); - call->event.signal(); - } - else - { - // Handle "normal" messages - MessageManager::getInstance()->deliverMessage (msg); - } - + MessageManager::getInstance()->deliverMessage (msg); return true; } }; @@ -236,7 +208,6 @@ juce_ImplementSingleton_SingleThreaded (InternalMessageQueue); //============================================================================== namespace LinuxErrorHandling { - //============================================================================== static bool errorOccurred = false; static bool keyboardBreakOccurred = false; static XErrorHandler oldErrorHandler = (XErrorHandler) 0; @@ -244,7 +215,7 @@ namespace LinuxErrorHandling //============================================================================== // Usually happens when client-server connection is broken - static int ioErrorHandler (Display* display) + int ioErrorHandler (Display* display) { DBG ("ERROR: connection to X server broken.. terminating."); @@ -255,8 +226,7 @@ namespace LinuxErrorHandling return 0; } - // A protocol error has occurred - static int juce_XErrorHandler (Display* display, XErrorEvent* event) + int errorHandler (Display* display, XErrorEvent* event) { #if JUCE_DEBUG_XERRORS char errorStr[64] = { 0 }; @@ -270,13 +240,13 @@ namespace LinuxErrorHandling return 0; } - static void installXErrorHandlers() + void installXErrorHandlers() { oldIOErrorHandler = XSetIOErrorHandler (ioErrorHandler); - oldErrorHandler = XSetErrorHandler (juce_XErrorHandler); + oldErrorHandler = XSetErrorHandler (errorHandler); } - static void removeXErrorHandlers() + void removeXErrorHandlers() { if (JUCEApplicationBase::isStandaloneApp()) { @@ -289,13 +259,13 @@ namespace LinuxErrorHandling } //============================================================================== - static void keyboardBreakSignalHandler (int sig) + void keyboardBreakSignalHandler (int sig) { if (sig == SIGINT) keyboardBreakOccurred = true; } - static void installKeyboardBreakHandler() + void installKeyboardBreakHandler() { struct sigaction saction; sigset_t maskSet; @@ -419,7 +389,7 @@ private: void* volatile result; AsyncFunctionCaller (MessageCallbackFunction* func_, void* parameter_) - : result (0), func (func_), parameter (parameter_) + : result (nullptr), func (func_), parameter (parameter_) {} JUCE_DECLARE_NON_COPYABLE (AsyncFunctionCaller); @@ -448,13 +418,16 @@ bool MessageManager::dispatchNextMessageOnSystemQueue (bool returnIfNoPendingMes break; } - if (InternalMessageQueue::getInstanceWithoutCreating()->dispatchNextEvent()) + InternalMessageQueue* const queue = InternalMessageQueue::getInstanceWithoutCreating(); + jassert (queue != nullptr); + + if (queue->dispatchNextEvent()) return true; if (returnIfNoPendingMessages) break; - InternalMessageQueue::getInstanceWithoutCreating()->sleepUntilEvent (2000); + queue->sleepUntilEvent (2000); } return false; diff --git a/modules/juce_graphics/native/juce_linux_Fonts.cpp b/modules/juce_graphics/native/juce_linux_Fonts.cpp index 2a138c8ab2..b42df9b3d5 100644 --- a/modules/juce_graphics/native/juce_linux_Fonts.cpp +++ b/modules/juce_graphics/native/juce_linux_Fonts.cpp @@ -23,60 +23,52 @@ ============================================================================== */ -class FreeTypeFontFace +struct FTLibWrapper : public ReferenceCountedObject { -public: - //============================================================================== - enum FontStyle + FTLibWrapper() : library (0) { - Plain = 0, - Bold = 1, - Italic = 2 - }; - - //============================================================================== - FreeTypeFontFace (const String& familyName) - : hasSerif (false), - monospaced (false) - { - family = familyName; - } - - void setFileName (const String& name, const int faceIndex, FontStyle style) - { - if (names [(int) style].fileName.isEmpty()) + if (FT_Init_FreeType (&library) != 0) { - names [(int) style].fileName = name; - names [(int) style].faceIndex = faceIndex; + library = 0; + DBG ("Failed to initialize FreeType"); } } - const String& getFamilyName() const noexcept { return family; } - - const String& getFileName (const int style, int& faceIndex) const noexcept + ~FTLibWrapper() { - faceIndex = names[style].faceIndex; - return names[style].fileName; + if (library != 0) + FT_Done_FreeType (library); } - void setMonospaced (bool mono) noexcept { monospaced = mono; } - bool getMonospaced() const noexcept { return monospaced; } + FT_Library library; - void setSerif (const bool serif) noexcept { hasSerif = serif; } - bool getSerif() const noexcept { return hasSerif; } + typedef ReferenceCountedObjectPtr Ptr; -private: - //============================================================================== - String family; + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FTLibWrapper); +}; - struct FontNameIndex +//============================================================================== +struct FTFaceWrapper : public ReferenceCountedObject +{ + FTFaceWrapper (const FTLibWrapper::Ptr& ftLib, const File& file, int faceIndex) + : face (0), library (ftLib) { - String fileName; - int faceIndex; - }; + if (FT_New_Face (ftLib->library, file.getFullPathName().toUTF8(), faceIndex, &face) != 0) + face = 0; + } - FontNameIndex names[4]; - bool hasSerif, monospaced; + ~FTFaceWrapper() + { + if (face != 0) + FT_Done_Face (face); + } + + FT_Face face; + FTLibWrapper::Ptr library; + + typedef ReferenceCountedObjectPtr Ptr; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FTFaceWrapper); }; //============================================================================== @@ -130,67 +122,35 @@ private: StringArray fontDirs; int index; ScopedPointer iter; -}; + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LinuxFontFileIterator); +}; //============================================================================== -class FreeTypeInterface : public DeletedAtShutdown +class FTTypefaceList : public DeletedAtShutdown { public: - //============================================================================== - FreeTypeInterface() - : ftLib (0), - lastFace (0), - lastBold (false), - lastItalic (false) + FTTypefaceList() + : library (new FTLibWrapper()) { - if (FT_Init_FreeType (&ftLib) != 0) - { - ftLib = 0; - DBG ("Failed to initialize FreeType"); - } - LinuxFontFileIterator fontFileIterator; while (fontFileIterator.next()) { - FT_Face face; int faceIndex = 0; int numFaces = 0; do { - if (FT_New_Face (ftLib, fontFileIterator.getFile().getFullPathName().toUTF8(), - faceIndex, &face) == 0) + FTFaceWrapper face (library, fontFileIterator.getFile(), faceIndex); + + if (face.face != 0) { if (faceIndex == 0) - numFaces = face->num_faces; - - if ((face->face_flags & FT_FACE_FLAG_SCALABLE) != 0) - { - FreeTypeFontFace* const newFace = findOrCreate (face->family_name, true); - int style = (int) FreeTypeFontFace::Plain; - - if ((face->style_flags & FT_STYLE_FLAG_BOLD) != 0) - style |= (int) FreeTypeFontFace::Bold; - - if ((face->style_flags & FT_STYLE_FLAG_ITALIC) != 0) - style |= (int) FreeTypeFontFace::Italic; - - newFace->setFileName (fontFileIterator.getFile().getFullPathName(), - faceIndex, (FreeTypeFontFace::FontStyle) style); - newFace->setMonospaced ((face->face_flags & FT_FACE_FLAG_FIXED_WIDTH) != 0); - - // Surely there must be a better way to do this? - const String name (face->family_name); - newFace->setSerif (! (name.containsIgnoreCase ("Sans") - || name.containsIgnoreCase ("Verdana") - || name.containsIgnoreCase ("Arial"))); - - //DBG (fontFileIterator.getFile().getFullPathName() << " - " << name); - } + numFaces = face.face->num_faces; - FT_Done_Face (face); + if ((face.face->face_flags & FT_FACE_FLAG_SCALABLE) != 0) + faces.add (new KnownTypeface (fontFileIterator.getFile(), faceIndex, face)); } ++faceIndex; @@ -199,129 +159,200 @@ public: } } - ~FreeTypeInterface() + ~FTTypefaceList() { - if (lastFace != 0) - FT_Done_Face (lastFace); - - if (ftLib != 0) - FT_Done_FreeType (ftLib); - clearSingletonInstance(); } //============================================================================== - FreeTypeFontFace* findOrCreate (const String& familyName, const bool create = false) + struct KnownTypeface { - for (int i = 0; i < faces.size(); i++) - if (faces[i]->getFamilyName() == familyName) - return faces[i]; - - if (! create) - return nullptr; + KnownTypeface (const File& file_, const int faceIndex_, const FTFaceWrapper& face) + : file (file_), + family (face.face->family_name), + faceIndex (faceIndex_), + isBold ((face.face->style_flags & FT_STYLE_FLAG_BOLD) != 0), + isItalic ((face.face->style_flags & FT_STYLE_FLAG_ITALIC) != 0), + isMonospaced ((face.face->face_flags & FT_FACE_FLAG_FIXED_WIDTH) != 0), + isSansSerif (isFaceSansSerif (family)) + { + } - FreeTypeFontFace* newFace = new FreeTypeFontFace (familyName); - faces.add (newFace); + const File file; + const String family; + const int faceIndex; + const bool isBold, isItalic, isMonospaced, isSansSerif; - return newFace; - } + JUCE_DECLARE_NON_COPYABLE (KnownTypeface); + }; - // Create a FreeType face object for a given font - FT_Face createFT_Face (const String& fontName, const bool bold, const bool italic) + //============================================================================== + FTFaceWrapper::Ptr createFace (const String& fontName, const bool bold, const bool italic) { - FT_Face face = 0; + const KnownTypeface* ftFace = matchTypeface (fontName, bold, italic); - if (fontName == lastFontName && bold == lastBold && italic == lastItalic) + if (ftFace == nullptr) { - face = lastFace; + ftFace = matchTypeface (fontName, ! bold, italic); + + if (ftFace == nullptr) + { + ftFace = matchTypeface (fontName, bold, ! italic); + + if (ftFace == nullptr) + ftFace = matchTypeface (fontName, ! bold, ! italic); + } } - else + + if (ftFace != nullptr) { - if (lastFace != 0) + FTFaceWrapper::Ptr face (new FTFaceWrapper (library, ftFace->file, ftFace->faceIndex)); + + if (face->face != 0) { - FT_Done_Face (lastFace); - lastFace = 0; + // If there isn't a unicode charmap then select the first one. + if (FT_Select_Charmap (face->face, ft_encoding_unicode) != 0) + FT_Set_Charmap (face->face, face->face->charmaps[0]); + + return face; } + } - lastFontName = fontName; - lastBold = bold; - lastItalic = italic; + return nullptr; + } - FreeTypeFontFace* const ftFace = findOrCreate (fontName); + //============================================================================== + void getFamilyNames (StringArray& familyNames) const + { + for (int i = 0; i < faces.size(); i++) + familyNames.addIfNotAlreadyThere (faces.getUnchecked(i)->family); + } - if (ftFace != 0) - { - int style = (int) FreeTypeFontFace::Plain; + void getMonospacedNames (StringArray& monoSpaced) const + { + for (int i = 0; i < faces.size(); i++) + if (faces.getUnchecked(i)->isMonospaced) + monoSpaced.addIfNotAlreadyThere (faces.getUnchecked(i)->family); + } + + void getSerifNames (StringArray& serif) const + { + for (int i = 0; i < faces.size(); i++) + if (! faces.getUnchecked(i)->isSansSerif) + serif.addIfNotAlreadyThere (faces.getUnchecked(i)->family); + } + + void getSansSerifNames (StringArray& sansSerif) const + { + for (int i = 0; i < faces.size(); i++) + if (faces.getUnchecked(i)->isSansSerif) + sansSerif.addIfNotAlreadyThere (faces.getUnchecked(i)->family); + } - if (bold) - style |= (int) FreeTypeFontFace::Bold; + juce_DeclareSingleton_SingleThreaded_Minimal (FTTypefaceList); - if (italic) - style |= (int) FreeTypeFontFace::Italic; +private: + FTLibWrapper::Ptr library; + OwnedArray faces; - int faceIndex; - String fileName (ftFace->getFileName (style, faceIndex)); + const KnownTypeface* matchTypeface (const String& familyName, const bool wantBold, const bool wantItalic) const noexcept + { + for (int i = 0; i < faces.size(); ++i) + { + const KnownTypeface* const face = faces.getUnchecked(i); - if (fileName.isEmpty()) - { - style ^= (int) FreeTypeFontFace::Bold; + if (face->family == familyName + && face->isBold == wantBold + && face->isItalic == wantItalic) + return face; + } - fileName = ftFace->getFileName (style, faceIndex); + return nullptr; + } - if (fileName.isEmpty()) - { - style ^= (int) FreeTypeFontFace::Bold; - style ^= (int) FreeTypeFontFace::Italic; + static bool isFaceSansSerif (const String& family) + { + const char* sansNames[] = { "Sans", "Verdana", "Arial", "Ubuntu" }; - fileName = ftFace->getFileName (style, faceIndex); + for (int i = 0; i < numElementsInArray (sansNames); ++i) + if (family.containsIgnoreCase (sansNames[i])) + return true; - if (! fileName.length()) - { - style ^= (int) FreeTypeFontFace::Bold; - fileName = ftFace->getFileName (style, faceIndex); - } - } - } + return false; + } + + JUCE_DECLARE_NON_COPYABLE (FTTypefaceList); +}; + +juce_ImplementSingleton_SingleThreaded (FTTypefaceList) + + +//============================================================================== +class FreeTypeTypeface : public CustomTypeface +{ +public: + FreeTypeTypeface (const Font& font) + : faceWrapper (FTTypefaceList::getInstance() + ->createFace (font.getTypefaceName(), font.isBold(), font.isItalic())) + { + if (faceWrapper != nullptr) + { + setCharacteristics (font.getTypefaceName(), + faceWrapper->face->ascender / (float) (faceWrapper->face->ascender - faceWrapper->face->descender), + font.isBold(), font.isItalic(), + L' '); + } + else + { + DBG ("Failed to create typeface: " << font.getTypefaceName() << " " + << (font.isBold() ? 'B' : ' ') << (font.isItalic() ? 'I' : ' ')); + } + } + + bool loadGlyphIfPossible (const juce_wchar character) + { + if (faceWrapper != nullptr) + { + FT_Face face = faceWrapper->face; + const unsigned int glyphIndex = FT_Get_Char_Index (face, character); + + if (FT_Load_Glyph (face, glyphIndex, FT_LOAD_NO_SCALE | FT_LOAD_NO_BITMAP | FT_LOAD_IGNORE_TRANSFORM) == 0 + && face->glyph->format == ft_glyph_format_outline) + { + const float scale = 1.0f / (float) (face->ascender - face->descender); + Path destShape; - if (! FT_New_Face (ftLib, fileName.toUTF8(), faceIndex, &lastFace)) + if (getGlyphShape (destShape, face->glyph->outline, scale)) { - face = lastFace; + addGlyph (character, destShape, face->glyph->metrics.horiAdvance * scale); - // If there isn't a unicode charmap then select the first one. - if (FT_Select_Charmap (face, ft_encoding_unicode)) - FT_Set_Charmap (face, face->charmaps[0]); + if ((face->face_flags & FT_FACE_FLAG_KERNING) != 0) + addKerning (face, character, glyphIndex); + + return true; } } } - return face; + return false; } - bool addGlyph (FT_Face face, CustomTypeface& dest, uint32 character) - { - const unsigned int glyphIndex = FT_Get_Char_Index (face, character); - const float height = (float) (face->ascender - face->descender); - const float scaleX = 1.0f / height; - const float scaleY = -1.0f / height; - Path destShape; - - if (FT_Load_Glyph (face, glyphIndex, FT_LOAD_NO_SCALE | FT_LOAD_NO_BITMAP | FT_LOAD_IGNORE_TRANSFORM) != 0 - || face->glyph->format != ft_glyph_format_outline) - { - return false; - } +private: + FTFaceWrapper::Ptr faceWrapper; - const FT_Outline* const outline = &face->glyph->outline; - const short* const contours = outline->contours; - const char* const tags = outline->tags; - FT_Vector* const points = outline->points; + bool getGlyphShape (Path& destShape, const FT_Outline& outline, const float scaleX) + { + const float scaleY = -scaleX; + const short* const contours = outline.contours; + const char* const tags = outline.tags; + const FT_Vector* const points = outline.points; - for (int c = 0; c < outline->n_contours; c++) + for (int c = 0; c < outline.n_contours; ++c) { const int startPoint = (c == 0) ? 0 : contours [c - 1] + 1; const int endPoint = contours[c]; - for (int p = startPoint; p <= endPoint; p++) + for (int p = startPoint; p <= endPoint; ++p) { const float x = scaleX * points[p].x; const float y = scaleY * points[p].y; @@ -372,21 +403,19 @@ public: } else if (FT_CURVE_TAG (tags[p]) == FT_Curve_Tag_Cubic) { - if (p >= endPoint) - return false; - const int next1 = p + 1; - const int next2 = (p == (endPoint - 1)) ? startPoint : p + 2; + const int next2 = (p == (endPoint - 1)) ? startPoint : (p + 2); + + if (p >= endPoint + || FT_CURVE_TAG (tags[next1]) != FT_Curve_Tag_Cubic + || FT_CURVE_TAG (tags[next2]) != FT_Curve_Tag_On) + return false; const float x2 = scaleX * points [next1].x; const float y2 = scaleY * points [next1].y; const float x3 = scaleX * points [next2].x; const float y3 = scaleY * points [next2].y; - if (FT_CURVE_TAG (tags[next1]) != FT_Curve_Tag_Cubic - || FT_CURVE_TAG (tags[next2]) != FT_Curve_Tag_On) - return false; - destShape.cubicTo (x, y, x2, y2, x3, y3); p += 2; } @@ -395,15 +424,10 @@ public: destShape.closeSubPath(); } - dest.addGlyph (character, destShape, face->glyph->metrics.horiAdvance / height); - - if ((face->face_flags & FT_FACE_FLAG_KERNING) != 0) - addKerning (face, dest, character, glyphIndex); - return true; } - void addKerning (FT_Face face, CustomTypeface& dest, const uint32 character, const uint32 glyphIndex) + void addKerning (FT_Face face, const uint32 character, const uint32 glyphIndex) { const float height = (float) (face->ascender - face->descender); @@ -414,177 +438,94 @@ public: { FT_Vector kerning; - if (FT_Get_Kerning (face, glyphIndex, rightGlyphIndex, ft_kerning_unscaled, &kerning) == 0) - { - if (kerning.x != 0) - dest.addKerningPair (character, rightCharCode, kerning.x / height); - } + if (FT_Get_Kerning (face, glyphIndex, rightGlyphIndex, ft_kerning_unscaled, &kerning) == 0 + && kerning.x != 0) + addKerningPair (character, rightCharCode, kerning.x / height); rightCharCode = FT_Get_Next_Char (face, rightCharCode, &rightGlyphIndex); } } - // Add a glyph to a font - bool addGlyphToFont (const uint32 character, const String& fontName, - bool bold, bool italic, CustomTypeface& dest) - { - FT_Face face = createFT_Face (fontName, bold, italic); - - return face != 0 && addGlyph (face, dest, character); - } - - //============================================================================== - void getFamilyNames (StringArray& familyNames) const - { - for (int i = 0; i < faces.size(); i++) - familyNames.add (faces[i]->getFamilyName()); - } - - void getMonospacedNames (StringArray& monoSpaced) const - { - for (int i = 0; i < faces.size(); i++) - if (faces[i]->getMonospaced()) - monoSpaced.add (faces[i]->getFamilyName()); - } - - void getSerifNames (StringArray& serif) const - { - for (int i = 0; i < faces.size(); i++) - if (faces[i]->getSerif()) - serif.add (faces[i]->getFamilyName()); - } - - void getSansSerifNames (StringArray& sansSerif) const - { - for (int i = 0; i < faces.size(); i++) - if (! faces[i]->getSerif()) - sansSerif.add (faces[i]->getFamilyName()); - } - - juce_DeclareSingleton_SingleThreaded_Minimal (FreeTypeInterface); - -private: - //============================================================================== - FT_Library ftLib; - FT_Face lastFace; - String lastFontName; - bool lastBold, lastItalic; - OwnedArray faces; + JUCE_DECLARE_NON_COPYABLE (FreeTypeTypeface); }; -juce_ImplementSingleton_SingleThreaded (FreeTypeInterface) - - //============================================================================== -class FreetypeTypeface : public CustomTypeface -{ -public: - FreetypeTypeface (const Font& font) - { - FT_Face face = FreeTypeInterface::getInstance() - ->createFT_Face (font.getTypefaceName(), font.isBold(), font.isItalic()); - - if (face == 0) - { - #if JUCE_DEBUG - String msg ("Failed to create typeface: "); - msg << font.getTypefaceName() << " " << (font.isBold() ? 'B' : ' ') << (font.isItalic() ? 'I' : ' '); - DBG (msg); - #endif - } - else - { - setCharacteristics (font.getTypefaceName(), - face->ascender / (float) (face->ascender - face->descender), - font.isBold(), font.isItalic(), - L' '); - } - } - - bool loadGlyphIfPossible (juce_wchar character) - { - return FreeTypeInterface::getInstance() - ->addGlyphToFont (character, name, isBold, isItalic, *this); - } -}; - Typeface::Ptr Typeface::createSystemTypefaceFor (const Font& font) { - return new FreetypeTypeface (font); + return new FreeTypeTypeface (font); } -//============================================================================== StringArray Font::findAllTypefaceNames() { StringArray s; - FreeTypeInterface::getInstance()->getFamilyNames (s); + FTTypefaceList::getInstance()->getFamilyNames (s); s.sort (true); return s; } -namespace LinuxFontHelpers +//============================================================================== +struct DefaultFontNames { - String pickBestFont (const StringArray& names, - const char* const* choicesString) + DefaultFontNames() + : defaultSans (getDefaultSansSerifFontName()), + defaultSerif (getDefaultSerifFontName()), + defaultFixed (getDefaultMonospacedFontName()) { - const StringArray choices (choicesString); + } - int i, j; + String defaultSans, defaultSerif, defaultFixed; + +private: + static String pickBestFont (const StringArray& names, const char* const* choicesArray) + { + const StringArray choices (choicesArray); + + int j; for (j = 0; j < choices.size(); ++j) if (names.contains (choices[j], true)) return choices[j]; for (j = 0; j < choices.size(); ++j) - for (i = 0; i < names.size(); i++) + for (int i = 0; i < names.size(); ++i) if (names[i].startsWithIgnoreCase (choices[j])) return names[i]; for (j = 0; j < choices.size(); ++j) - for (i = 0; i < names.size(); i++) + for (int i = 0; i < names.size(); ++i) if (names[i].containsIgnoreCase (choices[j])) return names[i]; return names[0]; } - String getDefaultSansSerifFontName() + static String getDefaultSansSerifFontName() { StringArray allFonts; - FreeTypeInterface::getInstance()->getSansSerifNames (allFonts); + FTTypefaceList::getInstance()->getSansSerifNames (allFonts); const char* targets[] = { "Verdana", "Bitstream Vera Sans", "Luxi Sans", "Sans", 0 }; return pickBestFont (allFonts, targets); } - String getDefaultSerifFontName() + static String getDefaultSerifFontName() { StringArray allFonts; - FreeTypeInterface::getInstance()->getSerifNames (allFonts); + FTTypefaceList::getInstance()->getSerifNames (allFonts); const char* targets[] = { "Bitstream Vera Serif", "Times", "Nimbus Roman", "Serif", 0 }; return pickBestFont (allFonts, targets); } - String getDefaultMonospacedFontName() + static String getDefaultMonospacedFontName() { StringArray allFonts; - FreeTypeInterface::getInstance()->getMonospacedNames (allFonts); + FTTypefaceList::getInstance()->getMonospacedNames (allFonts); const char* targets[] = { "Bitstream Vera Sans Mono", "Courier", "Sans Mono", "Mono", 0 }; return pickBestFont (allFonts, targets); } -} - -struct DefaultFontNames -{ - DefaultFontNames() - : defaultSans (LinuxFontHelpers::getDefaultSansSerifFontName()), - defaultSerif (LinuxFontHelpers::getDefaultSerifFontName()), - defaultFixed (LinuxFontHelpers::getDefaultMonospacedFontName()) - { - } - String defaultSans, defaultSerif, defaultFixed; + JUCE_DECLARE_NON_COPYABLE (DefaultFontNames); }; Typeface::Ptr Font::getDefaultTypefaceForFont (const Font& font) @@ -593,9 +534,9 @@ Typeface::Ptr Font::getDefaultTypefaceForFont (const Font& font) String faceName (font.getTypefaceName()); - if (faceName == Font::getDefaultSansSerifFontName()) faceName = defaultNames.defaultSans; - else if (faceName == Font::getDefaultSerifFontName()) faceName = defaultNames.defaultSerif; - else if (faceName == Font::getDefaultMonospacedFontName()) faceName = defaultNames.defaultFixed; + if (faceName == getDefaultSansSerifFontName()) faceName = defaultNames.defaultSans; + else if (faceName == getDefaultSerifFontName()) faceName = defaultNames.defaultSerif; + else if (faceName == getDefaultMonospacedFontName()) faceName = defaultNames.defaultFixed; Font f (font); f.setTypefaceName (faceName); diff --git a/modules/juce_gui_basics/filebrowser/juce_FileChooser.cpp b/modules/juce_gui_basics/filebrowser/juce_FileChooser.cpp index a64d815a3b..4dd43b7f44 100644 --- a/modules/juce_gui_basics/filebrowser/juce_FileChooser.cpp +++ b/modules/juce_gui_basics/filebrowser/juce_FileChooser.cpp @@ -35,9 +35,12 @@ FileChooser::FileChooser (const String& chooserBoxTitle, startingFile (currentFileOrDirectory), useNativeDialogBox (useNativeDialogBox_) { - #if JUCE_LINUX - useNativeDialogBox = false; - #endif + if (useNativeDialogBox) + { + static bool canUseNativeBox = isPlatformDialogAvailable(); + if (! canUseNativeBox) + useNativeDialogBox = false; + } if (! fileFilters.containsNonWhitespaceChars()) filters = "*"; @@ -90,7 +93,7 @@ bool FileChooser::showDialog (const bool selectsDirectories, #if JUCE_WINDOWS if (useNativeDialogBox && ! (selectsFiles && selectsDirectories)) - #elif JUCE_MAC + #elif JUCE_MAC || JUCE_LINUX if (useNativeDialogBox && (previewComponent == nullptr)) #else if (false) diff --git a/modules/juce_gui_basics/filebrowser/juce_FileChooser.h b/modules/juce_gui_basics/filebrowser/juce_FileChooser.h index 584e0e55b2..32899e9033 100644 --- a/modules/juce_gui_basics/filebrowser/juce_FileChooser.h +++ b/modules/juce_gui_basics/filebrowser/juce_FileChooser.h @@ -184,6 +184,7 @@ private: const String& filters, bool selectsDirectories, bool selectsFiles, bool isSave, bool warnAboutOverwritingExistingFiles, bool selectMultipleFiles, FilePreviewComponent* previewComponent); + static bool isPlatformDialogAvailable(); JUCE_LEAK_DETECTOR (FileChooser); }; diff --git a/modules/juce_gui_basics/native/juce_android_FileChooser.cpp b/modules/juce_gui_basics/native/juce_android_FileChooser.cpp index d78fa650d0..92a139c1e1 100644 --- a/modules/juce_gui_basics/native/juce_android_FileChooser.cpp +++ b/modules/juce_gui_basics/native/juce_android_FileChooser.cpp @@ -38,3 +38,8 @@ void FileChooser::showPlatformDialog (Array& results, } + +bool FileChooser::isPlatformDialogAvailable() +{ + return false; +} diff --git a/modules/juce_gui_basics/native/juce_linux_Clipboard.cpp b/modules/juce_gui_basics/native/juce_linux_Clipboard.cpp index 49cae73ada..0b797361e3 100644 --- a/modules/juce_gui_basics/native/juce_linux_Clipboard.cpp +++ b/modules/juce_gui_basics/native/juce_linux_Clipboard.cpp @@ -34,7 +34,7 @@ namespace ClipboardHelpers static Atom atom_TARGETS; //============================================================================== - static void initSelectionAtoms() + void initSelectionAtoms() { static bool isInitialised = false; if (! isInitialised) @@ -48,7 +48,7 @@ namespace ClipboardHelpers //============================================================================== // Read the content of a window property as either a locale-dependent string or an utf8 string // works only for strings shorter than 1000000 bytes - static String readWindowProperty (Window window, Atom prop, Atom fmt) + String readWindowProperty (Window window, Atom prop, Atom fmt) { String returnData; char* clipData; @@ -79,7 +79,7 @@ namespace ClipboardHelpers //============================================================================== // Send a SelectionRequest to the window owning the selection and waits for its answer (with a timeout) */ - static bool requestSelectionContent (String& selectionContent, Atom selection, Atom requestedFormat) + bool requestSelectionContent (String& selectionContent, Atom selection, Atom requestedFormat) { Atom property_name = XInternAtom (display, "JUCE_SEL", false); diff --git a/modules/juce_gui_basics/native/juce_linux_FileChooser.cpp b/modules/juce_gui_basics/native/juce_linux_FileChooser.cpp index f33fc4181c..320cfd3ab8 100644 --- a/modules/juce_gui_basics/native/juce_linux_FileChooser.cpp +++ b/modules/juce_gui_basics/native/juce_linux_FileChooser.cpp @@ -23,6 +23,16 @@ ============================================================================== */ +bool FileChooser::isPlatformDialogAvailable() +{ + ChildProcess child; + const bool ok = child.start ("which zenity") + && child.readAllProcessOutput().trim().isNotEmpty(); + + child.waitForProcessToFinish (60 * 1000); + return ok; +} + void FileChooser::showPlatformDialog (Array& results, const String& title, const File& file, @@ -37,58 +47,28 @@ void FileChooser::showPlatformDialog (Array& results, const String separator (":"); String command ("zenity --file-selection"); - if (title.isNotEmpty()) - command << " --title=\"" << title << "\""; - - if (file != File::nonexistent) - command << " --filename=\"" << file.getFullPathName () << "\""; - - if (isDirectory) - command << " --directory"; - - if (isSave) - command << " --save"; - - if (selectMultipleFiles) - command << " --multiple --separator=\"" << separator << "\""; + if (title.isNotEmpty()) command << " --title=\"" << title << "\""; + if (file != File::nonexistent) command << " --filename=\"" << file.getFullPathName () << "\""; + if (isDirectory) command << " --directory"; + if (isSave) command << " --save"; + if (selectMultipleFiles) command << " --multiple --separator=" << separator; command << " 2>&1"; - MemoryOutputStream result; - int status = -1; - FILE* stream = popen (command.toUTF8(), "r"); - - if (stream != 0) - { - for (;;) - { - char buffer [1024]; - const int bytesRead = fread (buffer, 1, sizeof (buffer), stream); - - if (bytesRead <= 0) - break; - - result.write (buffer, bytesRead); - } - - status = pclose (stream); - } - - if (status == 0) + ChildProcess child; + if (child.start (command)) { + const String result (child.readAllProcessOutput()); StringArray tokens; if (selectMultipleFiles) - tokens.addTokens (result.toUTF8(), separator, String::empty); + tokens.addTokens (result, separator, "\""); else - tokens.add (result.toUTF8()); + tokens.add (result); for (int i = 0; i < tokens.size(); i++) results.add (File (tokens[i])); - return; + child.waitForProcessToFinish (60 * 1000); } - - //xxx ain't got one! - jassertfalse; } diff --git a/modules/juce_gui_basics/native/juce_mac_FileChooser.mm b/modules/juce_gui_basics/native/juce_mac_FileChooser.mm index f134203dcf..c7efc0370c 100644 --- a/modules/juce_gui_basics/native/juce_mac_FileChooser.mm +++ b/modules/juce_gui_basics/native/juce_mac_FileChooser.mm @@ -93,6 +93,11 @@ using namespace juce; BEGIN_JUCE_NAMESPACE //============================================================================== +bool FileChooser::isPlatformDialogAvailable() +{ + return true; +} + void FileChooser::showPlatformDialog (Array& results, const String& title, const File& currentFileOrDirectory, @@ -172,6 +177,11 @@ void FileChooser::showPlatformDialog (Array& results, #else //============================================================================== +bool FileChooser::isPlatformDialogAvailable() +{ + return false; +} + void FileChooser::showPlatformDialog (Array& results, const String& title, const File& currentFileOrDirectory, diff --git a/modules/juce_gui_basics/native/juce_win32_FileChooser.cpp b/modules/juce_gui_basics/native/juce_win32_FileChooser.cpp index c7feaa050b..50d224fd6b 100644 --- a/modules/juce_gui_basics/native/juce_win32_FileChooser.cpp +++ b/modules/juce_gui_basics/native/juce_win32_FileChooser.cpp @@ -134,6 +134,11 @@ namespace FileChooserHelpers } //============================================================================== +bool FileChooser::isPlatformDialogAvailable() +{ + return true; +} + void FileChooser::showPlatformDialog (Array& results, const String& title_, const File& currentFileOrDirectory, const String& filter, bool selectsDirectory, bool /*selectsFiles*/, bool isSaveDialogue, bool warnAboutOverwritingExistingFiles,