summaryrefslogtreecommitdiff
path: root/src/renderers/pieRenderer.vala
diff options
context:
space:
mode:
Diffstat (limited to 'src/renderers/pieRenderer.vala')
-rw-r--r--src/renderers/pieRenderer.vala890
1 files changed, 890 insertions, 0 deletions
diff --git a/src/renderers/pieRenderer.vala b/src/renderers/pieRenderer.vala
new file mode 100644
index 0000000..1ff2b3e
--- /dev/null
+++ b/src/renderers/pieRenderer.vala
@@ -0,0 +1,890 @@
+/////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2011-2015 by Simon Schneegans
+//
+// 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 3 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, see <http://www.gnu.org/licenses/>.
+/////////////////////////////////////////////////////////////////////////
+
+using GLib.Math;
+
+namespace GnomePie {
+
+/////////////////////////////////////////////////////////////////////////
+/// This class renders a Pie. In order to accomplish that, it owns a
+/// CenterRenderer and some SliceRenderers.
+/////////////////////////////////////////////////////////////////////////
+
+public class PieRenderer : GLib.Object {
+
+ /////////////////////////////////////////////////////////////////////
+ /// The index of the slice used for quick action. (The action which
+ /// gets executed when the user clicks on the middle of the pie)
+ /////////////////////////////////////////////////////////////////////
+
+ public int quickaction { get; private set; }
+
+ /////////////////////////////////////////////////////////////////////
+ /// The index of the currently active slice.
+ /////////////////////////////////////////////////////////////////////
+
+ public int active_slice { get; private set; }
+
+ /////////////////////////////////////////////////////////////////////
+ /// True, if the hot keys are currently displayed.
+ /////////////////////////////////////////////////////////////////////
+
+ public bool show_hotkeys { get; set; }
+
+ /////////////////////////////////////////////////////////////////////
+ /// The width and height of the Pie in pixels.
+ /////////////////////////////////////////////////////////////////////
+
+ public int size_w { get; private set; }
+ public int size_h { get; private set; }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Center position relative to window top-left corner
+ /////////////////////////////////////////////////////////////////////
+
+ public int center_x { get; private set; }
+ public int center_y { get; private set; }
+
+
+ ////////////////////////////////////////////////////////////////////
+ /// Possible show pie modes.
+ /// FULL_PIE: Show the pie as a complete circle.
+ /// HPIE_LEFT: Eat half pie so it can be shown at the left of the screen.
+ /// HPIE_RIGHT: Eat half pie so it can be shown at the right of the screen.
+ /// HPIE_TOP: Eat half pie so it can be shown at the top of the screen.
+ /// HPIE_BOTTOM: Eat half pie so it can be shown at the bottom of the screen.
+ /// CPIE_TOP_LEFT: Eat 3/4 pie so it can be shown at the top-left corner.
+ /// CPIE_TOP_RIGHT: Eat 3/4 pie so it can be shown at the top-right corner.
+ /// CPIE_BOT_LEFT: Eat 3/4 pie so it can be shown at the bottom-left corner.
+ /// CPIE_BOT_RIGHT: Eat 3/4 pie so it can be shown at the bottom-right corner.
+ /////////////////////////////////////////////////////////////////////
+
+ public enum ShowPieMode {
+ FULL_PIE,
+ HPIE_LEFT, HPIE_RIGHT, HPIE_TOP, HPIE_BOTTOM,
+ CPIE_TOP_LEFT, CPIE_TOP_RIGHT, CPIE_BOT_LEFT, CPIE_BOT_RIGHT}
+
+ /////////////////////////////////////////////////////////////////////
+ /// Show pie mode: full, half-circle, corner
+ /////////////////////////////////////////////////////////////////////
+
+ public ShowPieMode pie_show_mode { get; private set; default= ShowPieMode.FULL_PIE; }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Number of visible slices
+ /////////////////////////////////////////////////////////////////////
+
+ public int visible_slice_count { get; private set; }
+
+ public int original_visible_slice_count { get; private set; }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Number of slices in full pie (visible or not)
+ /////////////////////////////////////////////////////////////////////
+
+ public int total_slice_count { get; private set; }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Maximun number of visible slices in a full pie
+ /////////////////////////////////////////////////////////////////////
+
+ public int max_visible_slices { get; private set; }
+
+ /////////////////////////////////////////////////////////////////////
+ /// The index of the first visible slice
+ /////////////////////////////////////////////////////////////////////
+
+ public int first_slice_idx { get; private set; }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Angular position of the first visible slice
+ /////////////////////////////////////////////////////////////////////
+
+ public double first_slice_angle { get; private set; }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Index of the slice where to go when up/down/left/right key is pressed
+ /// or -1 if that side of the pie was eaten
+ /////////////////////////////////////////////////////////////////////
+
+ public int up_slice_idx { get; private set; }
+ public int down_slice_idx { get; private set; }
+ public int left_slice_idx { get; private set; }
+ public int right_slice_idx { get; private set; }
+
+
+ /////////////////////////////////////////////////////////////////////
+ /// The ID of the currently loaded Pie.
+ /////////////////////////////////////////////////////////////////////
+
+ public string id { get; private set; }
+
+ /////////////////////////////////////////////////////////////////////
+ /// True if the pie is currently navigated with the keyboard. This is
+ /// set to false as soon as the mouse moves.
+ /////////////////////////////////////////////////////////////////////
+
+ public bool key_board_control { get; set; default=false; }
+
+ /////////////////////////////////////////////////////////////////////
+ /// All SliceRenderers used to draw this Pie.
+ /////////////////////////////////////////////////////////////////////
+
+ private Gee.ArrayList<SliceRenderer?> slices;
+
+ /////////////////////////////////////////////////////////////////////
+ /// The renderer for the center of this pie.
+ /////////////////////////////////////////////////////////////////////
+
+ private CenterRenderer center;
+
+ /////////////////////////////////////////////////////////////////////
+ /// Maximum distance from the center that activates the slices
+ /////////////////////////////////////////////////////////////////////
+ private int activation_range;
+
+ /////////////////////////////////////////////////////////////////////
+ /// C'tor, initializes members.
+ /////////////////////////////////////////////////////////////////////
+
+ public PieRenderer() {
+ this.slices = new Gee.ArrayList<SliceRenderer?>();
+ this.center = new CenterRenderer(this);
+ this.quickaction = -1;
+ this.active_slice = -2;
+ this.size_w = 0;
+ this.size_h = 0;
+ this.activation_range= 300;
+
+ this.max_visible_slices= Config.global.max_visible_slices;
+
+ set_show_mode(ShowPieMode.FULL_PIE);
+ }
+
+
+ private void get_mouse_and_screen(out int mousex, out int mousey, out int screenx, out int screeny) {
+ // get the mouse position and screen resolution
+ double x = 0.0;
+ double y = 0.0;
+
+ var display = Gdk.Display.get_default();
+ var manager = display.get_device_manager();
+ GLib.List<weak Gdk.Device?> list = manager.list_devices(Gdk.DeviceType.MASTER);
+
+ foreach(var device in list) {
+ if (device.input_source != Gdk.InputSource.KEYBOARD) {
+ Gdk.Screen screen;
+ device.get_position( out screen, out x, out y );
+ }
+ }
+ mousex= (int) x;
+ mousey= (int) y;
+ screenx= Gdk.Screen.width();
+ screeny= Gdk.Screen.height();
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Loads a Pie. All members are initialized accordingly.
+ /////////////////////////////////////////////////////////////////////
+
+ public void load_pie(Pie pie) {
+ this.slices.clear();
+
+ this.id = pie.id;
+
+ int count = 0;
+ foreach (var group in pie.action_groups) {
+ foreach (var action in group.actions) {
+ var renderer = new SliceRenderer(this);
+ this.slices.add(renderer);
+ renderer.load(action, slices.size-1);
+
+ if (action.is_quickaction) {
+ this.quickaction = count;
+ }
+
+ ++count;
+ }
+ }
+
+ this.select_by_index(this.quickaction);
+
+
+ ShowPieMode showpie= ShowPieMode.FULL_PIE;
+ //set full pie to determine the number of visible slices
+ set_show_mode(showpie);
+
+ int sz0= (int)fmax(2*Config.global.theme.radius + 2*Config.global.theme.visible_slice_radius*Config.global.theme.max_zoom,
+ 2*Config.global.theme.center_radius);
+
+ int sz= sz0;
+ // increase size if there are many slices
+ if (this.total_slice_count > 0) {
+ sz = (int)fmax(sz0,
+ (((Config.global.theme.slice_radius + Config.global.theme.slice_gap)/tan(PI/this.total_slice_count))
+ + Config.global.theme.visible_slice_radius)*2*Config.global.theme.max_zoom);
+ }
+
+
+
+
+ // get mouse position and screen resolution
+ int mouse_x, mouse_y, screen_x, screen_y;
+ get_mouse_and_screen( out mouse_x, out mouse_y, out screen_x, out screen_y );
+
+ //reduce the window size if needed to get closer to the actual mouse position
+ int reduce_szx= 1;
+ int reduce_szy= 1;
+
+ if (PieManager.get_is_auto_shape(pie.id) && !PieManager.get_is_centered(pie.id)) {
+ //set the best show mode that put the mouse near the center
+ if (mouse_x < sz/2) {
+ if (mouse_y < sz/2)
+ showpie= ShowPieMode.CPIE_TOP_LEFT; //show 1/4 pie
+ else if (screen_y > 0 && screen_y-mouse_y < sz/2)
+ showpie= ShowPieMode.CPIE_BOT_LEFT; //show 1/4 pie
+ else
+ showpie= ShowPieMode.HPIE_LEFT; //show 1/2 pie
+
+ } else if (mouse_y < sz/2) {
+ if (screen_x > 0 && screen_x-mouse_x < sz/2)
+ showpie= ShowPieMode.CPIE_TOP_RIGHT; //show 1/4 pie
+ else
+ showpie= ShowPieMode.HPIE_TOP; //show 1/2 pie
+
+ } else if (screen_x > 0 && screen_x-mouse_x < sz/2) {
+ if (screen_y > 0 && screen_y-mouse_y < sz/2)
+ showpie= ShowPieMode.CPIE_BOT_RIGHT; //show 1/4 pie
+ else
+ showpie= ShowPieMode.HPIE_RIGHT; //show 1/2 pie
+
+ } else if (screen_y > 0 && screen_y-mouse_y < sz/2)
+ showpie= ShowPieMode.HPIE_BOTTOM; //show 1/2 pie
+
+
+ } else {
+ //if the pie is centered in the screen, don't reduce the size
+ if (PieManager.get_is_centered(pie.id)) {
+ reduce_szx= 0;
+ reduce_szy= 0;
+ }
+
+ //select the configured shape
+ //convert from radio-buttum number to ShowPieMode enum
+ switch( PieManager.get_shape_number(pie.id) ) {
+ case 1:
+ showpie= ShowPieMode.CPIE_BOT_RIGHT;
+ if (screen_x-mouse_x > sz/2)
+ reduce_szx= 0; //keep full width
+ if (screen_y-mouse_y > sz/2)
+ reduce_szy= 0; //keep full height
+ break;
+ case 2:
+ showpie= ShowPieMode.HPIE_RIGHT;
+ if (screen_x-mouse_x > sz/2)
+ reduce_szx= 0; //keep full width
+ break;
+ case 3:
+ showpie= ShowPieMode.CPIE_TOP_RIGHT;
+ if (screen_x-mouse_x > sz/2)
+ reduce_szx= 0; //keep full width
+ if (mouse_y > sz/2)
+ reduce_szy= 0; //keep full height
+ break;
+ case 4:
+ showpie= ShowPieMode.HPIE_BOTTOM;
+ if (screen_y-mouse_y > sz/2)
+ reduce_szy= 0; //keep full height
+ break;
+ case 6:
+ showpie= ShowPieMode.HPIE_TOP;
+ if (mouse_y > sz/2)
+ reduce_szy= 0; //keep full height
+ break;
+ case 7:
+ showpie= ShowPieMode.CPIE_BOT_LEFT;
+ if (mouse_x > sz/2)
+ reduce_szx= 0; //keep full width
+ if (screen_y-mouse_y > sz/2)
+ reduce_szy= 0; //keep full height
+ break;
+ case 8:
+ showpie= ShowPieMode.HPIE_LEFT;
+ if (mouse_x > sz/2)
+ reduce_szx= 0; //keep full width
+ break;
+ case 9:
+ showpie= ShowPieMode.CPIE_TOP_LEFT;
+ if (mouse_x > sz/2)
+ reduce_szx= 0; //keep full width
+ if (mouse_y > sz/2)
+ reduce_szy= 0; //keep full height
+ break;
+ }
+ }
+ //set the new show pie mode
+ set_show_mode(showpie);
+
+ //recalc size
+ sz = sz0;
+ if (this.total_slice_count > 0) {
+ sz = (int)fmax(sz0,
+ (((Config.global.theme.slice_radius + Config.global.theme.slice_gap)/tan(PI/this.total_slice_count))
+ + Config.global.theme.visible_slice_radius)*2*Config.global.theme.max_zoom);
+ }
+ //activation_range = normal pie radius + "outer" activation_range
+ this.activation_range= (int)((double)Config.global.activation_range + sz/(2*Config.global.theme.max_zoom));
+
+ int szx = 1; //full width
+ int szy = 1; //full height
+ switch(this.pie_show_mode) {
+ //half pie
+ case ShowPieMode.HPIE_LEFT:
+ szx = 0; //half width, center to the left
+ break;
+ case ShowPieMode.HPIE_RIGHT:
+ szx = 2; //half width, center to the right
+ break;
+ case ShowPieMode.HPIE_TOP:
+ szy = 0; //half height, center to the top
+ break;
+ case ShowPieMode.HPIE_BOTTOM:
+ szy = 2; //half height, center to the bottom
+ break;
+
+ //cuarter pie
+ case ShowPieMode.CPIE_TOP_LEFT:
+ szx = 0; //half width, center to the left
+ szy = 0; //half height, center to the top
+ break;
+ case ShowPieMode.CPIE_TOP_RIGHT:
+ szx = 2; //half width, center to the right
+ szy = 0; //half height, center to the top
+ break;
+ case ShowPieMode.CPIE_BOT_LEFT:
+ szx = 0; //half width, center to the left
+ szy = 2; //half height, center to the bottom
+ break;
+ case ShowPieMode.CPIE_BOT_RIGHT:
+ szx = 2; //half width, center to the right
+ szy = 2; //half height, center to the bottom
+ break;
+ }
+ if (reduce_szx == 0)
+ szx = 1; //don't reduce width
+ if (reduce_szy == 0)
+ szy = 1; //don't reduce height
+
+ int rc = (int)Config.global.theme.center_radius;
+ if (szx == 1 ) {
+ //full width
+ this.size_w = sz;
+ this.center_x = sz/2; //center position
+ } else {
+ //half width
+ this.size_w = sz/2 + rc;
+ if (szx == 0) {
+ this.center_x = rc; //center to the left
+ } else {
+ this.center_x = this.size_w-rc; //center to the right
+ }
+ }
+ if (szy == 1) {
+ //full heigth
+ this.size_h = sz;
+ this.center_y = sz/2; //center position
+ } else {
+ //half heigth
+ this.size_h = sz/2 + rc;
+ if (szy == 0) {
+ this.center_y = rc; //center to the top
+ } else {
+ this.center_y = this.size_h-rc; //center to the bottom
+ }
+ }
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Activates the currently active slice.
+ /////////////////////////////////////////////////////////////////////
+
+ public void activate(uint32 time_stamp) {
+ if (this.active_slice >= this.first_slice_idx
+ && this.active_slice < this.first_slice_idx+this.visible_slice_count) {
+ slices[active_slice].activate(time_stamp);
+ }
+
+ //foreach (var slice in this.slices)
+ // slice.fade_out();
+ for (int i= 0; i < this.visible_slice_count; ++i) {
+ this.slices[ i+this.first_slice_idx ].fade_out();
+ }
+
+ center.fade_out();
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Asks all renders to fade out.
+ /////////////////////////////////////////////////////////////////////
+
+ public void cancel() {
+ //foreach (var slice in this.slices)
+ // slice.fade_out();
+ for (int i= 0; i < this.visible_slice_count; ++i) {
+ this.slices[ i+this.first_slice_idx ].fade_out();
+ }
+
+ center.fade_out();
+ }
+
+
+ /////////////////////////////////////////////////////////////////////
+ /// Called when the up-key is pressed. Selects the next slice towards
+ /// the top.
+ /////////////////////////////////////////////////////////////////////
+
+ public void select_up() {
+ move_active_slice(this.up_slice_idx, this.down_slice_idx);
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Called when the down-key is pressed. Selects the next slice
+ /// towards the bottom.
+ /////////////////////////////////////////////////////////////////////
+
+ public void select_down() {
+ move_active_slice(this.down_slice_idx, this.up_slice_idx);
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Called when the left-key is pressed. Selects the next slice
+ /// towards the left.
+ /////////////////////////////////////////////////////////////////////
+
+ public void select_left() {
+ move_active_slice(this.left_slice_idx, this.right_slice_idx);
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Called when the right-key is pressed. Selects the next slice
+ /// towards the right.
+ /////////////////////////////////////////////////////////////////////
+
+ public void select_right() {
+ move_active_slice(this.right_slice_idx, this.left_slice_idx);
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Called when the page_up-key is pressed. Selects the next
+ /// group of slices.
+ /////////////////////////////////////////////////////////////////////
+
+ public void select_nextpage() {
+ if (this.first_slice_idx+this.visible_slice_count < slices.size) {
+ //advance one page
+ this.first_slice_idx += this.visible_slice_count;
+ if (this.first_slice_idx+this.visible_slice_count >= slices.size) {
+ this.visible_slice_count= slices.size - this.first_slice_idx;
+ }
+ this.reset_slice_anim();
+ this.select_by_index(-1);
+ calc_key_navigation_pos();
+ this.key_board_control = true;
+
+ } else if (this.first_slice_idx > 0) {
+ //go to first page
+ this.first_slice_idx= 0;
+ this.reset_slice_anim();
+ //recover the original value
+ this.visible_slice_count= this.original_visible_slice_count;
+ this.reset_slice_anim();
+ this.select_by_index(-1);
+ calc_key_navigation_pos();
+ this.key_board_control = true;
+ }
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Called when the page_down-key is pressed. Selects the previous
+ /// group of slices.
+ /////////////////////////////////////////////////////////////////////
+
+ public void select_prevpage() {
+ if (this.first_slice_idx > 0) {
+ //go back one page
+ //recover the original value
+ this.visible_slice_count= this.original_visible_slice_count;
+ this.first_slice_idx -= this.visible_slice_count;
+ if (this.first_slice_idx < 0) {
+ this.first_slice_idx= 0;
+ }
+ this.reset_slice_anim();
+ this.select_by_index(-1);
+ calc_key_navigation_pos();
+ this.key_board_control = true;
+
+ } else if (this.visible_slice_count < slices.size) {
+ //go to last page
+ int n= slices.size % this.original_visible_slice_count;
+ if (n == 0)
+ //all pages have the same number of slices
+ this.visible_slice_count= this.original_visible_slice_count;
+ else
+ //last page has less slices than previous
+ this.visible_slice_count= n;
+ this.first_slice_idx= slices.size - this.visible_slice_count;
+ this.reset_slice_anim();
+ this.select_by_index(-1);
+ calc_key_navigation_pos();
+ this.key_board_control = true;
+ }
+ }
+
+ private void reset_slice_anim() {
+ //reset animation values in all the new visible slices
+ for (int i= 0; i < this.visible_slice_count; ++i)
+ this.slices[ i+this.first_slice_idx ].reset_anim();
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Selects a slice based on a search string.
+ /////////////////////////////////////////////////////////////////////
+
+ public void select_by_string(string search) {
+ float max_similarity = 0;
+ int index = -1;
+
+ for (int i=0; i<this.visible_slice_count; ++i) {
+ float similarity = 0;
+ int cur_pos = 0;
+ var name = slices[this.first_slice_idx+i].action.name.down();
+
+ for (int j=0; j<search.length; ++j) {
+ int next_pos = name.index_of(search.substring(j, 1), cur_pos);
+
+ if (next_pos != -1) {
+ cur_pos = next_pos;
+ similarity += (float)(name.length-next_pos)/name.length + 2;
+ }
+ }
+
+ if (similarity > max_similarity) {
+ index = this.first_slice_idx+i;
+ max_similarity = similarity;
+ }
+ }
+
+ if (index >= 0 && index < slice_count()) {
+ key_board_control = true;
+ select_by_index(index);
+ }
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Returns the amount of slices in this pie.
+ /////////////////////////////////////////////////////////////////////
+
+ public int slice_count() {
+ return slices.size;
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Draws the entire pie.
+ /////////////////////////////////////////////////////////////////////
+
+ public void draw(double frame_time, Cairo.Context ctx, int mouse_x, int mouse_y) {
+ if (this.size_w > 0) {
+ double distance = sqrt(mouse_x*mouse_x + mouse_y*mouse_y);
+ double angle = 0.0;
+ int slice_track= 0;
+
+ if (this.key_board_control) {
+ int n= this.active_slice - this.first_slice_idx;
+ angle = 2.0*PI*n/(double)this.total_slice_count + this.first_slice_angle;
+ slice_track= 1;
+ } else {
+
+ if (distance > 0) {
+ angle = acos(mouse_x/distance);
+ if (mouse_y < 0)
+ angle = 2*PI - angle;
+ }
+
+ int next_active_slice = this.active_slice;
+
+ if (distance < Config.global.theme.active_radius
+ && this.quickaction >= this.first_slice_idx
+ && this.quickaction < this.first_slice_idx+this.visible_slice_count) {
+
+ next_active_slice = this.quickaction;
+ int n= this.quickaction - this.first_slice_idx;
+ angle = 2.0*PI*n/(double)this.total_slice_count + this.first_slice_angle;
+
+ } else if (distance > Config.global.theme.active_radius && this.total_slice_count > 0
+ && distance < this.activation_range) {
+ double a= angle-this.first_slice_angle;
+ if (a < 0)
+ a= a + 2*PI;
+ next_active_slice = (int)(a*this.total_slice_count/(2*PI) + 0.5) % this.total_slice_count;
+ if (next_active_slice >= this.visible_slice_count)
+ next_active_slice = -1;
+ else {
+ next_active_slice = next_active_slice + this.first_slice_idx;
+ slice_track= 1;
+ }
+ } else {
+ next_active_slice = -1;
+ }
+
+ this.select_by_index(next_active_slice);
+ }
+
+ center.draw(frame_time, ctx, angle, slice_track);
+
+ for (int i= 0; i < this.visible_slice_count; ++i) {
+ this.slices[ i+this.first_slice_idx ].draw(frame_time, ctx, angle, slice_track);
+ }
+ }
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Called when the user moves the mouse.
+ /////////////////////////////////////////////////////////////////////
+
+ public void on_mouse_move() {
+ this.key_board_control = false;
+ }
+
+ /////////////////////////////////////////////////////////////////////
+ /// Called when the currently active slice changes.
+ /////////////////////////////////////////////////////////////////////
+
+ public void select_by_index(int index) {
+ if (index != this.active_slice) {
+ if (index >= this.first_slice_idx && index < this.first_slice_idx+this.visible_slice_count)
+ this.active_slice = index;
+ else
+ this.active_slice = -1;
+
+ SliceRenderer? active = (this.active_slice >= 0 && this.active_slice < slices.size) ?
+ this.slices[this.active_slice] : null;
+
+ center.set_active_slice(active);
+
+ for (int i= 0; i < this.visible_slice_count; ++i) {
+ this.slices[ i+this.first_slice_idx ].set_active_slice(active);
+ }
+ }
+ }
+
+ private void set_show_mode(ShowPieMode show_mode) {
+ //The index of the first visible slice
+ this.first_slice_idx= 0;
+ //Angular position of the first visible slice
+ this.first_slice_angle= 0;
+
+ int mult= 1;
+ switch(show_mode) {
+ //half pie
+ case ShowPieMode.HPIE_LEFT:
+ mult= 2;
+ this.first_slice_angle= -PI/2;
+ break;
+ case ShowPieMode.HPIE_RIGHT:
+ mult= 2;
+ this.first_slice_angle= PI/2;
+ break;
+ case ShowPieMode.HPIE_TOP:
+ mult= 2;
+ break;
+ case ShowPieMode.HPIE_BOTTOM:
+ this.first_slice_angle= PI;
+ mult= 2;
+ break;
+
+ //cuarter pie
+ case ShowPieMode.CPIE_TOP_LEFT:
+ mult= 4;
+ break;
+ case ShowPieMode.CPIE_TOP_RIGHT:
+ this.first_slice_angle= PI/2;
+ mult= 4;
+ break;
+ case ShowPieMode.CPIE_BOT_LEFT:
+ this.first_slice_angle= -PI/2;
+ mult= 4;
+ break;
+ case ShowPieMode.CPIE_BOT_RIGHT:
+ this.first_slice_angle= PI;
+ mult= 4;
+ break;
+
+ default: //ShowPieMode.FULL_PIE or invalid values
+ show_mode= ShowPieMode.FULL_PIE;
+ break;
+ }
+ this.pie_show_mode= show_mode;
+ //limit the number of visible slices
+ int maxview= this.max_visible_slices / mult;
+ //Number of visible slices
+ this.visible_slice_count= (int)fmin(slices.size, maxview);
+ //Number of slices in full pie (visible or not)
+ this.total_slice_count= this.visible_slice_count*mult;
+ if (mult > 1 && slices.size > 1) {
+ this.total_slice_count -= mult;
+ }
+
+ //keep a copy of the original value since page up/down change it
+ original_visible_slice_count= visible_slice_count;
+
+ calc_key_navigation_pos();
+ }
+
+ private void calc_key_navigation_pos() {
+ //calc slices index for keyboard navigation
+
+ int a= this.first_slice_idx;
+ int b= this.first_slice_idx + this.visible_slice_count/4;
+ int c= this.first_slice_idx + this.visible_slice_count/2;
+ int d= this.first_slice_idx + (this.visible_slice_count*3)/4;
+ int e= this.first_slice_idx + this.visible_slice_count -1;
+ switch(this.pie_show_mode) {
+ //half pie
+ case ShowPieMode.HPIE_LEFT:
+ this.up_slice_idx= a;
+ this.right_slice_idx= c;
+ this.down_slice_idx= e;
+ this.left_slice_idx= -1; //no left slice, go up instead
+ break;
+ case ShowPieMode.HPIE_RIGHT:
+ this.down_slice_idx= a;
+ this.left_slice_idx= c;
+ this.up_slice_idx= e;
+ this.right_slice_idx= -1; //no right slice, go down instead
+ break;
+ case ShowPieMode.HPIE_TOP:
+ this.right_slice_idx= a;
+ this.down_slice_idx= c;
+ this.left_slice_idx= e;
+ this.up_slice_idx= -1; //no up slice, go left instead
+ break;
+ case ShowPieMode.HPIE_BOTTOM:
+ this.left_slice_idx= a;
+ this.up_slice_idx= c;
+ this.right_slice_idx= e;
+ this.down_slice_idx= -1; //no down slice, go right instead
+ break;
+
+ //cuarter pie
+ case ShowPieMode.CPIE_TOP_LEFT:
+ this.right_slice_idx= a;
+ this.down_slice_idx= e;
+ this.up_slice_idx= -1; //no up slice, go right instead
+ this.left_slice_idx= -1; //no left slice, go down instead
+ break;
+ case ShowPieMode.CPIE_TOP_RIGHT:
+ this.down_slice_idx= a;
+ this.left_slice_idx= e;
+ this.up_slice_idx= -1; //no up slice, go left instead
+ this.right_slice_idx= -1; //no righ slice, go down instead
+ break;
+ case ShowPieMode.CPIE_BOT_LEFT:
+ this.up_slice_idx= a;
+ this.right_slice_idx= e;
+ this.down_slice_idx= -1; //no down slice, go right instead
+ this.left_slice_idx= -1; //no left slice, go up instead
+ break;
+ case ShowPieMode.CPIE_BOT_RIGHT:
+ this.left_slice_idx= a;
+ this.up_slice_idx= e;
+ this.down_slice_idx= -1; //no down slice, go left instead
+ this.right_slice_idx= -1; //no right slice, go up instead
+ break;
+
+ default: //ShowPieMode.FULL_PIE or invalid values
+ this.right_slice_idx= a;
+ this.down_slice_idx= b;
+ this.left_slice_idx= c;
+ this.up_slice_idx= d;
+ break;
+ }
+ }
+
+
+ /////////////////////////////////////////////////////////////////////
+ /// keyboard navigation helper
+ /// move current position one slice towards the given extreme
+ /////////////////////////////////////////////////////////////////////
+
+ private void move_active_slice(int extreme, int other_extreme ) {
+ int pos= this.active_slice;
+
+ if (pos < 0 || pos == extreme) {
+ //no actual position or allready at the extreme
+ pos= extreme; //go to the extreme pos
+
+ } else if (extreme == -1) {
+ //the extreme was eaten, just go away from the other_extreme
+ if (pos > other_extreme || other_extreme == 0) {
+ if (pos < this.visible_slice_count+this.first_slice_idx-1)
+ pos++;
+ } else if (pos > this.first_slice_idx)
+ pos--;
+
+ } else if (other_extreme == -1) {
+ //the other_extreme was eaten, just get closer to the extreme
+ if (pos < extreme)
+ pos++;
+ else if (pos > extreme)
+ pos--;
+
+ } else if (pos == other_extreme) {
+ //both extremes are present
+ //jump quickly form one extreme to the other
+ pos= extreme; //go to the extreme pos
+
+ } else {
+ //both extremes are present
+ //add or substract 1 to position in a circular manner
+ if (extreme > other_extreme) {
+ if (pos > other_extreme && pos < extreme)
+ //other_extreme < pos < extreme
+ pos= pos+1;
+ else
+ pos= pos-1;
+ } else {
+ if (pos > extreme && pos < other_extreme)
+ //extreme < pos < other_extreme
+ pos= pos-1;
+ else
+ pos= pos+1;
+ }
+
+ if (pos < this.first_slice_idx)
+ pos= this.visible_slice_count-1+this.first_slice_idx;
+
+ if (pos >= this.visible_slice_count+this.first_slice_idx)
+ pos= this.first_slice_idx;
+ }
+
+ this.select_by_index(pos);
+
+ this.key_board_control = true;
+ }
+}
+
+}