001 /**
002 * @author Václav Mikolášek
003 * nicklaus@students.zcu.cz
004 */
005
006 package animace.label;
007
008 import animace.*;
009 import javax.swing.*;
010 import javax.imageio.*;
011 import java.awt.*;
012 import java.awt.image.*;
013 import java.awt.event.*;
014 import java.io.*;
015 import java.util.*;
016
017
018 /**
019 * Třída MoveablePicture poskytuje ukázku, jak by měli vypadat
020 * grafické komponenty pro třídu animace.label.PictureLabel.
021 * Tedy komponenty pro systém, kde se programátor stará o vykreslování sám.
022 * Podstatný je princip vykreslování. Objekt pri nějaké změně (pohyb) nastaví
023 * proměnou needUpdate na true a volá repaint(). Metoda repaint() je volána s parametry
024 * označující tzv. dirty region. Takto by měla být tato metoda volána pokaždé, jinak dochází často zbytečně
025 * k překreslováni celé obrazovky (panelu).
026 * I když není potřeba objekt překreslit, protože na něm nedošlo ke změně (needUpdate nastaveno na false),
027 * musíme ještě otestovat, zda jej jiný objekt "nepřejel",
028 * to se zjistí z Clip oblasti nastavené v Graphics g. Viz níže metoda paint(Graphics g).
029 * Dále stojí za povšimnuti, že je implementováno přidávání posluchačů udalostí stejně, jako jsme zvyklí
030 * u klasických komponent, tedy metodou add"Něco"Listener, v tomto případě addActionListener. To je proto,
031 * že lze očekávat, že se na objekty bude klikat. V komponentě, na které tyto objekty visí, je nutné ještě
032 * zajistit, že bude volat metodu processEvent(...) nad tou komponentou, na kterou uživatel kliknul.
033 * Je toho dost co musíme napsat, abychom zajistili základní funkčnost.
034 */
035 public class MoveablePicture implements Moveable, PaintControled {
036 private Component owner;
037 private BufferedImage img;
038 private PaintControl pc = null;
039 private ActionListener actionListener = null;
040
041 /**
042 * Indikuje, že na objetku nastaly změny, a že je nutné jej znovu překreslit.
043 */
044 public boolean needUpdate = true;
045
046 private int x = 0; // location
047 private int y = 0; // location
048 private int imgWidth = 0;
049 private int imgHeight = 0;
050
051 /**
052 * Konstruktor vyžaduje jako parametr grafického vlastnika (rodiče) objektu,
053 * tedy nejakou Componentu na ktere spočívá.
054 * @param owner Componenta, na které je MoveablePicture nakreslen
055 * @param img vlastní obrázek, který se bude pohybovat
056 */
057 public MoveablePicture(Component owner,BufferedImage img) {
058 this.owner = owner;
059 this.img = img;
060 imgWidth = img.getWidth();
061 imgHeight = img.getHeight();
062 }
063
064 /**
065 * Nakreslí tento objekt do grafického kontextu předaného parametrem.
066 * Algortimus je následující. Pokud se s objektem hýbalo a je třeba jej překreslit,
067 * needUpdate == true, pak nic nezmůžeme a musíme jej kreslit. Ale pokud needUpdate == false a
068 * tento objekt není v "dirty regionu" (g.getClipBounds()) kreslit jej nemusíme a ušetříme čas.
069 */
070 public void paint(Graphics g) {
071 if (!needUpdate) {
072 Rectangle toDraw = g.getClipBounds().intersection(new Rectangle(x,y,imgWidth,imgHeight));
073 if (!toDraw.isEmpty()) {
074 g.drawImage(img,x,y,imgWidth,imgHeight,null);
075 }
076 }
077 else {
078 g.drawImage(img,x,y,imgWidth,imgHeight,null);
079 needUpdate = false;
080 }
081 }
082
083 /**
084 * Vrací obrázek
085 */
086 public BufferedImage getImage() {
087 return img;
088 }
089
090 /**
091 * Vrací preferovanou velikost - rozměry obrázku.
092 */
093 public Dimension getPrefferedSize(){
094 return new Dimension(img.getWidth(),img.getHeight());
095 }
096
097 /**
098 * Vrací šířku obrázku
099 */
100 public int getWidth() {
101 return img.getWidth();
102 }
103
104 /**
105 * Vrací víšku obrázku
106 */
107 public int getHeight()
108 {
109 return img.getHeight();
110 }
111
112 /**
113 * Metoda z rozhraní Moveable. Nastaví novou pozici objektu a hodnotu needUpdate na true.
114 * Poté zavolá owner.repaint(int x, int y, int width, int height), která
115 * zaregistruje požadavek na překreslení pouze té části obrazovky,
116 * kde došlo ke změnám na grafice.
117 * Při zavolani této metody, nedochází k žádným kontrolám,
118 * zda jsou údaje správné
119 */
120 public synchronized void setLocation(int x,int y) {
121 int left = (this.x < x ? this.x : x) - 1;
122 int up = (this.y < y ? this.y : y) - 1;
123 int dx = Math.abs(this.x - x) + 2;
124 int dy = Math.abs(this.y - y) + 2;
125 this.x = x;
126 this.y = y;
127 needUpdate = true;
128 owner.repaint(left,up,imgWidth+dx,imgHeight+dy);
129 }
130
131 /**
132 * Metoda z rozhrani Moveable, Funguje standardně, vrací pozici objektu
133 */
134 public Point getLocation(){
135 return new Point(x,y);
136 }
137
138 /**
139 * Metoda z rozhraní PaintControled
140 */
141 public void setPaintControl(PaintControl pc) {
142 this.pc = pc;
143 }
144
145 /**
146 * Metoda z rozhraní PaintControled
147 */
148 public PaintControl getPaintControl() {
149 return pc;
150 }
151
152 /**
153 * Metoda addActionListener, jak jsme zvyklí z java.AWT.Component
154 * Zde implementována pomocí třídy AWTEventMulticaster.
155 */
156 public void addActionListener(ActionListener newActionListener) {
157 actionListener = AWTEventMulticaster.add(actionListener, newActionListener);
158 }
159
160 /**
161 * Metoda processEvent, kterou bude volat zejména předek objektu,
162 * v našem případě PictureLabel, provede zavolání metody actionPerformed(...) nad všemi
163 * zaregistrovanými posluchači.
164 */
165 public void processEvent(AWTEvent e) {
166 if (actionListener != null) {
167 actionListener.actionPerformed(new ActionEvent(this,0,""));
168 }
169 }
170
171 /**
172 * Vrací true, jestliže objekt obsahuje určitý bod, hodi se pri testu na kliknuti
173 * @param point bod, který je testován, zda leží na obrazku
174 */
175 public boolean contains(Point point) {
176 return (new Rectangle(x,y,imgWidth,imgHeight)).contains(point);
177 }
178
179 }
180