1 /* === S Y N F I G ========================================================= */
2 /*! \file dockmanager.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 <synfig/general.h>
34
35 #include "docks/dockmanager.h"
36 #include <stdexcept>
37 #include "docks/dockable.h"
38 #include "docks/dockbook.h"
39 #include "docks/dockdialog.h"
40 #include <synfigapp/settings.h>
41 #include <synfigapp/main.h>
42 #include <gdkmm/general.h>
43
44 #include <gui/localization.h>
45
46 #include <gtkmm/paned.h>
47 #include <gtkmm/box.h>
48 #include <gtkmm/window.h>
49
50 #include "app.h"
51 #include "mainwindow.h"
52 #include "canvasview.h"
53
54 #endif
55
56 /* === U S I N G =========================================================== */
57
58 using namespace std;
59 using namespace etl;
60 using namespace synfig;
61 using namespace studio;
62
63 /* === M A C R O S ========================================================= */
64
65 /* === P R O C E D U R E S ================================================= */
66
67 std::map<Gtk::Container*, bool> DockManager::containers_to_remove_;
68
69 namespace studio {
70 class DockLinkPoint {
71 public:
72 Gtk::Bin *bin;
73 Gtk::Paned *paned;
74 Gtk::Window *window;
75 bool is_first;
76
DockLinkPoint()77 DockLinkPoint(): bin(NULL), paned(NULL), window(NULL), is_first(false) { }
DockLinkPoint(Gtk::Bin * bin)78 explicit DockLinkPoint(Gtk::Bin *bin): bin(bin), paned(NULL), window(NULL), is_first(false) { }
DockLinkPoint(Gtk::Paned * paned,bool is_first)79 explicit DockLinkPoint(Gtk::Paned *paned, bool is_first): bin(NULL), paned(paned), window(NULL), is_first(is_first) { }
DockLinkPoint(Gtk::Window * window)80 explicit DockLinkPoint(Gtk::Window *window): bin(NULL), paned(NULL), window(window), is_first(false) { }
DockLinkPoint(Gtk::Widget & widget)81 explicit DockLinkPoint(Gtk::Widget &widget) {
82 Gtk::Container *container = widget.get_parent();
83 bin = dynamic_cast<Gtk::Bin*>(container);
84 paned = dynamic_cast<Gtk::Paned*>(container);
85 window = dynamic_cast<Gtk::Window*>(container);
86 is_first = paned != NULL && paned->get_child1() == &widget;
87 }
88
is_valid()89 bool is_valid() { return bin || paned || window; }
90
unlink()91 void unlink() {
92 if (paned && is_first && paned->get_child1())
93 paned->remove(*paned->get_child1());
94 else
95 if (paned && !is_first && paned->get_child2())
96 paned->remove(*paned->get_child2());
97 else
98 if (window)
99 window->remove();
100 if (bin)
101 bin->remove();
102 }
103
link(Gtk::Widget & widget)104 void link(Gtk::Widget &widget)
105 {
106 if (paned && is_first)
107 paned->pack1(widget, true, false);
108 else
109 if (paned && !is_first)
110 paned->pack2(widget, true, false);
111 else
112 if (window)
113 window->add(widget);
114 else
115 if (bin)
116 bin->add(widget);
117 }
118 };
119 }
120
121 class studio::DockSettings : public synfigapp::Settings
122 {
123 DockManager* dock_manager;
124
125 public:
DockSettings(DockManager * dock_manager)126 DockSettings(DockManager* dock_manager):dock_manager(dock_manager)
127 {
128 synfigapp::Main::settings().add_domain(this,"dock");
129 }
130
~DockSettings()131 virtual ~DockSettings()
132 {
133 synfigapp::Main::settings().remove_domain("dock");
134 }
135
get_value(const synfig::String & key_,synfig::String & value) const136 virtual bool get_value(const synfig::String& key_, synfig::String& value)const
137 {
138 try
139 {
140 if (key_ == "layout")
141 {
142 value = dock_manager->save_layout_to_string();
143 return true;
144 }
145 }catch (...) { return false; }
146 return synfigapp::Settings::get_value(key_,value);
147 }
148
set_value(const synfig::String & key_,const synfig::String & value)149 virtual bool set_value(const synfig::String& key_,const synfig::String& value)
150 {
151 try
152 {
153 if (key_ == "layout")
154 {
155 dock_manager->load_layout_from_string(value);
156 return true;
157 }
158 }catch (...) { return false; }
159 return synfigapp::Settings::set_value(key_,value);
160 }
161
get_key_list() const162 virtual KeyList get_key_list()const
163 {
164 synfigapp::Settings::KeyList ret(synfigapp::Settings::get_key_list());
165 ret.push_back("layout");
166 return ret;
167 }
168 };
169
170 /* === M E T H O D S ======================================================= */
171
DockManager()172 DockManager::DockManager():
173 dock_settings(new DockSettings(this))
174 {
175 }
176
~DockManager()177 DockManager::~DockManager()
178 {
179 while(!dock_dialog_list_.empty())
180 {
181 dock_dialog_list_.back()->close();
182 }
183 while(!dockable_list_.empty())
184 {
185 Dockable* dockable(dockable_list_.back());
186 // synfig::info("DockManager::~DockManager(): Deleting dockable \"%s\"",dockable->get_name().c_str());
187 dockable_list_.pop_back();
188 delete dockable;
189 }
190 }
191
192 void
register_dockable(Dockable & x)193 DockManager::register_dockable(Dockable& x)
194 {
195 dockable_list_.push_back(&x);
196 // synfig::info("DockManager::register_dockable(): Registered dockable \"%s\"",dockable_list_.back()->get_name().c_str());
197 signal_dockable_registered()(&x);
198 }
199
200 bool
unregister_dockable(Dockable & x)201 DockManager::unregister_dockable(Dockable& x)
202 {
203 std::list<Dockable*>::iterator iter;
204 for(iter=dockable_list_.begin();iter!=dockable_list_.end();++iter)
205 {
206 if(&x==*iter)
207 {
208 remove_widget_recursive(x);
209 dockable_list_.erase(iter);
210 signal_dockable_unregistered()(&x);
211 synfig::info("DockManager::unregister_dockable(): \"%s\" has been Unregistered",x.get_name().c_str());
212 update_window_titles();
213 return true;
214 }
215 }
216 return false;
217 }
218
219 Dockable&
find_dockable(const synfig::String & x)220 DockManager::find_dockable(const synfig::String& x)
221 {
222 std::list<Dockable*>::iterator iter;
223 for(iter=dockable_list_.begin();iter!=dockable_list_.end();++iter)
224 if((*iter)->get_name()==x)
225 return **iter;
226
227 throw std::runtime_error("DockManager::find_dockable(): not found");
228 }
229
230 void
present(synfig::String x)231 DockManager::present(synfig::String x)
232 {
233 try
234 {
235 find_dockable(x).present();
236 }
237 catch(...)
238 {
239 }
240 }
241
242 DockDialog&
find_dock_dialog(int id)243 DockManager::find_dock_dialog(int id)
244 {
245 std::list<DockDialog*>::iterator iter;
246 for(iter=dock_dialog_list_.begin();iter!=dock_dialog_list_.end();++iter)
247 if((*iter)->get_id()==id)
248 return **iter;
249
250 DockDialog* dock_dialog(new DockDialog());
251 dock_dialog->set_id(id);
252 return *dock_dialog;
253 }
254
255 const DockDialog&
find_dock_dialog(int id) const256 DockManager::find_dock_dialog(int id)const
257 {
258 std::list<DockDialog*>::const_iterator iter;
259 for(iter=dock_dialog_list_.begin();iter!=dock_dialog_list_.end();++iter)
260 if((*iter)->get_id()==id)
261 return **iter;
262
263 throw std::runtime_error("DockManager::find_dock_dialog(int id)const: not found");
264 }
265
266 void
show_all_dock_dialogs()267 DockManager::show_all_dock_dialogs()
268 {
269 std::list<DockDialog*>::iterator iter;
270 for(iter=dock_dialog_list_.begin();iter!=dock_dialog_list_.end();++iter)
271 (*iter)->present();
272 }
273
274 bool
swap_widgets(Gtk::Widget & widget1,Gtk::Widget & widget2)275 DockManager::swap_widgets(Gtk::Widget &widget1, Gtk::Widget &widget2)
276 {
277 DockLinkPoint point1(widget1);
278 DockLinkPoint point2(widget2);
279 if (point1.is_valid() && point2.is_valid())
280 {
281 point1.unlink();
282 point2.unlink();
283 point1.link(widget2);
284 point2.link(widget1);
285 return true;
286 }
287 return false;
288 }
289
290 void
remove_empty_container_recursive(Gtk::Container & container)291 DockManager::remove_empty_container_recursive(Gtk::Container &container)
292 {
293 containers_to_remove_.erase(&container);
294 Gtk::Paned *paned = dynamic_cast<Gtk::Paned*>(&container);
295 Gtk::Window *window = dynamic_cast<Gtk::Window*>(&container);
296 DockBook *book = dynamic_cast<DockBook*>(&container);
297
298 if (paned)
299 {
300 if (paned->get_child1() && paned->get_child2()) return;
301 Gtk::Widget *child = paned->get_child1() ? paned->get_child1() : paned->get_child2();
302 if (child)
303 {
304 DockLinkPoint link(*paned);
305 if (link.is_valid())
306 {
307 paned->remove(*child);
308 link.unlink();
309 link.link(*child);
310 delete paned;
311 }
312 }
313 else
314 {
315 remove_widget_recursive(*paned);
316 delete paned;
317 return;
318 }
319 }
320 else
321 if (window)
322 {
323 if (!window->get_child())
324 window->hide();
325 }
326 else
327 if (book)
328 {
329 if (!book->allow_empty && book->get_n_pages() == 0)
330 {
331 remove_widget_recursive(*book);
332 delete book;
333 }
334 }
335 }
336
337 void
remove_widget_recursive(Gtk::Widget & widget)338 DockManager::remove_widget_recursive(Gtk::Widget &widget)
339 {
340 Gtk::Container *container = widget.get_parent();
341 if (container)
342 {
343 container->remove(widget);
344 remove_empty_container_recursive(*container);
345 }
346 }
347
348 bool
add_widget(Gtk::Widget & dest_widget,Gtk::Widget & src_widget,bool vertical,bool first)349 DockManager::add_widget(Gtk::Widget &dest_widget, Gtk::Widget &src_widget, bool vertical, bool first)
350 {
351 if (&src_widget == &dest_widget) return false;
352
353 // check for src widget is parent for dest_widget
354 for(Gtk::Widget *parent = src_widget.get_parent(); parent != NULL; parent = parent->get_parent())
355 if (parent == &dest_widget)
356 return swap_widgets(src_widget, dest_widget);
357
358 // unlink dest_widget
359 DockLinkPoint dest_link(dest_widget);
360 if (!dest_link.is_valid()) return false;
361 dest_link.unlink();
362
363 // unlink src_widget
364 remove_widget_recursive(src_widget);
365
366 // create new paned and link all
367 Gtk::Paned *paned = manage(vertical ? (Gtk::Paned*)new Gtk::VPaned() : (Gtk::Paned*)new Gtk::HPaned());
368 paned->show();
369 DockLinkPoint(paned, first).link(src_widget);
370 DockLinkPoint(paned, !first).link(dest_widget);
371 dest_link.link(*paned);
372 return true;
373 }
374
375 bool
add_dockable(Gtk::Widget & dest_widget,Dockable & dockable,bool vertical,bool first)376 DockManager::add_dockable(Gtk::Widget &dest_widget, Dockable &dockable, bool vertical, bool first)
377 {
378 DockBook *book = manage(new DockBook());
379 book->show();
380 if (add_widget(dest_widget, *book, vertical, first))
381 {
382 book->add(dockable);
383 return true;
384 }
385 delete book;
386 return false;
387 }
388
read_separator(std::string & x)389 bool DockManager::read_separator(std::string &x)
390 {
391 size_t pos = x.find_first_of("|]");
392 if (pos == std::string::npos) { x.clear(); return false; }
393 if (x[pos] == '|') { x = x.substr(pos+1); return true; }
394 if (x[pos] == ']') x = x.substr(pos+1);
395 return false;
396 }
397
read_string(std::string & x)398 std::string DockManager::read_string(std::string &x)
399 {
400 size_t pos = x.find_first_of("|]");
401 std::string res = x.substr(0, pos);
402 if (pos == std::string::npos) x.clear(); else x = x.substr(pos);
403 return res;
404 }
405
read_int(std::string & x)406 int DockManager::read_int(std::string &x)
407 {
408 return strtol(read_string(x).c_str(), NULL, 10);
409 }
410
read_bool(std::string & x)411 bool DockManager::read_bool(std::string &x)
412 {
413 return read_string(x) == "true";
414 }
415
read_widget(std::string & x)416 Gtk::Widget* DockManager::read_widget(std::string &x)
417 {
418 bool hor = x.substr(0, 5) == "[hor|";
419 bool vert = x.substr(0, 6) == "[vert|";
420
421 // paned
422 if (hor || vert)
423 {
424 // skip "[hor|" or "[vert|"
425 x = x.substr(1);
426 if (!read_separator(x)) return NULL;
427
428 int size = read_int(x);
429 if (!read_separator(x)) return NULL;
430
431 Gtk::Widget *first = NULL;
432 Gtk::Widget *second = NULL;
433
434 first = read_widget(x);
435 if (!read_separator(x)) return first;
436 second = read_widget(x);
437 read_separator(x);
438
439 if (!first && !second) return NULL;
440 if (first && !second) return first;
441 if (!first && second) return second;
442
443 // create paned
444 Gtk::Paned *paned = manage(hor ? (Gtk::Paned*)new Gtk::HPaned() : (Gtk::Paned*)new Gtk::VPaned());
445 paned->pack1(*first, true, false);
446 paned->pack2(*second, true, false);
447 paned->set_position(size);
448 paned->show();
449 return paned;
450 }
451 else
452 if (x.substr(0, 6) == "[book|")
453 {
454 // skip "[book|"
455 x = x.substr(1);
456 if (!read_separator(x)) return NULL;
457
458 DockBook *book = NULL;
459 do
460 {
461 std::string name = read_string(x);
462 if (!name.empty())
463 {
464 Dockable *dockable = &find_dockable(name);
465 if (dockable != NULL)
466 {
467 Gtk::Container *container = dockable->get_parent();
468 if (container)
469 {
470 container->remove(*dockable);
471 containers_to_remove_[container] = true;
472 }
473 if (book == NULL) { book = manage(new DockBook()); book->show(); }
474 book->add(*dockable);
475 }
476 }
477 } while (read_separator(x));
478
479 return book;
480 }
481 else
482 if (x.substr(0, 8) == "[dialog|")
483 {
484 // skip "[dialog|"
485 x = x.substr(1);
486 if (!read_separator(x)) return NULL;
487
488 int left = read_int(x);
489 if (!read_separator(x)) return NULL;
490 int top = read_int(x);
491 if (!read_separator(x)) return NULL;
492 int width = read_int(x);
493 if (!read_separator(x)) return NULL;
494 int height = read_int(x);
495 if (!read_separator(x)) return NULL;
496
497 Gtk::Widget *widget = read_widget(x);
498 read_separator(x);
499
500 if (!widget) return NULL;
501
502 DockDialog *dialog = new DockDialog();
503 dialog->add(*widget);
504 dialog->move(left, top);
505 dialog->set_default_size(width, height);
506 dialog->resize(width, height);
507 dialog->present();
508
509 return NULL;
510 }
511 else
512 if (x.substr(0, 12) == "[mainwindow|")
513 {
514 // skip "[dialog|"
515 x = x.substr(1);
516 if (!read_separator(x)) return NULL;
517
518 int left = read_int(x);
519 if (!read_separator(x)) return NULL;
520 int top = read_int(x);
521 if (!read_separator(x)) return NULL;
522 int width = read_int(x);
523 if (!read_separator(x)) return NULL;
524 int height = read_int(x);
525 if (!read_separator(x)) return NULL;
526
527 Gtk::Widget *widget = read_widget(x);
528 read_separator(x);
529
530 if (!widget) return NULL;
531
532 Gtk::Widget *child = App::main_window->root().get_child();
533 App::main_window->root().remove();
534 if (child && child != &App::main_window->main_dock_book())
535 delete child;
536 App::main_window->root().add(*widget);
537
538 App::main_window->move(left, top);
539 App::main_window->set_default_size(width, height);
540 App::main_window->resize(width, height);
541 App::main_window->present();
542
543 return NULL;
544 }
545 else
546 if (x.substr(0, 14) == "[mainnotebook]")
547 {
548 x = x.substr(14);
549 if (App::main_window->main_dock_book().get_parent())
550 App::main_window->main_dock_book().get_parent()->remove(App::main_window->main_dock_book());
551 return &App::main_window->main_dock_book();
552 }
553
554 return NULL;
555 }
556
write_string(std::string & x,const std::string & str)557 void DockManager::write_string(std::string &x, const std::string &str)
558 { x += str; }
write_separator(std::string & x,bool continue_)559 void DockManager::write_separator(std::string &x, bool continue_)
560 { write_string(x, continue_ ? "|" : "]"); }
write_int(std::string & x,int i)561 void DockManager::write_int(std::string &x, int i)
562 { write_string(x, strprintf("%d", i)); }
write_bool(std::string & x,bool b)563 void DockManager::write_bool(std::string &x, bool b)
564 { write_string(x, b ? "true" : "false"); }
565
write_widget(std::string & x,Gtk::Widget * widget)566 void DockManager::write_widget(std::string &x, Gtk::Widget* widget)
567 {
568 Gtk::Paned *paned = dynamic_cast<Gtk::Paned*>(widget);
569 Gtk::HPaned *hpaned = dynamic_cast<Gtk::HPaned*>(widget);
570 DockBook *book = dynamic_cast<DockBook*>(widget);
571 DockDialog *dialog = dynamic_cast<DockDialog*>(widget);
572
573 if (widget == NULL)
574 {
575 return;
576 }
577 else
578 if (widget == App::main_window)
579 {
580 write_string(x, "[mainwindow|");
581 int left = 0, top = 0, width = 0, height = 0;
582 App::main_window->get_position(left, top);
583 App::main_window->get_size(width, height);
584 write_int(x, left);
585 write_separator(x);
586 write_int(x, top);
587 write_separator(x);
588 write_int(x, width);
589 write_separator(x);
590 write_int(x, height);
591 write_separator(x);
592
593 write_widget(x, App::main_window->root().get_child());
594 write_separator(x, false);
595 }
596 else
597 if (widget == &App::main_window->main_dock_book())
598 {
599 write_string(x, "[mainnotebook]");
600 }
601 else
602 if (dialog)
603 {
604 write_string(x, "[dialog|");
605 int left = 0, top = 0, width = 0, height = 0;
606 dialog->get_position(left, top);
607 dialog->get_size(width, height);
608 write_int(x, left);
609 write_separator(x);
610 write_int(x, top);
611 write_separator(x);
612 write_int(x, width);
613 write_separator(x);
614 write_int(x, height);
615 write_separator(x);
616
617 write_widget(x, dialog->get_child());
618 write_separator(x, false);
619 }
620 else
621 if (paned)
622 {
623 write_string(x, hpaned ? "[hor|" : "[vert|");
624 write_int(x, paned->get_position());
625 write_separator(x);
626 write_widget(x, paned->get_child1());
627 write_separator(x);
628 write_widget(x, paned->get_child2());
629 write_separator(x, false);
630 }
631 else
632 if (book)
633 {
634 write_string(x, "[book");
635 for(int i = 0; i < book->get_n_pages(); ++i)
636 {
637 Dockable *dockable = dynamic_cast<Dockable*>(book->get_nth_page(i));
638 if (dockable)
639 {
640 write_separator(x);
641 write_string(x, dockable->get_name());
642 }
643 }
644 write_separator(x, false);
645 }
646 }
647
save_widget_to_string(Gtk::Widget * widget)648 std::string DockManager::save_widget_to_string(Gtk::Widget *widget)
649 {
650 std::string res;
651 write_widget(res, widget);
652 return res;
653 }
654
load_widget_from_string(const std::string & x)655 Gtk::Widget* DockManager::load_widget_from_string(const std::string &x)
656 {
657 std::string copy(x);
658 Gtk::Widget *widget = read_widget(copy);
659 while (!containers_to_remove_.empty())
660 remove_empty_container_recursive(*containers_to_remove_.begin()->first);
661 return widget;
662 }
663
save_layout_to_string()664 std::string DockManager::save_layout_to_string()
665 {
666 std::string res;
667 for(std::list<DockDialog*>::iterator i = dock_dialog_list_.begin(); i != dock_dialog_list_.end(); i++)
668 {
669 write_widget(res, *i);
670 write_separator(res);
671 }
672 write_widget(res, App::main_window);
673 return res;
674 }
675
load_layout_from_string(const std::string & x)676 void DockManager::load_layout_from_string(const std::string &x)
677 {
678 std::string copy(x);
679 do
680 {
681 read_widget(copy);
682 } while (read_separator(copy));
683 while (!containers_to_remove_.empty())
684 remove_empty_container_recursive(*containers_to_remove_.begin()->first);
685 }
686
layout_from_template(const std::string & tpl,float dx,float dy,float sx,float sy)687 std::string DockManager::layout_from_template(const std::string &tpl, float dx, float dy, float sx, float sy)
688 {
689 std::string res;
690 size_t pos_begin;
691 size_t pos_end = 0;
692 while(true)
693 {
694 pos_begin = tpl.find_first_of("%", pos_end);
695 if (pos_begin == std::string::npos)
696 { res+=tpl.substr(pos_end); break; }
697 res+=tpl.substr(pos_end, pos_begin-pos_end);
698 pos_end = tpl.find_first_of("xyXY", pos_begin);
699 if (pos_end == std::string::npos) break;
700 float f = (float)strtol(tpl.c_str()+pos_begin+1, NULL, 10);
701 if (tpl[pos_end] == 'X') res += strprintf("%d", (int)roundf(dx+f*sx/100.f));
702 if (tpl[pos_end] == 'Y') res += strprintf("%d", (int)roundf(dy+f*sy/100.f));
703 if (tpl[pos_end] == 'x') res += strprintf("%d", (int)roundf(f*sx/100.f));
704 if (tpl[pos_end] == 'y') res += strprintf("%d", (int)roundf(f*sy/100.f));
705 pos_end++;
706 }
707 return res;
708 }
709
710
711 void
update_window_titles()712 DockManager::update_window_titles()
713 {
714 // build maps
715 typedef std::map< CanvasView::ActivationIndex, CanvasView* > CanvasViewMap;
716 typedef std::map< Glib::RefPtr<Gdk::Window>, std::string > TitleMap;
717 CanvasViewMap canvas_view_map;
718 TitleMap title_map;
719 for(std::list<Dockable*>::iterator i = dockable_list_.begin(); i != dockable_list_.end(); i++)
720 {
721 if ((*i)->get_parent_window())
722 {
723 title_map[(*i)->get_parent_window()] = (*i)->get_parent_window() == App::main_window->get_window()
724 ? _("Synfig Studio") : _("Dock Panel");
725 CanvasView *canvas_view = dynamic_cast<CanvasView*>(*i);
726 if (canvas_view)
727 canvas_view_map[canvas_view->get_activation_index()] = canvas_view;
728 }
729 }
730
731 // prepare titles
732 for(CanvasViewMap::iterator i = canvas_view_map.begin(); i != canvas_view_map.end(); i++)
733 title_map[ i->second->get_parent_window() ] =
734 i->second->get_local_name() + " - " + _("Synfig Studio");
735
736 // set titles
737 for(TitleMap::iterator i = title_map.begin(); i != title_map.end(); i++)
738 i->first->set_title(i->second);
739 }
740