///////////////////////////////////////////////////////////////////////// // Copyright 2011-2018 Simon Schneegans // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY // CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ///////////////////////////////////////////////////////////////////////// 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 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(); 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); } ///////////////////////////////////////////////////////////////////// /// 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; #if HAVE_GTK_3_20 var seat = Gdk.Display.get_default().get_default_seat(); seat.get_pointer().get_position(null, out mouse_x, out mouse_y); #else double x = 0.0; double y = 0.0; var display = Gdk.Display.get_default(); var manager = display.get_device_manager(); GLib.List 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 ); } } mouse_x = (int) x; mouse_y = (int) y; #endif #if HAVE_GTK_3_22 var monitor = Gdk.Display.get_default().get_monitor_at_point(mouse_x, mouse_y).get_geometry(); int monitor_x = monitor.width; int monitor_y = monitor.height; #else var screen = Gdk.Screen.get_default().get_root_window(); int monitor_x = screen.get_width(); int monitor_y = screen.get_height(); #endif //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 (monitor_y > 0 && monitor_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 (monitor_x > 0 && monitor_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 (monitor_x > 0 && monitor_x-mouse_x < sz/2) { if (monitor_y > 0 && monitor_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 (monitor_y > 0 && monitor_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 (monitor_x-mouse_x > sz/2) reduce_szx= 0; //keep full width if (monitor_y-mouse_y > sz/2) reduce_szy= 0; //keep full height break; case 2: showpie= ShowPieMode.HPIE_RIGHT; if (monitor_x-mouse_x > sz/2) reduce_szx= 0; //keep full width break; case 3: showpie= ShowPieMode.CPIE_TOP_RIGHT; if (monitor_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 (monitor_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 (monitor_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, bool fade_out) { 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); } if (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() { 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 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; } } }