1 /* GG is a GUI for OpenGL. 2 3 Copyright (C) 2015 Mitten-O 4 5 This library is free software; you can redistribute it and/or 6 modify it under the terms of the GNU Lesser General Public License 7 as published by the Free Software Foundation; either version 2.1 8 of the License, or (at your option) any later version. 9 10 This library is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 Lesser General Public License for more details. 14 15 You should have received a copy of the GNU Lesser General Public 16 License along with this library; if not, write to the Free 17 Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 18 02111-1307 USA 19 20 If you do not wish to comply with the terms of the LGPL please 21 contact the author as other terms are available for a fee. 22 23 Zach Laine 24 whatwasthataddress@gmail.com */ 25 26 #include <GG/RichText/ImageBlock.h> 27 28 #include <GG/Texture.h> 29 #include <GG/RichText/RichText.h> 30 #include <GG/TextControl.h> 31 #include <GG/DrawUtil.h> 32 #include <GG/utf8/checked.h> 33 #include <GG/dialogs/FileDlg.h> 34 35 #include <boost/filesystem.hpp> 36 #include <algorithm> 37 38 namespace GG { 39 namespace fs = boost::filesystem; 40 41 const std::string ImageBlock::IMAGE_TAG("img"); 42 ImageBlock(const fs::path & path,X x,Y y,X w,GG::Flags<GG::WndFlag> flags)43 ImageBlock::ImageBlock(const fs::path& path, X x, Y y, X w, 44 GG::Flags<GG::WndFlag> flags) : 45 BlockControl(x, y, w, flags) 46 { 47 try { 48 auto texture = GetTextureManager().GetTexture(path); 49 m_graphic = Wnd::Create<StaticGraphic>(texture, GRAPHIC_PROPSCALE | GRAPHIC_SHRINKFIT | GRAPHIC_CENTER); 50 } catch (GG::Texture::BadFile&) { 51 try { 52 auto vector_texture = GetVectorTextureManager().GetTexture(path); 53 m_graphic = Wnd::Create<StaticGraphic>(vector_texture, GRAPHIC_PROPSCALE | GRAPHIC_SHRINKFIT | GRAPHIC_CENTER); 54 } catch (GG::Texture::BadFile&) { 55 // No can do inside GiGi. 56 } 57 } 58 } 59 CompleteConstruction()60 void ImageBlock::CompleteConstruction() 61 { 62 if (m_graphic) 63 AttachChild(m_graphic); 64 } 65 SetMaxWidth(X width)66 Pt ImageBlock::SetMaxWidth(X width) 67 { 68 if (m_graphic) { 69 // Give the graphic the set width and give it liberty with the height. 70 m_graphic->Resize(Pt(width, Y(INT_MAX))); 71 72 // Get the actual space the graphic decided to use. 73 Rect rect = m_graphic->RenderedArea(); 74 Pt size = rect.LowerRight() - rect.UpperLeft(); 75 76 // Take up the full width to center the image. 77 size.x = width; 78 79 // Don't take the extra vertical space. 80 m_graphic->Resize(size); 81 82 // Update our size to match the graphic we are wrapping. 83 Resize(size); 84 85 // Return the size we decided to be. 86 return size; 87 } else { 88 // We don't have an image. Take a quarter of width to show an X. 89 Pt size(width, Y(Value(width) / 4)); 90 Resize(size); 91 return size; 92 } 93 } 94 Render()95 void ImageBlock::Render() 96 { 97 if (m_graphic) 98 return; 99 100 // Error: no image. Draw red x. 101 Pt ul = UpperLeft(); 102 Pt lr = LowerRight(); 103 Pt size = lr - ul; 104 ul.x += size.x / 2 - X(Value(size.y)) / 2; 105 lr.x -= size.x / 2 - X(Value(size.y)) / 2; 106 FlatX(ul, lr, CLR_RED); 107 } 108 109 // A factory for creating image blocks from tags. 110 class ImageBlockFactory : public RichText::IBlockControlFactory { 111 public: ImageBlockFactory()112 ImageBlockFactory() : 113 m_root_path() 114 {} 115 116 //! Create a Text block from a plain text tag. CreateFromTag(const std::string & tag,const RichText::TAG_PARAMS & params,const std::string & content,const std::shared_ptr<Font> & font,const Clr & color,Flags<TextFormat> format)117 std::shared_ptr<BlockControl> CreateFromTag(const std::string& tag, 118 const RichText::TAG_PARAMS& params, 119 const std::string& content, 120 const std::shared_ptr<Font>& font, 121 const Clr& color, 122 Flags<TextFormat> format) override 123 { 124 // Get the path from the parameters. 125 fs::path param_path = ExtractPath(params); 126 fs::path combined_path = fs::exists(param_path) ? param_path : (m_root_path / param_path); 127 128 if (!fs::exists(combined_path)) 129 return nullptr; 130 131 // Create a new image block, basing the path on the root path. 132 return Wnd::Create<ImageBlock>(combined_path, X0, Y0, X1, Flags<WndFlag>()); 133 } 134 135 // Sets the root of image search path. SetRootPath(const fs::path & path)136 void SetRootPath(const fs::path& path) 137 { m_root_path = path; } 138 139 private: 140 fs::path m_root_path; 141 142 // Extracts the path from the given params. ExtractPath(const RichText::TAG_PARAMS & params)143 static fs::path ExtractPath(const RichText::TAG_PARAMS& params) 144 { 145 // Find the src. 146 auto src_param = params.find("src"); 147 148 // If src not found, error out. 149 if (src_param == params.end()) { 150 return fs::path(); 151 } else { 152 #if defined(_WIN32) 153 // convert UTF-8 path string to UTF-16 154 fs::path::string_type str_native; 155 utf8::utf8to16(src_param->second.begin(), src_param->second.end(), std::back_inserter(str_native)); 156 return fs::path(str_native); 157 #else 158 return fs::path(src_param->second); 159 #endif 160 } 161 } 162 }; 163 164 // Register image block as the image tag handler. 165 static int dummy = RichText::RegisterDefaultBlock(ImageBlock::IMAGE_TAG, std::make_shared<ImageBlockFactory>()); 166 167 //! Set the root path from which to look for images with the factory. SetImagePath(RichText::IBlockControlFactory * factory,const fs::path & path)168 bool ImageBlock::SetImagePath(RichText::IBlockControlFactory* factory, const fs::path& path) 169 { 170 // Try to convert the factory to an image factory. 171 ImageBlockFactory* image_factory = dynamic_cast<ImageBlockFactory*>(factory); 172 173 // If successful, set the root path. 174 if (image_factory) { 175 image_factory->SetRootPath(path); 176 return true; 177 } else { 178 return false; 179 } 180 } 181 182 //! Set the root path from which to look for images with the factory. SetDefaultImagePath(const fs::path & path)183 bool ImageBlock::SetDefaultImagePath(const fs::path& path) 184 { 185 // Find the image block factory from the default map and give it the path. 186 auto factory_it = RichText::DefaultBlockFactoryMap()->find(IMAGE_TAG); 187 if (factory_it != RichText::DefaultBlockFactoryMap()->end()) { 188 if (auto factory = dynamic_cast<ImageBlockFactory*>(factory_it->second.get())) { 189 return SetImagePath(factory, path); 190 } 191 } 192 return false; 193 } 194 } 195