1 /**
2  * \file RenderGraphic.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Angus Leeming
7  *
8  * Full author contact details are available in file CREDITS.
9  */
10 
11 #include <config.h>
12 
13 #include "RenderGraphic.h"
14 
15 #include "insets/Inset.h"
16 
17 #include "Buffer.h"
18 #include "LyX.h"
19 #include "LyXRC.h"
20 #include "MetricsInfo.h"
21 
22 #include "frontends/FontMetrics.h"
23 #include "frontends/Painter.h"
24 
25 #include "graphics/GraphicsImage.h"
26 
27 #include "support/FileName.h"
28 #include "support/filetools.h"
29 #include "support/gettext.h"
30 
31 #include "support/bind.h"
32 
33 using namespace std;
34 
35 namespace lyx {
36 
37 
RenderGraphic(Inset const * inset)38 RenderGraphic::RenderGraphic(Inset const * inset)
39 	: loader_(inset->buffer().fileName())
40 {
41 	loader_.connect(bind(&Inset::updateFrontend, inset));
42 }
43 
44 
RenderGraphic(RenderGraphic const & other,Inset const * inset)45 RenderGraphic::RenderGraphic(RenderGraphic const & other, Inset const * inset)
46 	: RenderBase(other), loader_(other.loader_), params_(other.params_)
47 {
48 	loader_.connect(bind(&Inset::updateFrontend, inset));
49 }
50 
51 
clone(Inset const * inset) const52 RenderBase * RenderGraphic::clone(Inset const * inset) const
53 {
54 	return new RenderGraphic(*this, inset);
55 }
56 
reload() const57 void RenderGraphic::reload() const
58 {
59 	loader_.reload();
60 }
61 
update(graphics::Params const & params)62 void RenderGraphic::update(graphics::Params const & params)
63 {
64 	params_ = params;
65 
66 	if (!params_.filename.empty())
67 		loader_.reset(params_.filename, params_);
68 }
69 
70 
71 namespace {
72 
displayGraphic(graphics::Params const & params)73 bool displayGraphic(graphics::Params const & params)
74 {
75 	return params.display && lyxrc.display_graphics;
76 }
77 
78 
statusMessage(graphics::Params const & params,graphics::ImageStatus status)79 docstring const statusMessage(graphics::Params const & params,
80 			   graphics::ImageStatus status)
81 {
82 	docstring ret;
83 
84 	if (!displayGraphic(params))
85 		ret = _("Not shown.");
86 	else {
87 		switch (status) {
88 		case graphics::WaitingToLoad:
89 			ret = _("Not shown.");
90 			break;
91 		case graphics::Loading:
92 			ret = _("Loading...");
93 			break;
94 		case graphics::Converting:
95 			ret = _("Converting to loadable format...");
96 			break;
97 		case graphics::Loaded:
98 			ret = _("Loaded into memory. Generating pixmap...");
99 			break;
100 		case graphics::ScalingEtc:
101 			ret = _("Scaling etc...");
102 			break;
103 		case graphics::Ready:
104 			ret = _("Ready to display");
105 			break;
106 		case graphics::ErrorNoFile:
107 			ret = _("No file found!");
108 			break;
109 		case graphics::ErrorConverting:
110 			ret = _("Error converting to loadable format");
111 			break;
112 		case graphics::ErrorLoading:
113 			ret = _("Error loading file into memory");
114 			break;
115 		case graphics::ErrorGeneratingPixmap:
116 			ret = _("Error generating the pixmap");
117 			break;
118 		case graphics::ErrorUnknown:
119 			ret = _("No image");
120 			break;
121 		}
122 	}
123 
124 	return ret;
125 }
126 
127 
readyToDisplay(graphics::Loader const & loader)128 bool readyToDisplay(graphics::Loader const & loader)
129 {
130 	if (!loader.image() || loader.status() != graphics::Ready)
131 		return false;
132 	return loader.image()->isDrawable();
133 }
134 
135 } // namespace
136 
137 
metrics(MetricsInfo & mi,Dimension & dim) const138 void RenderGraphic::metrics(MetricsInfo & mi, Dimension & dim) const
139 {
140 	if (displayGraphic(params_)) {
141 		if (loader_.status() == graphics::WaitingToLoad)
142 			loader_.startLoading();
143 		if (!loader_.monitoring())
144 			loader_.startMonitoring();
145 		loader_.checkModifiedAsync();
146 	}
147 
148 	bool const image_ready = displayGraphic(params_) && readyToDisplay(loader_);
149 	if (image_ready) {
150 		dim.wid = loader_.image()->width() + 2 * Inset::TEXT_TO_INSET_OFFSET;
151 		dim.asc = loader_.image()->height();
152 		dim_ = dim;
153 		return;
154 	}
155 
156 	dim.asc = 50;
157 	dim.des = 0;
158 
159 	int font_width = 0;
160 	int font_height = 0;
161 
162 	FontInfo msgFont(mi.base.font);
163 	msgFont.setFamily(SANS_FAMILY);
164 
165 	// FIXME UNICODE
166 	docstring const justname = from_utf8(params_.filename.onlyFileName());
167 	if (!justname.empty()) {
168 		msgFont.setSize(FONT_SIZE_FOOTNOTE);
169 		font_width = theFontMetrics(msgFont).width(justname);
170 		font_height = theFontMetrics(msgFont).maxHeight();
171 	}
172 
173 	docstring const msg = statusMessage(params_, loader_.status());
174 	if (!msg.empty()) {
175 		msgFont.setSize(FONT_SIZE_TINY);
176 		font_width = max(font_width,
177 			theFontMetrics(msgFont).width(msg));
178 		font_height += theFontMetrics(msgFont).maxAscent();
179 		dim.des = theFontMetrics(msgFont).maxDescent();
180 	}
181 
182 	dim.wid = max(50, font_width + 15);
183 	dim.asc = max(50, font_height + 15);
184 
185 	dim_ = dim;
186 }
187 
188 
draw(PainterInfo & pi,int x,int y) const189 void RenderGraphic::draw(PainterInfo & pi, int x, int y) const
190 {
191 	// This will draw the graphics. If the graphics has not been
192 	// loaded yet, we draw just a rectangle.
193 	int const x1 = x + Inset::TEXT_TO_INSET_OFFSET;
194 	int const y1 = y - dim_.asc;
195 	int const w = dim_.wid - 2 * Inset::TEXT_TO_INSET_OFFSET;
196 	int const h = dim_.height();
197 
198 	if (displayGraphic(params_) && readyToDisplay(loader_))
199 		pi.pain.image(x1, y1, w, h, *loader_.image());
200 
201 	else {
202 		Color c = pi.change_.changed() ? pi.change_.color() : Color_foreground;
203 		pi.pain.rectangle(x1, y1, w, h, c);
204 
205 		// Print the file name.
206 		FontInfo msgFont = pi.base.font;
207 		msgFont.setPaintColor(c);
208 		msgFont.setFamily(SANS_FAMILY);
209 		string const justname = params_.filename.onlyFileName();
210 
211 		if (!justname.empty()) {
212 			msgFont.setSize(FONT_SIZE_FOOTNOTE);
213 			pi.pain.text(x1 + 6, y - theFontMetrics(msgFont).maxAscent() - 4,
214 			             from_utf8(justname), msgFont);
215 		}
216 
217 		// Print the message.
218 		docstring const msg = statusMessage(params_, loader_.status());
219 		if (!msg.empty()) {
220 			msgFont.setSize(FONT_SIZE_TINY);
221 			pi.pain.text(x1 + 6, y - 4, msg, msgFont);
222 		}
223 	}
224 	pi.change_.paintCue(pi, x1, y1, x1 + w, y1 + h);
225 }
226 
227 
228 } // namespace lyx
229