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