1 //------------------------------------------------------------------------------
2 // emSvgFilePanel.cpp
3 //
4 // Copyright (C) 2010-2011,2014-2016 Oliver Hamann.
5 //
6 // Homepage: http://eaglemode.sourceforge.net/
7 //
8 // This program is free software: you can redistribute it and/or modify it under
9 // the terms of the GNU General Public License version 3 as published by the
10 // Free Software Foundation.
11 //
12 // This program is distributed in the hope that it will be useful, but WITHOUT
13 // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14 // FOR A PARTICULAR PURPOSE. See the GNU General Public License version 3 for
15 // more details.
16 //
17 // You should have received a copy of the GNU General Public License version 3
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
19 //------------------------------------------------------------------------------
20 
21 #include <emSvg/emSvgFilePanel.h>
22 #include <emCore/emRes.h>
23 #include <emCore/emToolkit.h>
24 
25 
emSvgFilePanel(ParentArg parent,const emString & name,emSvgFileModel * fileModel,bool updateFileModel)26 emSvgFilePanel::emSvgFilePanel(
27 	ParentArg parent, const emString & name, emSvgFileModel * fileModel,
28 	bool updateFileModel
29 )
30 	: emFilePanel(parent,name),
31 	JobDelayTimer(GetScheduler()),
32 	IconTimer(GetScheduler())
33 {
34 	ServerModel=emSvgServerModel::Acquire(GetRootContext());
35 	Job=NULL;
36 	JobUpToDate=false;
37 	JobDelayStartTime=emGetClockMS();
38 	RenderIcon=emGetInsResImage(GetRootContext(),"emPs","rendering.tga");
39 	ShowIcon=false;
40 	AddWakeUpSignal(GetVirFileStateSignal());
41 	AddWakeUpSignal(JobDelayTimer.GetSignal());
42 	AddWakeUpSignal(IconTimer.GetSignal());
43 	SetFileModel(fileModel,updateFileModel);
44 }
45 
46 
~emSvgFilePanel()47 emSvgFilePanel::~emSvgFilePanel()
48 {
49 	ClearSvgDisplay();
50 }
51 
52 
SetFileModel(emFileModel * fileModel,bool updateFileModel)53 void emSvgFilePanel::SetFileModel(
54 	emFileModel * fileModel, bool updateFileModel
55 )
56 {
57 	if (fileModel && (dynamic_cast<emSvgFileModel*>(fileModel))==NULL) {
58 		fileModel=NULL;
59 	}
60 	emFilePanel::SetFileModel(fileModel,updateFileModel);
61 }
62 
63 
GetIconFileName() const64 emString emSvgFilePanel::GetIconFileName() const
65 {
66 	return "drawing.tga";
67 }
68 
69 
GetEssenceRect(double * pX,double * pY,double * pW,double * pH) const70 void emSvgFilePanel::GetEssenceRect(
71 	double * pX, double * pY, double * pW, double * pH
72 ) const
73 {
74 	if (IsVFSGood() && RenderError.IsEmpty()) {
75 		GetOutputRect(pX,pY,pW,pH);
76 	}
77 	else {
78 		emFilePanel::GetEssenceRect(pX,pY,pW,pH);
79 	}
80 }
81 
82 
Cycle()83 bool emSvgFilePanel::Cycle()
84 {
85 	if (IsSignaled(GetVirFileStateSignal())) {
86 		InvalidateControlPanel(); //??? very cheap solution, but okay for now.
87 		ClearSvgDisplay();
88 	}
89 
90 	UpdateSvgDisplay(false);
91 
92 	return emFilePanel::Cycle();
93 }
94 
95 
Notice(NoticeFlags flags)96 void emSvgFilePanel::Notice(NoticeFlags flags)
97 {
98 	if (flags&NF_VIEWING_CHANGED) {
99 		UpdateSvgDisplay(true);
100 	}
101 	if (flags&NF_UPDATE_PRIORITY_CHANGED) {
102 		if (Job) {
103 			ServerModel->SetJobPriority(Job,GetUpdatePriority());
104 		}
105 	}
106 	emFilePanel::Notice(flags);
107 }
108 
109 
IsOpaque() const110 bool emSvgFilePanel::IsOpaque() const
111 {
112 	if (!IsVFSGood()) {
113 		return emFilePanel::IsOpaque();
114 	}
115 	else if (!RenderError.IsEmpty()) {
116 		return true;
117 	}
118 	else {
119 		return false;
120 	}
121 }
122 
123 
Paint(const emPainter & painter,emColor canvasColor) const124 void emSvgFilePanel::Paint(const emPainter & painter, emColor canvasColor) const
125 {
126 	static const emColor RENDER_COLOR=emColor(0xEEEEFFFF);
127 	emSvgFileModel * fm;
128 	double fw,fh,ox,oy,ow,oh,sx,sy,sw,sh,ix,iy,iw,ih,t;
129 	emColor c;
130 
131 	if (!IsVFSGood()) {
132 		emFilePanel::Paint(painter,canvasColor);
133 		return;
134 	}
135 
136 	if (!RenderError.IsEmpty()) {
137 		c.Set(128,0,0);
138 		painter.Clear(c,canvasColor);
139 		painter.PaintTextBoxed(
140 			0.05,
141 			GetHeight()*0.15,
142 			0.9,
143 			GetHeight()*0.1,
144 			"Rendering Failed",
145 			GetHeight()*0.1,
146 			emColor(204,136,0),
147 			c,
148 			EM_ALIGN_CENTER,
149 			EM_ALIGN_LEFT,
150 			1.0
151 		);
152 		painter.PaintTextBoxed(
153 			0.05,
154 			GetHeight()*0.3,
155 			0.9,
156 			GetHeight()*0.4,
157 			RenderError,
158 			GetHeight()*0.4,
159 			emColor(255,255,0),
160 			c,
161 			EM_ALIGN_CENTER,
162 			EM_ALIGN_LEFT,
163 			1.0
164 		);
165 		return;
166 	}
167 
168 	GetOutputRect(&ox,&oy,&ow,&oh);
169 
170 	if (Img.IsEmpty()) {
171 		painter.PaintRect(
172 			ox,oy,ow,oh,
173 			RENDER_COLOR,
174 			canvasColor
175 		);
176 		canvasColor=RENDER_COLOR;
177 	}
178 	else {
179 		fm=(emSvgFileModel*)GetFileModel();
180 		fw=fm->GetWidth();
181 		fh=fm->GetHeight();
182 		sx=ox+SrcX*ow/fw;
183 		sy=oy+SrcY*oh/fh;
184 		sw=SrcW*ow/fw;
185 		sh=SrcH*oh/fh;
186 		emPainter(
187 			painter,
188 			painter.GetOriginX()+ox*painter.GetScaleX(),
189 			painter.GetOriginY()+oy*painter.GetScaleY(),
190 			painter.GetOriginX()+(ox+ow)*painter.GetScaleX(),
191 			painter.GetOriginY()+(oy+oh)*painter.GetScaleY()
192 		).PaintImage(
193 			sx,sy,sw,sh,
194 			Img,
195 			255,
196 			canvasColor
197 		);
198 		if (sy>oy) painter.PaintRect(
199 			ox,oy,ow,sy-oy,
200 			RENDER_COLOR,
201 			canvasColor
202 		);
203 		if (sx>ox) painter.PaintRect(
204 			ox,emMax(sy,oy),sx-ox,emMin(sy+sh,oy+oh)-emMax(sy,oy),
205 			RENDER_COLOR,
206 			canvasColor
207 		);
208 		if (sx+sw<ox+ow) painter.PaintRect(
209 			sx+sw,emMax(sy,oy),ox+ow-sx-sw,emMin(sy+sh,oy+oh)-emMax(sy,oy),
210 			RENDER_COLOR,
211 			canvasColor
212 		);
213 		if (sy+sh<oy+oh) painter.PaintRect(
214 			ox,sy+sh,ow,oy+oh-sy-sh,
215 			RENDER_COLOR,
216 			canvasColor
217 		);
218 		canvasColor=0;
219 	}
220 
221 	if (ShowIcon) {
222 		iw=ViewToPanelDeltaX(RenderIcon.GetWidth());
223 		if (iw>ow) iw=ow;
224 		ih=RenderIcon.GetHeight()*iw/RenderIcon.GetWidth();
225 		if (ih>oh) { iw=iw/ih*oh; ih=oh; }
226 		t=sqrt(oh*iw/ih)/5;
227 		if (iw>t) { ih=ih/iw*t; iw=t; }
228 		ix=ViewToPanelX(GetClipX1());
229 		iy=ViewToPanelY(GetClipY1());
230 		if (ix<ox) ix=ox;
231 		if (iy<oy) iy=oy;
232 		if (ix>ox+ow-iw) ix=ox+ow-iw;
233 		if (iy>oy+oh-ih) iy=oy+oh-ih;
234 		painter.PaintImage(ix,iy,iw,ih,RenderIcon,255,canvasColor);
235 	}
236 }
237 
238 
CreateControlPanel(ParentArg parent,const emString & name)239 emPanel * emSvgFilePanel::CreateControlPanel(
240 	ParentArg parent, const emString & name
241 )
242 {
243 	emSvgFileModel * fm;
244 	emLinearLayout * mainLayout;
245 	emLinearGroup * grp;
246 	emTextField * tf;
247 
248 	if (IsVFSGood()) {
249 		fm=(emSvgFileModel*)GetFileModel();
250 		mainLayout=new emLinearLayout(parent,name);
251 		mainLayout->SetMinChildTallness(0.03);
252 		mainLayout->SetMaxChildTallness(0.6);
253 		mainLayout->SetAlignment(EM_ALIGN_TOP_LEFT);
254 		grp=new emLinearGroup(
255 			mainLayout,
256 			"",
257 			"SVG File Info"
258 		);
259 		grp->SetOrientationThresholdTallness(0.07);
260 		tf=new emTextField(
261 			grp,
262 			"title",
263 			"Title",
264 			emString(),
265 			emImage(),
266 			fm->GetTitle()
267 		);
268 		tf->SetMultiLineMode();
269 		tf=new emTextField(
270 			grp,
271 			"desc",
272 			"Description",
273 			emString(),
274 			emImage(),
275 			fm->GetDescription()
276 		);
277 		tf->SetMultiLineMode();
278 		tf=new emTextField(
279 			grp,
280 			"size",
281 			"Default Size (Pixels)",
282 			emString(),
283 			emImage(),
284 			emString::Format(
285 				"%lg x %lg",
286 				fm->GetWidth(),
287 				fm->GetHeight()
288 			)
289 		);
290 		return mainLayout;
291 	}
292 	else {
293 		return emFilePanel::CreateControlPanel(parent,name);
294 	}
295 }
296 
297 
GetOutputRect(double * pX,double * pY,double * pW,double * pH) const298 void emSvgFilePanel::GetOutputRect(
299 	double * pX, double * pY, double * pW, double * pH
300 ) const
301 {
302 	const emSvgFileModel * fm;
303 	double x,y,w,h,d,fw,fh;
304 
305 	if (IsVFSGood()) {
306 		fm=(const emSvgFileModel*)GetFileModel();
307 		fw=fm->GetWidth();
308 		fh=fm->GetHeight();
309 	}
310 	else {
311 		fw=4.0;
312 		fh=3.0;
313 	}
314 	x=0;
315 	y=0;
316 	w=1;
317 	h=GetHeight();
318 	if (fw*h>=fh*w) {
319 		d=w*fh/fw;
320 		y+=(h-d)/2;
321 		h=d;
322 	}
323 	else {
324 		d=h*fw/fh;
325 		x+=(w-d)/2;
326 		w=d;
327 	}
328 	*pX=x;
329 	*pY=y;
330 	*pW=w;
331 	*pH=h;
332 }
333 
334 
ClearSvgDisplay()335 void emSvgFilePanel::ClearSvgDisplay()
336 {
337 	if (Job) {
338 		ServerModel->CloseJob(Job);
339 		Job=NULL;
340 	}
341 	if (!JobImg.IsEmpty()) {
342 		JobImg.Clear();
343 	}
344 	if (!Img.IsEmpty()) {
345 		Img.Clear();
346 		InvalidatePainting();
347 	}
348 	if (!RenderError.IsEmpty()) {
349 		RenderError.Clear();
350 		InvalidatePainting();
351 	}
352 	JobUpToDate=false;
353 	IconTimer.Stop(true);
354 	ShowIcon=false;
355 }
356 
357 
UpdateSvgDisplay(bool viewingChanged)358 void emSvgFilePanel::UpdateSvgDisplay(bool viewingChanged)
359 {
360 	emSvgFileModel * fm;
361 	double fw,fh,ox,oy,ow,oh,ix,iy,iw,ih,sx,sy,sw,sh,qx1,qx2,qy1,qy2,q;
362 	emUInt64 tm,dt;
363 
364 	if (!IsVFSGood()) return;
365 	if (!RenderError.IsEmpty()) return;
366 	if (!IsViewed()) return;
367 
368 	if (JobUpToDate) JobDelayStartTime=emGetClockMS();
369 	if (viewingChanged) JobUpToDate=false;
370 
371 	if (Job) {
372 		switch (ServerModel->GetJobState(Job)) {
373 		case emSvgServerModel::JS_WAITING:
374 		case emSvgServerModel::JS_RUNNING:
375 			if (!ShowIcon && !IconTimer.IsRunning()) {
376 				ShowIcon=true;
377 				InvalidatePainting();
378 			}
379 			return;
380 		case emSvgServerModel::JS_ERROR:
381 			RenderError=ServerModel->GetJobErrorText(Job);
382 			if (RenderError.IsEmpty()) RenderError="unknown error";
383 			ServerModel->CloseJob(Job);
384 			Job=NULL;
385 			JobImg.Clear();
386 			Img.Clear();
387 			JobUpToDate=false;
388 			IconTimer.Stop(true);
389 			ShowIcon=false;
390 			InvalidatePainting();
391 			return;
392 		case emSvgServerModel::JS_SUCCESS:
393 			ServerModel->CloseJob(Job);
394 			Job=NULL;
395 			Img=JobImg;
396 			SrcX=JobSrcX;
397 			SrcY=JobSrcY;
398 			SrcW=JobSrcW;
399 			SrcH=JobSrcH;
400 			JobImg.Clear();
401 			if (JobUpToDate) {
402 				IconTimer.Stop(true);
403 				ShowIcon=false;
404 			}
405 			JobDelayStartTime=emGetClockMS();
406 			InvalidatePainting();
407 			break;
408 		}
409 	}
410 
411 	if (JobUpToDate) return;
412 
413 	fm=(emSvgFileModel*)GetFileModel();
414 	fw=fm->GetWidth();
415 	fh=fm->GetHeight();
416 
417 	GetOutputRect(&ox,&oy,&ow,&oh);
418 	ox=PanelToViewX(ox);
419 	oy=PanelToViewY(oy);
420 	ow=PanelToViewDeltaX(ow);
421 	oh=PanelToViewDeltaY(oh);
422 
423 	ix=floor(emMax(GetClipX1(),ox));
424 	iy=floor(emMax(GetClipY1(),oy));
425 	iw=ceil(emMin(GetClipX2(),ox+ow))-ix;
426 	ih=ceil(emMin(GetClipY2(),oy+oh))-iy;
427 
428 	sx=(ix-ox)*fw/ow;
429 	sy=(iy-oy)*fh/oh;
430 	sw=iw*fw/ow;
431 	sh=ih*fh/oh;
432 
433 	if (iw<1.0 || ih<1.0) {
434 		Img.Clear();
435 		SrcX=sx;
436 		SrcY=sy;
437 		SrcW=sw;
438 		SrcH=sh;
439 		InvalidatePainting();
440 		JobUpToDate=true;
441 		return;
442 	}
443 
444 	if (!Img.IsEmpty()) {
445 		qx1=emMax(SrcX,sx);
446 		qx2=emMin(SrcX+SrcW,sx+sw);
447 		qy1=emMax(SrcY,sy);
448 		qy2=emMin(SrcY+SrcH,sy+sh);
449 		if (qx2<qx1) qx2=qx1;
450 		if (qy2<qy1) qy2=qy1;
451 		q=(qx2-qx1)*(qy2-qy1)/(sw*sh);
452 		q=(q-0.9)*10.0;
453 		if (q>0.0 && Img.GetWidth()/SrcW>0.9*iw/sw) {
454 			dt=(emUInt64)(q*q*500.0+0.5);
455 			tm=emGetClockMS();
456 			if (JobDelayStartTime+dt>tm) {
457 				JobDelayTimer.Start(JobDelayStartTime+dt-tm);
458 				return;
459 			}
460 		}
461 	}
462 
463 	JobSrcX=sx;
464 	JobSrcY=sy;
465 	JobSrcW=sw;
466 	JobSrcH=sh;
467 	JobImg.Setup((int)(iw+0.5),(int)(ih+0.5),3);
468 
469 	Job=ServerModel->StartRenderJob(
470 		fm->GetSvgHandle(),
471 		JobSrcX,
472 		JobSrcY,
473 		JobSrcW,
474 		JobSrcH,
475 		emColor(0xffffffff),
476 		&JobImg,
477 		GetUpdatePriority(),
478 		this
479 	);
480 	if (!ShowIcon) IconTimer.Start(500);
481 	JobUpToDate=true;
482 }
483