1 /* $Id: World2DPopupMenuListener.java 32 2013-05-04 13:29:36Z mihlon $ */
2
3 //////////////////////////////////////////////////////////////////////////////
4 // //
5 // This program is free software: you can redistribute it and/or modify //
6 // it under the terms of the GNU General Public License as published by //
7 // the Free Software Foundation, either version 3 of the License, or //
8 // at your option any later version. //
9 // //
10 // This program is distributed in the hope that it will be useful, //
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of //
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
13 // GNU General Public License for more details. //
14 // //
15 // You should have received a copy of the GNU General Public License //
16 // along with this program. If not, see <http://www.gnu.org/licenses/>. //
17 // //
18 //////////////////////////////////////////////////////////////////////////////
19
20 package cs.pancava.caltha.menu;
21
22 import cs.pancava.caltha.Editor;
23 import cs.pancava.caltha.MainMenu;
24 import cs.pancava.caltha.graphics.EditorGraphicsWorld2D;
25 import cs.pancava.caltha.worlds.GameEntity;
26 import cs.pancava.caltha.worlds.Room;
27 import cs.pancava.caltha.worlds.Route;
28 import java.awt.Dimension;
29 import java.awt.Rectangle;
30 import java.awt.event.ActionEvent;
31 import java.awt.event.ActionListener;
32 import java.awt.event.MouseAdapter;
33 import java.awt.event.MouseEvent;
34 import javax.swing.JMenuItem;
35 import javax.swing.JPopupMenu;
36
37 /**
38 * <p><b>Tato trida definuje popup menu pro ovladani editoru pro tvorbu 2D svetu.</b></p>
39 *
40 * @author Milan Vaclavik<br />
41 * @version $Revision: 32 $<br />
42 * $LastChangedBy: mihlon $<br />
43 */
44 public class World2DPopupMenuListener extends MouseAdapter
45 {
46 /**
47 * Hlavni popup menu editoru.
48 */
49 private static JPopupMenu popupMenu;
50 /**
51 * Nabidka popup menu - Pridat mistnost.
52 */
53 private static JMenuItem addRoomIM;
54 /**
55 * Nabidka popup menu - Spojit propojit mistnosti.
56 */
57 private static JMenuItem connectRoomIM;
58 /**
59 * Nabidka popup menu - Odstranit mistnost.
60 */
61 private static JMenuItem removeRoomIM;
62 /**
63 * Mab9dka popum menu - Rozpojeni propojeni mistnosti.
64 */
65 private static JMenuItem disconnectRoomIM;
66
67 /**
68 * X-ova souradnice mysi v dobe stisku tlacitka mysi.
69 */
70 private int mouseX;
71
72 /**
73 * Y-ova souradnice mysi v dobe stisku tlacitka mysi.
74 */
75 private int mouseY;
76
77 /**
78 * Velikost zobrazovane mistnosti.
79 *
80 * TODO: Mozno nastavovat velikost primo v editoru a nastavenou hodnotu ukladat do konfiguracniho souboru.
81 */
82 private final int ROOM_SIZE = 50;
83
84 /**
85 * Reference na tridu, ktera umoznuje prekreslit graficke objekty.
86 */
87 private EditorGraphicsWorld2D editorGraphicsWorld2D;
88
89 /**
90 * Vzhledem k tomu, ze zobrazovane popup menu orizne nove vytvoreny graficky objekt,
91 * tak je treba vyvolat graficke prekresleni grafickych objektu, ktere obnovi oriznuty objekt.
92 * @param graphicsWorld2D EditorGraphicsWorld2D : Reference na tridu, ktera umoznuje prekreslit graficke objekty.
93 */
94 public World2DPopupMenuListener(final EditorGraphicsWorld2D graphicsWorld2D)
95 {
96 this.editorGraphicsWorld2D = graphicsWorld2D;
97
98 World2DPopupMenuListener.popupMenu = new JPopupMenu();
99
100 this.createItemMenuAddRoom();
101 this.createItemMenuConnectRoom();
102 this.createItemMenuRemoveRoom();
103 this.createItemMenuDisconnectRoom();
104 }
105
106 /**
107 * Volano pri zmene jazyka - lokalizace.
108 */
109 public static void updateText()
110 {
111 if (World2DPopupMenuListener.addRoomIM != null)
112 {
113 World2DPopupMenuListener.addRoomIM.setText(Editor.getResourcesBundleString("cs.pancava.caltha.menu.World2DPopupMenuListener-createItemMenuAddRoom_addRoomIM"));
114 MainMenu.updateTextTipMenuItem(World2DPopupMenuListener.addRoomIM, "cs.pancava.caltha.menu.World2DPopupMenuListener-createItemMenuAddRoom_addRoomIM_tip");
115 }
116
117 if (World2DPopupMenuListener.connectRoomIM != null)
118 {
119 World2DPopupMenuListener.connectRoomIM.setText(Editor.getResourcesBundleString("cs.pancava.caltha.menu.World2DPopupMenuListener-createItemMenuConnectRoom_connectRoomIM"));
120 MainMenu.updateTextTipMenuItem(World2DPopupMenuListener.connectRoomIM, "cs.pancava.caltha.menu.World2DPopupMenuListener-createItemMenuConnectRoom_connectRoomIM_tip");
121 }
122
123 if (World2DPopupMenuListener.removeRoomIM != null)
124 {
125 World2DPopupMenuListener.removeRoomIM.setText(Editor.getResourcesBundleString("cs.pancava.caltha.menu.World2DPopupMenuListener-createItemMenuRemoveRoom_removeRoomIM"));
126 MainMenu.updateTextTipMenuItem(World2DPopupMenuListener.removeRoomIM, "cs.pancava.caltha.menu.World2DPopupMenuListener-createItemMenuRemoveRoom_removeRoomIM_tip");
127 }
128
129 if (World2DPopupMenuListener.disconnectRoomIM != null)
130 {
131 World2DPopupMenuListener.disconnectRoomIM.setText(Editor.getResourcesBundleString("cs.pancava.caltha.menu.World2DPopupMenuListener-createItemMenuDisconnectRoom_disconnectRoomIM"));
132 MainMenu.updateTextTipMenuItem(World2DPopupMenuListener.disconnectRoomIM, "cs.pancava.caltha.menu.World2DPopupMenuListener-createItemMenuDisconnectRoom_disconnectRoomIM_tip");
133 }
134 }
135
136 /**
137 * Prida na danou pozici mistnost v hernim svete.
138 * @param x : X-ova souradice umisteni mistnosti.
139 * @param y : Y-ova souradice umisteni mistnosti.
140 */
141 private void addRoom(final int x, final int y)
142 {
143 final GameEntity tmpRoom2 = new Room(x, y, this.ROOM_SIZE);
144 GameEntity tmpRoom1 = null;
145
146 // TODO: Vyhledavani by slo optimalizovat tim, ze by jsme meli promennou, ktera by si pamatovala naposledy oznaceny objekt.
147 // TODO: Tim by se mozna vyresil i ten problem, kdy mohlo byt vice oznacenych mistnosti a proto bych nemusel vzdy preventivne mazat
148 // uplne vsechmy mistnosti.
149 for (GameEntity ge : Editor.getEditorDesktop().getWorld().getWorldRoomsAL())
150 {
151 if (ge.isSeleted())
152 {
153 // Nalezli jsme oznacenou mistnost, proto vytvorime spoj mezi
154 // touto a nove vytvorenou mistnosti.
155 tmpRoom1 = ge;
156
157 // Ukoncime hledani, jelikoz je vzdy jen jedna mistnost oznacena.
158 //return;
159 }
160 }
161
162 Editor.getEditorDesktop().getWorld().addGameEntity(tmpRoom2);
163
164 if (tmpRoom1 != null)
165 {
166 // Nema cenu vytvaret spoj mezi dvema objekty, kdyz jeden z nich neexistuje
167 Editor.getEditorDesktop().getWorld().addGameEntity(new Route(tmpRoom1, tmpRoom2));
168 }
169 }
170
171 /**
172 * Zajisti pripadne propojeni dvou mistnosti.
173 */
174 private void connectRoom()
175 {
176 this.editorGraphicsWorld2D.setConnectRoom(true);
177 }
178
179 /**
180 * Vytvori koncovou polozku menu - Pridat mistnost.
181 */
182 private void createItemMenuAddRoom()
183 {
184 World2DPopupMenuListener.addRoomIM = MainMenu.addMenuItem("cs.pancava.caltha.menu.World2DPopupMenuListener-createItemMenuAddRoom_addRoomIM",
185 "cs.pancava.caltha.menu.World2DPopupMenuListener-createItemMenuAddRoom_addRoomIM_tip",
186 true);
187
188 World2DPopupMenuListener.addRoomIM.addActionListener(new ActionListener()
189 {
190 @Override
191 public void actionPerformed(final ActionEvent e)
192 {
193 // TODO: Vsechmy metody uvnitr metody actionPerformed by meli byt mimo tuto tridu !
194 // Asi by idealne meli byt ve tride: EditorGraphicsWorld2D
195 // Je treba oddelit grafickou cast od vykonne casti !
196 // Dale se tu nejspis resi metody, ktere do teto tridy nepatri !
197 addRoom(mouseX, mouseY);
198
199 // Nove vytvoreny graficky obekt je oriznut diky popup menu.
200 // Proto je nutne opetovne prekresleni !
201 editorGraphicsWorld2D.repaint();
202
203 // Test, zda-li je treba rozsirit plochu (kreslici oblast) vytvareni sveta.
204 expandGraphicsWorld2DArea();
205
206 // Test, zda-li je treba posunout s viditelnou oblasti plochy pri vytvoreni noveho objektu,
207 // aby byl nove vytvoreny objekt viditelny
208 moveVisibleArea();
209 }
210 });
211 World2DPopupMenuListener.popupMenu.add(World2DPopupMenuListener.addRoomIM);
212 }
213
214 /**
215 * Vytvori koncovou polozku menu - Spojit mistnosti.
216 */
217 private void createItemMenuConnectRoom()
218 {
219 World2DPopupMenuListener.connectRoomIM = MainMenu.addMenuItem("cs.pancava.caltha.menu.World2DPopupMenuListener-createItemMenuConnectRoom_connectRoomIM",
220 "cs.pancava.caltha.menu.World2DPopupMenuListener-createItemMenuConnectRoom_connectRoomIM_tip",
221 false);
222
223 World2DPopupMenuListener.connectRoomIM.addActionListener(new ActionListener()
224 {
225 @Override
226 public void actionPerformed(final ActionEvent e)
227 {
228 // TODO: Vsechmy metody uvnitr metody actionPerformed by meli byt mimo tuto tridu !
229 // Asi by idealne meli byt ve tride: EditorGraphicsWorld2D
230 // Je treba oddelit grafickou cast od vykonne casti !
231 // Dale se tu nejspis resi metody, ktere do teto tridy nepatri !
232 connectRoom();
233
234 // Nove vytvoreny graficky obekt je oriznut diky popup menu.
235 // Proto je nutne opetovne prekresleni !
236 editorGraphicsWorld2D.repaint();
237 }
238 });
239 World2DPopupMenuListener.popupMenu.add(World2DPopupMenuListener.connectRoomIM);
240 }
241
242 /**
243 * Vytvori koncovou polozku menu - Rozpojit mistnosti.
244 */
245 private void createItemMenuDisconnectRoom()
246 {
247 World2DPopupMenuListener.disconnectRoomIM = MainMenu.addMenuItem("cs.pancava.caltha.menu.World2DPopupMenuListener-createItemMenuDisconnectRoom_disconnectRoomIM",
248 "cs.pancava.caltha.menu.World2DPopupMenuListener-createItemMenuDisconnectRoom_disconnectRoomIM_tip",
249 false);
250
251 World2DPopupMenuListener.disconnectRoomIM.addActionListener(new ActionListener()
252 {
253 @Override
254 public void actionPerformed(final ActionEvent e)
255 {
256 // TODO: Vsechmy metody uvnitr metody actionPerformed by meli byt mimo tuto tridu !
257 // Asi by idealne meli byt ve tride: EditorGraphicsWorld2D
258 // Je treba oddelit grafickou cast od vykonne casti !
259 // Dale se tu nejspis resi metody, ktere do teto tridy nepatri !
260 disconnectRoom();
261
262 // Nove vytvoreny graficky obekt je oriznut diky popup menu.
263 // Proto je nutne opetovne prekresleni !
264 editorGraphicsWorld2D.repaint();
265 }
266 });
267 World2DPopupMenuListener.popupMenu.add(World2DPopupMenuListener.disconnectRoomIM);
268 }
269
270 /**
271 * Vytvori koncovou polozku menu - Odtranit mistnost.
272 */
273 private void createItemMenuRemoveRoom()
274 {
275 World2DPopupMenuListener.removeRoomIM = MainMenu.addMenuItem("cs.pancava.caltha.menu.World2DPopupMenuListener-createItemMenuRemoveRoom_removeRoomIM",
276 "cs.pancava.caltha.menu.World2DPopupMenuListener-createItemMenuRemoveRoom_removeRoomIM_tip",
277 true);
278
279 World2DPopupMenuListener.removeRoomIM.addActionListener(new ActionListener()
280 {
281 @Override
282 public void actionPerformed(final ActionEvent e)
283 {
284 // TODO: Vsechmy metody uvnitr metody actionPerformed by meli byt mimo tuto tridu !
285 // Asi by idealne meli byt ve tride: EditorGraphicsWorld2D
286 // Je treba oddelit grafickou cast od vykonne casti !
287 // Dale se tu nejspis resi metody, ktere do teto tridy nepatri !
288 removeRoom();
289
290 // Nove vytvoreny graficky obekt je oriznut diky popup menu.
291 // Proto je nutne opetovne prekresleni !
292 editorGraphicsWorld2D.repaint();
293 }
294 });
295 World2DPopupMenuListener.popupMenu.add(World2DPopupMenuListener.removeRoomIM);
296 }
297
298 /**
299 * Zajisti pripadne rozpojeni dvou mistnosti.
300 */
301 private void disconnectRoom()
302 {
303 this.editorGraphicsWorld2D.setDisconnectRoom(true);
304 }
305
306 /**
307 * Metoda testuje, zda-li jsme vytvotili novy objekt na samem kraji kreslici oblasti.
308 * Pokud ano, tak zvetsime kreslici oblast.
309 * Pokud ne, nic dalsiho delat nebudem.
310 */
311 private void expandGraphicsWorld2DArea()
312 {
313 // Ziskani aktualni velikosti kreslici plochy
314 Dimension tmpDimension = editorGraphicsWorld2D.getPreferredSize();
315
316 // Urcuje, zda-li jsme klikli do oblasti, ktera umoznuje rozsirit kreslici plochu
317 boolean scrollbarGrowingArea = false;
318
319 if (tmpDimension.width - editorGraphicsWorld2D.getScrollbarGrowingAreaX() < mouseX)
320 {
321 tmpDimension.setSize(tmpDimension.width + editorGraphicsWorld2D.getNewAreaX(), tmpDimension.height);
322 scrollbarGrowingArea = true;
323 }
324
325 if (tmpDimension.height - editorGraphicsWorld2D.getScrollbarGrowingAreaY() < mouseY)
326 {
327 tmpDimension.setSize(tmpDimension.width, tmpDimension.height + editorGraphicsWorld2D.getNewAreaY());
328 scrollbarGrowingArea = true;
329 }
330
331 if (scrollbarGrowingArea)
332 {
333 // Doslo ke zmene velikosti kreslici plochy - musime nastavit novou hodnotu
334 editorGraphicsWorld2D.setPreferredSize(tmpDimension);
335 editorGraphicsWorld2D.revalidate();
336 }
337 }
338
339 /**
340 * Jednorazova udalost mysi - Stisknuti tlacitka mysi.
341 * @param e MouseEvent
342 */
343 @Override
344 public final void mousePressed(final MouseEvent e)
345 {
346 // Pokud je nejaka mistnost vybrana, tak jsou dodatecne aktivovany nasledujici polozky popup menu.
347 // Jakmile neni zadna mistnost vybrana, tak se deaktivuji !
348 // FIXME: Jakmile dojde ke smazani nejake mistnosti, tak stale zustavaji nasleduji polozky aktivni v popup menu !
349 World2DPopupMenuListener.connectRoomIM.setEnabled(this.editorGraphicsWorld2D.getSelectedObject());
350 World2DPopupMenuListener.disconnectRoomIM.setEnabled(this.editorGraphicsWorld2D.getSelectedObject());
351
352 this.maybeShowPopup(e);
353 }
354
355 /**
356 * Jednorazova udalost mysi - Uvolneni tlacitka mysi.
357 * @param e MouseEvent
358 */
359 @Override
360 public final void mouseReleased(final MouseEvent e)
361 {
362 this.maybeShowPopup(e);
363 }
364
365 /**
366 * Metoda testuje, zda-li jsme vytvotili novy objekt na kraji viditelne kreslici oblasti.
367 * Pokud ano, tak posuneme kreslici oblasti tak, aby byl videt cely nove vkladany objekt.
368 * Pokud ne, nic dalsiho delat nebudem.
369 */
370 private void moveVisibleArea()
371 {
372 Rectangle tmpVisibleArea = new Rectangle(editorGraphicsWorld2D.getVisibleRect());
373
374 if (tmpVisibleArea.x + ROOM_SIZE > mouseX)
375 {
376 editorGraphicsWorld2D.scrollRectToVisible(new Rectangle(tmpVisibleArea.x - ROOM_SIZE, tmpVisibleArea.y, tmpVisibleArea.width, tmpVisibleArea.height));
377 // Jelikoz jsme posunuli s viditelnou oblasti, musi se znovu nacist aktualni hodnota viditelne oblasti
378 tmpVisibleArea = new Rectangle(editorGraphicsWorld2D.getVisibleRect());
379 }
380
381 if (tmpVisibleArea.x + tmpVisibleArea.width - ROOM_SIZE < mouseX)
382 {
383 editorGraphicsWorld2D.scrollRectToVisible(new Rectangle(tmpVisibleArea.x + ROOM_SIZE, tmpVisibleArea.y, tmpVisibleArea.width, tmpVisibleArea.height));
384 tmpVisibleArea = new Rectangle(editorGraphicsWorld2D.getVisibleRect());
385 }
386
387 if (tmpVisibleArea.y + ROOM_SIZE > mouseY)
388 {
389 editorGraphicsWorld2D.scrollRectToVisible(new Rectangle(tmpVisibleArea.x, tmpVisibleArea.y - ROOM_SIZE, tmpVisibleArea.width, tmpVisibleArea.height));
390 tmpVisibleArea = new Rectangle(editorGraphicsWorld2D.getVisibleRect());
391 }
392
393 if (tmpVisibleArea.y + tmpVisibleArea.height - ROOM_SIZE < mouseY)
394 {
395 editorGraphicsWorld2D.scrollRectToVisible(new Rectangle(tmpVisibleArea.x, tmpVisibleArea.y + ROOM_SIZE, tmpVisibleArea.width, tmpVisibleArea.height));
396 // Jelikoz se uz vycerpaly vsechny moznosti (strany + rohy oblasti), tak neni treba znovu aktualizovat hodnotu tmpVisibleArea.
397 }
398 }
399
400 /**
401 * Zobrazi popup menu na danych souradnicich mysi.
402 * @param e MouseEvent
403 */
404 private void maybeShowPopup(final MouseEvent e)
405 {
406 if (e.isPopupTrigger())
407 {
408 this.mouseX = e.getX();
409 this.mouseY = e.getY();
410
411 World2DPopupMenuListener.popupMenu.show(e.getComponent(), e.getX(), e.getY());
412 }
413 }
414
415 /**
416 * Zajisti pripadne odstraneni mistnosti.
417 */
418 private void removeRoom()
419 {
420 this.editorGraphicsWorld2D.setRemoveRoom(true);
421 }
422 }