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