/*
* Copyright (C) 2014 Alfons Wirtz
* website www.freerouting.net
*
* 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 at <http://www.gnu.org/licenses/>
* for more details.
*
* BoardPanel.java
*
* Created on 3. Oktober 2002, 18:47
*/
package gui;
import interactive.BoardHandling;
import interactive.ScreenMessages;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.geom.Point2D;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JViewport;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
/**
*
* Panel containing the graphical representation of a routing board.
*
* @author Alfons Wirtz
*/
public class BoardPanel extends javax.swing.JPanel
{
/** Creates a new BoardPanel in an Application */
public BoardPanel(ScreenMessages p_screen_messages, BoardFrame p_board_frame,
boolean p_is_web_application, java.util.Locale p_locale)
{
screen_messages = p_screen_messages;
if (!p_is_web_application)
{
try
{
// used to be able to change the location of the mouse pointer
robot = new java.awt.Robot();
}
catch (java.awt.AWTException e)
{
System.out.println("unable to create robot");
}
}
board_frame = p_board_frame;
this.scroll_pane = board_frame.scroll_pane;
default_init(p_locale);
}
private void default_init(java.util.Locale p_locale)
{
setLayout(new java.awt.BorderLayout());
setBackground(new java.awt.Color(0, 0, 0));
setMaximumSize(new java.awt.Dimension(30000, 20000));
setMinimumSize(new java.awt.Dimension(90, 60));
setPreferredSize(new java.awt.Dimension(1200, 900));
addMouseMotionListener(new java.awt.event.MouseMotionAdapter()
{
public void mouseDragged(java.awt.event.MouseEvent evt)
{
mouse_dragged_action(evt);
}
public void mouseMoved(java.awt.event.MouseEvent evt)
{
mouse_moved_action(evt);
}
});
addKeyListener(new java.awt.event.KeyAdapter()
{
public void keyTyped(java.awt.event.KeyEvent evt)
{
board_handling.key_typed_action(evt.getKeyChar());
}
});
addMouseListener(new java.awt.event.MouseAdapter()
{
public void mouseClicked(java.awt.event.MouseEvent evt)
{
mouse_clicked_action(evt);
}
public void mousePressed(java.awt.event.MouseEvent evt)
{
mouse_pressed_action(evt);
}
public void mouseReleased(java.awt.event.MouseEvent evt)
{
board_handling.button_released();
middle_drag_position = null ;
}
});
addMouseWheelListener(new java.awt.event.MouseWheelListener()
{
public void mouseWheelMoved(java.awt.event.MouseWheelEvent evt)
{
board_handling.mouse_wheel_moved(evt.getWheelRotation());
}
});
board_handling = new BoardHandling(this, p_locale);
setAutoscrolls(true);
this.setCursor(new java.awt.Cursor(java.awt.Cursor.CROSSHAIR_CURSOR));
}
void create_popup_menus()
{
popup_menu_main = new PopupMenuMain(this.board_frame);
popup_menu_dynamic_route = new PopupMenuDynamicRoute(this.board_frame);
popup_menu_stitch_route = new PopupMenuStitchRoute(this.board_frame);
popup_menu_corneritem_construction = new PupupMenuCornerItemConstruction(this.board_frame);
popup_menu_select = new PopupMenuSelectedItems(this.board_frame);
popup_menu_insert_cancel = new PopupMenuInsertCancel(this.board_frame);
popup_menu_copy = new PopupMenuCopy(this.board_frame);
popup_menu_move = new PopupMenuMove(this.board_frame);
}
public void zoom_with_mouse_wheel(java.awt.geom.Point2D p_point, int p_wheel_rotation)
{
if (this.middle_drag_position != null || p_wheel_rotation == 0)
{
return; // scrolling with the middle mouse butten in progress
}
double zoom_factor = 1 - 0.1 * p_wheel_rotation;
zoom_factor = Math.max(zoom_factor, 0.5);
zoom(zoom_factor, p_point);
}
private void mouse_pressed_action(java.awt.event.MouseEvent evt)
{
if (evt.getButton() == 1)
{
board_handling.mouse_pressed(evt.getPoint());
}
else if (evt.getButton() == 2 && middle_drag_position == null)
{
middle_drag_position = new java.awt.Point(evt.getPoint()) ;
}
}
private void mouse_dragged_action(java.awt.event.MouseEvent evt)
{
if (middle_drag_position != null)
{
scroll_middle_mouse(evt) ;
}
else
{
board_handling.mouse_dragged(evt.getPoint());
scroll_near_border(evt);
}
}
private void mouse_moved_action(java.awt.event.MouseEvent p_evt)
{
this.requestFocusInWindow(); // to enable keyboard aliases
if (board_handling != null)
{
board_handling.mouse_moved(p_evt.getPoint());
}
if (this.custom_cursor != null)
{
this.custom_cursor.set_location(p_evt.getPoint());
this.repaint();
}
}
private void mouse_clicked_action(java.awt.event.MouseEvent evt)
{
if (evt.getButton() == 1)
{
board_handling.left_button_clicked(evt.getPoint());
}
else if (evt.getButton() == 3)
{
JPopupMenu curr_menu = board_handling.get_current_popup_menu();
if (curr_menu != null)
{
int curr_x = evt.getX();
int curr_y = evt.getY();
if (curr_menu == popup_menu_dynamic_route && board_frame.is_web_start)
{
int dx = curr_menu.getWidth();
if (dx <= 0)
{
// force the width to be calculated
curr_menu.show(this, curr_x, curr_y);
dx = curr_menu.getWidth();
}
curr_x -= dx;
}
curr_menu.show(this, curr_x, curr_y);
}
right_button_click_location = evt.getPoint();
}
}
/**
* overwrites the paintComponent method to draw the routing board
*/
public void paintComponent(Graphics p_g)
{
super.paintComponent(p_g);
if (board_handling != null)
{
board_handling.draw(p_g) ;
}
if (this.custom_cursor != null)
{
this.custom_cursor.draw(p_g);
}
}
/**
* Returns the position of the viewport
*/
public java.awt.Point get_viewport_position()
{
JViewport viewport = scroll_pane.getViewport();
return viewport.getViewPosition();
}
/**
* Sets the position of the viewport
*/
void set_viewport_position(java.awt.Point p_position)
{
JViewport viewport = scroll_pane.getViewport();
viewport.setViewPosition(p_position);
}
/**
* zooms in at p_position
*/
public void zoom_in(java.awt.geom.Point2D p_position)
{
zoom(c_zoom_factor, p_position);
}
/**
* zooms out at p_position
*/
public void zoom_out(java.awt.geom.Point2D p_position)
{
double zoom_factor = 1/c_zoom_factor;
zoom(zoom_factor, p_position);
}
/**
* zooms to frame
*/
public void zoom_frame(java.awt.geom.Point2D p_position1, java.awt.geom.Point2D p_position2)
{
double width_of_zoom_frame = Math.abs(p_position1.getX() - p_position2.getX()) ;
double height_of_zoom_frame = Math.abs(p_position1.getY() - p_position2.getY()) ;
double center_x = Math.min(p_position1.getX(), p_position2.getX()) + (width_of_zoom_frame/2) ;
double center_y = Math.min(p_position1.getY(), p_position2.getY()) + (height_of_zoom_frame/2) ;
Point2D center_point = new Point2D.Double(center_x, center_y) ;
java.awt.Rectangle display_rect = get_viewport_bounds();
double width_factor = display_rect.getWidth() / width_of_zoom_frame ;
double height_factor = display_rect.getHeight() / height_of_zoom_frame ;
java.awt.geom.Point2D changed_location = zoom(Math.min(width_factor, height_factor), center_point) ;
set_viewport_center(changed_location);
}
public void center_display(Point2D p_new_center)
{
java.awt.Point delta = set_viewport_center(p_new_center);
java.awt.geom.Point2D new_center = get_viewport_center();
java.awt.Point new_mouse_location =
new java.awt.Point((int)(new_center.getX() - delta.getX()), (int)(new_center.getY() - delta.getY()));
move_mouse(new_mouse_location);
repaint();
this.board_handling.logfile.start_scope(interactive.LogfileScope.CENTER_DISPLAY);
geometry.planar.FloatPoint curr_corner = new geometry.planar.FloatPoint(p_new_center.getX(), p_new_center.getY());
this.board_handling.logfile.add_corner(curr_corner);
}
public java.awt.geom.Point2D get_viewport_center()
{
java.awt.Point pos = get_viewport_position();
java.awt.Rectangle display_rect = get_viewport_bounds();
return new java.awt.geom.Point2D.Double(pos.getX() + display_rect.getCenterX(), pos.getY() + display_rect.getCenterY());
}
/**
* zooms the content of the board by p_factor
* Returns the change of the cursor location
*/
public java.awt.geom.Point2D zoom(double p_factor, java.awt.geom.Point2D p_location)
{
final int max_panel_size = 10000000;
Dimension old_size = this.getSize();
java.awt.geom.Point2D old_center = get_viewport_center();
if (p_factor > 1 && Math.max(old_size.getWidth(), old_size.getHeight()) >= max_panel_size)
{
return p_location; // to prevent an sun.dc.pr.PRException, which I do not know, how to handle; maybe a bug in Java.
}
int new_width = (int) Math.round(p_factor * old_size.getWidth());
int new_height = (int) Math.round(p_factor * old_size.getHeight());
Dimension new_size = new Dimension(new_width, new_height);
board_handling.graphics_context.change_panel_size(new_size);
setPreferredSize(new_size);
setSize(new_size);
revalidate();
java.awt.geom.Point2D new_cursor = new java.awt.geom.Point2D.Double(p_location.getX()* p_factor, p_location.getY()* p_factor);
double dx = new_cursor.getX() - p_location.getX();
double dy = new_cursor.getY() - p_location.getY();
java.awt.geom.Point2D new_center = new java.awt.geom.Point2D.Double(old_center.getX() + dx, old_center.getY() + dy);
java.awt.geom.Point2D adjustment_vector = set_viewport_center(new_center);
repaint();
java.awt.geom.Point2D adjusted_new_cursor = new java.awt.geom.Point2D.Double(new_cursor.getX() + adjustment_vector.getX() + 0.5, new_cursor.getY() + adjustment_vector.getY() + 0.5);
return adjusted_new_cursor;
}
/**
* Returns the viewport bounds of the scroll pane
*/
java.awt.Rectangle get_viewport_bounds()
{
return scroll_pane.getViewportBorderBounds();
}
/**
* Sets the viewport center to p_point.
* Adjust the result, if p_point is near the border of the viewport.
* Returns the adjustment vector
*/
java.awt.Point set_viewport_center(java.awt.geom.Point2D p_point)
{
java.awt.Rectangle display_rect = get_viewport_bounds();
double x_corner = p_point.getX() - display_rect.getWidth() / 2;
double y_corner = p_point.getY() - display_rect.getHeight()/ 2;
Dimension panel_size = getSize();
double adjusted_x_corner = Math.min(x_corner, panel_size.getWidth());
adjusted_x_corner = Math.max(x_corner, 0);
double adjusted_y_corner = Math.min(y_corner, panel_size.getHeight());
adjusted_y_corner = Math.max(y_corner, 0);
java.awt.Point new_position = new java.awt.Point((int) adjusted_x_corner, (int)adjusted_y_corner);
set_viewport_position(new_position);
java.awt.Point adjustment_vector =
new java.awt.Point((int)(adjusted_x_corner - x_corner), (int)(adjusted_y_corner - y_corner));
return adjustment_vector;
}
/**
* Selects the p_signal_layer_no-th layer in the select_parameter_window.
*/
public void set_selected_signal_layer(int p_signal_layer_no)
{
if (this.board_frame.select_parameter_window != null)
{
this.board_frame.select_parameter_window.select(p_signal_layer_no);
this.popup_menu_dynamic_route.disable_layer_item(p_signal_layer_no);
this.popup_menu_stitch_route.disable_layer_item(p_signal_layer_no);
this.popup_menu_copy.disable_layer_item(p_signal_layer_no);
}
}
void init_colors()
{
board_handling.graphics_context.item_color_table.addTableModelListener(new ColorTableListener());
board_handling.graphics_context.other_color_table.addTableModelListener(new ColorTableListener());
setBackground(board_handling.graphics_context.get_background_color());
}
private void scroll_near_border(java.awt.event.MouseEvent p_evt)
{
final int border_dist = 50;
java.awt.Rectangle r =
new java.awt.Rectangle(p_evt.getX() - border_dist, p_evt.getY() - border_dist, 2 * border_dist, 2 * border_dist);
((JPanel)p_evt.getSource()).scrollRectToVisible(r);
}
private void scroll_middle_mouse(java.awt.event.MouseEvent p_evt)
{
double delta_x = middle_drag_position.x - p_evt.getX();
double delta_y = middle_drag_position.y - p_evt.getY();
java.awt.Point view_position = get_viewport_position();
double x = (view_position.x + delta_x) ;
double y = (view_position.y + delta_y) ;
Dimension panel_size = this.getSize();
x = Math.min(x, panel_size.getWidth() - this.get_viewport_bounds().getWidth());
y = Math.min(y, panel_size.getHeight() - this.get_viewport_bounds().getHeight());
x = Math.max(x, 0);
y = Math.max(y, 0);
java.awt.Point p = new java.awt.Point((int) x, (int) y) ;
set_viewport_position(p) ;
}
public void move_mouse(Point2D p_location)
{
if (robot == null)
{
return;
}
java.awt.Point absolute_panel_location = board_frame.absolute_panel_location();
java.awt.Point view_position = get_viewport_position();
int x = (int) Math.round(absolute_panel_location.getX() - view_position.getX() + p_location.getX()) + 1;
int y = (int) Math.round(absolute_panel_location.getY() - view_position.getY() + p_location.getY() + 1);
robot.mouseMove(x, y);
}
/**
* If p_value is true, the custom crosshair cursor will be used in display.
* Otherwise the standard Cursor will be used.
* Using the custom cursor may slow down the display performance a lot.
*/
public void set_custom_crosshair_cursor(boolean p_value)
{
if (p_value)
{
this.custom_cursor = Cursor.get_45_degree_cross_hair_cursor();
}
else
{
this.custom_cursor = null;
}
board_frame.refresh_windows();
repaint();
}
/**
* If the result is true, the custom crosshair cursor will be used in display.
* Otherwise the standard Cursor will be used.
* Using the custom cursor may slow down the display performance a lot.
*/
public boolean is_custom_cross_hair_cursor()
{
return this.custom_cursor != null;
}
public javax.swing.JPopupMenu popup_menu_insert_cancel;
public PopupMenuCopy popup_menu_copy;
public PopupMenuMove popup_menu_move;
public javax.swing.JPopupMenu popup_menu_corneritem_construction;
public javax.swing.JPopupMenu popup_menu_main;
public PopupMenuDynamicRoute popup_menu_dynamic_route;
public PopupMenuStitchRoute popup_menu_stitch_route;
public javax.swing.JPopupMenu popup_menu_select;
public final ScreenMessages screen_messages;
public final BoardFrame board_frame;
BoardHandling board_handling = null;
private final JScrollPane scroll_pane;
Point2D right_button_click_location = null;
private static final double c_zoom_factor = 2.0;
private java.awt.Robot robot;
private java.awt.Point middle_drag_position = null ;
/**
* Defines the appearance of the custom custom_cursor in the board panel.
* Null, if the standard custom_cursor is used.
*/
private Cursor custom_cursor = null;
private class ColorTableListener implements TableModelListener
{
public void tableChanged(TableModelEvent p_event)
{
//redisplay board because some colors have changed.
setBackground(board_handling.graphics_context.get_background_color());
repaint();
}
}
}