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