1 package freeguide.plugins.ui.horizontal.manylabels; 2 3 import freeguide.common.lib.fgspecific.Application; 4 import freeguide.common.lib.fgspecific.data.TVProgramme; 5 import freeguide.common.lib.general.TemplateParser; 6 7 import freeguide.common.plugininterfaces.IModuleReminder; 8 9 import freeguide.plugins.ui.horizontal.manylabels.templates.HandlerProgrammeInfo; 10 11 import java.awt.Color; 12 import java.awt.Graphics; 13 import java.awt.Graphics2D; 14 import java.awt.Insets; 15 import java.awt.Rectangle; 16 import java.awt.RenderingHints; 17 import java.awt.Shape; 18 import java.awt.geom.AffineTransform; 19 import java.awt.geom.GeneralPath; 20 21 import java.io.StringWriter; 22 23 import java.text.DateFormat; 24 25 import java.util.Date; 26 import java.util.logging.Level; 27 28 import javax.swing.BorderFactory; 29 import javax.swing.JComponent; 30 import javax.swing.JLabel; 31 import javax.swing.ToolTipManager; 32 import javax.swing.border.Border; 33 34 /** 35 * Label for display programme info. 36 * 37 * @author Alex Buloichik (alex73 at zaval.org) 38 */ 39 public class JLabelProgramme extends JLabel 40 { 41 protected static Border DEFAULT_BORDER; 42 protected static Border DEFAULT_FOCUS; 43 protected static Border MOVIE_BORDER; 44 protected static Border MOVIE_FOCUS; 45 protected static Border NEW_BORDER; 46 protected static Border NEW_FOCUS; 47 protected static Border INGUIDE_DEFAULT_BORDER; 48 protected static Border INGUIDE_DEFAULT_FOCUS; 49 protected static Border INGUIDE_MOVIE_BORDER; 50 protected static Border INGUIDE_MOVIE_FOCUS; 51 protected static Border INGUIDE_NEW_BORDER; 52 protected static Border INGUIDE_NEW_FOCUS; 53 54 /** Standard reminder. */ 55 protected static IModuleReminder REMINDER; 56 57 /** Heart shape. */ 58 protected final static Shape HEART_SHAPE; 59 60 static 61 { 62 GeneralPath path = new GeneralPath( ); 63 64 path.moveTo( 300, 200 ); 65 66 path.curveTo( 100, 0, 0, 400, 300, 580 ); 67 68 path.moveTo( 300, 580 ); 69 70 path.curveTo( 600, 400, 500, 0, 300, 200 ); 71 72 HEART_SHAPE = path; 73 74 } 75 76 /** Programme for current label. */ 77 final protected TVProgramme programme; 78 79 /** Parent controller. */ 80 final protected HorizontalViewer controller; 81 82 /** Need to draw heart. */ 83 protected boolean isDrawHeart; 84 85 /** Cached tooltip text. */ 86 private String tooltip; 87 protected final boolean moveNames; 88 protected final DateFormat timeFormat; 89 90 /** 91 * Creates a new JLabelProgramme object. 92 * 93 * @param programme DOCUMENT ME! 94 * @param main DOCUMENT ME! 95 * @param moveNames DOCUMENT ME! 96 * @param timeFormat DOCUMENT ME! 97 */ JLabelProgramme( final TVProgramme programme, final HorizontalViewer main, final boolean moveNames, final DateFormat timeFormat )98 public JLabelProgramme( 99 final TVProgramme programme, final HorizontalViewer main, 100 final boolean moveNames, final DateFormat timeFormat ) 101 { 102 this.timeFormat = timeFormat; 103 this.programme = programme; 104 this.controller = main; 105 this.moveNames = moveNames; 106 setText( getTitle( programme ) ); 107 setupColors( main.getDate() ); 108 setupHeart( ); 109 setOpaque( true ); 110 setFocusable( true ); 111 addMouseListener( main.handlers.labelProgrammeMouseListener ); 112 addFocusListener( main.handlers.labelProgrammeFocusListener ); 113 114 setActionMap( main.handlers.labelProgrammeActionMap ); 115 setInputMap( 116 JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, 117 main.handlers.labelProgrammeInputMap ); 118 119 ToolTipManager tipManager = ToolTipManager.sharedInstance( ); 120 121 // Register this component for tooltip management. 122 // This is normally done by setting the tooltip text, 123 // but we want to "lazily" evaluate tooltip text--we defer 124 // creation of the tip text until the tip is actually needed. 125 // (see the getToolTipText() method below) 126 tipManager.registerComponent( this ); 127 } 128 129 /** 130 * Generate title for label. 131 * 132 * @param programme 133 * 134 * @return title text 135 */ getTitle( final TVProgramme programme )136 protected String getTitle( final TVProgramme programme ) 137 { 138 final StringBuffer toAppendTo = new StringBuffer( ); 139 140 long programmeStart = programme.getStart( ); 141 142 String programmeTitle = programme.getTitle( ); 143 144 String programmeSubTitle = programme.getSubTitle( ); 145 146 String programmeStarString = programme.getStarString( ); 147 148 if( controller.config.displayTime && ( timeFormat != null ) ) 149 { 150 toAppendTo.append( 151 timeFormat.format( new Date( programmeStart ) ) ).append( " " ); 152 } 153 154 toAppendTo.append( programmeTitle ); 155 156 if( programmeSubTitle != null ) 157 { 158 toAppendTo.append( ": " ).append( programmeSubTitle ); 159 160 } 161 162 if( programme.getIsMovie( ) && ( programmeStarString != null ) ) 163 { 164 toAppendTo.append( " " ).append( programmeStarString ); 165 166 } 167 168 if( programme.getPreviouslyShown( ) ) 169 { 170 toAppendTo.append( " " ); 171 172 toAppendTo.append( 173 Application.getInstance( ).getLocalizedMessage( "r" ) ); 174 175 } 176 177 return toAppendTo.toString( ); 178 } 179 180 /** 181 * Get programme for label. 182 * 183 * @return programme 184 */ getProgramme( )185 public TVProgramme getProgramme( ) 186 { 187 return programme; 188 } 189 190 /** 191 * Setup colors for current label. 192 */ setupColors( final long theDate )193 public void setupColors( final long theDate ) 194 { 195 final boolean isMovie = programme.getIsMovie( ); 196 final boolean isNew = programme.isAirDay( theDate ); 197 final boolean isFocus = isFocusOwner( ); 198 199 if( ( REMINDER != null ) && REMINDER.isSelected( programme ) ) 200 { 201 setBackground( controller.config.colorTicked ); 202 if ( isNew ) 203 { 204 setBorder( isFocus ? INGUIDE_NEW_FOCUS : INGUIDE_NEW_BORDER ); 205 } 206 else if ( isMovie ) 207 { 208 setBorder( isFocus ? INGUIDE_MOVIE_FOCUS : INGUIDE_MOVIE_BORDER ); 209 } 210 else 211 { 212 setBorder( isFocus ? INGUIDE_DEFAULT_FOCUS : INGUIDE_DEFAULT_BORDER ); 213 } 214 } 215 else 216 { 217 if ( isNew ) 218 { 219 setBackground( controller.config.colorNew ); 220 setBorder( isFocus ? NEW_FOCUS : NEW_BORDER ); 221 } 222 else if ( isMovie ) 223 { 224 setBackground( controller.config.colorMovie ); 225 setBorder( isFocus ? MOVIE_FOCUS : MOVIE_BORDER ); 226 } 227 else 228 { 229 setBackground( controller.config.colorNonTicked ); 230 setBorder( isFocus ? DEFAULT_FOCUS : DEFAULT_BORDER ); 231 } 232 } 233 } 234 235 /** 236 * Setup colors and borders using parent controller's config. 237 * 238 * @param main 239 */ setupLabel( final HorizontalViewer main )240 public static void setupLabel( final HorizontalViewer main ) 241 { 242 // TODO change 243 IModuleReminder[] rems = Application.getInstance( ).getReminders( ); 244 245 if( rems.length > 0 ) 246 { 247 REMINDER = rems[0]; 248 } 249 else 250 { 251 REMINDER = null; 252 } 253 254 DEFAULT_BORDER = BorderFactory.createCompoundBorder( 255 BorderFactory.createLineBorder( Color.BLACK, 1 ), 256 BorderFactory.createLineBorder( main.config.colorNonTicked, 2 ) ); 257 DEFAULT_FOCUS = BorderFactory.createCompoundBorder( 258 BorderFactory.createLineBorder( Color.BLUE, 2 ), 259 BorderFactory.createLineBorder( main.config.colorNonTicked, 1 ) ); 260 261 MOVIE_BORDER = BorderFactory.createCompoundBorder( 262 BorderFactory.createLineBorder( Color.BLACK, 1 ), 263 BorderFactory.createLineBorder( main.config.colorMovie, 2 ) ); 264 MOVIE_FOCUS = BorderFactory.createCompoundBorder( 265 BorderFactory.createLineBorder( Color.BLUE, 2 ), 266 BorderFactory.createLineBorder( main.config.colorMovie, 1 ) ); 267 268 NEW_BORDER = BorderFactory.createCompoundBorder( 269 BorderFactory.createLineBorder( Color.BLACK, 1 ), 270 BorderFactory.createLineBorder( main.config.colorNew, 2 ) ); 271 NEW_FOCUS = BorderFactory.createCompoundBorder( 272 BorderFactory.createLineBorder( Color.BLUE, 2 ), 273 BorderFactory.createLineBorder( main.config.colorNew, 1 ) ); 274 275 INGUIDE_DEFAULT_BORDER = BorderFactory.createCompoundBorder( 276 BorderFactory.createLineBorder( Color.BLACK, 1 ), 277 BorderFactory.createLineBorder( main.config.colorTicked, 2 ) ); 278 INGUIDE_DEFAULT_FOCUS = BorderFactory.createCompoundBorder( 279 BorderFactory.createLineBorder( Color.BLUE, 2 ), 280 BorderFactory.createLineBorder( main.config.colorTicked, 1 ) ); 281 282 INGUIDE_MOVIE_BORDER = BorderFactory.createCompoundBorder( 283 BorderFactory.createLineBorder( Color.BLACK, 1 ), 284 BorderFactory.createLineBorder( main.config.colorMovie, 2 ) ); 285 INGUIDE_MOVIE_FOCUS = BorderFactory.createCompoundBorder( 286 BorderFactory.createLineBorder( Color.BLUE, 2 ), 287 BorderFactory.createLineBorder( main.config.colorMovie, 1 ) ); 288 289 INGUIDE_NEW_BORDER = BorderFactory.createCompoundBorder( 290 BorderFactory.createLineBorder( Color.BLACK, 1 ), 291 BorderFactory.createLineBorder( main.config.colorNew, 2 ) ); 292 INGUIDE_NEW_FOCUS = BorderFactory.createCompoundBorder( 293 BorderFactory.createLineBorder( Color.BLUE, 2 ), 294 BorderFactory.createLineBorder( main.config.colorNew, 1 ) ); 295 296 } 297 298 /** 299 * Setup need to draw heart for current label. 300 */ setupHeart( )301 protected void setupHeart( ) 302 { 303 if( REMINDER == null ) 304 { 305 isDrawHeart = false; 306 } 307 else 308 { 309 freeguide.common.lib.fgspecific.selection.Favourite f = 310 REMINDER.getFavourite( programme ); 311 isDrawHeart = ( f != null ); 312 } 313 } 314 315 /** 316 * Paint component override. 317 * 318 * @param g DOCUMENT ME! 319 */ paintComponent( Graphics g )320 protected void paintComponent( Graphics g ) 321 { 322 if( moveNames ) 323 { 324 // Paint our own text, aligning to the left of the screen 325 g.setClip( this.getVisibleRect( ) ); 326 327 // First paint background 328 g.setColor( this.getBackground( ) ); 329 330 g.fillRect( 0, 0, this.getWidth( ), this.getHeight( ) ); 331 332 // Then set everything to draw the text 333 g.setFont( this.getFont( ) ); 334 335 g.setColor( this.getForeground( ) ); 336 337 // Compute the right place from the left border 338 int fontX = 0; 339 340 Insets ins = this.getInsets( ); 341 342 if( this.getBorder( ) != null ) 343 { 344 fontX = ins.left; 345 346 } 347 348 if( 349 ( g.getClipBounds( ) != null ) 350 && ( fontX < g.getClipBounds( ).x ) ) 351 { 352 fontX = g.getClipBounds( ).x + ins.left; 353 354 } 355 356 Graphics2D g2d = (Graphics2D) g; 357 g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, // Anti-alias! 358 RenderingHints.VALUE_ANTIALIAS_ON); 359 360 // now we now where, draw the text 361 g.drawString( 362 getText( ), fontX, 363 ( ins.top 364 + ( getHeight( ) - ins.top - ins.bottom 365 + g.getFontMetrics( ).getAscent( ) ) ) >> 1 ); 366 367 } 368 else 369 { 370 super.paintComponent( g ); 371 } 372 373 if( isDrawHeart ) 374 { 375 Graphics2D graphics = (Graphics2D)g; 376 AffineTransform originalTransform = graphics.getTransform( ); 377 378 graphics.setColor( Color.RED ); 379 380 // switch on anti-aliasing 381 graphics.setRenderingHint( 382 RenderingHints.KEY_ANTIALIASING, 383 RenderingHints.VALUE_ANTIALIAS_ON ); 384 385 // Scale and position appropriately--taking into account the borders 386 Rectangle bounds = HEART_SHAPE.getBounds( ); 387 388 double scale = 0.45 * ( getHeight( ) / bounds.getHeight( ) ); 389 390 double right = getWidth( ) - 2 - ( scale * bounds.getWidth( ) ); 391 392 graphics.translate( right, 2 ); 393 394 graphics.scale( scale, scale ); 395 396 graphics.fill( HEART_SHAPE ); 397 398 graphics.setTransform( originalTransform ); 399 } 400 } 401 402 /** 403 * DOCUMENT_ME! 404 * 405 * @return DOCUMENT_ME! 406 */ getToolTipText( )407 public String getToolTipText( ) 408 { 409 if( !controller.config.displayTooltips ) 410 { 411 return null; 412 } 413 414 String tooltip = super.getToolTipText( ); 415 416 if( tooltip != null ) 417 { 418 return tooltip; 419 } 420 421 if( ( this.tooltip != null ) ) 422 { 423 return this.tooltip; 424 } 425 426 try 427 { 428 final StringWriter out = new StringWriter( ); 429 TemplateParser parser = 430 new TemplateParser( 431 "resources/plugins/ui/horizontal/manylabels/templates/TemplateProgrammeTooltip.html" ); 432 parser.process( 433 new HandlerProgrammeInfo( 434 controller.getLocalizer( ), programme, timeFormat ), out ); 435 this.tooltip = out.toString( ); 436 } 437 catch( Exception ex ) 438 { 439 Application.getInstance( ).getLogger( ) 440 .log( Level.WARNING, "Error generate tooltip text", ex ); 441 } 442 443 return this.tooltip; 444 } 445 446 /** 447 * DOCUMENT_ME! 448 * 449 * @param startMin DOCUMENT_ME! 450 * @param endMax DOCUMENT_ME! 451 * 452 * @return DOCUMENT_ME! 453 */ getMiddle( final long startMin, final long endMax )454 public long getMiddle( final long startMin, final long endMax ) 455 { 456 long start = Math.max( getProgramme( ).getStart( ), startMin ); 457 long end = Math.min( getProgramme( ).getEnd( ), endMax ); 458 459 return ( start + end ) / 2; 460 } 461 462 /** 463 * DOCUMENT_ME! 464 * 465 * @param middleTime DOCUMENT_ME! 466 * @param startMin DOCUMENT_ME! 467 * @param endMax DOCUMENT_ME! 468 * 469 * @return DOCUMENT_ME! 470 */ isOverlap( final long middleTime, final long startMin, final long endMax )471 public boolean isOverlap( 472 final long middleTime, final long startMin, final long endMax ) 473 { 474 long start = Math.max( getProgramme( ).getStart( ), startMin ); 475 long end = Math.min( getProgramme( ).getEnd( ), endMax ); 476 477 return ( middleTime >= start ) && ( middleTime < end ); 478 } 479 } 480