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