Set the dialog windows to be 'transient for' the toolbox window. In XFCE4 this makes...
[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         set_transient_for(*App::toolbox);
107
108         // Set up the window
109         //set_type_hint(Gdk::WINDOW_TYPE_HINT_UTILITY);
110         set_title("Dock Dialog");
111
112         // Register with the dock manager
113         App::dock_manager->dock_dialog_list_.push_back(this);
114
115
116         // connect our signals
117         signal_delete_event().connect(
118                 sigc::hide(
119                         sigc::mem_fun(*this,&DockDialog::close)
120                 )
121         );
122
123 /*
124         App::signal_canvas_view_focus().connect(
125                 sigc::hide(
126                         sigc::mem_fun(
127                                 *this,
128                                 &DockDialog::refresh_accel_group
129                         )
130                 )
131         );
132 */
133
134         add_accel_group(App::ui_manager()->get_accel_group());
135         App::signal_present_all().connect(sigc::mem_fun0(*this,&DockDialog::present));
136
137 }
138
139 DockDialog::~DockDialog()
140 {
141         empty_sig.disconnect();
142
143         is_deleting=true;
144
145         DEBUGPOINT();
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                 // 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                 // caues a crash.
162                 manage(dock_book_list.front());
163         }
164
165         // Remove us from the dock manager
166         if(App::dock_manager)try{
167                 std::list<DockDialog*>::iterator iter;
168                 for(iter=App::dock_manager->dock_dialog_list_.begin();iter!=App::dock_manager->dock_dialog_list_.end();++iter)
169                         if(*iter==this)
170                         {
171                                 App::dock_manager->dock_dialog_list_.erase(iter);
172                                 break;
173                         }
174         }
175         catch(...)
176         {
177                 synfig::warning("DockDialog::~DockDialog(): Exception thrown when trying to remove from dock manager...?");
178         }
179
180         delete widget_comp_select;
181
182         DEBUGPOINT();
183 }
184
185 void
186 DockDialog::drop_on_prepend(const Glib::RefPtr<Gdk::DragContext>& context, int, int, const Gtk::SelectionData& selection_data, guint, guint time)
187 {
188         if ((selection_data.get_length() >= 0) && (selection_data.get_format() == 8))
189         {
190                 Dockable& dockable(**reinterpret_cast<Dockable**>(const_cast<guint8*>(selection_data.get_data())));
191                 prepend_dock_book()->add(dockable);
192                 context->drag_finish(true, false, time);
193                 return;
194         }
195
196         context->drag_finish(false, false, time);
197 }
198
199 void
200 DockDialog::drop_on_append(const Glib::RefPtr<Gdk::DragContext>& context, int, int, const Gtk::SelectionData& selection_data, guint, guint time)
201 {
202         if ((selection_data.get_length() >= 0) && (selection_data.get_format() == 8))
203         {
204                 Dockable& dockable(**reinterpret_cast<Dockable**>(const_cast<guint8*>(selection_data.get_data())));
205                 append_dock_book()->add(dockable);
206                 context->drag_finish(true, false, time);
207                 return;
208         }
209
210         context->drag_finish(false, false, time);
211 }
212
213
214 void
215 DockDialog::on_hide()
216 {
217         Gtk::Window::on_hide();
218         close();
219 }
220
221 DockBook*
222 DockDialog::prepend_dock_book()
223 {
224         if(is_deleting)return 0;
225
226         dock_book_list.push_front(new DockBook);
227         last_dock_book=dock_book_list.front();
228
229
230         last_dock_book->signal_empty().connect(
231                 sigc::bind(
232                         sigc::mem_fun(*this,&DockDialog::erase_dock_book),
233                         last_dock_book
234                 )
235         );
236
237         dock_book_sizes_.insert(dock_book_sizes_.begin(),225);
238         refresh();
239         return last_dock_book;
240 }
241
242 DockBook*
243 DockDialog::append_dock_book()
244 {
245         if(is_deleting)return 0;
246
247         dock_book_list.push_back(new DockBook);
248         last_dock_book=dock_book_list.back();
249         last_dock_book->signal_empty().connect(
250                 sigc::bind(
251                         sigc::mem_fun(*this,&DockDialog::erase_dock_book),
252                         last_dock_book
253                 )
254         );
255         last_dock_book->signal_changed().connect(
256                 sigc::mem_fun(*this,&DockDialog::refresh_title)
257         );
258         last_dock_book->signal_changed().connect(
259                 sigc::mem_fun(*this,&DockDialog::refresh_title)
260         );
261         dock_book_sizes_.push_back(225);
262
263         //last_dock_book->show();
264         refresh();
265         return last_dock_book;
266 }
267
268 void
269 DockDialog::erase_dock_book(DockBook* dock_book)
270 {
271         if(is_deleting)return;
272
273         std::list<DockBook*>::iterator iter;
274         for(iter=dock_book_list.begin();iter!=dock_book_list.end();++iter)
275                 if(*iter==dock_book)
276                 {
277                         dock_book_list.erase(iter);
278
279                         if(dock_book_list.empty())
280                         {
281                                 last_dock_book=0;
282                                 close();
283                                 return;
284                         }
285                         else
286                         {
287                                 if(last_dock_book==dock_book)
288                                         last_dock_book=dock_book_list.front();
289                         }
290
291                         refresh();
292
293                         return;
294                 }
295 }
296
297 void
298 DockDialog::refresh()
299 {
300         synfig::info("dock_book_list.size()=%d",dock_book_list.size());
301         //remove();
302
303         if(dock_book_list.empty())
304                 return;
305
306         if(box)delete box;
307         box=(manage(is_horizontal?(Gtk::Box*)new Gtk::HBox:(Gtk::Box*)new Gtk::VBox));
308         add(*box);
309
310         box->pack_start(*widget_comp_select,false,true);
311
312         Gtk::Button* append_button(manage(new Gtk::Button));
313         Gtk::Button* prepend_button(manage(new Gtk::Button));
314
315         std::list<Gtk::TargetEntry> listTargets;
316         listTargets.push_back( Gtk::TargetEntry("DOCK") );
317
318         append_button->drag_dest_set(listTargets);
319         prepend_button->drag_dest_set(listTargets);
320
321         append_button->signal_drag_data_received().connect(
322                 sigc::mem_fun(*this,&DockDialog::drop_on_append)
323         );
324
325         prepend_button->signal_drag_data_received().connect(
326                 sigc::mem_fun(*this,&DockDialog::drop_on_prepend)
327         );
328
329         box->pack_start(*prepend_button,false,true);
330         box->pack_end(*append_button,false,true);
331
332         //prepend_button->show();
333         //append_button->show();
334         pannels_.clear();
335
336         if(dock_book_list.size()==1)
337         {
338                 box->pack_start(get_dock_book(),true,true);
339         }
340         else
341         {
342                 Gtk::Paned* parent(manage(is_horizontal?(Gtk::Paned*)new Gtk::HPaned:(Gtk::Paned*)new Gtk::VPaned));
343
344                 pannels_.push_back(parent);
345
346                 if(pannels_.size()<=dock_book_sizes_.size())
347                         pannels_.back()->set_position(dock_book_sizes_[pannels_.size()-1]);
348                 pannels_.back()->property_position().signal_changed().connect(
349                         sigc::mem_fun(*this,&DockDialog::rebuild_sizes)
350                 );
351                 //parent->show();
352                 parent->add1(*dock_book_list.front());
353                 //dock_book_list.front()->show();
354
355                 box->pack_start(*parent,true,true);
356
357                 std::list<DockBook*>::iterator iter,next;
358                 for(next=dock_book_list.begin(),next++,iter=next++;next!=dock_book_list.end();iter=next++)
359                 {
360                         Gtk::Paned* current(manage(is_horizontal?(Gtk::Paned*)new Gtk::HPaned:(Gtk::Paned*)new Gtk::VPaned));
361                         pannels_.push_back(current);
362
363                         if(pannels_.size()<=dock_book_sizes_.size())
364                                 pannels_.back()->set_position(dock_book_sizes_[pannels_.size()-1]);
365                         pannels_.back()->property_position().signal_changed().connect(
366                                 sigc::mem_fun(*this,&DockDialog::rebuild_sizes)
367                         );
368
369
370                         parent->add2(*current);
371
372                         current->add1(**iter);
373                         //(*iter)->show();
374                         //current->show();
375
376                         parent=current;
377                 }
378                 parent->add2(**iter);
379                 //(*iter)->show();
380         }
381
382         box->show_all();
383         if(!composition_selector_)
384                 widget_comp_select->hide();
385         rebuild_sizes();
386 }
387
388 void
389 DockDialog::rebuild_sizes()
390 {
391         unsigned int i=0;
392         dock_book_sizes_.clear();
393         for(i=0;i<pannels_.size();i++)
394         {
395                 dock_book_sizes_.push_back(pannels_[i]->get_position());
396         }
397 }
398
399 void
400 DockDialog::set_dock_book_sizes(const std::vector<int>& new_sizes)
401 {
402         unsigned int i=0;
403         for(i=0;i<pannels_.size() && i<new_sizes.size();i++)
404         {
405                 pannels_[i]->set_position(new_sizes[i]);
406         }
407         dock_book_sizes_=new_sizes;
408         //rebuild_sizes();
409 }
410
411 void
412 DockDialog::refresh_accel_group()
413 {
414 /*
415         if(last_accel_group_)
416         {
417                 last_accel_group_->unlock();
418                 remove_accel_group(last_accel_group_);
419                 last_accel_group_=Glib::RefPtr<Gtk::AccelGroup>();
420         }
421
422         etl::loose_handle<CanvasView> canvas_view(App::get_selected_canvas_view());
423         if(canvas_view)
424         {
425                 last_accel_group_=canvas_view->get_accel_group();
426                 last_accel_group_->lock();
427                 add_accel_group(last_accel_group_);
428         }
429 */
430         etl::loose_handle<CanvasView> canvas_view(App::get_selected_canvas_view());
431         if(canvas_view)
432         {
433                 canvas_view->mainmenu.accelerate(*this);
434         }
435 }
436
437 bool
438 DockDialog::close()
439 {
440         synfig::info("DockDialog::close(): DELETED!");
441         empty_sig.disconnect();
442         //get_dock_book().clear();
443         delete this;
444         return true;
445 }
446
447 DockBook&
448 DockDialog::get_dock_book()
449 {
450         if(!last_dock_book)
451                 return *append_dock_book();
452         return *last_dock_book;
453 }
454
455 const DockBook&
456 DockDialog::get_dock_book()const
457 {
458         return *last_dock_book;
459 }
460
461
462 synfig::String
463 DockDialog::get_contents()const
464 {
465         synfig::String ret;
466
467         std::list<DockBook*>::const_iterator iter;
468         for(iter=dock_book_list.begin();iter!=dock_book_list.end();++iter)
469         {
470                 if(!ret.empty())
471                         ret+=is_horizontal?" | ":" - ";
472                 ret+=(*iter)->get_contents();
473         }
474
475
476         return ret;
477 }
478
479 void
480 DockDialog::set_contents(const synfig::String& z)
481 {
482         int x,y;
483         get_size(x,y);
484
485         synfig::String str(z);
486         while(!str.empty())
487         {
488                 synfig::String::size_type separator=str.find_first_of('-');
489                 {
490                         synfig::String::size_type sep2=str.find_first_of('|');
491                         if(separator!=synfig::String::npos || sep2!=synfig::String::npos)
492                         {
493                                 if((separator==synfig::String::npos || sep2<separator) && sep2!=synfig::String::npos)
494                                 {
495                                         separator=sep2;
496                                         is_horizontal=true;
497                                 }
498                                 else
499                                         is_horizontal=false;
500                         }
501                 }
502
503                 synfig::String book_contents;
504                 if(separator==synfig::String::npos)
505                 {
506                         book_contents=str;
507                         str.clear();
508                 }
509                 else
510                 {
511                         book_contents=String(str.begin(),str.begin()+separator);
512                         str=String(str.begin()+separator+1,str.end());
513                 }
514
515                 try
516                 {
517                         append_dock_book()->set_contents(book_contents);
518                 }catch(...) { }
519         }
520
521         resize(x,y);
522 }
523
524 void
525 DockDialog::set_composition_selector(bool x)
526 {
527         if(x==get_composition_selector())
528                 return;
529         composition_selector_=x;
530         if(x)
531                 widget_comp_select->show();
532         else
533                 widget_comp_select->hide();
534 }
535
536 void
537 DockDialog::refresh_title()
538 {
539         if(is_deleting)return;
540         if(dock_book_list.size())
541         {
542                 synfig::String title;
543
544                 std::list<DockBook*>::const_iterator iter;
545                 for(iter=dock_book_list.begin();iter!=dock_book_list.end();++iter)
546                 {
547                         if(!title.empty())
548                                 title+=", ";
549                         title+=(*iter)->get_local_contents();
550                 }
551                 set_title(title);
552         }
553         else
554                 set_title(_("Empty Dock Dialog"));
555 }