// src/cairo_widget.cc - Copyright 2005, 2006, University // of Padova, dept. of Pure and Applied // Mathematics // // This file is part of SGPEMv2. // // This is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // SGPEMv2 is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with SGPEMv2; if not, write to the Free Software // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #include "cairo_elements.hh" #include "cairo_widget.hh" #include #include #include #include using namespace sgpem; CairoWidget::CairoWidget() : Glib::ObjectBase("sgpem_CairoWidget"), Gtk::Widget(), _draw_w(1), _draw_h(1), _buf(NULL), _client_w(0), _client_h(0), _pixmap_w(0), _pixmap_h(0), _need_redraw(true), _scaling_mode(scaling_none) { set_flags(Gtk::NO_WINDOW); } CairoWidget::~CairoWidget() { } CairoWidget::scaling_mode CairoWidget::set_scaling_mode(CairoWidget::scaling_mode scaling) { CairoWidget::scaling_mode old_scaling = _scaling_mode; _scaling_mode = scaling; return old_scaling; } CairoWidget::scaling_mode CairoWidget::get_scaling_mode() { return _scaling_mode; } void CairoWidget::on_realize() { // std::cout << " on_realize" << std::endl; if (is_realized()) return; Gtk::Widget::on_realize(); ensure_style(); GdkWindowAttr attributes; memset(&attributes, 0, sizeof(attributes)); Gtk::Allocation alloc = get_allocation(); attributes.x = alloc.get_x(); attributes.y = alloc.get_y(); attributes.width = alloc.get_width(); attributes.height = alloc.get_height(); attributes.wclass = GDK_INPUT_OUTPUT; attributes.event_mask = get_events() | GDK_EXPOSURE_MASK | GDK_ALL_EVENTS_MASK; attributes.window_type = GDK_WINDOW_CHILD; attributes.visual = get_visual()->gobj(); attributes.colormap = get_colormap()->gobj(); _client_w = alloc.get_width(); _client_h = alloc.get_height(); // std::cout << " on_realize, alloc.get_width() : " << alloc.get_width() << ", alloc.get_height() : " << alloc.get_height() << std::endl; static const gint attributes_mask = Gdk::WA_X | Gdk::WA_Y | Gdk::WA_WMCLASS | Gdk::WA_VISUAL | Gdk::WA_COLORMAP; Glib::RefPtr window = Gdk::Window::create(get_parent_window(), &attributes, attributes_mask); unset_flags(Gtk::NO_WINDOW); set_window(window); window->set_user_data(gobj()); // Not sure if the following line is needed: gtk_style_attach(get_style()->gobj(), window->gobj()); get_style()->set_background(window, Gtk::STATE_ACTIVE); } void CairoWidget::on_size_allocate(const Gtk::Allocation& allocation) { // std::cout << " on_size_allocate, allocation.get_width() : " << allocation.get_width() << ", allocation.get_height() : " << allocation.get_height() << std::endl; set_allocation(allocation); if (is_realized()) { get_window()->move_resize(allocation.get_x(), allocation.get_y(), allocation.get_width(), allocation.get_height()); _client_w = allocation.get_width(); _client_h = allocation.get_height(); _need_redraw = true; } } void CairoWidget::on_size_request(Gtk::Requisition* requisition) { // Gtk+ asks the desired widget dimension // std::cout << " on_size_request, requisition->width : " << requisition->width << ", requisition->height : " << requisition->height << std::endl; _client_w = requisition->width; _client_h = requisition->height; calc_widget_size(_client_w, _client_h); requisition->width = _client_w; requisition->height = _client_h; } bool CairoWidget::on_expose_event(GdkEventExpose* event) { // std::cout << " on_expose_event START..."; if (event == NULL || event->count > 0) return false; // std::cout << " on_expose_event CONTINUE." << std::endl; int actual_w = get_width(); int actual_h = get_height(); // std::cout << " get_width() : " << actual_w << ", get_height() : " << actual_h << std::endl; // std::cout << " _client_w : " << _client_w << ", _client_h : " << _client_h << std::endl; // std::cout << " _pixmap_w : " << _pixmap_w << ", _pixmap_h : " << _pixmap_h << std::endl; if(_need_redraw || actual_w!=_client_w || actual_h!=_client_h) { _need_redraw = false; _client_w = actual_w; _client_h = actual_h; // lazy technique: allocate only if growing... if(_client_w>_pixmap_w || _client_h>_pixmap_h) { // previdence in the code: allocate more than actually needed _pixmap_w = (size_t) (_client_w*1.2); _pixmap_h = (size_t) (_client_h*1.2); // std::cout << " on_expose_event CREATE Pixmap. w : " << _pixmap_w << ", h : " << _pixmap_h<< std::endl; _buf = Gdk::Pixmap::create(get_window(), _pixmap_w, _pixmap_h); } // Draw the widget background as the first thing. // I've seen this is how it's done in the very Gtk+ toolkit // for the GtkProgressBar widget. GtkStyle* gStyle = get_style()->gobj(); // std::cout << " on_expose_event gtk_paint_box." << std::endl; gtk_paint_box(gStyle, _buf->gobj(), GTK_STATE_NORMAL, GTK_SHADOW_ETCHED_IN, NULL, (GtkWidget*)this->gobj(), "through", 0, 0, _client_w, _client_h); cairo_t* ctx = gdk_cairo_create(_buf->gobj()); // Determine the final drawing dimensions before to start drawing _draw_w = _draw_h = 0; // default no scaling calc_drawing_size(ctx, _draw_w, _draw_h); calc_scale_factors(); // do the drawing... cairo_scale(ctx, _scale_x, _scale_y); // std::cout << " on_expose_event Dimensions, w : " << _draw_w << ", h : " << _draw_h << std::endl; draw_widget(ctx); cairo_destroy(ctx); } // calculated dinamically on update(): // set_size_request(_draw_w, _draw_h); // Clip to redraw only the smallest possible area // Use double buffering or we're CPU-dead // Copy from the buffer to the screen if (_buf) get_window()->draw_drawable(get_style()->get_black_gc(), _buf, event->area.x, event->area.y, event->area.x, event->area.y, event->area.width, event->area.height); return true; } void CairoWidget::calc_scale_factors() { if(_scaling_mode!=scaling_none && _draw_w!=0 && _draw_h!=0) { _scale_x = (double)_client_w/(double)_draw_w; _scale_y = (double)_client_h/(double)_draw_h; switch(_scaling_mode) { case scaling_none: // not necessary! _scale_x = _scale_y = 1.0; break; case scaling_to_w: _scale_y = _scale_x; break; case scaling_to_h: _scale_x = _scale_y; break; case scaling_min: if(_scale_x<=_scale_y) _scale_y = _scale_x; else _scale_x = _scale_y; break; case scaling_max: if(_scale_x>=_scale_y) _scale_y = _scale_x; else _scale_x = _scale_y; break; case scaling_all: // nothing to do... break; } } else { _scale_x = _scale_y = 1.0; } } bool CairoWidget::on_button_press_event(GdkEventButton* event) { return false; } void CairoWidget::redraw() { // Force redraw _need_redraw = true; queue_draw(); } void CairoWidget::resize_redraw() { // std::cout << " resize_redraw BEGIN width: " << get_width() << ", height: " << get_height() << std::endl; if(_buf) { cairo_t* ctx = gdk_cairo_create(_buf->gobj()); if(ctx) { // Determine the final drawing dimensions before to start drawing _draw_w = _draw_h = 0; // default no scaling calc_drawing_size(ctx, _draw_w, _draw_h); calc_scale_factors(); set_size_request((int)_scale_x*_draw_w, (int)_scale_y*_draw_h); cairo_destroy(ctx); //std::cout << " resize_redraw DO width: " << _scale_x*_draw_w << ", height: " << _scale_y*_draw_h << std::endl; } } // Force redraw _need_redraw = true; queue_draw(); } void CairoWidget::calc_drawing_size(cairo_t* ctx, size_t& width, size_t& height) { } void CairoWidget::calc_widget_size(size_t& width, size_t& height) { }