1 /* $Id: EditorGraphicsWorld2D.java 33 2014-02-01 17:51:58Z 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.graphics;
21
22 import cs.pancava.caltha.Editor;
23 import cs.pancava.caltha.worlds.GameEntity;
24 import cs.pancava.caltha.worlds.Route;
25 import java.awt.Color;
26 import java.awt.Container;
27 import java.awt.Cursor;
28 import java.awt.Dimension;
29 import java.awt.Graphics;
30 import java.awt.Graphics2D;
31 import java.awt.Point;
32 import java.awt.event.MouseEvent;
33 import java.awt.event.MouseListener;
34 import java.awt.event.MouseMotionListener;
35 import java.awt.geom.RectangularShape;
36 import javax.swing.JPanel;
37 import javax.swing.JViewport;
38 import javax.swing.SwingUtilities;
39
40 /**
41 * <p><b>Tato trida definuje plochu (panel) pro vytvareni grafickych objektu
42 * herniho sveta.</b></p>
43 *
44 * @author Milan Vaclavik<br />
45 * @version $Revision: 33 $<br />
46 * $LastChangedBy: mihlon $<br />
47 */
48 public class EditorGraphicsWorld2D extends JPanel implements MouseListener, MouseMotionListener
49 {
50 /**
51 * Identifikace tridy - datum vzniku tridy :).
52 */
53 private static final long serialVersionUID = 20081129192122L;
54
55 /**
56 * Index obektu, se kterym se prave manipuluje.
57 */
58 private int indexObektu;
59
60 /**
61 * Uchovava X souradnici grafickeho objektu (mistnosti)
62 */
63 private int lastLocationX;
64
65 /**
66 * Uchovava Y souradnici grafickeho objektu (mistnosti)
67 */
68 private int lastLocationY;
69
70 /**
71 * Uchovava souradnici X posledniho stisku mysi.
72 */
73 private int lastLocationMouseX;
74
75 /**
76 * Uchovava souradnici Y posledniho stisku mysi.
77 */
78 private int lastLocationMouseY;
79
80 /**
81 * True - jestlize tlacitko mysi bylo stisknuto, drzeno nebo uvolneno mimo oblast daneho grafickeho objektu.
82 * False - v ostanich pripadech
83 */
84 private boolean pressOut;
85
86 /**
87 * True - nejaky objekt je vybran.
88 * False - zadny objekt neni vybran.
89 */
90 private boolean selectedObject;
91
92 /**
93 * True - Maji se propojit mistnosti dohromady.
94 * False - Nic se propojovat nebude.
95 */
96 private boolean connectRoom;
97
98 /**
99 * True - Maji se rozpojit mistnosti.
100 * False - Nic se rozpojovat nebude.
101 */
102 private boolean disconnectRoom;
103
104 /**
105 * Nasobek, ktery s pomoci konstanty SCROLLBAR_GROWING_AREA_X urci velikost plochy ve smeru X o kterou se zvetsi kreslici plocha.
106 */
107 private final int MULTIPLE_NEW_AREA_X = 5;
108
109 /**
110 * Nasobek, ktery s pomoci konstanty SCROLLBAR_GROWING_AREA_Y urci velikost plochy ve smeru Y o kterou se zvetsi kreslici plocha.
111 */
112 private final int MULTIPLE_NEW_AREA_Y = 5;
113
114 /**
115 * True - Bude se mazat nejaka mistnost
116 * False - Nic se nebude mazat.
117 */
118 private boolean removeRoom;
119
120 /**
121 * Pomaha urcit oblast, ve ktere dojde k rozsireni kreslici plochy.
122 */
123 private final int SCROLLBAR_GROWING_AREA_X = 100;
124
125 /**
126 * Pomaha urcit oblast, ve ktere dojde k rozsireni kreslici plochy.
127 */
128 private final int SCROLLBAR_GROWING_AREA_Y = 100;
129
130 /**
131 * Urcuje, zda-li je zmeneny tvar kurzoru mysi.
132 */
133 private boolean changedMouseCursor;
134
135 /**
136 * Konstruktor - Nastavi barvu pozadi,
137 * registruje posluchace mysi,
138 * vytvori texturu pro zobrazovane objekty.
139 */
140 public EditorGraphicsWorld2D()
141 {
142 this.setBackground(Color.white);
143
144 this.addMouseMotionListener(this);
145 this.addMouseListener(this);
146 }
147
148 /**
149 * Oznamuje, zda-li je nejaky objekt vybran nebo ne.
150 * @return boolean : True - nejaky objekt je vybran, False - zadny objekt neni vybran.
151 */
152 public final boolean getSelectedObject()
153 {
154 return this.selectedObject;
155 }
156
157 /**
158 * Oznamuje, zda-li se budou propojovat mistnosti.
159 * @return boolean : True - Budou s propojovat mistnosti.
160 * False - Nic se propojovat nebude.
161 */
162 public final boolean getConnectRoom()
163 {
164 return this.connectRoom;
165 }
166
167 /**
168 * Oznamuje, zda-li se budou rozpojovat mistnosti.
169 * @return boolean : True - Budou s rozpojovat mistnosti.
170 * False - Nic se rozpojovat nebude.
171 */
172 public final boolean getDisconnectRoom()
173 {
174 return this.disconnectRoom;
175 }
176
177 /**
178 * Vrati velikost plochy ve smeru X, o kterou se ma zvetsit kreslici plocha.
179 * @return int: velikost plochy ve smeru X, o kterou se zvetsi kreslici plocha.
180 */
181 public final int getNewAreaX()
182 {
183 return MULTIPLE_NEW_AREA_X * SCROLLBAR_GROWING_AREA_X;
184 }
185
186 /**
187 * Vrati velikost plochy ve smeru X, o kterou se ma zvetsit kreslici plocha.
188 * @return int: velikost plochy ve smeru X, o kterou se zvetsi kreslici plocha.
189 */
190 public final int getNewAreaY()
191 {
192 return MULTIPLE_NEW_AREA_Y * SCROLLBAR_GROWING_AREA_Y;
193 }
194
195 /**
196 * Oznamuje, zda-li se bude mazat mistnost.
197 * @return boolean : True - Bude se mazat mistnost.
198 * False - Nic se mazat nebude.
199 */
200 public final boolean getRemoveRoom()
201 {
202 return this.removeRoom;
203 }
204
205 /**
206 * Vraci hodnotu oblasti, ktera pomaha zjistit, zda-li je treba zvetsit kreslici oblast.
207 * @return SCROLLBAR_GROWING_AREA_X
208 */
209 public final int getScrollbarGrowingAreaX()
210 {
211 return SCROLLBAR_GROWING_AREA_X;
212 }
213
214 /**
215 * Vraci hodnotu oblasti, ktera pomaha zjistit, zda-li je treba zvetsit kreslici oblast.
216 * @return SCROLLBAR_GROWING_AREA_Y
217 */
218 public final int getScrollbarGrowingAreaY()
219 {
220 return SCROLLBAR_GROWING_AREA_Y;
221 }
222
223 /**
224 * Jednorazova udalost mysi - Stisknuti tlacitka mysi.
225 * @param e MouseEvent
226 */
227 @Override
228 public final void mousePressed(final MouseEvent e)
229 {
230 if (SwingUtilities.isLeftMouseButton(e))
231 {
232 Boolean nalezenObjekt = false;
233
234 lastLocationMouseX = e.getX();
235 lastLocationMouseY = e.getY();
236
237 for (GameEntity ge : Editor.getEditorDesktop().getWorld().getWorldRoomsAL())
238 {
239 final RectangularShape gg = (RectangularShape) ge.getGraphicsObject();
240
241 // Test, zda-li se mys, v dobe stisku tlacitka, nachazi v oblasti daneho grafickeho objektu.
242 //if (ge.getGraphicsObject().contains(e.getX(), e.getY()))
243 if (gg.contains(e.getX(), e.getY()))
244 {
245 if (this.getRemoveRoom())
246 {
247 this.removeRoom(ge);
248
249 // Uz je zbytecne dale cokoliv delat.
250 return;
251 }
252
253 if (this.getConnectRoom())
254 {
255 // Nejspis se budou propojovat mistnosti.
256 this.connectRoom(ge);
257 }
258
259 if (this.getDisconnectRoom())
260 {
261 // Nejspis se budou rozopojovat mistnosti.
262 this.disconnectRoom(ge);
263 }
264 // Vzhledem k tomu, ze zatim lze umistit nekolik mistnosti pres sebe,
265 // tak dochazi k vicenasobnemu oznacovani mistnosti, coz je nezadouci !
266 // Proto se musi preventivne vsechny objekty projit a odznacit je :(
267 // Optimalni by bylo odznacit predchozi vybrany objekt, ale bohuzel to nelze vzdy zarucit.
268 this.deselectAllObject();
269
270 ge.setSelected(true);
271 this.setSelectedObject(true);
272
273 this.indexObektu = ge.getId();
274
275 // Ziskani X-ove a Y-ove souradnice mysi v okamziku stisku tlacitka mysi
276 this.lastLocationX = ge.getLocationX() - e.getX();
277 this.lastLocationY = ge.getLocationY() - e.getY();
278
279 // Mys se nachazi v dane oblasti
280 this.pressOut = false;
281 this.updateLocation(ge, e);
282
283 nalezenObjekt = true;
284 // Ukoncime hledani, jelikoz chceme pohybovat pouze s jednim objektem !
285 return;
286 }
287 else
288 {
289 this.pressOut = true;
290 }
291 }
292
293 // Nesmazali jsme zadnou mistnost, proto smazeme priznak
294 this.setRemoveRoom(false);
295
296 if (!nalezenObjekt)
297 {
298 final int m = e.getModifiers();
299
300 if (SwingUtilities.isLeftMouseButton(e))
301 {
302 // Stiskli jsme leve tlacitko mysi mimo jakoukoliv mistnot - prazdna plocha,
303 // proto zrusime oznaceni prave vybrane mistnosti
304 //this.deselectPreviousObject(this.indexObektu);
305
306 // Vzhledem k tomu, ze zatim lze umistit nekolik mistnosti pres sebe,
307 // tak dochazi k vicenasobnemu oznacovani mistnosti, coz je nezadouci !
308 // Proto se musi preventivne vsechny objekty projit a odznacit je :(
309 this.deselectAllObject();
310 this.setSelectedObject(false);
311 this.repaint();
312 }
313 }
314 }
315 }
316
317 /**
318 * Spojita udalost mysi - Zachytava pohyb mysi behem stisknuteho tlacitka.
319 * @param e MouseEvent
320 */
321 @Override
322 public final void mouseDragged(final MouseEvent e)
323 {
324 if (SwingUtilities.isLeftMouseButton(e))
325 {
326 changeMouseCursor(Cursor.MOVE_CURSOR);
327
328 if (!this.pressOut)
329 {
330 // Vim, ze zatim mohu presouvat pouze mistnosti
331 if (Editor.getEditorDesktop().getWorld().getWorldRoomsAL(this.indexObektu) != null)
332 {
333 this.updateLocation(Editor.getEditorDesktop().getWorld().getWorldRoomsAL(this.indexObektu), e);
334 }
335 }
336
337 if (!getSelectedObject())
338 {
339 // S pozadim hybeme pouze tehdy, kdyz jsme nestiskli mys nad nejakym objektem
340 scrollGraphicsWorld(e);
341 }
342 }
343 }
344
345 /**
346 * Jednorazova udalost mysi - Uvolneni tlacitka mysi.
347 * @param e MouseEvent
348 */
349 @Override
350 public final void mouseReleased(final MouseEvent e)
351 {
352 restoreMouseCursor();
353
354 for (GameEntity ge : Editor.getEditorDesktop().getWorld().getWorldRoomsAL())
355 {
356 final RectangularShape gg = (RectangularShape) ge.getGraphicsObject();
357
358 // Test, zda-li se mys, v dobe uvolneni tlacitka, nachazi v oblasti daneho grafickeho objektu.
359 //if (ge.getGraphicsObject().contains(e.getX(), e.getY()) && (!this.pressOut))
360 if (gg.contains(e.getX(), e.getY()) && (!this.pressOut))
361 {
362 this.indexObektu = ge.getId();
363
364 ge.setLocationX(this.lastLocationX + e.getX());
365 ge.setLocationY(this.lastLocationY + e.getY());
366
367 this.updateLocation(ge, e);
368
369 // Ukoncime hledani, jelikoz chceme pohybovat pouze s jednim objektem !
370 return;
371 }
372 else
373 {
374 this.pressOut = false;
375 }
376 }
377 }
378
379 /**
380 * Tato metoda je vyzadovana diky rozhrani MouseListener.
381 * @param e MouseEvent
382 */
383 @Override
384 public final void mouseMoved(final MouseEvent e)
385 {
386 }
387
388 /**
389 * Tato metoda je vyzadovana diky rozhrani MouseMotionListener.
390 * @param e MouseEvent
391 */
392 @Override
393 public final void mouseClicked(final MouseEvent e)
394 {
395 }
396
397 /**
398 * Tato metoda je vyzadovana diky rozhrani MouseMotionListener.
399 * @param e MouseEvent
400 */
401 @Override
402 public final void mouseExited(final MouseEvent e)
403 {
404 }
405
406 /**
407 * Tato metoda je vyzadovana diky rozhrani MouseMotionListener.
408 * @param e MouseEvent
409 */
410 @Override
411 public final void mouseEntered(final MouseEvent e)
412 {
413 }
414
415 /**
416 * Oznamuje, zda se budou propojovat mistnosti.
417 * @param b boolean : True - Budou s propojovat mistnosti.
418 * False - Nic se propojovat nebude.
419 */
420 public final void setConnectRoom(final boolean b)
421 {
422 this.connectRoom = b;
423 }
424
425 /**
426 * Oznamuje, zda se budou rozpojovat mistnosti.
427 * @param b boolean : True - Budou s rozpojovat mistnosti.
428 * False - Nic se rozpojovat nebude.
429 */
430 public final void setDisconnectRoom(final boolean b)
431 {
432 this.disconnectRoom = b;
433 }
434
435 /**
436 * Oznamuje, zda-li se bude mazat mistnost.
437 * @param b boolean : True - Bude se mazat mistnost,
438 * False - Nic se mazat nebude.
439 */
440 public final void setRemoveRoom(final boolean b)
441 {
442 this.removeRoom = b;
443 }
444
445 /**
446 * Aktualizace umisteni daneho grafickeho objektu, se kterym bylo pohybovano.
447 * @param ge GameEntity : Herni objekt, ktery budeme aktualizovat.
448 * @param e MouseEvent
449 */
450 public final void updateLocation(final GameEntity ge, final MouseEvent e)
451 {
452 Editor.getEditorDesktop().getWorld().updateGameEntityLocation(ge, this.indexObektu, this.lastLocationX + e.getX(), this.lastLocationY + e.getY());
453
454 this.repaint();
455 }
456
457 /**
458 * Vykreslovani grafickeh obsahu.
459 * @param g Graphics
460 */
461 @Override
462 public final void paintComponent(final Graphics g)
463 {
464 super.paintComponent(g);
465 this.update(g);
466 }
467
468 /**
469 * Vlastni vykreslovani grafickeho obsahu.
470 * @param g Graphics
471 */
472 @Override
473 public final void update(final Graphics g)
474 {
475 final Graphics2D g2 = (Graphics2D) g;
476 final Dimension dim = this.getSize();
477 final int w = (int) dim.getWidth();
478 final int h = (int) dim.getHeight();
479
480 // TODO: Mozno optimalizovat -> neprekreslovat celou plochu, ale jen dane objekty vykreslit barvou pozadi !
481 // Smazeme celou plochu graficke oblasti.
482 g2.setPaint(Color.white);
483 g2.fillRect(0, 0, w, h);
484
485 this.showGameEntitys(g2);
486 }
487
488 /**
489 * Zmeni typ kurzoru mysi podle zadaneho parametru.
490 * @param cursorType int
491 */
492 private void changeMouseCursor(final int cursorType)
493 {
494 setCursor(Cursor.getPredefinedCursor(cursorType));
495 changedMouseCursor = true;
496 }
497
498 /**
499 * Propoji dve mistnosti mezi sebou za te podminky, ze se nejedna o tu samou mistnost.
500 * @param ge GameEntity : Tuto mistnost budeme propojovat s vybranou mistnosti.
501 */
502 private void connectRoom(final GameEntity ge)
503 {
504 for (GameEntity ge2 : Editor.getEditorDesktop().getWorld().getWorldRoomsAL())
505 {
506 if (ge2.isSeleted() && !ge2.equals(ge))
507 {
508 // Nasli jsme vybranou mistnost pro propojeni.
509 Editor.getEditorDesktop().getWorld().addGameEntity(new Route(ge2, ge));
510
511 // Uz dale nechceme propojovat dalsi mistnosti s vybranou mistnosti.
512 this.setConnectRoom(false);
513
514 // Ukoncime dalsi hledani.
515 return;
516 }
517 }
518 }
519
520 /**
521 * Zrusi zvyrazneni predchoziho objektu.
522 * @param i int : Index objektu, u ktereho rusime zvyrazneni.
523 */
524 private void deselectPreviousObject(final int i)
525 {
526 if (!Editor.getEditorDesktop().getWorld().getWorldRoomsAL().isEmpty())
527 {
528 Editor.getEditorDesktop().getWorld().getWorldRoomsAL().get(i).setSelected(false);
529 }
530 }
531
532 /**
533 * Zrusi zvyrazneni vsech objektu.
534 */
535 private void deselectAllObject()
536 {
537 for (GameEntity ge : Editor.getEditorDesktop().getWorld().getWorldRoomsAL())
538 {
539 ge.setSelected(false);
540 }
541 }
542
543 /**
544 * Rozpoji dve mistnosti od sebe za te podminky, ze se nejedna o tu samou mistnost.
545 * @param ge GameEntity : Tuto mistnost budeme rozpojovat s vybranou mistnosti.
546 */
547 private void disconnectRoom(final GameEntity ge)
548 {
549 for (GameEntity ge2 : Editor.getEditorDesktop().getWorld().getWorldRoomsAL())
550 {
551 if (ge2.isSeleted() && !ge2.equals(ge))
552 {
553 // Nasli jsme druhou mistnost pro rozpojeni.
554
555 // Hledame spoj, ktery obsahuje mistnosti ge a ge2
556 if (!Editor.getEditorDesktop().getWorld().getWorldRoutesAL().isEmpty())
557 {
558 GameEntity tmpGE;
559
560 // Tato konstrukce nelze pouzit, pri mazani daneho prvku dojde k vyhozeni vyjimky, ze se dany objekt jestepouziva
561 //for (GameEntity ger : Editor.getEditorDesktop().getWorld().getWorldRoutesAL())
562 for (int i = 0; i < Editor.getEditorDesktop().getWorld().getWorldRoutesAL().size(); i += 1)
563 {
564 tmpGE = Editor.getEditorDesktop().getWorld().getWorldRoutesAL(i);
565
566 if (tmpGE != null && ((tmpGE.getFirstRoom().equals(ge) && tmpGE.getSecondRoom().equals(ge2))
567 || (tmpGE.getFirstRoom().equals(ge2) && tmpGE.getSecondRoom().equals(ge))))
568 {
569 Editor.getEditorDesktop().getWorld().getWorldRoutesAL().remove(tmpGE);
570
571 // Ano je to tak ! Potrebuju restartovat cyklus ! Mozna, ze by to slo i pres while ?
572 // FIXME: Prepsat for na while
573 i = -1;
574 }
575 }
576 }
577 // Uz dale nechceme rozpojovat dalsi mistnosti s vybranou mistnosti.
578 this.setDisconnectRoom(false);
579
580 // Ukoncime dalsi hledani.
581 return;
582 }
583 }
584 }
585
586 /**
587 * Odstrani vybranou mistnost.
588 * @param ge GameEntity : Vybrana mistnost k odstraneni.
589 */
590 private void removeRoom(final GameEntity ge)
591 {
592 // Hledame, zda-li neni nejaka mistnost spojena s touto zemi, kterou chceme odstranit.
593 if (!Editor.getEditorDesktop().getWorld().getWorldRoutesAL().isEmpty())
594 {
595 GameEntity tmpGE;
596
597 // Tato konstrukce nelze pouzit, pri mazani daneho prvku dojde k vyhozeni vyjimky, ze se dany objekt jestepouziva
598 //for (GameEntity ger : Editor.getEditorDesktop().getWorld().getWorldRoutesAL())
599 for (int i = 0; i < Editor.getEditorDesktop().getWorld().getWorldRoutesAL().size(); i += 1)
600 {
601 tmpGE = Editor.getEditorDesktop().getWorld().getWorldRoutesAL(i);
602
603 if (tmpGE != null && (tmpGE.getFirstRoom().equals(ge) || tmpGE.getSecondRoom().equals(ge)))
604 {
605 Editor.getEditorDesktop().getWorld().getWorldRoutesAL().remove(tmpGE);
606
607 // Ano je to tak ! Potrebuju restartovat cyklus ! Mozna, ze by to slo i pres while ?
608 // FIXME: Prepsat for na while
609 i = -1;
610 }
611 }
612 }
613
614 if (!Editor.getEditorDesktop().getWorld().getWorldRoomsAL().isEmpty())
615 {
616 // Odstraneni mistnosti ze seznamu.
617 Editor.getEditorDesktop().getWorld().getWorldRoomsAL().remove(ge);
618
619 // Opravime indexy jednotlivych mistnosti.
620 Editor.getEditorDesktop().getWorld().updateRooms(ge.getId());
621
622 // Aktualizujeme grafickou plochu.
623 this.repaint();
624 }
625
626 this.setRemoveRoom(false);
627 }
628
629 /**
630 * Obnovi puvodni tvar kurzoru mysi.
631 */
632 private void restoreMouseCursor()
633 {
634 if (changedMouseCursor)
635 {
636 setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
637 }
638 }
639
640 /**
641 * Primo scrolluje s grafickou plochou.
642 * @param e MouseEvent
643 */
644 private void scrollGraphicsWorld(final MouseEvent e)
645 {
646 Container c = this.getParent();
647
648 if (c instanceof JViewport)
649 {
650 JViewport jv = (JViewport) c;
651 Point p = jv.getViewPosition();
652
653 int newX = p.x - (e.getX() - lastLocationMouseX);
654 int newY = p.y - (e.getY() - lastLocationMouseY);
655
656 int maxX = this.getWidth() - jv.getWidth();
657 int maxY = this.getHeight() - jv.getHeight();
658
659 if (newX < 0)
660 {
661 newX = 0;
662 }
663
664 if (newX > maxX)
665 {
666 newX = maxX;
667 }
668
669 if (newY < 0)
670 {
671 newY = 0;
672 }
673
674 if (newY > maxY)
675 {
676 newY = maxY;
677 }
678
679 jv.setViewPosition(new Point(newX, newY));
680 }
681 }
682
683 /**
684 * Oznamuje zda existuje nejaky vybrany objekt nebo ne.
685 * @param b boolean : True - nejaky objekt je vybran, jinak false.
686 */
687 private void setSelectedObject(final boolean b)
688 {
689 this.selectedObject = b;
690 }
691
692 /**
693 * Zobrazi obekty herniho sveta.
694 * @param g2 Graphics2D
695 */
696 private void showGameEntitys(final Graphics2D g2)
697 {
698 for (GameEntity ge : Editor.getEditorDesktop().getWorld().getWorldRoutesAL())
699 {
700 ge.showGraphicsObject(g2);
701 }
702
703 for (GameEntity ge : Editor.getEditorDesktop().getWorld().getWorldRoomsAL())
704 {
705 ge.showGraphicsObject(g2);
706 }
707 }
708 }