13
0
livetrax/libs/gtkmm2ext/scroomer.cc

392 lines
8.8 KiB
C++
Raw Normal View History

/*
Copyright (C) 2008 Paul Davis
This program 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.
This program 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 this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <iostream>
#include <gtkmm2ext/scroomer.h>
using namespace Gtkmm2ext;
using namespace Gtk;
using namespace Gdk;
using namespace std;
Scroomer::Scroomer(Gtk::Adjustment& adjustment)
: adj(adjustment)
, handle_size(0)
, grab_comp(None) {
position[TopBase] = 0;
position[Handle1] = 0;
position[Slider] = 0;
position[Handle2] = 0;
position[BottomBase] = 0;
position[Total] = 0;
add_events (Gdk::BUTTON_PRESS_MASK |
Gdk::BUTTON_RELEASE_MASK |
Gdk::POINTER_MOTION_MASK |
Gdk::SCROLL_MASK);
adjustment.signal_value_changed().connect (mem_fun (*this, &Scroomer::adjustment_changed));
//adjustment.signal_changed().connect (mem_fun (*this, &Scroomer::adjustment_changed));
}
Scroomer::~Scroomer() {
}
bool
Scroomer::on_motion_notify_event (GdkEventMotion* ev) {
double range = adj.get_upper() - adj.get_lower();
double pixel2val = range / get_height();
double val_at_pointer = ((get_height() - ev->y) * pixel2val) + adj.get_lower();
double delta_y = ev->y - grab_y;
double half_min_page = min_page_size / 2;
double fract = delta_y / position[Total];
double scale, temp, zoom;
double val, page;
if(grab_comp == None || grab_comp == Total) {
return true;
}
if (ev->window != grab_window) {
grab_y = ev->y;
grab_window = ev->window;
return true;
}
grab_y = ev->y;
if (ev->state & GDK_CONTROL_MASK) {
if (ev->state & GDK_MOD1_MASK) {
scale = 0.05;
} else {
scale = 0.1;
}
} else {
scale = 1.0;
}
fract = min (1.0, fract);
fract = max (-1.0, fract);
fract = -fract;
switch(grab_comp) {
case TopBase:
case BottomBase:
unzoomed_val += scale * fract * range;
unzoomed_val = min(unzoomed_val, adj.get_upper() - unzoomed_page);
unzoomed_val = max(unzoomed_val, adj.get_lower());
break;
case Slider:
unzoomed_val += scale * fract * range;
unzoomed_val = min(unzoomed_val, adj.get_upper() - unzoomed_page);
unzoomed_val = max(unzoomed_val, adj.get_lower());
break;
case Handle1:
unzoomed_page += scale * fract * range;
unzoomed_page = min(unzoomed_page, adj.get_upper() - unzoomed_val);
unzoomed_page = max(unzoomed_page, min_page_size);
break;
case Handle2:
temp = unzoomed_val + unzoomed_page;
unzoomed_val += scale * fract * range;
unzoomed_val = min(unzoomed_val, temp - min_page_size);
unzoomed_val = max(unzoomed_val, adj.get_lower());
unzoomed_page = temp - unzoomed_val;
unzoomed_page = max(unzoomed_page, min_page_size);
break;
default:
break;
}
/*
* Then we handle zoom, which is dragging horizontally. We zoom around the area that is
* the current y pointer value, not from the area that was the start of the drag.
* the point of zoom must have the same
*/
if(ev->x > get_width()) {
zoom = ev->x - get_width();
double higher = unzoomed_val + unzoomed_page - half_min_page - val_at_pointer;
double lower = val_at_pointer - (unzoomed_val + half_min_page);
higher *= zoom / 128;
lower *= zoom / 128;
val = unzoomed_val + lower;
page = unzoomed_page - higher - lower;
page = max(page, min_page_size);
if(lower < 0) {
val = max(val, val_at_pointer - half_min_page);
}
else if(lower > 0) {
val = min(val, val_at_pointer - half_min_page);
}
val = min(val, adj.get_upper() - min_page_size);
page = min(page, adj.get_upper() - val);
}
else if (ev->x < 0) {
/* on zoom out increase the page size as well as moving the range towards the mouse pos*/
zoom = abs(ev->x);
/*double higher = unzoomed_val + unzoomed_page - half_min_page - val_at_pointer;
double lower = val_at_pointer - (unzoomed_val + half_min_page);
higher *= zoom / 128;
lower *= zoom / 128;
val = unzoomed_val + lower;
page = unzoomed_page - higher - lower;
page = max(page, min_page_size);
if(lower < 0) {
val = max(val, val_at_pointer - half_min_page);
}
else if(lower > 0) {
val = min(val, val_at_pointer - half_min_page);
}
val = min(val, adj.get_upper() - min_page_size);
page = min(page, adj.get_upper() - val);*/
val = unzoomed_val;
page = unzoomed_page;
}
else {
val = unzoomed_val;
page = unzoomed_page;
}
adj.set_page_size(page);
if(val == adj.get_value()) {
adj.value_changed();
}
if(val < adj.get_lower()) {
adj.value_changed();
}
else if(val > adj.get_upper()) {
adj.value_changed();
}
else {
adj.set_value(val);
}
return true;
}
bool
Scroomer::on_button_press_event (GdkEventButton* ev) {
if(ev->button == 1) {
Component comp = point_in(ev->y);
cerr << get_comp_name(comp) << " pressed" << endl;
if(comp == Total || comp == None) {
return false;
}
add_modal_grab();
grab_comp = comp;
grab_y = ev->y;
unzoomed_val = adj.get_value();
unzoomed_page = adj.get_page_size();
grab_window = ev->window;
}
return false;
}
bool
Scroomer::on_button_release_event (GdkEventButton* ev) {
if(grab_comp == None || grab_comp == Total) {
return true;
}
if (ev->window != grab_window) {
grab_y = ev->y;
grab_window = ev->window;
return true;
}
if (ev->button != 1) {
return true;
}
switch(grab_comp) {
case TopBase:
break;
case Handle1:
break;
case Slider:
break;
case Handle2:
break;
case BottomBase:
break;
default:
break;
}
grab_comp = None;
remove_modal_grab();
return true;
}
bool
Scroomer::on_scroll_event (GdkEventScroll*) {
return true;
}
void
Scroomer::on_size_allocate (Allocation& a) {
Gtk::DrawingArea::on_size_allocate(a);
position[Total] = a.get_height();
set_min_page_size(min_page_size);
update();
}
/*
* assumes that x and width are correct, and they will not be altered
*/
void
Scroomer::set_comp_rect(GdkRectangle& r, Component c) const {
int index = (int) c;
switch(c) {
case None:
return;
case Total:
r.y = 0;
r.height = position[Total];
break;
default:
r.y = position[index];
r.height = position[index+1] - position[index];
break;
}
}
Scroomer::Component
Scroomer::point_in(double point) const {
for(int i = 0; i < Total; ++i) {
if(position[i+1] >= point) {
return (Component) i;
}
}
return None;
}
void
Scroomer::set_min_page_size(double ps) {
double coeff = ((double)position[Total]) / (adj.get_upper() - adj.get_lower());
min_page_size = ps;
handle_size = (int) floor((ps * coeff) / 2);
}
void
Scroomer::update() {
double range = adj.get_upper() - adj.get_lower();
//double value = adj.get_value() - adj.get_lower();
int height = position[Total];
double coeff = ((double) height) / range;
/* save the old positions to calculate update regions later*/
for(int i = Handle1; i < Total; ++i) {
old_pos[i] = position[i];
}
position[BottomBase] = (int) floor(height - (adj.get_value() * coeff));
position[Handle2] = position[BottomBase] - handle_size;
position[Handle1] = (int) floor(height - ((adj.get_value() + adj.get_page_size()) * coeff));
position[Slider] = position[Handle1] + handle_size;
}
void
Scroomer::adjustment_changed() {
//cerr << floor(adj.get_value()) << " " << floor(adj.get_value() + adj.get_page_size()) << endl;
Gdk::Rectangle rect;
Glib::RefPtr<Gdk::Window> win = get_window();
update();
if(!win) {
return;
}
rect.set_x(0);
rect.set_width(get_width());
if(position[Handle1] < old_pos[Handle1]) {
rect.set_y(position[Handle1]);
rect.set_height(old_pos[Slider] - position[Handle1]);
win->invalidate_rect(rect, false);
}
else if(position[Handle1] > old_pos[Handle1]) {
rect.set_y(old_pos[Handle1]);
rect.set_height(position[Slider] - old_pos[Handle1]);
win->invalidate_rect(rect, false);
}
if(position[Handle2] < old_pos[Handle2]) {
rect.set_y(position[Handle2]);
rect.set_height(old_pos[BottomBase] - position[Handle2]);
win->invalidate_rect(rect, false);
}
else if(position[Handle2] > old_pos[Handle2]) {
rect.set_y(old_pos[Handle2]);
rect.set_height(position[BottomBase] - old_pos[Handle2]);
win->invalidate_rect(rect, false);
}
win->process_updates(false);
}
std::string
Scroomer::get_comp_name(Component c) {
switch(c) {
case TopBase:
return "TopBase";
case Handle1:
return "Handle1";
case Slider:
return "Slider";
case Handle2:
return "Handle2";
case BottomBase:
return "BottomBase";
case Total:
return "Total";
case None:
return "None";
default:
return "ERROR";
}
}