Fix a crash when running single-threaded and dragging the time slider.
[synfig.git] / synfig-studio / trunk / src / gtkmm / dockdialog.cpp
1 /* === S Y N F I G ========================================================= */
2 /*!     \file dockdialog.cpp
3 **      \brief Template File
4 **
5 **      $Id$
6 **
7 **      \legal
8 **      Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
9 **      Copyright (c) 2007 Chris Moore
10 **
11 **      This package is free software; you can redistribute it and/or
12 **      modify it under the terms of the GNU General Public License as
13 **      published by the Free Software Foundation; either version 2 of
14 **      the License, or (at your option) any later version.
15 **
16 **      This package is distributed in the hope that it will be useful,
17 **      but WITHOUT ANY WARRANTY; without even the implied warranty of
18 **      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 **      General Public License for more details.
20 **      \endlegal
21 */
22 /* ========================================================================= */
23
24 /* === H E A D E R S ======================================================= */
25
26 #ifdef USING_PCH
27 #       include "pch.h"
28 #else
29 #ifdef HAVE_CONFIG_H
30 #       include <config.h>
31 #endif
32
33 #include "app.h"
34 #include <sigc++/adaptors/hide.h>
35
36 #include "dockdialog.h"
37 #include "dockbook.h"
38 #include "dockmanager.h"
39 #include "toolbox.h"
40 #include "widget_compselect.h"
41 #include <synfig/general.h>
42 #include <synfig/uniqueid.h>
43 #include <gtkmm/table.h>
44 #include <sigc++/hide.h>
45 #include <sigc++/slot.h>
46 #include <sigc++/retype_return.h>
47 #include <sigc++/retype.h>
48 #include "canvasview.h"
49 #include <gtkmm/paned.h>
50 #include <gtkmm/box.h>
51 #include <synfigapp/main.h>
52
53 #endif
54
55 /* === U S I N G =========================================================== */
56
57 using namespace std;
58 using namespace etl;
59 using namespace synfig;
60 using namespace studio;
61
62 /* === M A C R O S ========================================================= */
63
64 #define GRAB_HINT_DATA(y,default)       { \
65                 String x; \
66                 if(synfigapp::Main::settings().get_value(String("pref.")+y+"_hints",x)) \
67                 { \
68                         set_type_hint((Gdk::WindowTypeHint)atoi(x.c_str()));    \
69                 } else {\
70                         set_type_hint(default); \
71                 } \
72         }
73
74 /* === G L O B A L S ======================================================= */
75
76 /* === P R O C E D U R E S ================================================= */
77
78 /* === M E T H O D S ======================================================= */
79
80 DockDialog::DockDialog():
81         Gtk::Window(Gtk::WINDOW_TOPLEVEL)
82 {
83         composition_selector_=false;
84         is_deleting=false;
85         is_horizontal=false;
86         last_dock_book=0;
87         box=0;
88
89         widget_comp_select=new Widget_CompSelect();
90
91         // Give ourselves an ID that is most likely unique
92         set_id(synfig::UniqueID().get_uid()^reinterpret_cast<long>(this));
93
94         set_role(strprintf("dock_dialog_%d",get_id()));
95         GRAB_HINT_DATA(
96                 "dock_dialog",
97 #ifdef __APPLE__
98                 Gdk::WINDOW_TYPE_HINT_NORMAL
99 #else
100                 Gdk::WINDOW_TYPE_HINT_UTILITY
101 #endif
102         );
103         set_keep_above(false);
104
105         //! \todo can we set dialog windows transient for all normal windows, not just the toolbox?
106         //! paragraph 3 of http://standards.freedesktop.org/wm-spec/1.3/ar01s07.html suggests we can
107         // this seems to have bad effects on KDE, so leave it disabled by default
108         if(getenv("SYNFIG_TRANSIENT_DIALOGS"))
109                 set_transient_for(*App::toolbox);
110
111         // Set up the window
112         //set_type_hint(Gdk::WINDOW_TYPE_HINT_UTILITY);
113         set_title("Dock Dialog");
114
115         // Register with the dock manager
116         App::dock_manager->dock_dialog_list_.push_back(this);
117
118
119         // connect our signals
120         signal_delete_event().connect(
121                 sigc::hide(
122                         sigc::mem_fun(*this,&DockDialog::close)
123                 )
124         );
125
126 /*
127         App::signal_canvas_view_focus().connect(
128                 sigc::hide(
129                         sigc::mem_fun(
130                                 *this,
131                                 &DockDialog::refresh_accel_group
132                         )
133                 )
134         );
135 */
136
137         add_accel_group(App::ui_manager()->get_accel_group());
138         App::signal_present_all().connect(sigc::mem_fun0(*this,&DockDialog::present));
139
140 }
141
142 DockDialog::~DockDialog()
143 {
144         empty_sig.disconnect();
145
146         is_deleting=true;
147
148         // Remove all of the dock books
149         for(;!dock_book_list.empty();dock_book_list.pop_front())
150         {
151                 dock_book_list.front()->clear();
152
153                 //! \fixme: UGLY HACK
154                 // The following line really should be uncommented,
155                 // but it causes crashes. Without it, a small
156                 // memory hole is created--but at least it doesn't crash
157                 // delete dock_book_list.front();
158
159                 // Oddly enough, the following line should
160                 // theoreticly do the same thing after this
161                 // class is destroyed, but it doesn't seem to
162                 // cause a crash.  It does, however, trigger this warning:
163                 //
164                 //   A floating object was finalized. This means that someone
165                 //   called g_object_unref() on an object that had only a
166                 //   floating reference; the initial floating reference is not
167                 //   owned by anyone and must be removed with g_object_ref_sink().
168                 //
169                 // manage(dock_book_list.front());
170         }
171
172         // Remove us from the dock manager
173         if(App::dock_manager)try{
174                 std::list<DockDialog*>::iterator iter;
175                 for(iter=App::dock_manager->dock_dialog_list_.begin();iter!=App::dock_manager->dock_dialog_list_.end();++iter)
176                         if(*iter==this)
177                         {
178                                 App::dock_manager->dock_dialog_list_.erase(iter);
179                                 break;
180                         }
181         }
182         catch(...)
183         {
184                 synfig::warning("DockDialog::~DockDialog(): Exception thrown when trying to remove from dock manager...?");
185         }
186
187         delete widget_comp_select;
188 }
189
190 void
191 DockDialog::drop_on_prepend(const Glib::RefPtr<Gdk::DragContext>& context, int, int, const Gtk::SelectionData& selection_data, guint, guint time)
192 {
193         if ((selection_data.get_length() >= 0) && (selection_data.get_format() == 8))
194         {
195                 Dockable& dockable(**reinterpret_cast<Dockable**>(const_cast<guint8*>(selection_data.get_data())));
196                 prepend_dock_book()->add(dockable);
197                 context->drag_finish(true, false, time);
198                 return;
199         }
200
201         context->drag_finish(false, false, time);
202 }
203
204 void
205 DockDialog::drop_on_append(const Glib::RefPtr<Gdk::DragContext>& context, int, int, const Gtk::SelectionData& selection_data, guint, guint time)
206 {
207         if ((selection_data.get_length() >= 0) && (selection_data.get_format() == 8))
208         {
209                 Dockable& dockable(**reinterpret_cast<Dockable**>(const_cast<guint8*>(selection_data.get_data())));
210                 append_dock_book()->add(dockable);
211                 context->drag_finish(true, false, time);
212                 return;
213         }
214
215         context->drag_finish(false, false, time);
216 }
217
218
219 void
220 DockDialog::on_hide()
221 {
222         Gtk::Window::on_hide();
223         close();
224 }
225
226 DockBook*
227 DockDialog::prepend_dock_book()
228 {
229         if(is_deleting)return 0;
230
231         dock_book_list.push_front(new DockBook);
232         last_dock_book=dock_book_list.front();
233
234
235         last_dock_book->signal_empty().connect(
236                 sigc::bind(
237                         sigc::mem_fun(*this,&DockDialog::erase_dock_book),
238                         last_dock_book
239                 )
240         );
241
242         dock_book_sizes_.insert(dock_book_sizes_.begin(),225);
243         refresh();
244         return last_dock_book;
245 }
246
247 DockBook*
248 DockDialog::append_dock_book()
249 {
250         if(is_deleting)return 0;
251
252         dock_book_list.push_back(new DockBook);
253         last_dock_book=dock_book_list.back();
254         last_dock_book->signal_empty().connect(
255                 sigc::bind(
256                         sigc::mem_fun(*this,&DockDialog::erase_dock_book),
257                         last_dock_book
258                 )
259         );
260         last_dock_book->signal_changed().connect(
261                 sigc::mem_fun(*this,&DockDialog::refresh_title)
262         );
263         last_dock_book->signal_changed().connect(
264                 sigc::mem_fun(*this,&DockDialog::refresh_title)
265         );
266         dock_book_sizes_.push_back(225);
267
268         //last_dock_book->show();
269         refresh();
270         return last_dock_book;
271 }
272
273 void
274 DockDialog::erase_dock_book(DockBook* dock_book)
275 {
276         if(is_deleting)return;
277
278         std::list<DockBook*>::iterator iter;
279         for(iter=dock_book_list.begin();iter!=dock_book_list.end();++iter)
280                 if(*iter==dock_book)
281                 {
282                         dock_book_list.erase(iter);
283
284                         if(dock_book_list.empty())
285                         {
286                                 last_dock_book=0;
287                                 close();
288                                 return;
289                         }
290                         else
291                         {
292                                 if(last_dock_book==dock_book)
293                                         last_dock_book=dock_book_list.front();
294                         }
295
296                         refresh();
297
298                         return;
299                 }
300 }
301
302 void
303 DockDialog::refresh()
304 {
305         // synfig::info("dock_book_list.size()=%d",dock_book_list.size());
306         //remove();
307
308         if(dock_book_list.empty())
309                 return;
310
311         if(box)delete box;
312         box=(manage(is_horizontal?(Gtk::Box*)new Gtk::HBox:(Gtk::Box*)new Gtk::VBox));
313         add(*box);
314
315         box->pack_start(*widget_comp_select,false,true);
316
317         Gtk::Button* append_button(manage(new Gtk::Button));
318         Gtk::Button* prepend_button(manage(new Gtk::Button));
319
320         std::list<Gtk::TargetEntry> listTargets;
321         listTargets.push_back( Gtk::TargetEntry("DOCK") );
322
323         append_button->drag_dest_set(listTargets);
324         prepend_button->drag_dest_set(listTargets);
325
326         append_button->signal_drag_data_received().connect(
327                 sigc::mem_fun(*this,&DockDialog::drop_on_append)
328         );
329
330         prepend_button->signal_drag_data_received().connect(
331                 sigc::mem_fun(*this,&DockDialog::drop_on_prepend)
332         );
333
334         box->pack_start(*prepend_button,false,true);
335         box->pack_end(*append_button,false,true);
336
337         //prepend_button->show();
338         //append_button->show();
339         pannels_.clear();
340
341         if(dock_book_list.size()==1)
342         {
343                 box->pack_start(get_dock_book(),true,true);
344         }
345         else
346         {
347                 Gtk::Paned* parent(manage(is_horizontal?(Gtk::Paned*)new Gtk::HPaned:(Gtk::Paned*)new Gtk::VPaned));
348
349                 pannels_.push_back(parent);
350
351                 if(pannels_.size()<=dock_book_sizes_.size())
352                         pannels_.back()->set_position(dock_book_sizes_[pannels_.size()-1]);
353                 pannels_.back()->property_position().signal_changed().connect(
354                         sigc::mem_fun(*this,&DockDialog::rebuild_sizes)
355                 );
356                 //parent->show();
357                 parent->add1(*dock_book_list.front());
358                 //dock_book_list.front()->show();
359
360                 box->pack_start(*parent,true,true);
361
362                 std::list<DockBook*>::iterator iter,next;
363                 for(next=dock_book_list.begin(),next++,iter=next++;next!=dock_book_list.end();iter=next++)
364                 {
365                         Gtk::Paned* current(manage(is_horizontal?(Gtk::Paned*)new Gtk::HPaned:(Gtk::Paned*)new Gtk::VPaned));
366                         pannels_.push_back(current);
367
368                         if(pannels_.size()<=dock_book_sizes_.size())
369                                 pannels_.back()->set_position(dock_book_sizes_[pannels_.size()-1]);
370                         pannels_.back()->property_position().signal_changed().connect(
371                                 sigc::mem_fun(*this,&DockDialog::rebuild_sizes)
372                         );
373
374
375                         parent->add2(*current);
376
377                         current->add1(**iter);
378                         //(*iter)->show();
379                         //current->show();
380
381                         parent=current;
382                 }
383                 parent->add2(**iter);
384                 //(*iter)->show();
385         }
386
387         box->show_all();
388         if(!composition_selector_)
389                 widget_comp_select->hide();
390         rebuild_sizes();
391 }
392
393 void
394 DockDialog::rebuild_sizes()
395 {
396         unsigned int i=0;
397         dock_book_sizes_.clear();
398         for(i=0;i<pannels_.size();i++)
399         {
400                 dock_book_sizes_.push_back(pannels_[i]->get_position());
401         }
402 }
403
404 void
405 DockDialog::set_dock_book_sizes(const std::vector<int>& new_sizes)
406 {
407         unsigned int i=0;
408         for(i=0;i<pannels_.size() && i<new_sizes.size();i++)
409         {
410                 pannels_[i]->set_position(new_sizes[i]);
411         }
412         dock_book_sizes_=new_sizes;
413         //rebuild_sizes();
414 }
415
416 void
417 DockDialog::refresh_accel_group()
418 {
419 /*
420         if(last_accel_group_)
421         {
422                 last_accel_group_->unlock();
423                 remove_accel_group(last_accel_group_);
424                 last_accel_group_=Glib::RefPtr<Gtk::AccelGroup>();
425         }
426
427         etl::loose_handle<CanvasView> canvas_view(App::get_selected_canvas_view());
428         if(canvas_view)
429         {
430                 last_accel_group_=canvas_view->get_accel_group();
431                 last_accel_group_->lock();
432                 add_accel_group(last_accel_group_);
433         }
434 */
435         etl::loose_handle<CanvasView> canvas_view(App::get_selected_canvas_view());
436         if(canvas_view)
437         {
438                 canvas_view->mainmenu.accelerate(*this);
439         }
440 }
441
442 bool
443 DockDialog::close()
444 {
445         synfig::info("DockDialog::close(): DELETED!");
446         empty_sig.disconnect();
447         //get_dock_book().clear();
448         delete this;
449         return true;
450 }
451
452 DockBook&
453 DockDialog::get_dock_book()
454 {
455         if(!last_dock_book)
456                 return *append_dock_book();
457         return *last_dock_book;
458 }
459
460 const DockBook&
461 DockDialog::get_dock_book()const
462 {
463         return *last_dock_book;
464 }
465
466
467 synfig::String
468 DockDialog::get_contents()const
469 {
470         synfig::String ret;
471
472         std::list<DockBook*>::const_iterator iter;
473         for(iter=dock_book_list.begin();iter!=dock_book_list.end();++iter)
474         {
475                 if(!ret.empty())
476                         ret+=is_horizontal?" | ":" - ";
477                 ret+=(*iter)->get_contents();
478         }
479
480
481         return ret;
482 }
483
484 void
485 DockDialog::set_contents(const synfig::String& z)
486 {
487         int x,y;
488         get_size(x,y);
489
490         synfig::String str(z);
491         while(!str.empty())
492         {
493                 synfig::String::size_type separator=str.find_first_of('-');
494                 {
495                         synfig::String::size_type sep2=str.find_first_of('|');
496                         if(separator!=synfig::String::npos || sep2!=synfig::String::npos)
497                         {
498                                 if((separator==synfig::String::npos || sep2<separator) && sep2!=synfig::String::npos)
499                                 {
500                                         separator=sep2;
501                                         is_horizontal=true;
502                                 }
503                                 else
504                                         is_horizontal=false;
505                         }
506                 }
507
508                 synfig::String book_contents;
509                 if(separator==synfig::String::npos)
510                 {
511                         book_contents=str;
512                         str.clear();
513                 }
514                 else
515                 {
516                         book_contents=String(str.begin(),str.begin()+separator);
517                         str=String(str.begin()+separator+1,str.end());
518                 }
519
520                 try
521                 {
522                         append_dock_book()->set_contents(book_contents);
523                 }catch(...) { }
524         }
525
526         resize(x,y);
527 }
528
529 void
530 DockDialog::set_composition_selector(bool x)
531 {
532         if(x==get_composition_selector())
533                 return;
534         composition_selector_=x;
535         if(x)
536                 widget_comp_select->show();
537         else
538                 widget_comp_select->hide();
539 }
540
541 void
542 DockDialog::refresh_title()
543 {
544         if(is_deleting)return;
545         if(dock_book_list.size())
546         {
547                 synfig::String title;
548
549                 std::list<DockBook*>::const_iterator iter;
550                 for(iter=dock_book_list.begin();iter!=dock_book_list.end();++iter)
551                 {
552                         if(!title.empty())
553                                 title+=", ";
554                         title+=(*iter)->get_local_contents();
555                 }
556                 set_title(title);
557         }
558         else
559                 set_title(_("Empty Dock Dialog"));
560 }