my log
[synfig.git] / synfig-studio / trunk / src / gtkmm / dock_navigator.cpp
1 /* === S Y N F I G ========================================================= */
2 /*!     \file dock_navigator.cpp
3 **      \brief Dock Nagivator File
4 **
5 **      $Id: dock_navigator.cpp,v 1.3 2005/01/12 00:31:11 darco Exp $
6 **
7 **      \legal
8 **      Copyright (c) 2002 Robert B. Quattlebaum Jr.
9 **
10 **      This software and associated documentation
11 **      are CONFIDENTIAL and PROPRIETARY property of
12 **      the above-mentioned copyright holder.
13 **
14 **      You may not copy, print, publish, or in any
15 **      other way distribute this software without
16 **      a prior written agreement with
17 **      the copyright holder.
18 **      \endlegal
19 */
20 /* ========================================================================= */
21
22 /* === H E A D E R S ======================================================= */
23
24 #ifdef USING_PCH
25 #       include "pch.h"
26 #else
27 #ifdef HAVE_CONFIG_H
28 #       include <config.h>
29 #endif
30
31 #include "dock_navigator.h"
32 #include "canvasview.h"
33 #include "workarea.h"
34
35 #include <cassert>
36 #include <synfig/canvas.h>
37 #include <synfig/context.h>
38 #include <synfig/target_scanline.h>
39 #include <synfig/surface.h>
40
41 #include <gtkmm/separator.h>
42
43 #include "asyncrenderer.h"
44
45 #endif
46
47 /* === U S I N G =========================================================== */
48
49 using namespace std;
50 using namespace etl;
51 using namespace synfig;
52
53 /* === M A C R O S ========================================================= */
54
55 const double log_10_2 = log(2.0);
56
57 /* === G L O B A L S ======================================================= */
58
59 /* === P R O C E D U R E S ================================================= */
60
61 /* === M E T H O D S ======================================================= */
62
63 /* === E N T R Y P O I N T ================================================= */
64 studio::Widget_NavView::Widget_NavView(CanvasView::LooseHandle cv)
65 :canvview(cv),
66 adj_zoom(0,-4,4,1,2),
67 surface(new synfig::Surface)
68 {
69         attach(drawto,0,4,0,1);
70         
71         attach(*manage(new Gtk::HSeparator),0,4,1,2,Gtk::SHRINK|Gtk::FILL,Gtk::SHRINK|Gtk::FILL);
72         
73         //zooming stuff
74         attach(zoom_print,0,1,2,3,Gtk::SHRINK|Gtk::FILL,Gtk::SHRINK|Gtk::FILL);
75         zoom_print.set_size_request(40,-1);
76         
77         Gtk::HScale *s = manage(new Gtk::HScale(adj_zoom));
78         s->set_draw_value(false);
79         //s->set_update_policy(Gtk::UPDATE_DELAYED);
80         //s->signal_event().connect(sigc::mem_fun(*this,&Dock_Navigator::on_scroll_event));
81         attach(*s,1,4,2,3,Gtk::EXPAND|Gtk::FILL,Gtk::SHRINK|Gtk::FILL);
82                 
83         show_all();
84         
85         adj_zoom.signal_value_changed().connect(sigc::mem_fun(*this,&Widget_NavView::on_number_modify));
86         
87         if(cv)
88         {
89                 drawto.signal_expose_event().connect(sigc::mem_fun(*this,&Widget_NavView::on_expose_draw));
90                 drawto.signal_event().connect(sigc::mem_fun(*this,&Widget_NavView::on_mouse_event));
91                 
92                 drawto.add_events(Gdk::BUTTON_MOTION_MASK|Gdk::BUTTON_PRESS_MASK);
93                 
94                 //get_canvas_view()->canvas_interface()->signal_dirty_preview()
95                 //                              .connect(sigc::mem_fun(*this,&Widget_NavView::on_dirty_preview));
96                 get_canvas_view()->work_area->signal_rendering()
97                                                 .connect(sigc::mem_fun(*this,&Widget_NavView::on_dirty_preview));
98                 
99                 get_canvas_view()->work_area->signal_view_window_changed()
100                                                 .connect(sigc::mem_fun(*this,&Widget_NavView::on_workarea_view_change));
101                 
102                 //update with this canvas' view
103                 on_workarea_view_change();
104                                 
105                 dirty = true;
106                 queue_draw();
107         }
108         
109         adj_zoom.set_value(0);
110 }
111
112 studio::Widget_NavView::~Widget_NavView()
113 {
114 }
115
116
117 static void freegu8(const guint8 *p)
118 {
119         delete [] p;
120 }
121
122 void studio::Widget_NavView::on_start_render()
123 {
124         if(dirty)
125         {
126                 //synfig::warning("Nav: Starting render");
127                 //synfig::warning("Nav: Rendering canvas");
128                 etl::handle<Target_Scanline>    targ = surface_target(surface.get());
129                 
130                 targ->set_canvas(get_canvas_view()->get_canvas());
131                 targ->set_remove_alpha();
132                 targ->set_avoid_time_sync();
133                 targ->set_quality(get_canvas_view()->get_work_area()->get_quality());
134                 //synfig::info("Set the quality level to: %d", get_canvas_view()->get_work_area()->get_quality());
135                 
136                 //this should set it to render a single frame
137                 RendDesc        r = get_canvas_view()->get_canvas()->rend_desc();
138                 r.set_time(get_canvas_view()->canvas_interface()->get_time());
139                 
140                 //this changes the size of the canvas to the closest thing we can find
141                 int sw = r.get_w(), sh = r.get_h();
142                 
143                 //synfig::warning("Nav: source image is %d x %d", sw,sh);
144                 
145                 //resize so largest dimension is 128
146                 int dw = sw > sh ? 128 : sw*128/sh,
147                         dh = sh > sw ? 128 : sh*128/sw;
148                 
149                 //synfig::warning("Nav: dest image is %d x %d", dw,dh);
150                 
151                 r.set_w(dw);
152                 r.set_h(dh);
153
154                 //get the pw and ph
155                 //float pw = r.get_pw();
156                 //float ph = r.get_ph();
157                 
158                 //synfig::warning("Nav: pixel size is %f x %f", pw,ph);
159                 
160                 //this renders that single frame
161                 targ->set_rend_desc(&r);
162                 
163                 //synfig::warning("Nav: Building async renderer and starting it...");
164                 
165                 renderer = new AsyncRenderer(targ);
166                 renderer->signal_success().connect(sigc::mem_fun(*this,&Widget_NavView::on_finish_render));
167                 renderer->start();
168                 dirty = false;
169         }
170 }
171
172 void studio::Widget_NavView::on_finish_render()
173 {
174         //convert it into our pixmap
175         PixelFormat pf(PF_RGB);
176         
177         //synfig::warning("Nav: It hath succeeded!!!");
178         
179         //assert(renderer && renderer->has_success());
180         DEBUGPOINT();
181         //synfig::warning("Nav: now we know it really succeeded");
182         if(!*surface)
183         {
184                 synfig::warning("dock_navigator: Bad surface");
185                 return;
186         }
187         
188         int w = 0, h = 0;
189         int dw = surface->get_w();
190         int dh = surface->get_h();
191         
192         if(prev)
193         {
194                 w = prev->get_width();
195                 h = prev->get_height();
196         }
197         
198         if(w != dw || h != dh || !prev)
199         {
200                 const int total_bytes(dw*dh*synfig::channels(pf));
201                 
202                 //synfig::warning("Nav: Updating the pixbuf to be the right size, etc. (%d bytes)", total_bytes);
203                 
204                 prev.clear();
205                 guint8 *bytes = new guint8[total_bytes]; //24 bits per pixel
206                 
207                 //convert into our buffered dataS
208                 //synfig::warning("Nav: converting color format into buffer");
209                 convert_color_format((unsigned char *)bytes, (*surface)[0], dw*dh, pf, App::gamma);
210                 
211                 prev = 
212                 Gdk::Pixbuf::create_from_data(
213                         bytes,  // pointer to the data
214                         Gdk::COLORSPACE_RGB, // the colorspace
215                         ((pf&PF_A)==PF_A), // has alpha?
216                         8, // bits per sample
217                         dw,     // width
218                         dh,     // height
219                         dw*synfig::channels(pf), // stride (pitch)
220                         SigC::slot(freegu8)
221                 );
222         }
223         else
224         {
225                 //synfig::warning("Nav: Don't need to resize");
226                 //convert into our buffered dataS
227                 //synfig::warning("Nav: converting color format into buffer");
228                 if(prev) //just in case we're stupid
229                 {
230                         convert_color_format((unsigned char *)prev->get_pixels(), (*surface)[0], dw*dh, pf, App::gamma);
231                 }
232         }
233         queue_draw();   
234 }
235
236 /*      zoom slider is on exponential scale
237
238         map: -4,4 -> small number,1600 with 100 at 0
239
240         f(x) = 100*2^x
241 */
242
243 static double unit_to_zoom(double f)
244 {
245         return pow(2.0,f);
246 }
247
248 static double zoom_to_unit(double f)
249 {
250         if(f > 0)
251         {
252                 return log(f) / log_10_2;
253         }else return -999999.0;
254 }
255
256 bool studio::Widget_NavView::on_expose_draw(GdkEventExpose *exp)
257 {
258         //print out the zoom
259         //HACK kind of...
260         //zoom_print.set_text(strprintf("%.1f%%",100*unit_to_zoom(adj_zoom.get_value())));
261         
262         //draw the good stuff
263         on_start_render();
264         
265         //if we've got a preview etc. display it...
266         if(get_canvas_view() && prev)
267         {
268                 //axis transform from units to pixel coords
269                 float xaxis = 0, yaxis = 0;
270                 
271                 int canvw = get_canvas_view()->get_canvas()->rend_desc().get_w();
272                 //int canvh = get_canvas_view()->get_canvas()->rend_desc().get_h();
273                 
274                 float pw = get_canvas_view()->get_canvas()->rend_desc().get_pw();
275                 float ph = get_canvas_view()->get_canvas()->rend_desc().get_ph();
276                 
277                 int w = prev->get_width();
278                 int h = prev->get_height();
279                                 
280                 //scale up/down to the nearest pixel ratio...
281                 //and center in center
282                 int offx=0, offy=0;
283                 
284                 float sx, sy;
285                 int nw,nh;
286                 
287                 sx = drawto.get_width() / (float)w;
288                 sy = drawto.get_height() / (float)h;
289                 
290                 //synfig::warning("Nav redraw: now to scale the bitmap: %.3f x %.3f",sx,sy);
291                 
292                 //round to smallest scale (fit entire thing in window without distortion)
293                 if(sx > sy) sx = sy;
294                 //else sy = sx;
295                 
296                 //scaling and stuff
297                 // the point to navpixel space conversion should be:
298                 //              (navpixels / canvpixels) * (canvpixels / canvsize)
299                 //      or (navpixels / prevpixels) * (prevpixels / navpixels)
300                 xaxis = sx * w / (float)canvw;
301                 yaxis = xaxis/ph;
302                 xaxis /= pw;
303                 
304                 //scale to a new pixmap and then copy over to the window
305                 nw = (int)(w*sx);
306                 nh = (int)(h*sx);
307                 
308                 //must now center to be cool
309                 offx = (drawto.get_width() - nw)/2;
310                 offy = (drawto.get_height() - nh)/2;
311                 
312                 //trivial escape
313                 if(nw == 0 || nh == 0)return true;
314                                                         
315                 //draw to drawing area
316                 Glib::RefPtr<Gdk::GC>   gc = Gdk::GC::create(drawto.get_window());
317                 
318                 //synfig::warning("Nav: Scaling pixmap to off (%d,%d) with size (%d,%d)", offx,offy,nw, nh);
319                 Glib::RefPtr<Gdk::Pixbuf> scalepx = prev->scale_simple(nw,nh,Gdk::INTERP_NEAREST);
320                 
321                 //synfig::warning("Nav: Drawing scaled bitmap");
322                 drawto.get_window()->draw_pixbuf(
323                         gc, //GC
324                         scalepx, //pixbuf
325                         0, 0,   // Source X and Y
326                         offx, offy,     // Dest X and Y
327                         -1,-1,  // Width and Height
328                         Gdk::RGB_DITHER_MAX, // RgbDither
329                         2, 2 // Dither offset X and Y
330                 );
331                                 
332                 //draw fancy red rectangle around focus point
333                 const Point &wtl = get_canvas_view()->work_area->get_window_tl(),
334                                         &wbr = get_canvas_view()->work_area->get_window_br();
335                 
336                 gc->set_rgb_fg_color(Gdk::Color("#ff0000"));
337                 gc->set_line_attributes(2,Gdk::LINE_SOLID,Gdk::CAP_BUTT,Gdk::JOIN_MITER);
338                 
339                 //it must be clamped to the drawing area though
340                 int l=0,rw=0,t=0,rh=0;
341                 const Point fp = -get_canvas_view()->work_area->get_focus_point();
342                 
343                 //get focus point in normal space
344                 rw = (int)(abs((wtl[0]-wbr[0])*xaxis));
345                 rh = (int)(abs((wtl[1]-wbr[1])*yaxis));
346
347                 //transform into pixel space
348                 l = (int)(drawto.get_width()/2 + fp[0]*xaxis - rw/2);
349                 t = (int)(drawto.get_height()/2 + fp[1]*yaxis - rh/2);
350         
351                 //coord system:
352                 // tl : (offx,offy)
353                 // axis multipliers = xaxis,yaxis
354                 //synfig::warning("Nav: tl (%f,%f), br (%f,%f)", wtl[0],wtl[1],wbr[0],wbr[1]);
355                 //synfig::warning("Nav: tl (%f,%f), br (%f,%f)", wtl[0],wtl[1],wbr[0],wbr[1]);
356                 //synfig::warning("Nav: Drawing Rectangle (%d,%d) with dim (%d,%d)", l,t,rw,rh);
357                 drawto.get_window()->draw_rectangle(gc,false,l,t,rw,rh);
358         }
359         
360         return false; //draw everything else too
361 }
362
363 void studio::Widget_NavView::on_dirty_preview()
364 {
365         dirty = true;
366         queue_draw();
367 }
368
369 bool studio::Widget_NavView::on_scroll_event(GdkEvent *event)
370 {       
371         if(get_canvas_view() && get_canvas_view()->get_work_area())
372         {
373                 double z = unit_to_zoom(adj_zoom.get_value());
374                 
375                 switch(event->type)
376                 {
377                         case GDK_BUTTON_PRESS:
378                         {
379                                 if(event->button.button == 1)
380                                 {
381                                         scrolling = true;
382                                         get_canvas_view()->get_work_area()->set_zoom(z);                                        
383                                         scrolling = false;
384                                 }
385                                 break;
386                         }
387                         
388                         case GDK_MOTION_NOTIFY:
389                         {
390                                 if(Gdk::ModifierType(event->motion.state) & Gdk::BUTTON1_MASK)
391                                 {
392                                         scrolling = true;
393                                         get_canvas_view()->get_work_area()->set_zoom(z);
394                                         scrolling = false;
395                                 }
396                                 break;
397                         }
398                         
399                         default:
400                                 break;
401                 }
402         }
403         
404         return false;
405 }
406
407 void studio::Widget_NavView::on_number_modify()
408 {
409         double z = unit_to_zoom(adj_zoom.get_value());
410         zoom_print.set_text(strprintf("%.1f%%",z*100.0));       
411         //synfig::warning("Updating zoom to %f",adj_zoom.get_value());
412         
413         if(get_canvas_view() && z != get_canvas_view()->get_work_area()->get_zoom())
414         {
415                 scrolling = true;
416                 get_canvas_view()->get_work_area()->set_zoom(z);
417                 scrolling = false;
418         }
419 }
420
421 void studio::Widget_NavView::on_workarea_view_change()
422 {
423         double wz = get_canvas_view()->get_work_area()->get_zoom();
424         double z = zoom_to_unit(wz);
425
426         //synfig::warning("Updating zoom to %f -> %f",wz,z);
427         if(!scrolling && z != adj_zoom.get_value())
428         {
429                 adj_zoom.set_value(z);
430                 //adj_zoom.value_changed();
431         }
432         queue_draw();
433 }
434
435 bool studio::Widget_NavView::on_mouse_event(GdkEvent * e)
436 {
437         Point p;
438         bool    setpos = false;
439                 
440         if(e->type == GDK_BUTTON_PRESS && e->button.button == 1)
441         {
442                 p[0] = e->button.x - drawto.get_width()/2;
443                 p[1] = e->button.y - drawto.get_height()/2;
444                 
445                 setpos = true;
446         }
447         
448         if(e->type == GDK_MOTION_NOTIFY && (Gdk::ModifierType(e->motion.state) & Gdk::BUTTON1_MASK))
449         {               
450                 p[0] = e->motion.x - drawto.get_width()/2;
451                 p[1] = e->motion.y - drawto.get_height()/2;     
452                 
453                 setpos = true;
454         }
455         
456         if(setpos && prev && get_canvas_view())
457         {
458                 const Point &tl = get_canvas_view()->get_canvas()->rend_desc().get_tl();
459                 const Point &br = get_canvas_view()->get_canvas()->rend_desc().get_br();
460                 
461                 float max = abs((br[0]-tl[0]) / drawto.get_width());
462                 
463                 if((prev->get_width() / drawto.get_width()) < (prev->get_height() / drawto.get_height()))
464                         max = abs((br[1]-tl[1]) / drawto.get_height());
465                 
466                 float signx = (br[0]-tl[0]) < 0 ? -1 : 1;
467                 float signy = (br[1]-tl[1]) < 0 ? -1 : 1;
468                 
469                 Point pos;
470                 
471                 pos[0] = p[0] * max * signx;
472                 pos[1] = p[1] * max * signy;
473                 
474                 get_canvas_view()->get_work_area()->set_focus_point(-pos);
475                 
476                 return true;
477         }
478         
479         return false;
480 }
481
482 //Navigator Dock Definitions
483
484 studio::Dock_Navigator::Dock_Navigator()
485 :Dock_CanvasSpecific("navigator",_("Navigator"),Gtk::StockID("synfig-navigator"))
486 {
487         add(dummy);
488 }
489
490 studio::Dock_Navigator::~Dock_Navigator()
491 {
492 }
493
494 void studio::Dock_Navigator::changed_canvas_view_vfunc(etl::loose_handle<CanvasView> canvas_view)
495 {       
496         if(canvas_view)
497         {               
498                 Widget *v = canvas_view->get_ext_widget("navview");
499                 
500                 if(!v)
501                 {
502                         v = new Widget_NavView(canvas_view);
503                         canvas_view->set_ext_widget("navview",v);
504                 }
505
506                 add(*v);
507         }else
508         {
509                 clear_previous();
510                 //add(dummy);                   
511         }
512 }