1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package net.sf.firemox.zone;
20
21 import java.awt.Color;
22 import java.awt.Component;
23 import java.awt.Container;
24 import java.awt.FlowLayout;
25 import java.awt.Graphics;
26 import java.awt.Image;
27 import java.awt.LayoutManager;
28 import java.awt.Point;
29 import java.awt.event.MouseEvent;
30 import java.awt.event.MouseListener;
31 import java.io.IOException;
32 import java.io.InputStream;
33 import java.io.OutputStream;
34 import java.net.MalformedURLException;
35 import java.util.Arrays;
36 import java.util.Collections;
37 import java.util.List;
38
39 import javax.swing.JComponent;
40 import javax.swing.JPanel;
41 import javax.swing.JScrollPane;
42
43 import net.sf.firemox.clickable.Clickable;
44 import net.sf.firemox.clickable.ability.Ability;
45 import net.sf.firemox.clickable.target.Target;
46 import net.sf.firemox.clickable.target.card.AbstractCard;
47 import net.sf.firemox.clickable.target.card.MCard;
48 import net.sf.firemox.clickable.target.player.Player;
49 import net.sf.firemox.stack.StackManager;
50 import net.sf.firemox.test.Test;
51 import net.sf.firemox.token.IdPositions;
52 import net.sf.firemox.token.Visibility;
53 import net.sf.firemox.token.VisibilityChange;
54 import net.sf.firemox.token.Visible;
55 import net.sf.firemox.tools.Configuration;
56 import net.sf.firemox.tools.Log;
57 import net.sf.firemox.tools.MToolKit;
58 import net.sf.firemox.tools.Pair;
59 import net.sf.firemox.tools.Picture;
60 import net.sf.firemox.ui.component.TableTop;
61 import net.sf.firemox.ui.i18n.LanguageManagerMDB;
62 import net.sf.firemox.ui.layout.FlowLayout2;
63 import net.sf.firemox.ui.layout.WallpaperTypes;
64
65 /***
66 * A zone is a cards container.
67 *
68 * @author Fabrice Daugan
69 * @since 0.2c
70 * @since 0.3 feature "reverseImage" implemented
71 * @since 0.4 you can now change wallpaper/color of this MZone and setting
72 * @since 0.52 "enableReverse" option are saved.
73 * @since 0.71 visibility of cards can be set. Cards come into this zone
74 * returned or not immediately
75 */
76 public abstract class MZone extends JPanel implements MouseListener, Visible {
77
78 /***
79 * create an instance of MZone
80 *
81 * @param idZone
82 * id place of this panel
83 * @param layout
84 * is it's layout
85 * @param superPanel
86 * scroll panel containing this panel
87 * @param reverseImage
88 * if true the back picture will be reversed
89 * @param name
90 * the untranslated zone name
91 * @see net.sf.firemox.token.IdZones
92 */
93 protected MZone(int idZone, LayoutManager layout, JScrollPane superPanel,
94 boolean reverseImage, String name) {
95 super(layout);
96 setName(name);
97
98 this.superPanel = superPanel;
99 this.reverseImage = reverseImage;
100 this.idZone = idZone;
101
102
103 if (superPanel != null) {
104 superPanel.setBorder(null);
105 }
106 }
107
108 /***
109 * Initialize UI
110 */
111 public void initUI() {
112 final String getter = "zones." + getZoneName() + "."
113 + (reverseImage ? "up" : "down");
114 this.doMosaic = Configuration.getBoolean(getter + ".mosaic", true);
115
116 if (superPanel != null) {
117 superPanel.setViewportView(this);
118 MToolKit.addOverlay(superPanel);
119 }
120
121 setBorder(null);
122 setAutoscrolls(true);
123 setWallPaperFile(Configuration.getString(getter + ".wallpaper", ""));
124 setBackground(new Color(Configuration.getInt(getter + ".background", 0)));
125 addMouseListener(this);
126 }
127
128 @Override
129 public void paintComponent(Graphics g) {
130 if (getImage() != null) {
131 int width = getWidth();
132 int height = getHeight();
133 if (doMosaic && ZoneManager.bgDelegate != null) {
134
135
136 super.paintComponent(g);
137 } else {
138 if (reverseImage
139 && (Configuration.getBoolean("reverseSide", false) || Configuration
140 .getBoolean("reverseArt", true))) {
141 if (doMosaic) {
142 for (int y = height / picHeight + 1; y-- > 0;) {
143 for (int x = width / picWidth + 1; x-- > 0;) {
144 g.drawImage(getImage(), x * picWidth + picWidth, y * picHeight
145 + picHeight, x * picWidth, y * picHeight, 0, 0,
146 picWidth - 1, picHeight - 1, null);
147 }
148 }
149 } else {
150 g.drawImage(getImage(), width, height, 0, 0, 0, 0, picWidth,
151 picHeight, null);
152 }
153 } else {
154 if (doMosaic) {
155 for (int y = height / picHeight + 1; y-- > 0;) {
156 for (int x = width / picWidth + 1; x-- > 0;) {
157 g.drawImage(getImage(), x * picWidth, y * picHeight, picWidth,
158 picHeight, null);
159 }
160 }
161 } else {
162 g.drawImage(getImage(), 0, 0, width, height, null);
163 }
164 }
165 }
166 } else if (ZoneManager.bgDelegate != null) {
167
168
169 super.paintComponent(g);
170 }
171 }
172
173 /***
174 * return the card at index
175 *
176 * @param index
177 * is the index where is the element
178 * @return the card at the specified index
179 */
180 public final MCard getCard(int index) {
181 return (MCard) getComponent(index);
182 }
183
184 /***
185 * return the last card (top)
186 *
187 * @return the last card
188 */
189 public MCard getTop() {
190 return getCard(0);
191 }
192
193 /***
194 * return the first card (bottom)
195 *
196 * @return the first card
197 */
198 public MCard getBottom() {
199 return getCard(getComponentCount() - 1);
200 }
201
202 /***
203 * Adds the specified card to this zone with the specified constraints at the
204 * specified index. Also notifies the layout manager to add the component to
205 * the this container's layout using the specified constraints object.
206 *
207 * @param card
208 * the card to be added
209 * @param constraints
210 * an object expressing layout constraints for this
211 * @param position
212 * the position in the container's list at which to insert the
213 * component; <code>-1</code> means insert at the end card
214 * @see LayoutManager
215 */
216 protected void add(MCard card, Object constraints, int position) {
217 card.updateZoneVisibility(visibility);
218 card.getMUI().isAutoAlign = true;
219 if (position == -1) {
220 super.add(card, constraints);
221 } else {
222 super.add(card, constraints, position);
223 }
224 updatePanel();
225 }
226
227 /***
228 * Indicates whether this card suits to the specified position code.
229 *
230 * @param card
231 * is the card to locate.
232 * @param position
233 * the matching position code
234 * @return true if this card suits to the specified position code.
235 * @see net.sf.firemox.token.IdPositions#ON_THE_BOTTOM
236 * @see net.sf.firemox.token.IdPositions#ON_THE_TOP
237 */
238 public final boolean isSamePosition(MCard card, int position) {
239 if (getCardCount() == 0) {
240 return false;
241 }
242 if (position == IdPositions.ON_THE_BOTTOM) {
243 return getBottom() == card;
244 }
245 return getCardCount() > position && getCard(position) == card;
246 }
247
248 /***
249 * Return the index of the specified card within this zone
250 *
251 * @param card
252 * the card to search
253 * @return the found index
254 */
255 public Pair<Integer, Integer> getRealIndexOf(MCard card) {
256 int index = super.getComponentZOrder(card);
257 if (index != -1) {
258 return new Pair<Integer, Integer>(index, 0);
259 }
260
261 final Component[] components = getComponents();
262 for (index = components.length; index-- > 0;) {
263 final Component component = components[index];
264 if (component instanceof Container) {
265 final int subIndex = ((Container) component).getComponentZOrder(card);
266 if (subIndex != -1) {
267 return new Pair<Integer, Integer>(index, subIndex);
268 }
269 }
270 }
271 throw new InternalError("" + card + " has not been found in " + this);
272 }
273
274 /***
275 * return the backImage to display in this JPanel
276 *
277 * @return the backImage to display in this JPanel
278 */
279 private Image getImage() {
280 return backImage;
281 }
282
283 /***
284 * return the translated name of this zone
285 *
286 * @return the name of this panel
287 */
288 @Override
289 public String toString() {
290 return LanguageManagerMDB.getString("zone." + getZoneName());
291 }
292
293 /***
294 * This function returns the result of Component#getName() This will return
295 * the untranslated name of this zone. The toString()method returns the
296 * translated name of this zone
297 *
298 * @return the result of Component#getName()
299 * @see #toString()
300 */
301 public String getZoneName() {
302 return getName();
303 }
304
305 /***
306 * return the wallpaper file, return null if current panel is not in wallpaper
307 * mode.
308 *
309 * @return current wallpaper file
310 */
311 String getWallPaperFile() {
312 String getter = "zones." + getZoneName() + "."
313 + (reverseImage ? "up" : "down");
314 return Configuration.getString(getter + ".wallpaper", "");
315 }
316
317 /***
318 * set the new wallpaper to the picture from a specified file. The current
319 * display mode of this panel will be now as "wallpaper"
320 *
321 * @param newFile
322 * the new wallpaper. If null or null sized, wallpaper is disabled
323 */
324 final void setWallPaperFile(String newFile) {
325 String getter = "zones." + getZoneName() + "."
326 + (reverseImage ? "up" : "down");
327 if (newFile != null && newFile.length() > 0) {
328 try {
329 final Image backImage = Picture.loadImage(newFile);
330 picWidth = backImage.getWidth(this);
331 picHeight = backImage.getHeight(this);
332 this.backImage = backImage;
333 Configuration.setProperty(getter + ".wallpaper", newFile);
334 } catch (MalformedURLException e) {
335
336 }
337 } else {
338 Configuration.setProperty(getter + ".wallpaper", "");
339 backImage = null;
340 }
341 }
342
343 /***
344 * Set the new wallpaper for the current game.
345 *
346 * @param inputStream
347 * the input Stream containing the wallpaper configuration.
348 * @throws IOException
349 * If some other I/O error occurs
350 */
351 public void readWallPaperConfiguration(InputStream inputStream)
352 throws IOException {
353 WallpaperTypes type = WallpaperTypes.values()[inputStream.read()];
354 switch (type) {
355 case color:
356 backImage = null;
357 setBackground(new Color(MToolKit.readInt24(inputStream)));
358 break;
359 case watermark:
360
361 break;
362 case picture:
363 doMosaic = false;
364 backImage = MToolKit.readImage(inputStream);
365 picWidth = backImage.getWidth(this);
366 picHeight = backImage.getHeight(this);
367 break;
368 case mosaicPicture:
369 default:
370 doMosaic = true;
371 backImage = MToolKit.readImage(inputStream);
372 picWidth = backImage.getWidth(this);
373 picHeight = backImage.getHeight(this);
374 break;
375 }
376 }
377
378 /***
379 * Send the wallpaper configuration over the given output stream.
380 *
381 * @param out
382 * the output Stream containing the wallpaper configuration.
383 * @throws IOException
384 * If some other I/O error occurs
385 */
386 public void writeWallPaperConfiguration(OutputStream out) throws IOException {
387 if (ZoneManager.bgDelegate != null) {
388 out.write(WallpaperTypes.watermark.ordinal());
389 } else if (backImage != null) {
390 if (doMosaic) {
391 out.write(WallpaperTypes.mosaicPicture.ordinal());
392 } else {
393 out.write(WallpaperTypes.picture.ordinal());
394 }
395
396
397 MToolKit.writeFile(MToolKit.getFile(getWallPaperFile()), out);
398 } else {
399 out.write(WallpaperTypes.color.ordinal());
400 MToolKit.writeInt24(out, getBackground().getRGB());
401 }
402 }
403
404 @Override
405 public void removeAll() {
406 super.removeAll();
407 updatePanel();
408 }
409
410 /***
411 * update this panel in function of it's components
412 */
413 public void updatePanel() {
414
415 doLayout();
416 repaint();
417 }
418
419 /***
420 * Shuffle the zone
421 */
422 public void shuffle() {
423 final List<Component> components = Arrays.asList(getComponents());
424 Collections.shuffle(components, MToolKit.random);
425 removeAll();
426 for (Component component : components) {
427 add(component);
428 }
429 updatePanel();
430 }
431
432 public void mouseReleased(MouseEvent e) {
433 mousePressed(e);
434 }
435
436 public void mouseClicked(MouseEvent e) {
437 if (e.getClickCount() > 1) {
438 for (Component component : getComponents()) {
439 ((AbstractCard) component).getMUI().isAutoAlign = true;
440 }
441 doLayout();
442 }
443 }
444
445 public void mouseEntered(MouseEvent e) {
446
447 }
448
449
450 public void mouseExited(MouseEvent e) {
451
452 }
453
454 /***
455 * Update the "reversed" state of this component.
456 */
457 public void updateReversed() {
458
459 }
460
461 /***
462 * Update the layout of this panel depending on the owner.
463 *
464 * @param panel
465 * the panel to update
466 * @param reverse
467 * is this zone is reversed.
468 */
469 protected static void updateLayouts(JComponent panel, boolean reverse) {
470 if (reverse) {
471 panel.setLayout(new FlowLayout2());
472 } else {
473 panel.setLayout(new FlowLayout(FlowLayout.LEFT));
474 }
475 }
476
477 /***
478 * is called when you click on me
479 *
480 * @param e
481 * is the mouse event
482 */
483 public void mousePressed(MouseEvent e) {
484 PopupManager.instance.mousePressed(e, this);
485 }
486
487 /***
488 * Save wallpaper name, display options and back colors of this panel to a
489 * specified output stream
490 */
491 void saveSettings() {
492 String setter = "zones." + getZoneName() + "."
493 + (reverseImage ? "up" : "down");
494 Configuration.setProperty(setter + ".mosaic", doMosaic);
495 Configuration.setProperty(setter + ".background", getBackground().getRGB());
496 }
497
498 /***
499 * Add a card to this panel. If tag 'returnedCards' is true, this card comes
500 * returned into this zone.
501 *
502 * @param card
503 * the card to add to this zone.
504 */
505 public void remove(AbstractCard card) {
506 super.remove(card);
507 updatePanel();
508 }
509
510 /***
511 * return the number of cards in this panel
512 *
513 * @return the number of cards in this panel
514 */
515 public int getCardCount() {
516 return getComponentCount();
517 }
518
519 /***
520 * Checks all cards corresponding to the specified constraints including
521 * attached cards.
522 *
523 * @param test
524 * applied to count valid cards
525 * @param ability
526 * is the ability owning this test. The card component of this
527 * ability should correspond to the card owning this test too.
528 * @return amount of card matching with the specified test
529 */
530 public int countAllCardsOf(Test test, Ability ability) {
531 int result = 0;
532 for (Component component : getComponents()) {
533 result += ((AbstractCard) component).countAllCardsOf(test, ability, true);
534 }
535 return result;
536 }
537
538 /***
539 * Checks all cards corresponding to the specified constraints including
540 * attached cards.
541 *
542 * @param test
543 * applied to count valid cards
544 * @param ability
545 * is the ability owning this test. The card component of this
546 * ability should correspond to the card owning this test too.
547 * @param limit
548 * is the desired count.
549 * @param canBePreempted
550 * <code>true</code> if the valid targets can be determined before
551 * runtime.
552 * @return amount of cards matching with the specified test. Highest value is
553 * <code>limit</code>.
554 */
555 public int countAllCardsOf(Test test, Ability ability, int limit,
556 boolean canBePreempted) {
557 int result = 0;
558 int index = getCardCount();
559 while (index-- > 0 && result < limit) {
560 result += ((AbstractCard) getComponent(index)).countAllCardsOf(test,
561 ability, canBePreempted);
562 }
563 return result;
564 }
565
566 /***
567 * Checks all cards corresponding to this constraints
568 *
569 * @param test
570 * applied to count valid cards
571 * @param list
572 * the list containing the founded cards
573 * @param ability
574 * is the ability owning this test. The card component of this
575 * ability should correspond to the card owning this test too.
576 */
577 public void checkAllCardsOf(Test test, List<Target> list, Ability ability) {
578 for (Component component : getComponents()) {
579 ((AbstractCard) component).checkAllCardsOf(test, list, ability);
580 }
581 }
582
583 /***
584 * Disable all cards of this zone manager
585 */
586 public void disHighLightAll() {
587 for (Component component : getComponents()) {
588 if (component instanceof Clickable) {
589 ((Clickable) component).disHighLight();
590 }
591 }
592 disHighLight();
593 }
594
595 /***
596 * Disable only this component, not the components of this zone.
597 */
598 public void disHighLight() {
599
600 int id = TableTop.getInstance().tabbedPane.indexOfComponent(superPanel);
601 if (id == -1) {
602 Log.debug("error");
603 throw new InternalError("index=-1; zone=" + this);
604 } else if (TableTop.getInstance().tabbedPane.getBackgroundAt(id) != null) {
605 TableTop.getInstance().tabbedPane.setBackgroundAt(id, null);
606 }
607 }
608
609 /***
610 * Highlight only this component, not the components of this zone. Use instead
611 * the specific highlight color of the desired zone.
612 *
613 * @param color
614 * the color of highlight.
615 */
616 public void highLight(Color color) {
617 TableTop.getInstance().tabbedPane.setSelectedComponent(superPanel);
618 TableTop.getInstance().tabbedPane.setBackgroundAt(
619 TableTop.getInstance().tabbedPane.indexOfComponent(superPanel), color);
620 }
621
622 /***
623 * Remove all cards of this zone
624 */
625 public void reset() {
626 removeAll();
627 visibility = Visibility.HIDDEN;
628 }
629
630 public void setVisibility(Visibility visibility) {
631 if (this.visibility != visibility) {
632 this.visibility = visibility;
633
634 for (Component component : getComponents()) {
635 if (component instanceof MCard) {
636 ((MCard) component).setVisibility(visibility);
637 }
638 }
639 repaint();
640 }
641 }
642
643 /***
644 * Add a card at the bottom of this panel. If tag 'returnedCards' is true,
645 * this card comes returned into this zone.
646 *
647 * @param card
648 * the card to add to this zone.
649 */
650 public void addBottom(MCard card) {
651 add(card, -1);
652 }
653
654 /***
655 * Add a card at the top of this panel. If tag 'returnedCards' is true, this
656 * card comes returned into this zone. Be careful, you can use this function
657 * only if the specified component is not yet in this container.
658 *
659 * @param card
660 * the card to add to this zone.
661 */
662 public void addTop(MCard card) {
663 add(card, 0);
664 }
665
666 /***
667 * Add a card to this panel. If tag 'returnedCards' is true, this card comes
668 * returned into this zone.
669 *
670 * @param card
671 * the card to add to this zone.
672 * @param position
673 * the position index of insertion.
674 */
675 public void add(MCard card, int position) {
676 add(card, null, position);
677 }
678
679 /***
680 * Return player identifier of controller of this zone.
681 *
682 * @return player identifier of controller of this zone.
683 */
684 public int getControllerIdPlayer() {
685 return reverseImage ? 1 : 0;
686 }
687
688 /***
689 * Return controller of this zone.
690 *
691 * @return controller of this zone.
692 */
693 public Player getController() {
694 return StackManager.PLAYERS[getControllerIdPlayer()];
695 }
696
697 /***
698 * Return the zone identifier
699 *
700 * @return the zone identifier
701 */
702 public final int getZoneId() {
703 return idZone;
704 }
705
706 /***
707 * Start the drag and drop management for the given card.
708 *
709 * @param card
710 * The drag and drop component.
711 * @param mousePoint
712 * The drag and drop starting point.
713 * @return <code>true</code> if the drag and drop is managed by this zone.
714 */
715 public boolean startDragAndDrop(MCard card, Point mousePoint) {
716 dragAndDropComponent = card;
717 this.mousePoint = mousePoint;
718 return true;
719 }
720
721 /***
722 * Return <code>true</code> if the given card should be painted as reversed
723 * card.
724 *
725 * @param card
726 * the card to draw.
727 * @return <code>true</code> if the given card should be painted as reversed
728 * card.
729 */
730 public boolean isMustBePaintedReversed(MCard card) {
731 return card.reversed && Configuration.getBoolean("reverseArt", true);
732 }
733
734 /***
735 * Return <code>true</code> if the given card should be painted entirely.
736 *
737 * @param card
738 * the card to draw.
739 * @return <code>true</code> if the given card should be painted entirely.
740 */
741 public boolean isMustBePainted(MCard card) {
742 return true;
743 }
744
745 /***
746 * Is this zone is shared with all players.
747 *
748 * @return <code>true</code> if this zone is shared with all players.
749 */
750 public boolean isShared() {
751 return false;
752 }
753
754 public boolean isVisibleForYou() {
755 return visibility.isVisibleForYou(getController());
756 }
757
758 public boolean isVisibleForOpponent() {
759 return visibility.isVisibleForOpponent(getController());
760 }
761
762 public void increaseFor(Player player, VisibilityChange visibilityChange) {
763 setVisibility(visibility.increaseFor(player, visibilityChange));
764 }
765
766 public void decreaseFor(Player player, VisibilityChange visibilityChange) {
767 setVisibility(visibility.decreaseFor(player, visibilityChange));
768 }
769
770 public Visibility getVisibility() {
771 return visibility;
772 }
773
774 public void restoreVisibility() {
775 if (previousVisibility != null) {
776 restoreVisibility(previousVisibility);
777 }
778 }
779
780 public void restoreVisibility(Visibility visibility) {
781 this.previousVisibility = null;
782 setVisibility(visibility);
783 }
784
785 /***
786 * The previous visibility of this zone.
787 */
788 protected Visibility previousVisibility;
789
790 /***
791 * picture displayed in the panel
792 */
793 protected Image backImage = null;
794
795 /***
796 * The picture's height
797 */
798 private int picHeight;
799
800 /***
801 * The picture's width
802 */
803 private int picWidth;
804
805 /***
806 * Is the background picture is drawn as mosaic. Otherwise, is centered.
807 */
808 boolean doMosaic;
809
810 /***
811 * Are the images are reversed in this zone.
812 */
813 protected boolean reverseImage;
814
815 /***
816 * Indicates all cards of this zone are returned or not. All cards coming into
817 * this zone would be returned or not depending this flag.
818 *
819 * @since 0.80 cards are hidden by default
820 * @since 0.90 use Visibility class instead of boolean
821 */
822 private Visibility visibility = Visibility.PUBLIC;
823
824 /***
825 * the parent scroll pane
826 */
827 public final JScrollPane superPanel;
828
829 /***
830 * The zone identifier.
831 */
832 private final int idZone;
833
834 /***
835 * The drag and drop starting point
836 */
837 public Point mousePoint;
838
839 /***
840 * The drag and drop component.
841 */
842 public MCard dragAndDropComponent = null;
843
844 }