// DoubleClockApplet.java (c) Kari Laitinen // http://www.naturalprogramming.com/ // 2009-10-28 File created. // 2009-10-29 Last modification. // This applet shows two clocks on different panels, each panel sharing // its own half of the applet area. // Both panels run a thread which makes them to update the screen. // A call to repaint() updates only the component for which the // repaint() was invoked. import java.awt.* ; import java.awt.geom.* ; // Classes Line2D, Ellipse2D, etc. import java.util.* ; // Calendar, GregorianCalendar, TimeZone import javax.swing.* ; class Clock2DPanel extends JPanel implements Runnable { static final int HOUR_HAND_LENGTH = 152 ; static final int MINUTE_HAND_LENGTH = 188 ; static final int SECOND_HAND_LENGTH = 200 ; Thread thread_that_runs_the_clock ; boolean thread_must_be_executed ; TimeZone clock_time_zone ; String time_zone_text ; Font font_for_time_zone_text = new Font( "Serif", Font.BOLD, 20 ) ; int time_zone_text_width ; // The BasicStroke objects for drawing with different line widths // are created here as permanent objects. Not creating them in the // paintComponent() method may save some computing power. BasicStroke wide_stroke = new BasicStroke( 12, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND ) ; BasicStroke medium_stroke = new BasicStroke( 6, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND ) ; BasicStroke narrow_stroke = new BasicStroke( 2 ) ; Color background_color = new Color( 0xE7, 0xE7, 0xFF ) ; public Clock2DPanel() { clock_time_zone = TimeZone.getDefault() ; time_zone_text = "LOCAL (" + clock_time_zone.getDisplayName() + ")" ; FontMetrics metrics_for_font_for_time_zone_text = getFontMetrics( font_for_time_zone_text ) ; char[] time_zone_text_as_array = time_zone_text.toCharArray() ; time_zone_text_width = metrics_for_font_for_time_zone_text.charsWidth( time_zone_text_as_array, 0, time_zone_text_as_array.length ) ; setBackground( background_color ) ; } public Clock2DPanel( String given_place_name, TimeZone given_time_zone ) { clock_time_zone = given_time_zone ; time_zone_text = given_place_name + " (" + clock_time_zone.getDisplayName() + ")" ; FontMetrics metrics_for_font_for_time_zone_text = getFontMetrics( font_for_time_zone_text ) ; char[] time_zone_text_as_array = time_zone_text.toCharArray() ; time_zone_text_width = metrics_for_font_for_time_zone_text.charsWidth( time_zone_text_as_array, 0, time_zone_text_as_array.length ) ; setBackground( background_color ) ; } public void start_animation_thread() { if ( thread_that_runs_the_clock == null ) { thread_must_be_executed = true ; thread_that_runs_the_clock = new Thread( this ) ; thread_that_runs_the_clock.start() ; } } public void stop_animation_thread() { if ( thread_that_runs_the_clock != null ) { thread_that_runs_the_clock.interrupt() ; thread_must_be_executed = false ; thread_that_runs_the_clock = null ; } } public void run() { while ( thread_must_be_executed == true ) { repaint() ; try { Thread.sleep( 200 ) ; // Suspend for 0.2 seconds. } catch ( InterruptedException caught_exception ) { // No actions to handle the exception. } } } public void paintComponent( Graphics graphics ) { super.paintComponent( graphics ) ; Graphics2D graphics2D = (Graphics2D) graphics ; Calendar time_now = new GregorianCalendar() ; time_now.setTimeZone( clock_time_zone ) ; int current_hours = time_now.get( Calendar.HOUR_OF_DAY ) ; int current_minutes = time_now.get( Calendar.MINUTE ) ; int current_seconds = time_now.get( Calendar.SECOND ) ; int current_milliseconds = time_now.get( Calendar.MILLISECOND ) ; int clock_center_point_x = getWidth() / 2 ; int clock_center_point_y = getHeight() / 2 - 30 ; Ellipse2D.Double ring_around_the_clock = new Ellipse2D.Double( clock_center_point_x - ( SECOND_HAND_LENGTH + 16 ), clock_center_point_y - ( SECOND_HAND_LENGTH + 16 ), 2 * ( SECOND_HAND_LENGTH + 16 ), 2 * ( SECOND_HAND_LENGTH + 16 ) ) ; // The background of the clock face will be white. graphics2D.setColor( Color.WHITE ) ; graphics2D.fill( ring_around_the_clock ) ; graphics2D.setColor( Color.BLACK ) ; // The following loop prints dots on the clock dial. int dot_index = 0 ; while ( dot_index < 60 ) { double dot_angle = dot_index * Math.PI / 30 - Math.PI / 2 ; int dot_position_x = (int) (Math.cos( dot_angle ) * SECOND_HAND_LENGTH + clock_center_point_x ) ; int dot_position_y = (int) (Math.sin( dot_angle ) * SECOND_HAND_LENGTH + clock_center_point_y ) ; int dot_diameter = 4 ; if ( ( dot_index % 5 ) == 0 ) { // Every 5th dot on the clock circle is a larger dot. dot_diameter = 8 ; } graphics.fillOval( dot_position_x - dot_diameter / 2, dot_position_y - dot_diameter / 2, dot_diameter, dot_diameter ) ; dot_index = dot_index + 1 ; } double hour_hand_angle = ( current_hours * 30 + current_minutes / 2 ) * Math.PI / 180 - Math.PI /2 ; int hour_hand_end_x = (int) ( Math.cos( hour_hand_angle ) * HOUR_HAND_LENGTH + clock_center_point_x ) ; int hour_hand_end_y = (int) ( Math.sin( hour_hand_angle ) * HOUR_HAND_LENGTH + clock_center_point_y ) ; Line2D.Double current_hour_hand = new Line2D.Double( clock_center_point_x, clock_center_point_y, hour_hand_end_x, hour_hand_end_y ) ; double minute_hand_angle = ( (double) current_minutes + (double) current_seconds / 60.0 ) * Math.PI / 30 - Math.PI /2 ; int minute_hand_end_x = (int) ( Math.cos( minute_hand_angle ) * MINUTE_HAND_LENGTH + clock_center_point_x ) ; int minute_hand_end_y = (int) ( Math.sin( minute_hand_angle ) * MINUTE_HAND_LENGTH + clock_center_point_y ) ; Line2D.Double current_minute_hand = new Line2D.Double ( clock_center_point_x, clock_center_point_y, minute_hand_end_x, minute_hand_end_y ) ; graphics2D.setStroke( wide_stroke ) ; // The following statement draws a black ring around the clock face. graphics2D.draw( ring_around_the_clock ) ; // Let's draw the hour hand as thick black line. graphics2D.draw( current_hour_hand ) ; // Now we change color and line width to draw a 'phosphorous' // line on the hour hand. graphics2D.setPaint( Color.GREEN ) ; graphics2D.setStroke( medium_stroke ) ; graphics2D.draw( current_hour_hand ) ; // Next we'll draw the minute hand in the same way as the // hour hand was drawn. graphics2D.setPaint( Color.BLACK ) ; graphics2D.setStroke( wide_stroke ) ; graphics2D.draw( current_minute_hand ) ; graphics2D.setPaint( Color.GREEN ) ; graphics2D.setStroke( medium_stroke ) ; graphics2D.draw( current_minute_hand ) ; // Now, as the stroke width is 'medium', let's further // decorate the ring around the panel area. graphics2D.setPaint( Color.LIGHT_GRAY ) ; graphics2D.draw( ring_around_the_clock ) ; // Let's print a 16-point dot into the center of the clock. graphics2D.setPaint( Color.BLACK ) ; graphics2D.fill( new Ellipse2D.Double( clock_center_point_x - 8, clock_center_point_y - 8, 16, 16 ) ) ; // And finally we'll draw the second hand. graphics2D.setStroke( narrow_stroke ) ; graphics2D.setPaint( Color.DARK_GRAY ) ; double second_hand_angle = ((double) current_seconds + (double) current_milliseconds / 1000.0 ) * Math.PI / 30 - Math.PI /2 ; int second_hand_end_x = (int) ( Math.cos( second_hand_angle ) * SECOND_HAND_LENGTH + clock_center_point_x ) ; int second_hand_end_y = (int) ( Math.sin( second_hand_angle) * SECOND_HAND_LENGTH + clock_center_point_y ) ; graphics2D.draw( new Line2D.Double( clock_center_point_x, clock_center_point_y, second_hand_end_x, second_hand_end_y ) ) ; graphics2D.setFont( font_for_time_zone_text ) ; graphics2D.drawString( time_zone_text, clock_center_point_x - time_zone_text_width / 2, clock_center_point_y + SECOND_HAND_LENGTH + 80 ) ; } } public class DoubleClockApplet extends JApplet { Clock2DPanel panel_for_left_clock ; Clock2DPanel panel_for_right_clock ; public void init() { Container content_pane_of_this_applet = getContentPane() ; content_pane_of_this_applet.setLayout( new GridLayout( 1, 2 ) ) ; panel_for_left_clock = new Clock2DPanel() ; panel_for_right_clock = new Clock2DPanel( "TOKYO", TimeZone.getTimeZone( "Asia/Tokyo" ) ) ; content_pane_of_this_applet.add( panel_for_left_clock ) ; content_pane_of_this_applet.add( panel_for_right_clock ) ; /* If you want to change the time zone of the right clock, the following loop may help you to find out the available IDs that can be used in place of "Asia/Tokyo" String[] available_time_zone_ids = TimeZone.getAvailableIDs() ; for ( String available_time_zone_id : available_time_zone_ids ) { System.out.print( "\n " + available_time_zone_id ) ; } */ } public void start() { panel_for_left_clock.start_animation_thread() ; panel_for_right_clock.start_animation_thread() ; } public void stop() { panel_for_left_clock.stop_animation_thread() ; panel_for_right_clock.stop_animation_thread() ; } } /* NOTES: /old_versions/ClockWithDateApplet.java is an "ancestor" of this program. For more information about class GregorianCalendar, see program Showtime.java in javafiles3 folder. */