Fix a crash when running single-threaded and dragging the time slider.
[synfig.git] / synfig-studio / trunk / src / gtkmm / dockable.cpp
1 /* === S Y N F I G ========================================================= */
2 /*!     \file dockable.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++/hide.h>
34
35 #include "dockable.h"
36 #include "dockmanager.h"
37 #include "dockbook.h"
38 #include "dockdialog.h"
39 #include <synfig/general.h>
40 #include <gtkmm/table.h>
41 #include <gtk/gtk.h>
42
43 #endif
44
45 /* === U S I N G =========================================================== */
46
47 using namespace std;
48 using namespace etl;
49 using namespace synfig;
50 using namespace studio;
51
52 /* === M A C R O S ========================================================= */
53
54 #ifdef WIN32
55 #       ifdef IMAGE_DIR
56 #               undef IMAGE_DIR
57 #               define IMAGE_DIR "share\\pixmaps"
58 #       endif
59 #endif
60
61 #ifndef IMAGE_DIR
62 #       define IMAGE_DIR "/usr/local/share/pixmaps"
63 #endif
64
65 #ifndef IMAGE_EXT
66 #       define IMAGE_EXT        "png"
67 #endif
68
69 /* === G L O B A L S ======================================================= */
70
71 /* === P R O C E D U R E S ================================================= */
72
73 /* === M E T H O D S ======================================================= */
74
75 Dockable::Dockable(const synfig::String& name,const synfig::String& local_name,Gtk::StockID stock_id_):
76 //      Gtk::Window(Gtk::WINDOW_TOPLEVEL),
77         name_(name),
78         local_name_(local_name),
79 //      dialog_settings(this,name),
80         title_label_(local_name,Gtk::ALIGN_LEFT),
81         stock_id_(stock_id_)
82 {
83         parent_=0;
84         scrolled_=0;
85
86         use_scrolled_=true;
87
88         //set_title(local_name);
89         //set_type_hint(Gdk::WINDOW_TYPE_HINT_UTILITY);
90
91
92         title_label_.show();
93
94         attach_dnd_to(title_label_);
95
96         //scrolled_.set_policy(Gtk::POLICY_AUTOMATIC,Gtk::POLICY_AUTOMATIC);
97         //scrolled_.show();
98         //scrolled_.set_shadow_type(Gtk::SHADOW_NONE);
99
100         toolbar_=0;
101         //button_box_.show();
102
103         Gtk::Table* table(this);
104
105         {
106                 title_label_.set_padding(0,0);
107                 Gtk::EventBox* event_box(manage(new Gtk::EventBox()));
108                 event_box->set_border_width(0);
109                 event_box->add(title_label_);
110                 //table->attach(*event_box, 0, 1, 0,1, Gtk::EXPAND|Gtk::FILL, Gtk::SHRINK|Gtk::FILL, 0, 0);
111
112                 header_box_.pack_start(*event_box);
113
114                 attach_dnd_to(*event_box);
115                 event_box->show();
116         //      event_box->set_events(Gdk::ALL_EVENTS_MASK); //!< \todo change this to only allow what is necessary for DnD
117
118
119                 Gtk::Button* bttn_close(manage(new Gtk::Button("X")));
120                 //table->attach(*bttn_close, 1, 2, 0,1, Gtk::SHRINK|Gtk::FILL, Gtk::SHRINK|Gtk::FILL, 0, 0);
121                 header_box_.pack_end(*bttn_close,false,false);
122                 bttn_close->show();
123                 bttn_close->set_relief(Gtk::RELIEF_NONE);
124                 bttn_close->signal_clicked().connect(sigc::mem_fun(*this,&Dockable::detach));
125                 bttn_close->set_border_width(0);
126                 dynamic_cast<Gtk::Misc*>(bttn_close->get_child())->set_padding(0,0);
127         }
128
129         prev_widget_=manage(new Gtk::Label(" "));
130
131         //table->attach(header_box_, 0, 1, 0,1, Gtk::SHRINK|Gtk::FILL, Gtk::SHRINK|Gtk::FILL, 0, 0);
132         table->attach(*prev_widget_, 0, 1, 1,2, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
133         //table->attach(*toolbar_, 0, 1, 2,3, Gtk::EXPAND|Gtk::FILL, Gtk::SHRINK|Gtk::FILL, 0, 0);
134         set_toolbar(*manage(new Gtk::Toolbar));
135         table->show();
136
137         prev_widget_->show();
138
139         set_size_request(175,120);
140         //scrolled_.set_shadow_type(Gtk::SHADOW_NONE);
141
142 }
143
144 Dockable::~Dockable()
145 {
146         if(scrolled_)
147         {
148                 delete scrolled_;
149                 scrolled_=0;
150         }
151
152         /*if(App::dock_manager)try{
153                 App::dock_manager->unregister_dockable(*this);
154                 std::list<Dockable*>::iterator iter;
155                 for(iter=App::dock_manager->dockable_list_.begin();iter!=App::dock_manager->dockable_list_.end();++iter)
156                         if(*iter==this)
157                         {
158                                 App::dock_manager->dockable_list_.erase(iter);
159                                 return;
160                         }
161         } catch(...) { }
162 */
163         //if(App::dock_manager)
164         //      App::dock_manager->dockable_list_.erase(this);
165 }
166
167 void
168 Dockable::attach_dnd_to(Gtk::Widget& widget)
169 {
170         std::list<Gtk::TargetEntry> listTargets;
171         listTargets.push_back( Gtk::TargetEntry("DOCK") );
172
173         widget.drag_source_set(listTargets);
174         widget.drag_source_set_icon(get_stock_id());
175         widget.drag_dest_set(listTargets);
176
177
178         widget.signal_drag_data_get().connect(sigc::mem_fun(*this,&Dockable::on_drag_data_get));
179         widget.signal_drag_end().connect(sigc::mem_fun(*this,&Dockable::on_drag_end));
180         widget.signal_drag_begin().connect(sigc::mem_fun(*this,&Dockable::on_drag_begin));
181         widget.signal_drag_data_received().connect(sigc::mem_fun(*this,&Dockable::on_drag_data_received));
182 }
183
184 void
185 Dockable::on_drag_data_received(const Glib::RefPtr<Gdk::DragContext>& context, int, int, const Gtk::SelectionData& selection_data, guint, guint time)
186 {
187         if ((selection_data.get_length() >= 0) && (selection_data.get_format() == 8))
188         {
189                 Dockable& dockable(**reinterpret_cast<Dockable**>(const_cast<guint8*>(selection_data.get_data())));
190
191                 if(dockable.parent_ != parent_)
192                         parent_->add(dockable,parent_->page_num(*this));
193                 else
194                         parent_->reorder_child(dockable,parent_->page_num(*this));
195                 dockable.present();
196                 context->drag_finish(true, false, time);
197                 return;
198         }
199
200         context->drag_finish(false, false, time);
201 }
202
203 void
204 Dockable::on_drag_end(const Glib::RefPtr<Gdk::DragContext>&/*context*/)
205 {
206         if(!dnd_success_)
207         {
208                 detach();
209                 present();
210         }
211 }
212
213 void
214 Dockable::on_drag_begin(const Glib::RefPtr<Gdk::DragContext>&/*context*/)
215 {
216         dnd_success_=false;
217 }
218
219 void
220 Dockable::on_drag_data_get(const Glib::RefPtr<Gdk::DragContext>&, Gtk::SelectionData& selection_data, guint /*info*/, guint /*time*/)
221 {
222         Dockable* tmp(this);
223         dnd_success_=true;
224
225         selection_data.set(8, reinterpret_cast<const guchar*>(&tmp), 4);
226 }
227
228 void
229 Dockable::set_local_name(const synfig::String& local_name)
230 {
231         //set_title(local_name);
232         title_label_.set_text(local_name);
233 }
234
235 void
236 Dockable::clear()
237 {
238         //if(!toolbar_->children().empty())
239         //      toolbar_->children().clear();
240         set_toolbar(*manage(new Gtk::Toolbar));
241
242 }
243
244 void
245 Dockable::set_toolbar(Gtk::Toolbar& toolbar)
246 {
247         if(toolbar_)remove(*toolbar_);
248         toolbar_=0;
249         toolbar_=&toolbar;
250         if(toolbar_)
251         {
252                 attach(*toolbar_, 0, 1, 2,3, Gtk::EXPAND|Gtk::FILL, Gtk::SHRINK|Gtk::FILL, 0, 0);
253                 gtk_toolbar_set_icon_size(toolbar_->gobj(),GtkIconSize(1)/*GTK_ICON_SIZE_MENU*/);
254                 toolbar_->show();
255         }
256 }
257
258 bool
259 Dockable::clear_previous()
260 {
261         prev_widget_=0;
262         prev_widget_delete_connection.disconnect();
263         return false;
264 }
265
266 void
267 Dockable::add(Gtk::Widget& x)
268 {
269         if(prev_widget_)
270         {
271                 remove(*prev_widget_);
272                 //prev_widget_=0;
273                 clear_previous();
274         }
275
276         if(scrolled_)
277         {
278                 delete scrolled_;
279                 scrolled_=0;
280         }
281
282         if(use_scrolled_)
283         {
284                 scrolled_=new Gtk::ScrolledWindow;
285
286                 scrolled_->add(x);
287
288                 attach(*scrolled_, 0, 1, 1,2, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
289
290                 x.show();
291
292                 scrolled_->show();
293
294                 scrolled_->set_shadow_type(Gtk::SHADOW_NONE);
295                 scrolled_->set_policy(Gtk::POLICY_AUTOMATIC,Gtk::POLICY_AUTOMATIC);
296                 prev_widget_=scrolled_;
297         }
298         else
299         {
300                 attach(x, 0, 1, 1,2, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
301                 x.show();
302                 prev_widget_=&x;
303         }
304         prev_widget_delete_connection=prev_widget_->signal_delete_event().connect(
305                 sigc::hide(
306                         sigc::mem_fun(
307                                 *this,
308                                 &Dockable::clear_previous
309                         )
310                 )
311         );
312 }
313
314 Gtk::ToolButton*
315 Dockable::add_button(const Gtk::StockID& stock_id, const synfig::String& tooltip)
316 {
317         if(!toolbar_)
318                 set_toolbar(*manage(new Gtk::Toolbar));
319
320         //Gtk::IconSize iconsize(4);
321         //Gtk::IconSize iconsize(Gtk::IconSize::from_name("synfig-small_icon"));
322
323         Gtk::ToolButton* ret(manage(new Gtk::ToolButton(stock_id)));
324         //Gtk::Image* icon(manage(new Gtk::Image(stock_id,iconsize)));
325         //ret->add(*icon);
326         //ret->set_relief(Gtk::RELIEF_HALF);
327         //ret->set_relief(Gtk::RELIEF_NONE);
328         ret->set_label(tooltip);
329         //toolbar_->get_tooltips_object()->set_tip(*ret,tooltip);
330
331         ret->show();
332         //icon->show();
333         toolbar_->set_tooltips(true);
334
335         toolbar_->append(*ret);
336         //button_box_.pack_start(*ret,false,false);
337         //get_action_area()->pack_start(*ret,false,false);
338         //add_action_widget(*ret,1);
339         return ret;
340 }
341
342
343 void
344 Dockable::detach()
345 {
346         if(parent_)
347                 parent_->remove(*this);
348 }
349
350 void
351 Dockable::present()
352 {
353         if(parent_)
354         {
355                 parent_->set_current_page(parent_->page_num(*this));
356                 parent_->present();
357         }
358         else
359         {
360                 DockDialog* dock_dialog(new DockDialog());
361                 dock_dialog->get_dock_book().add(*this);
362                 //if(get_name()=="canvases")
363                 //      dock_dialog->set_composition_selector(true);
364                 dock_dialog->present();
365         }
366 }
367
368 Gtk::Widget*
369 Dockable::create_tab_label()
370 {
371         Gtk::EventBox* event_box(manage(new Gtk::EventBox()));
372
373         attach_dnd_to(*event_box);
374
375         {
376                 Gtk::StockID stock_id(get_stock_id());
377                 Gtk::StockItem item;
378
379                 // Check to make sure the icon is valid
380                 if(Gtk::Stock::lookup(stock_id,item))
381                 {
382                         Gtk::Image* icon(manage(new Gtk::Image(stock_id,Gtk::IconSize(4))));
383                         event_box->add(*icon);
384                         tooltips_.set_tip(*event_box,get_local_name());
385                         icon->show();
386                 }
387                 else
388                 {
389                         // Bad icon, try to make a label
390
391                         Glib::ustring text(get_local_name());
392
393                         Gtk::Label* label(manage(new Gtk::Label(text)));
394                         event_box->add(*label);
395                         label->show();
396                 }
397         }
398
399         return event_box;
400 }