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