00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024 #include <boost/foreach.hpp>
00025
00026 #include "timeline-header-container.hpp"
00027 #include "timeline-track.hpp"
00028 #include "../timeline-widget.hpp"
00029
00030 using namespace Gtk;
00031 using namespace std;
00032 using namespace boost;
00033 using namespace util;
00034
00035 namespace gui {
00036 namespace widgets {
00037 namespace timeline {
00038
00039 TimelineHeaderContainer::TimelineHeaderContainer(
00040 gui::widgets::TimelineWidget *timeline_widget) :
00041 Glib::ObjectBase("TimelineHeaderContainer"),
00042 timelineWidget(timeline_widget),
00043 margin(-1),
00044 expand_button_size(12)
00045 {
00046 REQUIRE(timeline_widget != NULL);
00047
00048
00049 set_flags(Gtk::NO_WINDOW);
00050
00051 set_redraw_on_allocate(false);
00052
00053
00054
00055 timelineWidget->verticalAdjustment.signal_value_changed().connect(
00056 sigc::mem_fun(this, &TimelineHeaderContainer::on_scroll) );
00057
00058
00059
00060 timelineWidget->hovering_track_changed_signal().connect(
00061 sigc::mem_fun(this,
00062 &TimelineHeaderContainer::on_hovering_track_changed) );
00063
00064
00065 Menu::MenuList& menu_list = contextMenu.items();
00066 menu_list.push_back( Menu_Helpers::MenuElem(_("_Add Track"),
00067 sigc::mem_fun(timelineWidget,
00068 &TimelineWidget::on_add_track_command) ) );
00069
00070
00071 register_styles();
00072 }
00073
00074 void
00075 TimelineHeaderContainer::update_headers()
00076 {
00077
00078 BOOST_FOREACH( shared_ptr<model::Track> model_track, get_tracks() )
00079 set_parent_recursive(model_track);
00080 }
00081
00082 void
00083 TimelineHeaderContainer::on_realize()
00084 {
00085 set_flags(Gtk::NO_WINDOW);
00086
00087
00088 Gtk::Container::on_realize();
00089
00090
00091 GdkWindowAttr attributes;
00092 memset(&attributes, 0, sizeof(attributes));
00093
00094 Allocation allocation = get_allocation();
00095
00096
00097 attributes.x = allocation.get_x();
00098 attributes.y = allocation.get_y();
00099 attributes.width = allocation.get_width();
00100 attributes.height = allocation.get_height();
00101
00102 attributes.event_mask = get_events () | Gdk::EXPOSURE_MASK;
00103 attributes.window_type = GDK_WINDOW_CHILD;
00104 attributes.wclass = GDK_INPUT_OUTPUT;
00105
00106 gdkWindow = Gdk::Window::create(get_window(), &attributes,
00107 GDK_WA_X | GDK_WA_Y);
00108 unset_flags(Gtk::NO_WINDOW);
00109 set_window(gdkWindow);
00110
00111
00112
00113 unset_bg(STATE_NORMAL);
00114
00115
00116 gdkWindow->set_user_data(gobj());
00117
00118
00119 add_events(
00120 Gdk::POINTER_MOTION_MASK |
00121 Gdk::BUTTON_PRESS_MASK |
00122 Gdk::BUTTON_RELEASE_MASK);
00123 }
00124
00125 void
00126 TimelineHeaderContainer::on_unrealize()
00127 {
00128
00129 gdkWindow.clear();
00130
00131
00132 Container::on_unrealize();
00133 }
00134
00135 bool TimelineHeaderContainer::on_button_press_event (
00136 GdkEventButton* event)
00137 {
00138 REQUIRE(event != NULL);
00139
00140 switch(event->button)
00141 {
00142 case 1:
00143
00144 if(hoveringExpander != NULL)
00145 {
00146
00147 clickedExpander = hoveringExpander;
00148 queue_draw();
00149 }
00150 break;
00151
00152 case 3:
00153 {
00154
00155 shared_ptr<Track> header = header_from_point(
00156 Gdk::Point(event->x, event->y));
00157
00158
00159 if(header)
00160 {
00161
00162 header->show_header_context_menu(
00163 event->button, event->time);
00164 }
00165 else
00166 {
00167
00168 contextMenu.popup(event->button, event->time);
00169 }
00170 break;
00171 }
00172 }
00173
00174 return true;
00175 }
00176
00177 bool TimelineHeaderContainer::on_button_release_event (
00178 GdkEventButton* event)
00179 {
00180
00181 if(clickedExpander != NULL)
00182 {
00183
00184 clickedExpander->set_expanded(!clickedExpander->get_expanded());
00185 clickedExpander.reset();
00186 layout_headers();
00187 }
00188
00189 return Container::on_button_release_event(event);
00190 }
00191
00192 bool TimelineHeaderContainer::on_motion_notify_event (
00193 GdkEventMotion* event)
00194 {
00195 REQUIRE(event != NULL);
00196
00197
00198 shared_ptr<timeline::Track> expander = expander_button_from_point(
00199 Gdk::Point(event->x, event->y));
00200
00201 if(expander != hoveringExpander)
00202 {
00203 hoveringExpander = expander;
00204 queue_draw();
00205 }
00206
00207 return Container::on_motion_notify_event(event);
00208 }
00209
00210 void
00211 TimelineHeaderContainer::on_size_request (Requisition* requisition)
00212 {
00213
00214
00215
00216 BOOST_FOREACH( shared_ptr<model::Track> model_track, get_tracks() )
00217 size_request_recursive(model_track);
00218
00219
00220 *requisition = Gtk::Requisition();
00221 requisition->width = TimelineWidget::HeaderWidth;
00222 requisition->height = 0;
00223 }
00224
00225 void
00226 TimelineHeaderContainer::on_size_allocate (Allocation& allocation)
00227 {
00228
00229 set_allocation(allocation);
00230
00231
00232 if(gdkWindow)
00233 {
00234 gdkWindow->move(allocation.get_x(), allocation.get_y());
00235 gdkWindow->resize(
00236 allocation.get_width(), allocation.get_height());
00237 }
00238
00239
00240 layout_headers();
00241 }
00242
00243 void
00244 TimelineHeaderContainer::forall_vfunc(gboolean ,
00245 GtkCallback callback, gpointer callback_data)
00246 {
00247 REQUIRE(callback != NULL);
00248
00249 BOOST_FOREACH( shared_ptr<model::Track> track, get_tracks() )
00250 {
00251 REQUIRE(track);
00252 forall_vfunc_recursive(track, callback, callback_data);
00253 }
00254 }
00255
00256 void
00257 TimelineHeaderContainer::on_remove(Widget* widget)
00258 {
00259
00260 }
00261
00262 bool
00263 TimelineHeaderContainer::on_expose_event(GdkEventExpose *event)
00264 {
00265 if(gdkWindow)
00266 {
00267
00268 int offset = -timelineWidget->get_y_scroll_offset();
00269
00270 const Allocation container_allocation = get_allocation();
00271
00272 read_styles();
00273
00274
00275 BOOST_FOREACH( shared_ptr<model::Track> model_track,
00276 get_tracks() )
00277 {
00278 REQUIRE(model_track);
00279
00280 draw_header_decoration(model_track,
00281 Gdk::Rectangle(0, 0,
00282 container_allocation.get_width(),
00283 container_allocation.get_height()));
00284 }
00285 }
00286
00287 return Container::on_expose_event(event);
00288 }
00289
00290 void
00291 TimelineHeaderContainer::on_scroll()
00292 {
00293
00294
00295 layout_headers();
00296 }
00297
00298 void
00299 TimelineHeaderContainer::on_hovering_track_changed(
00300 boost::shared_ptr<timeline::Track> hovering_track)
00301 {
00302
00303 queue_draw();
00304 }
00305
00306 void
00307 TimelineHeaderContainer::layout_headers()
00308 {
00309
00310 if(!gdkWindow)
00311 return;
00312
00313
00314 read_styles();
00315
00316
00317 headerBoxes.clear();
00318
00319
00320 int offset = -timelineWidget->get_y_scroll_offset();
00321
00322 const Allocation container_allocation = get_allocation();
00323 const int header_width = container_allocation.get_width();
00324
00325 BOOST_FOREACH( shared_ptr<model::Track> model_track, get_tracks() )
00326 layout_headers_recursive(
00327 model_track, offset, header_width, 0, true);
00328
00329
00330 queue_draw ();
00331 }
00332
00333 void
00334 TimelineHeaderContainer::layout_headers_recursive(
00335 shared_ptr<model::Track> model_track, int &offset,
00336 const int header_width, const int depth, bool parent_expanded)
00337 {
00338 REQUIRE(depth >= 0);
00339 REQUIRE(model_track != NULL);
00340
00341 shared_ptr<timeline::Track> timeline_track =
00342 lookup_timeline_track(model_track);
00343
00344 const int indent = depth * 10;
00345 Widget &widget = timeline_track->get_header_widget();
00346
00347 if(parent_expanded)
00348 {
00349 const int track_height = timeline_track->get_height();
00350
00351
00352 Gdk::Rectangle header_box(
00353 indent,
00354 offset,
00355 max( header_width - indent, 0 ),
00356 track_height);
00357 REQUIRE(header_box.get_height() >= 0);
00358
00359
00360 headerBoxes[timeline_track] = header_box;
00361
00362
00363 Allocation header_allocation(
00364 header_box.get_x() + margin + expand_button_size,
00365 header_box.get_y() + margin,
00366 max( header_box.get_width() - expand_button_size -
00367 margin * 2, 0 ),
00368 header_box.get_height() - margin * 2);
00369
00370
00371 widget.size_allocate (header_allocation);
00372 if(!widget.is_visible())
00373 widget.show();
00374
00375
00376 offset += track_height + TimelineWidget::TrackPadding;
00377 }
00378 else
00379 if(widget.is_visible())
00380 widget.hide();
00381
00382
00383 BOOST_FOREACH( boost::shared_ptr<model::Track> child,
00384 model_track->get_child_tracks() )
00385 layout_headers_recursive(
00386 child, offset, header_width, depth + 1,
00387 timeline_track->get_expanded() && parent_expanded);
00388 }
00389
00390 void
00391 TimelineHeaderContainer::set_parent_recursive(
00392 boost::shared_ptr<model::Track> model_track)
00393 {
00394
00395 Widget &widget = lookup_timeline_track(model_track)->
00396 get_header_widget();
00397
00398 const Container *parent = widget.get_parent();
00399 if(parent == NULL)
00400 widget.set_parent(*this);
00401 else if(parent != this)
00402 widget.reparent(*this);
00403
00404
00405 BOOST_FOREACH( shared_ptr<model::Track> child,
00406 model_track->get_child_tracks() )
00407 set_parent_recursive(child);
00408 }
00409
00410 void
00411 TimelineHeaderContainer::size_request_recursive(
00412 shared_ptr<model::Track> const model_track)
00413 {
00414 Widget &widget =
00415 lookup_timeline_track(model_track)->get_header_widget();
00416 if(widget.is_visible())
00417 widget.size_request();
00418
00419
00420 BOOST_FOREACH( shared_ptr<model::Track> child,
00421 model_track->get_child_tracks() )
00422 size_request_recursive(child);
00423 }
00424
00425 void
00426 TimelineHeaderContainer::forall_vfunc_recursive(
00427 shared_ptr<model::Track> model_track, GtkCallback callback,
00428 gpointer callback_data)
00429 {
00430 REQUIRE(callback != NULL);
00431
00432 callback( lookup_timeline_track(model_track)->
00433 get_header_widget().gobj(), callback_data) ;
00434
00435
00436 BOOST_FOREACH( shared_ptr<model::Track> child,
00437 model_track->get_child_tracks() )
00438 forall_vfunc_recursive(child, callback, callback_data);
00439 }
00440
00441 void
00442 TimelineHeaderContainer::draw_header_decoration(
00443 shared_ptr<model::Track> model_track,
00444 const Gdk::Rectangle &clip_rect)
00445 {
00446 REQUIRE(model_track != NULL);
00447 REQUIRE(clip_rect.get_width() > 0);
00448 REQUIRE(clip_rect.get_height() > 0);
00449
00450 Glib::RefPtr<Style> style = get_style();
00451 REQUIRE(style);
00452
00453 shared_ptr<timeline::Track> timeline_track =
00454 lookup_timeline_track(model_track);
00455
00456
00457 weak_ptr<timeline::Track> ptr(timeline_track);
00458 REQUIRE(contains(headerBoxes, ptr));
00459 const Gdk::Rectangle &box = headerBoxes[timeline_track];
00460
00461
00462 if(box.get_x() < clip_rect.get_width() &&
00463 box.get_height() > 0 &&
00464 box.get_y() + box.get_height() > clip_rect.get_y() &&
00465 box.get_y() < clip_rect.get_y() + clip_rect.get_height())
00466 {
00467
00468 style->paint_box(gdkWindow, STATE_NORMAL,
00469 SHADOW_OUT, clip_rect, *this, "",
00470 box.get_x(), box.get_y(),
00471 box.get_width(), box.get_height());
00472
00473
00474 StateType state_type = STATE_NORMAL;
00475 if(clickedExpander == timeline_track)
00476 state_type = STATE_SELECTED;
00477 else if(hoveringExpander == timeline_track)
00478 state_type = STATE_PRELIGHT;
00479
00480 const ExpanderStyle expander_style =
00481 timeline_track->get_expanded() ?
00482 EXPANDER_EXPANDED : EXPANDER_COLLAPSED;
00483
00484 if(!model_track->get_child_tracks().empty())
00485 style->paint_expander (gdkWindow,
00486 state_type,
00487 clip_rect, *this, "",
00488 box.get_x() + expand_button_size / 2 + margin,
00489 box.get_y() + box.get_height() / 2,
00490 expander_style);
00491 }
00492
00493
00494 if(timeline_track->get_expanded())
00495 BOOST_FOREACH( shared_ptr<model::Track> child,
00496 model_track->get_child_tracks() )
00497 draw_header_decoration(child, clip_rect);
00498 }
00499
00500 boost::shared_ptr<timeline::Track>
00501 TimelineHeaderContainer::header_from_point(const Gdk::Point &point)
00502 {
00503 std::pair<shared_ptr<timeline::Track>, Gdk::Rectangle> pair;
00504 BOOST_FOREACH( pair, headerBoxes )
00505 {
00506
00507 const Gdk::Rectangle &rect = pair.second;
00508
00509 if(point.get_x() >= rect.get_x() &&
00510 point.get_x() < rect.get_x() + rect.get_width() &&
00511 point.get_y() >= rect.get_y() &&
00512 point.get_y() < rect.get_y() + rect.get_height())
00513 return pair.first;
00514 }
00515
00516 return shared_ptr<timeline::Track>();
00517 }
00518
00519 shared_ptr<timeline::Track>
00520 TimelineHeaderContainer::expander_button_from_point(
00521 const Gdk::Point &point)
00522 {
00523 std::pair<shared_ptr<timeline::Track>, Gdk::Rectangle> pair;
00524 BOOST_FOREACH( pair, headerBoxes )
00525 {
00526
00527 const Gdk::Rectangle rect =
00528 get_expander_button_rectangle(pair.first);
00529
00530 if(point.get_x() >= rect.get_x() &&
00531 point.get_x() < rect.get_x() + rect.get_width() &&
00532 point.get_y() >= rect.get_y() &&
00533 point.get_y() < rect.get_y() + rect.get_height())
00534 return pair.first;
00535 }
00536
00537 return shared_ptr<timeline::Track>();
00538 }
00539
00540 const Gdk::Rectangle
00541 TimelineHeaderContainer::get_expander_button_rectangle(
00542 shared_ptr<Track> track)
00543 {
00544 REQUIRE(track != NULL);
00545 weak_ptr<timeline::Track> ptr(track);
00546 REQUIRE(contains(headerBoxes, ptr));
00547
00548 const Gdk::Rectangle &box = headerBoxes[track];
00549 return Gdk::Rectangle(
00550 margin + box.get_x(), margin + box.get_y(),
00551 expand_button_size, box.get_height() - margin * 2);
00552 }
00553
00554 shared_ptr<timeline::Track>
00555 TimelineHeaderContainer::lookup_timeline_track(
00556 shared_ptr<model::Track> model_track)
00557 {
00558 REQUIRE(model_track != NULL);
00559 REQUIRE(timelineWidget != NULL);
00560
00561 shared_ptr<timeline::Track> timeline_track =
00562 timelineWidget->lookup_timeline_track(model_track);
00563 ENSURE(timeline_track);
00564
00565 return timeline_track;
00566 }
00567
00568 const std::list< boost::shared_ptr<model::Track> >
00569 TimelineHeaderContainer::get_tracks() const
00570 {
00571 REQUIRE(timelineWidget != NULL);
00572 REQUIRE(timelineWidget->sequence);
00573 return timelineWidget->sequence->get_child_tracks();
00574 }
00575
00576 void
00577 TimelineHeaderContainer::register_styles() const
00578 {
00579 static bool registered = false;
00580 if(registered) return;
00581 registered = true;
00582
00583 GtkWidgetClass *klass = GTK_WIDGET_CLASS(G_OBJECT_GET_CLASS(gobj()));
00584
00585 gtk_widget_class_install_style_property(klass,
00586 g_param_spec_int("heading_margin",
00587 "Heading Margin",
00588 "The amount of padding around each header pixels.",
00589 0, G_MAXINT, 4, G_PARAM_READABLE));
00590 }
00591
00592 void
00593 TimelineHeaderContainer::read_styles()
00594 {
00595 if(margin <= 0)
00596 get_style_property("heading_margin", margin);
00597 }
00598
00599 }
00600 }
00601 }