View Javadoc

1   /*
2    *   Magic-Project is a turn based strategy simulator
3    *   Copyright (C) 2003-2007 Fabrice Daugan
4    *
5    *   This program is free software; you can redistribute it and/or modify it 
6    * under the terms of the GNU General Public License as published by the Free 
7    * Software Foundation; either version 2 of the License, or (at your option) any
8    * later version.
9    *
10   *   This program is distributed in the hope that it will be useful, but WITHOUT 
11   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12   * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more 
13   * details.
14   *
15   *   You should have received a copy of the GNU General Public License along  
16   * with this program; if not, write to the Free Software Foundation, Inc., 
17   * 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18   */
19  package net.sf.magicproject;
20  
21  import java.awt.BorderLayout;
22  import java.awt.Color;
23  import java.awt.Component;
24  import java.awt.Dimension;
25  import java.awt.Paint;
26  import java.awt.event.ActionEvent;
27  import java.awt.event.InputEvent;
28  import java.awt.event.KeyEvent;
29  import java.awt.event.MouseEvent;
30  import java.awt.event.WindowEvent;
31  import java.io.FileInputStream;
32  import java.io.IOException;
33  import java.util.ArrayList;
34  import java.util.Collection;
35  import java.util.Collections;
36  import java.util.HashMap;
37  import java.util.List;
38  import java.util.Map;
39  import java.util.TreeMap;
40  
41  import javax.swing.BoxLayout;
42  import javax.swing.JButton;
43  import javax.swing.JDialog;
44  import javax.swing.JFileChooser;
45  import javax.swing.JFrame;
46  import javax.swing.JLabel;
47  import javax.swing.JList;
48  import javax.swing.JMenu;
49  import javax.swing.JMenuBar;
50  import javax.swing.JMenuItem;
51  import javax.swing.JOptionPane;
52  import javax.swing.JPanel;
53  import javax.swing.JScrollPane;
54  import javax.swing.JSeparator;
55  import javax.swing.JTabbedPane;
56  import javax.swing.JTable;
57  import javax.swing.JTextField;
58  import javax.swing.JToggleButton;
59  import javax.swing.JToolBar;
60  import javax.swing.KeyStroke;
61  import javax.swing.ListSelectionModel;
62  import javax.swing.SwingConstants;
63  import javax.swing.border.EtchedBorder;
64  import javax.swing.event.ListSelectionEvent;
65  import javax.swing.event.ListSelectionListener;
66  import javax.swing.event.TableModelEvent;
67  import javax.swing.event.TableModelListener;
68  
69  import net.sf.magicproject.chart.CardColor;
70  import net.sf.magicproject.chart.CardManaCost;
71  import net.sf.magicproject.chart.CardTypes;
72  import net.sf.magicproject.chart.ChartFilter;
73  import net.sf.magicproject.chart.ChartSets;
74  import net.sf.magicproject.chart.IChartKey;
75  import net.sf.magicproject.chart.IDataProvider;
76  import net.sf.magicproject.chart.datasets.Dataset;
77  import net.sf.magicproject.clickable.targetable.card.CardFactory;
78  import net.sf.magicproject.clickable.targetable.card.CardModel;
79  import net.sf.magicproject.clickable.targetable.card.MCard;
80  import net.sf.magicproject.deckbuilder.CardView;
81  import net.sf.magicproject.deckbuilder.ConstraintsChecker;
82  import net.sf.magicproject.deckbuilder.Deck;
83  import net.sf.magicproject.deckbuilder.DeckReader;
84  import net.sf.magicproject.deckbuilder.DeckRules;
85  import net.sf.magicproject.deckbuilder.MdbLoader;
86  import net.sf.magicproject.token.IdCardColors;
87  import net.sf.magicproject.token.IdCommonToken;
88  import net.sf.magicproject.token.IdConst;
89  import net.sf.magicproject.tools.Configuration;
90  import net.sf.magicproject.tools.Converter;
91  import net.sf.magicproject.tools.FileFilterPlus;
92  import net.sf.magicproject.tools.MCardCompare;
93  import net.sf.magicproject.tools.MSaveDeck;
94  import net.sf.magicproject.tools.MToolKit;
95  import net.sf.magicproject.tools.Picture;
96  import net.sf.magicproject.ui.HireListener;
97  import net.sf.magicproject.ui.MCardTableModel;
98  import net.sf.magicproject.ui.MListModel;
99  import net.sf.magicproject.ui.RefreshableAdd;
100 import net.sf.magicproject.ui.UIHelper;
101 import net.sf.magicproject.ui.i18n.LanguageManager;
102 import net.sf.magicproject.ui.i18n.LanguageManagerMDB;
103 import net.sf.magicproject.ui.wizard.About;
104 
105 import org.jfree.chart.ChartPanel;
106 import org.jfree.chart.JFreeChart;
107 
108 /***
109  * This class is a deck builder compatible with Magic-project deck format. Can
110  * also export/import other formats.
111  * 
112  * @author <a href="mailto:fabdouglas@users.sourceforge.net">Fabrice Daugan </a>
113  * @author <a href="mailto:goldeneyemdk@users.sourceforge.net">Sebastien Genete
114  *         </a>
115  * @author Jan Blaha for deck loader and statistics
116  * @since 0.2 2003/12/06, settings are loaded.
117  * @since 0.2 you can load a deck file.
118  * @since 0.2 you can load a specified mdb file (is MagicProject card list
119  *        file).
120  * @since 0.3 2003/12/12, you can save a deck file.
121  * @since 0.3 new deck feature.
122  * @since 0.4 sort mdb file feature for optimized load.
123  * @since 0.4 deck are saved with alphabitacally sort.
124  * @since 0.5 statistics feature added.
125  * @since 0.81 deck converter added.
126  * @since 0.83 cleaned code, deck constraints form added.
127  * @since 0.93 pie chart added, color filtering
128  * @since 0.94 use of JTable instead of JList
129  */
130 public final class DeckBuilder extends AbstractMainForm implements
131 		ListSelectionListener, RefreshableAdd, IDataProvider, TableModelListener {
132 
133 	/***
134 	 * Creates new form DeckBuilder
135 	 */
136 	private DeckBuilder() {
137 		super("DeckBuilder");
138 		try {
139 			setIconImage(Picture.loadImage(IdConst.IMAGES_DIR + "deckbuilder.gif"));
140 		} catch (Exception e) {
141 			// IGNORING
142 		}
143 
144 		// Load settings
145 		loadSettings();
146 
147 		// Init components
148 		constraintsChecker = new ConstraintsChecker();
149 		constraintsChecker.setBorder(new EtchedBorder());
150 		final JScrollPane constraintsCheckerScroll = new JScrollPane(
151 				constraintsChecker);
152 		MToolKit.addOverlay(constraintsCheckerScroll);
153 		constraintsCheckerScroll.setMinimumSize(new Dimension(200, 270));
154 
155 		final JMenuItem newItem = UIHelper.buildMenu("menu_db_new", 'n', this);
156 		newItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_N,
157 				InputEvent.CTRL_MASK));
158 
159 		final JMenuItem loadItem = UIHelper.buildMenu("menu_db_load", 'o', this);
160 		loadItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_O,
161 				InputEvent.CTRL_MASK));
162 
163 		final JMenuItem saveAsItem = UIHelper
164 				.buildMenu("menu_db_saveas", 'a', this);
165 		saveAsItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F12, 0));
166 
167 		final JMenuItem saveItem = UIHelper.buildMenu("menu_db_save", 's', this);
168 		saveItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S,
169 				InputEvent.CTRL_MASK));
170 
171 		final JMenuItem quitItem = UIHelper.buildMenu("menu_db_exit", this);
172 		quitItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F4,
173 				InputEvent.ALT_MASK));
174 
175 		final JMenuItem deckConstraintsItem = UIHelper.buildMenu(
176 				"menu_db_constraints", 'c', this);
177 		deckConstraintsItem.setAccelerator(KeyStroke
178 				.getKeyStroke(KeyEvent.VK_F3, 0));
179 
180 		final JMenuItem aboutItem = UIHelper
181 				.buildMenu("menu_help_about", 'a', this);
182 		aboutItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F1,
183 				InputEvent.SHIFT_MASK));
184 
185 		final JMenuItem convertDCK = UIHelper
186 				.buildMenu("menu_convert_DCK_MP", this);
187 
188 		final JMenu mainMenu = UIHelper.buildMenu("menu_file");
189 		mainMenu.add(newItem);
190 		mainMenu.add(loadItem);
191 		mainMenu.add(saveAsItem);
192 		mainMenu.add(saveItem);
193 		mainMenu.add(new JSeparator());
194 		mainMenu.add(quitItem);
195 
196 		super.optionMenu = new JMenu("Options");
197 
198 		final JMenu convertMenu = UIHelper.buildMenu("menu_convert");
199 		convertMenu.add(convertDCK);
200 
201 		final JMenuItem helpItem = UIHelper.buildMenu("menu_help_help", 'h', this);
202 		helpItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F1, 0));
203 
204 		final JMenu helpMenu = new JMenu("?");
205 		helpMenu.add(helpItem);
206 		helpMenu.add(deckConstraintsItem);
207 		helpMenu.add(aboutItem);
208 
209 		final JMenuBar menuBar = new JMenuBar();
210 		menuBar.add(mainMenu);
211 		initAbstractMenu();
212 		menuBar.add(optionMenu);
213 		menuBar.add(convertMenu);
214 		menuBar.add(helpMenu);
215 		setJMenuBar(menuBar);
216 		addWindowListener(this);
217 
218 		// Build the panel containing amount of available cards
219 		JLabel amountLeft = new JLabel("<html>0/?", SwingConstants.RIGHT);
220 
221 		// Build the left list
222 		allListModel = new MListModel<MCardCompare>(amountLeft, false);
223 		leftList = new JList(allListModel);
224 		leftList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
225 		leftList.setLayoutOrientation(JList.VERTICAL);
226 		leftList.getSelectionModel().addListSelectionListener(this);
227 		leftList.addMouseListener(this);
228 		leftList.setVisibleRowCount(10);
229 
230 		// Init the texfield containing the amount to add
231 		qtyTxt = new JTextField("1");
232 		qtyTxt.setMaximumSize(new Dimension(20, 32010));
233 		qtyTxt.setPreferredSize(new Dimension(20, 16));
234 
235 		// Build the "Add" button
236 		addButton = new JButton(LanguageManager.getString("db_add"));
237 		addButton.setMnemonic('a');
238 
239 		// Build the panel containing the selected card name
240 		cardNameTxt = new JTextField();
241 		new HireListener(cardNameTxt, addButton, this, leftList);
242 
243 		// Build the panel containing : "Add" amount, name and "Add" button
244 		final JPanel addPanel = new JPanel(null);
245 		addPanel.setLayout(new BoxLayout(addPanel, BoxLayout.X_AXIS));
246 		addPanel.add(cardNameTxt);
247 		addPanel.add(addButton);
248 		addPanel.add(qtyTxt);
249 		addPanel.setMaximumSize(new Dimension(32010, 26));
250 
251 		final JScrollPane listScrollerLeft = new JScrollPane(leftList);
252 		MToolKit.addOverlay(listScrollerLeft);
253 
254 		// Build the left panel containing : list, available amount, "Add" panel
255 		final JPanel srcPanel = new JPanel(null);
256 		srcPanel.setLayout(new BoxLayout(srcPanel, BoxLayout.Y_AXIS));
257 		srcPanel.add(listScrollerLeft);
258 		srcPanel.add(amountLeft);
259 		srcPanel.add(addPanel);
260 		srcPanel.setMinimumSize(new Dimension(200, 0));
261 
262 		// Populate available cards and fill them into the left list
263 		DeckReader.readAvailableCards(allListModel);
264 
265 		// build some Pie Charts and a panel to display it
266 		initSets();
267 		datasets = new ChartSets();
268 		final int PREFERED_WIDTH = CardView.instance.getPreferredSize().width;
269 		final JTabbedPane tabbedPane = new JTabbedPane();
270 		tabbedPane.setPreferredSize(new Dimension(PREFERED_WIDTH, 300));
271 		tabbedPane.setMinimumSize(tabbedPane.getPreferredSize());
272 		tabbedPane.setMaximumSize(new Dimension(PREFERED_WIDTH, 32010));
273 		for (ChartFilter filter : ChartFilter.values()) {
274 			Dataset dataSet = filter.createDataSet(this);
275 			final JFreeChart chart = new JFreeChart(null, null, filter.createPlot(
276 					dataSet, painterMapper.get(filter)), false);
277 			datasets.addDataSet(filter, dataSet);
278 			ChartPanel pieChartPanel = new ChartPanel(chart, true);
279 			tabbedPane.add(pieChartPanel, filter.getTitle());
280 		}
281 
282 		cardPictureName = new JLabel("(no selected card)", SwingConstants.LEFT);
283 
284 		// The tollbar for color filtering
285 		toolBar = new JToolBar();
286 		final JButton clearButton = UIHelper.buildButton("clear");
287 		clearButton.addActionListener(this);
288 		toolBar.add(clearButton);
289 		final JToggleButton toggleColorlessButton = new JToggleButton(UIHelper
290 				.getTbsIcon("mana/colorless/small/" + MdbLoader.unknownSmlMana), true);
291 		toggleColorlessButton.setActionCommand("0");
292 		toggleColorlessButton.addActionListener(this);
293 		toolBar.add(toggleColorlessButton);
294 		for (int index = 1; index < IdCardColors.CARD_COLOR_NAMES.length; index++) {
295 			final JToggleButton toggleButton = new JToggleButton(
296 					UIHelper.getTbsIcon("mana/colored/small/"
297 							+ MdbLoader.coloredSmlManas[index]), true);
298 			toggleButton.setActionCommand(String.valueOf(index));
299 			toggleButton.addActionListener(this);
300 			toolBar.add(toggleButton);
301 		}
302 		getContentPane().add(toolBar, BorderLayout.NORTH);
303 
304 		// Build the panel containing amount of available cards
305 		JLabel rightAmount = new JLabel("0/?", SwingConstants.RIGHT);
306 
307 		// Build the right list
308 		rightListModel = new MCardTableModel(new MListModel<MCardCompare>(
309 				rightAmount, true));
310 		rightListModel.addTableModelListener(this);
311 		rightList = new JTable(rightListModel);
312 		rightList.setShowGrid(false);
313 		rightList.setTableHeader(null);
314 		rightList.getSelectionModel().addListSelectionListener(this);
315 		rightList.getColumnModel().getColumn(1).setMaxWidth(25);
316 		rightList.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);
317 		rightList.setMinimumSize(new Dimension(200, 0));
318 
319 		// Build the right panel containing : list, available amount, constraints
320 		final JScrollPane listScrollerRight = new JScrollPane(rightList);
321 		MToolKit.addOverlay(listScrollerRight);
322 		final JPanel destPanel = new JPanel();
323 		destPanel.setLayout(new BoxLayout(destPanel, BoxLayout.Y_AXIS));
324 		destPanel.add(listScrollerRight);
325 		destPanel.add(constraintsCheckerScroll);
326 		destPanel.add(rightAmount);
327 
328 		// Group the panels
329 		final JPanel viewCard = new JPanel(null);
330 		viewCard.add(cardPictureName);
331 		viewCard.add(CardView.instance);
332 		viewCard.add(tabbedPane);
333 		viewCard.setLayout(new BoxLayout(viewCard, BoxLayout.Y_AXIS));
334 
335 		final JPanel mainPanel = new JPanel(null);
336 		mainPanel.add(srcPanel);
337 		mainPanel.add(viewCard);
338 		mainPanel.add(destPanel);
339 		mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.X_AXIS));
340 
341 		// Add the main panel
342 		getContentPane().add(mainPanel, BorderLayout.CENTER);
343 
344 		// Load the last deck file
345 		if (Configuration.getString("decks.deck(0)", "").length() > 0) {
346 			loadDeck(Configuration.getString("decks.deck(0)"), rightListModel
347 					.getCards());
348 		} else {
349 			isNew = true;
350 		}
351 
352 		// Size this frame
353 		getRootPane().setPreferredSize(new Dimension(650, 700));
354 		getRootPane().setMinimumSize(new Dimension(650, 700));
355 		pack();
356 	}
357 
358 	/***
359 	 * @param string
360 	 * @param deckListModel2
361 	 * @param qtyListModel2
362 	 * @param builder
363 	 */
364 	private void loadDeck(String fileName, MListModel<MCardCompare> names) {
365 		// empty file name or deck?
366 		if (fileName == null || fileName.length() == 0) {
367 			return;
368 		}
369 
370 		// Append the deck name
371 		setTitle("DeckBuilder".concat(" : "
372 				+ fileName.substring(fileName.lastIndexOf("/") + 1)));
373 
374 		names.clear();
375 		datasets.removeAll();
376 		try {
377 			currentDeck = DeckReader.getDeck(this, fileName);
378 			if (currentDeck == null) {
379 				// Deck loading failure
380 				return;
381 			}
382 
383 			// Validate this deck
384 			DeckReader.validateDeck(this, currentDeck, (String) null);
385 
386 			// Update the card models of resolved cards
387 			final FileInputStream dbStream = MdbLoader.resetMdb();
388 			for (MCardCompare cardCompare : currentDeck.getCards()) {
389 				names.add(cardCompare);
390 				dbStream.getChannel().position(cardCompare.getMdbOffset());
391 				datasets.addCard(CardFactory.getCardModel(cardCompare.getName(),
392 						dbStream), cardCompare.getAmount());
393 			}
394 			constraintsChecker.setDeck(currentDeck);
395 			updateChecker();
396 		} catch (IOException e) {
397 			e.printStackTrace();
398 		}
399 	}
400 
401 	private void setCard(MCardCompare card) {
402 		CardView.instance.setCard(card);
403 		cardPictureName.setText(card.getName());
404 	}
405 
406 	public void valueChanged(ListSelectionEvent e) {
407 		if (!e.getValueIsAdjusting()) {
408 			if (e.getSource() == leftList.getSelectionModel()) {
409 				if (leftList.getSelectedIndex() == -1) {
410 					// No selection, disable fire button.
411 					addButton.setEnabled(false);
412 				} else {
413 					// Selection, enable the fire button.
414 					addButton.setEnabled(true);
415 					setCard((MCardCompare) leftList.getSelectedValue());
416 					cardNameTxt.setText(cardPictureName.getText());
417 				}
418 			} else if (e.getSource() == rightList.getSelectionModel()) {
419 				setCard(((MCardTableModel) rightList.getModel()).getCards()
420 						.getElementAt(rightList.getSelectedRow()));
421 			}
422 		}
423 	}
424 
425 	/***
426 	 * Exit the Application
427 	 */
428 	private void exitForm() {
429 		if (consoleMode) {
430 			System.exit(0);
431 		} else {
432 			setVisible(false);
433 		}
434 	}
435 
436 	/***
437 	 * Load the deckbuilder from the command line.
438 	 * 
439 	 * @param args
440 	 *          the command line arguments
441 	 */
442 	public static void main(String[] args) {
443 		consoleMode = true;
444 		JFrame.setDefaultLookAndFeelDecorated(true);
445 		JDialog.setDefaultLookAndFeelDecorated(true);
446 		autoLoad();
447 	}
448 
449 	/***
450 	 * Load the deckbuilder from Magic class.
451 	 */
452 	public static void loadFromMagic() {
453 		consoleMode = false;
454 		autoLoad();
455 	}
456 
457 	/***
458 	 * Load the deckbuilder
459 	 */
460 	private static void autoLoad() {
461 		if (deckbuilderForm != null) {
462 			// previous instance is running
463 			deckbuilderForm.setVisible(true);
464 			return;
465 		}
466 		deckbuilderForm = new DeckBuilder();
467 		deckbuilderForm.setLocation(
468 				(deckbuilderForm.getToolkit().getScreenSize().width - 650) / 2,
469 				(deckbuilderForm.getToolkit().getScreenSize().height - 700) / 2);
470 
471 		deckbuilderForm.setVisible(true);
472 	}
473 
474 	public void refreshAddComponent(boolean unitary) {
475 		String name = leftList.getSelectedValue().toString();
476 		int qty = 1;
477 		if (!unitary) {
478 			try {
479 				qty = Integer.parseInt(qtyTxt.getText());
480 			} catch (Exception e1) {
481 				JOptionPane.showMessageDialog(deckbuilderForm,
482 						"Malformed integer for quantity", "Internal Error",
483 						JOptionPane.ERROR_MESSAGE);
484 				return;
485 			}
486 		}
487 		// User didn't type in a unique name...
488 		if (name.length() == 0) {
489 			cardNameTxt.requestFocusInWindow();
490 			return;
491 		}
492 
493 		int index = rightListModel.getCards().indexOf(name);
494 		final MCardCompare card;
495 		if (index == -1) {
496 			// no selection, so insert at beginning
497 			int allIndex = allListModel.indexOf(name);
498 			assert allIndex != -1;
499 			card = allListModel.get(allIndex).clone();
500 			card.add(qty);
501 			currentDeck.addCard(card);
502 			rightListModel.getCards().insertElementAt(card, 0);
503 			rightListModel.fireTableRowsInserted(rightListModel.getCards().getSize(),
504 					rightListModel.getCards().getSize());
505 
506 			// Reset the text field.
507 			cardNameTxt.requestFocusInWindow();
508 			cardNameTxt.setText("");
509 		} else { // add after the selected item
510 			card = rightListModel.getCards().elementAt(index);
511 			card.add(qty);
512 			rightListModel.fireTableCellUpdated(index, 1);
513 		}
514 
515 		// Update the card models of resolved cards
516 		final FileInputStream dbStream = MdbLoader.resetMdb();
517 		datasets.addCard(getCardModel(name, dbStream), qty);
518 
519 		// deck list has been modifed
520 		modifiedSinceSave = true;
521 	}
522 
523 	/***
524 	 * Update the checker and repaint the right list.
525 	 */
526 	private void updateChecker() {
527 		constraintsChecker.updateCheckers();
528 		rightList.repaint();
529 	}
530 
531 	/***
532 	 * Return the associated cardModel to the given real card name. The card is
533 	 * searched in the inputstream starting from a given offset.
534 	 * 
535 	 * @param realCardName
536 	 *          the card name as written in the mdb stream.
537 	 * @param dbFile
538 	 *          the fileinput stream of current mdb.
539 	 * @return the corresponding CardModel instance, or null if the card has not
540 	 *         been matched.
541 	 */
542 	private CardModel getCardModel(String realCardName, FileInputStream dbFile) {
543 		try {
544 			int index = allListModel.indexOf(realCardName);
545 			MCardCompare card = allListModel.get(index);
546 			dbFile.getChannel().position(card.getMdbOffset());
547 			return CardFactory.getCardModel(realCardName, dbFile);
548 		} catch (IOException e) {
549 			return null;
550 		}
551 	}
552 
553 	/***
554 	 * check if the current deck has been modified since it has been saved. If the
555 	 * current deck has been modified, we ask to the user if he want to save it
556 	 * before loading another.
557 	 * 
558 	 * @return true if the user has choosen to save the deck before contuning and
559 	 *         if the save has success. False otherwise
560 	 */
561 	private boolean verifyModification() {
562 		if (!modifiedSinceSave) {
563 			// no modification since the last save
564 			return true;
565 		}
566 
567 		// ask to the user if we save the current deck before loading another
568 		Object[] options = { "Ok", "No", "Cancel" };
569 		switch (JOptionPane.showOptionDialog(deckbuilderForm,
570 				"Current deck has been modified, save it before loading another?",
571 				"Save changes", JOptionPane.YES_NO_CANCEL_OPTION,
572 				JOptionPane.WARNING_MESSAGE, null, options, null)) {
573 		case 0:
574 			// YES part
575 			return saveCurrentDeck();
576 		case 1:
577 			// NO part
578 			return true;
579 		default:
580 			// CANCEL part
581 			return false;
582 		}
583 	}
584 
585 	/***
586 	 * Save the current deck. If the current deck has not been saved once, user
587 	 * have to specify the file's name of this new deck
588 	 * 
589 	 * @return true if the current deck has been correctly saved. false otherwise.
590 	 */
591 	private boolean saveCurrentDeck() {
592 		if (isNew || Configuration.getString("decks.deck(0)", "").length() == 0) {
593 			if (MToolKit.getDeckFile(this, JFileChooser.SAVE_DIALOG) == null)
594 				return false;
595 		}
596 		return MSaveDeck.saveDeck(Configuration.getString("decks.deck(0)"),
597 				rightListModel.getCards(), deckbuilderForm);
598 	}
599 
600 	/***
601 	 * load the settings of jDecBuilder First byte is the Version Then :
602 	 * 
603 	 * @since 0.2 the mdb file
604 	 * @since 0.2 the last deck file used
605 	 * @since 0.7 the settings are read from .properties file
606 	 */
607 	private void loadSettings() {
608 		// read the mdb file
609 		if (consoleMode || MToolKit.tbsName == null) {
610 			/*
611 			 * The jDeckbuilder has not been launched from Magic form, or no TBS is
612 			 * currently defined, we load the last used TBS.
613 			 */
614 			LanguageManager.initLanguageManager(Configuration.getString("language",
615 					"auto"));
616 			optionMenu = new JMenu(LanguageManager.getString("options"));
617 			optionMenu.setMnemonic('o');
618 			initAbstractMenu();
619 			final String tbsName = Configuration.getString("lastTBS");
620 			if (tbsName != null && tbsName.length() != 0) {
621 				setMdb(tbsName);
622 			}
623 		}
624 		// read the last used deck file
625 	}
626 
627 	/***
628 	 * Invoked when an action occurs.
629 	 * 
630 	 * @param e
631 	 *          attached event
632 	 */
633 	public void actionPerformed(ActionEvent e) {
634 		String command = e.getActionCommand();
635 		if ("menu_help_about".equals(command)) {
636 			new About(deckbuilderForm).setVisible(true);
637 		} else if ("menu_db_exit".equals(command)) {
638 			exitForm();
639 		} else if ("menu_db_new".equals(command)) {
640 			if (!verifyModification()) {
641 				return;
642 			}
643 			setTitle("DeckBuilder");
644 			rightListModel.getCards().clear();
645 			datasets.removeAll();
646 			modifiedSinceSave = false;
647 			currentDeck.getCards().clear();
648 			updateChecker();
649 			isNew = true;
650 		} else if ("menu_convert_DCK_MP".equals(command)) {
651 			/*
652 			 * Convert an entire directory from a format to MP one
653 			 */
654 			try {
655 				Converter.convertDCK(MToolKit.showDialogFile(
656 						"Choose a directory of DCK to convert", null, null, 'o', null,
657 						FILTER_DECK, this, JFileChooser.OPEN_DIALOG,
658 						JFileChooser.DIRECTORIES_ONLY).getCanonicalPath());
659 			} catch (IOException e1) {
660 				JOptionPane.showMessageDialog(this,
661 						"Error occured reading the specified directory", "File problem",
662 						JOptionPane.ERROR_MESSAGE);
663 			} catch (Exception e1) {
664 				// cancel of user;
665 			}
666 		} else if ("menu_db_constraints".equals(command)) {
667 			// Show the deck constraints applied on the current TBS
668 			new DeckRules(this).setVisible(true);
669 		} else if ("menu_help_help".equals(command)) {
670 			/*
671 			 * This method is invoked when user has chosen to see the help file. <br>
672 			 * TODO documentation is not yet done for this form
673 			 */
674 			JOptionPane.showMessageDialog(deckbuilderForm,
675 					"Sorry, no documentation available for deck builder",
676 					"Negative yet implemented", JOptionPane.INFORMATION_MESSAGE);
677 		} else if ("menu_db_load".equals(command)) {
678 			if (verifyModification()) {
679 				String deckFile = MToolKit.getDeckFile(this, JFileChooser.OPEN_DIALOG);
680 				if (deckFile != null) {
681 					loadDeck(deckFile, rightListModel.getCards());
682 					modifiedSinceSave = false;
683 					isNew = false;
684 				}
685 			}
686 		} else if ("menu_db_saveas".equals(command)) {
687 			String deckFile = MToolKit.getDeckFile(this, JFileChooser.SAVE_DIALOG);
688 			if (deckFile != null) {
689 				MSaveDeck
690 						.saveDeck(deckFile, rightListModel.getCards(), deckbuilderForm);
691 				isNew = false;
692 				modifiedSinceSave = false;
693 			}
694 		} else if ("menu_db_save".equals(command)) {
695 			saveCurrentDeck();
696 			isNew = false;
697 			modifiedSinceSave = false;
698 		} else {
699 			final MListModel<MCardCompare> model = ((MListModel<MCardCompare>) leftList
700 					.getModel());
701 			if ("clear".equals(command)) {
702 				// Reset the filters
703 				for (Component component : toolBar.getComponents()) {
704 					if (component instanceof JToggleButton) {
705 						((JToggleButton) component).setSelected(true);
706 					}
707 				}
708 				model.addAll(model.removedDelegate);
709 			} else {
710 				// Filter selection
711 				final int colorIndex = Integer.valueOf(command);
712 				if (colorIndex >= 0) {
713 					// The filter color has been found, update the available cards
714 					final FileInputStream dbStream = MdbLoader.resetMdb();
715 					final JToggleButton colorButton = (JToggleButton) toolBar
716 							.getComponent(colorIndex + 1);
717 					if (colorButton.isSelected()) {
718 						// "Add" mode
719 						final List<MCardCompare> toAdd = new ArrayList<MCardCompare>();
720 						for (MCardCompare cardCompare : model.removedDelegate) {
721 							try {
722 								dbStream.getChannel().position(cardCompare.getMdbOffset());
723 								final CardModel cardModel = CardFactory.getCardModel(
724 										cardCompare.getName(), dbStream);
725 								if (colorIndex == 0) {
726 									if (cardModel.getIdColor() == 0) {
727 										toAdd.add(cardCompare);
728 									}
729 								} else if ((cardModel.getIdColor() & IdCardColors.CARD_COLOR_VALUES[colorIndex]) == IdCardColors.CARD_COLOR_VALUES[colorIndex]) {
730 									toAdd.add(cardCompare);
731 								}
732 							} catch (IOException e1) {
733 								e1.printStackTrace();
734 							}
735 						}
736 						model.addAll(toAdd);
737 					} else {
738 						// "Remove" mode
739 						final List<MCardCompare> toRemove = new ArrayList<MCardCompare>();
740 						for (MCardCompare cardCompare : model.delegate) {
741 							try {
742 								dbStream.getChannel().position(cardCompare.getMdbOffset());
743 								final CardModel cardModel = CardFactory.getCardModel(
744 										cardCompare.getName(), dbStream);
745 								if (colorIndex == 0) {
746 									if (cardModel.getIdColor() == 0) {
747 										toRemove.add(cardCompare);
748 									}
749 								} else if ((cardModel.getIdColor() & IdCardColors.CARD_COLOR_VALUES[colorIndex]) == IdCardColors.CARD_COLOR_VALUES[colorIndex]) {
750 									toRemove.add(cardCompare);
751 								}
752 							} catch (IOException e1) {
753 								e1.printStackTrace();
754 							}
755 						}
756 						model.removeAll(toRemove);
757 					}
758 				}
759 				leftList.repaint();
760 			}
761 		}
762 	}
763 
764 	@Override
765 	protected void setToolKitMdb(String tbsName) {
766 		super.setToolKitMdb(tbsName);
767 		DeckReader.readAvailableCards(allListModel);
768 	}
769 
770 	public void mouseClicked(MouseEvent e) {
771 		if (e.getSource() == leftList && e.getClickCount() == 2) {
772 			refreshAddComponent(true);
773 		} else if (e.getSource() == cardNameTxt) {
774 			cardNameTxt.selectAll();
775 		}
776 	}
777 
778 	private static final FileFilterPlus FILTER_DECK = new FileFilterPlus("dck",
779 			"DCK : Shandalar deck file");
780 
781 	private final MListModel<MCardCompare> allListModel;
782 
783 	private final MCardTableModel rightListModel;
784 
785 	private static boolean modifiedSinceSave = false;
786 
787 	private static DeckBuilder deckbuilderForm = null;
788 
789 	private final JButton addButton;
790 
791 	/***
792 	 * The right list : cards in the deck.
793 	 */
794 	private final JTable rightList;
795 
796 	/***
797 	 * The left list : available cards.
798 	 */
799 	private final JList leftList;
800 
801 	private final JTextField qtyTxt;
802 
803 	private final JTextField cardNameTxt;
804 
805 	/***
806 	 * The label displayed above the card picture
807 	 */
808 	private final JLabel cardPictureName;
809 
810 	/***
811 	 * indicates if jdeckbuilder was launched from console
812 	 */
813 	public static boolean consoleMode = false;
814 
815 	@Override
816 	public void windowClosing(WindowEvent e) {
817 		exitForm();
818 	}
819 
820 	/***
821 	 * Are we working with a new deck?
822 	 */
823 	private boolean isNew = false;
824 
825 	private final ChartSets datasets;
826 
827 	private Map<ChartFilter, Map<Integer, IChartKey>> keyMapper;
828 
829 	private Map<ChartFilter, Map<Integer, Paint>> painterMapper;
830 
831 	private void initSets() {
832 		final Map<Integer, Paint> painterColorMapper = new TreeMap<Integer, Paint>();
833 		for (int index = IdCardColors.CARD_COLOR_VALUES.length; index-- > 0;) {
834 			painterColorMapper.put(IdCardColors.CARD_COLOR_VALUES[index],
835 					IdCardColors.CARD_COLOR[index]);
836 		}
837 		painterColorMapper.put(ChartFilter.DEFAULT_KEY, Color.YELLOW.darker()
838 				.darker());
839 		painterMapper = new HashMap<ChartFilter, Map<Integer, Paint>>();
840 		painterMapper.put(ChartFilter.color, painterColorMapper);
841 
842 		final Map<Integer, IChartKey> keyColorMapper = new TreeMap<Integer, IChartKey>();
843 		for (int index = 0; index < IdCardColors.CARD_COLOR_VALUES.length; index++) {
844 			keyColorMapper.put(IdCardColors.CARD_COLOR_VALUES[index], new CardColor(
845 					LanguageManager.getString(IdCardColors.CARD_COLOR_NAMES[index]),
846 					IdCardColors.CARD_COLOR_VALUES[index]));
847 		}
848 		keyColorMapper.put(ChartFilter.DEFAULT_KEY, CardColor.UNKNOW_COLOR);
849 
850 		final Map<Integer, IChartKey> keyTypeMapper = new TreeMap<Integer, IChartKey>();
851 		for (int index = 0; index < CardFactory.exportedTypeNames.length; index++) {
852 			keyTypeMapper.put(CardFactory.exportedTypeValues[index], new CardTypes(
853 					LanguageManagerMDB.getString(CardFactory.exportedTypeNames[index]),
854 					CardFactory.exportedTypeValues[index]));
855 		}
856 		keyTypeMapper.put(ChartFilter.DEFAULT_KEY, CardTypes.UNKNOW_TYPE);
857 
858 		keyMapper = new HashMap<ChartFilter, Map<Integer, IChartKey>>();
859 		keyMapper.put(ChartFilter.color, keyColorMapper);
860 		keyMapper.put(ChartFilter.type, keyTypeMapper);
861 		keyMapper.put(ChartFilter.property, new TreeMap<Integer, IChartKey>());
862 		keyMapper.put(ChartFilter.manacost, new TreeMap<Integer, IChartKey>());
863 	}
864 
865 	private Map<Integer, IChartKey> getMapper(ChartFilter filter) {
866 		return keyMapper.get(filter);
867 	}
868 
869 	public List<IChartKey> getKeys(ChartFilter filter) {
870 		List<IChartKey> list = new ArrayList<IChartKey>(getMapper(filter).values());
871 		Collections.sort(list);
872 		return list;
873 	}
874 
875 	public Collection<IChartKey> getKeys(CardModel cardModel, ChartFilter filter) {
876 		Collection<IChartKey> result = new ArrayList<IChartKey>();
877 		switch (filter) {
878 		case color:
879 			int idColor = cardModel.getIdColor();
880 			if (idColor == 0)
881 				result.add(getMapper(filter).get(IdCardColors.CARD_COLOR_VALUES[0]));
882 			else {
883 				for (IChartKey value : getMapper(filter).values()) {
884 					if (value.getIntegerKey() != 0
885 							&& MCard.hasIdColor(idColor, value.getIntegerKey())) {
886 						result.add(value);
887 					}
888 				}
889 			}
890 			break;
891 		case type:
892 			int idCard = cardModel.getIdCard();
893 			for (IChartKey value : getMapper(filter).values()) {
894 				if (MCard.hasIdCard(idCard, value.getIntegerKey()))
895 					result.add(value);
896 			}
897 			break;
898 		case manacost:
899 			int total = 0;
900 			for (int index = IdCommonToken.WHITE_MANA + 1; index-- > 0;) {
901 				total += cardModel.getStaticRegisters()[index];
902 			}
903 			result.add(new CardManaCost(total));
904 			break;
905 		default:
906 			// TODO add the the other filters
907 		}
908 		if (result.isEmpty()) {
909 			IChartKey defaultKey = getMapper(filter).get(ChartFilter.DEFAULT_KEY);
910 			if (defaultKey != null)
911 				result.add(getMapper(filter).get(ChartFilter.DEFAULT_KEY));
912 		}
913 		return result;
914 	}
915 
916 	public void tableChanged(TableModelEvent e) {
917 		try {
918 			MCardCompare card = rightListModel.getCards().get(e.getFirstRow());
919 			if (card.getAmount() == 0) {
920 				currentDeck.removeCard(card);
921 			}
922 		} catch (Throwable t) {
923 			// IGNORE
924 		}
925 		updateChecker();
926 	}
927 
928 	/***
929 	 * The toolbar containg some filters.
930 	 */
931 	private final JToolBar toolBar;
932 
933 	private Deck currentDeck;
934 
935 	private final ConstraintsChecker constraintsChecker;
936 
937 }