// -*- c++ -*-
// Generated by assa-genesis
//------------------------------------------------------------------------------
// $Id: DeckManager.cpp,v 1.54 2008/05/23 03:01:39 vlg Exp $
//------------------------------------------------------------------------------
// DeckManager.cpp
//------------------------------------------------------------------------------
// Copyright (c) 2004,2006 by Vladislav Grinchenko
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version
// 2 of the License, or (at your option) any later version.
//------------------------------------------------------------------------------
//
// Date : Tue Feb 3 13:44:27 EST 2004
//
//------------------------------------------------------------------------------
#include <sstream>
#include <gtkmm/fileselection.h>
#include "Granule-main.h"
#include "Granule.h"
#include "Deck.h"
#include "DeckPlayer.h"
#include "DeckManager.h"
#include "DeckList.h"
#include "MainWindow.h"
#include "GrappConf.h"
#include "CardRef.h"
#include "CardBox.h"
#include "FileOpenDialog.h"
#include "FileSaveDialog.h"
// i18n macros - always last
#include "Intern.h"
static const char* DECK_REQEUST = "Select a Deck from the DeckList first";
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Toolbar callbacks
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
DeckManager::
DeckManager () : m_untitled_count (1)
{
trace_with_mask("DeckManager::DeckManager", GUITRACE);
}
DeckManager::
~DeckManager ()
{
trace_with_mask("DeckManager::~DeckManager", GUITRACE);
}
void
DeckManager::
new_deck_cb ()
{
trace_with_mask("DeckManager::new_deck_cb", GUITRACE);
std::ostringstream os;
os << "Untitled" << m_untitled_count++ << DECK_FILE_EXT;
Deck* deck = new Deck (os.str (), NEW_DECK);
on_deck_loaded (deck, LOAD_OK);
}
void
DeckManager::
play_deck_cb ()
{
trace_with_mask("DeckManager::play_deck_cb", GUITRACE);
Deck* selected = DECKLIST->get_selected ();
if (selected == NULL) {
Gtk::MessageDialog w (_(DECK_REQEUST), false, MESSAGE_WARNING);
w.run ();
return;
}
DeckPlayerVectorIter iter = m_players.begin ();
while (iter != m_players.end ())
{
if ((*iter)->get_deck () == selected)
{
(*iter)->run (*MAINWIN);
DECKLIST->unselect ();
/**
* This is clearly a hack and should not have
* been done from here.
* TODO: fix it.
*/
CARDBOX->mask_out_unused_boxes ();
return;
}
iter++;
}
}
/** If deck is not loaded, try to load the deck first.
Search it for the card in the deck and return it if found.
*/
CardRef*
DeckManager::
lookup_card (long id_, const string& path_)
{
trace_with_mask("DeckManager::lookup_card", GUITRACE);
Deck* deck = NULL;
VCard* card = NULL;
DeckList::iterator iter = DECKLIST->begin ();
while (iter != DECKLIST->end ()) {
deck = DECKLIST->get_deck (iter);
if (deck->get_name () == path_) {
break;
}
iter++;
}
/** Deck is not found
*/
if (iter == DECKLIST->end ()) {
deck = new Deck (INVALID_DECK);
if (deck->load (path_) < 0) {
DL ((GRAPP,"Failed to load deck \"%s\"\n", path_.c_str ()));
/** release resources?
delete deck;
*/
return (NULL);
}
}
if ((card = deck->find_card_by_id (id_)) != NULL) {
return (new CardRef (*card, *deck, CARDBOX->sched_db ().efactor ()));
}
else {
DL ((GRAPP,"Cound not find card id=%d in deck %s\n",
id_, path_.c_str ()));
}
return (NULL);
}
/** First, search the DeckList to see if we already have the deck
loaded.
*/
void
DeckManager::
open_deck_cb ()
{
trace_with_mask("DeckManager::open_deck_cb", GUITRACE);
FileOpenDialog f (_("Select Deck File"), MAINWIN,
"DeckFile", "*" DECK_FILE_EXT);
f.set_select_multiple ();
if (f.run () != Gtk::RESPONSE_OK) {
return;
}
f.hide ();
/** Handle multiple selection
*/
Glib::ustring fname;
DeckList::iterator dl_iter;
std::vector<Glib::ustring> answer_list = f.get_filenames ();
std::vector<Glib::ustring>::const_iterator citer = answer_list.begin ();
while (citer != answer_list.end ()) {
fname = (*citer);
dl_iter = DECKLIST->begin ();
while (dl_iter != DECKLIST->end ()) {
if (fname == DECKLIST->get_deck (dl_iter)->get_name ()) {
Gtk::MessageDialog msg (_("The Deck you have selected\n"
"is already loaded."),
false, MESSAGE_INFO);
msg.run ();
}
dl_iter++;
}
Deck* deck = new Deck (INVALID_DECK);
if (deck->load (fname) == 0) {
CONFIG->set_filesel_path (fname);
}
citer++;
}
}
/** Insert cards from a deck to the one currently selected.
*/
void
DeckManager::
insert_deck_cb ()
{
trace_with_mask("DeckManager::insert_deck_cb", GUITRACE);
Deck* selected = DECKLIST->get_selected ();
if (selected == NULL)
{
Gtk::MessageDialog w (_(DECK_REQEUST), false, MESSAGE_WARNING);
w.run ();
}
else {
FileOpenDialog f (_("Select a Deck to Insert"), MAINWIN,
"DeckFile", "*" DECK_FILE_EXT);
if (f.run () == Gtk::RESPONSE_OK)
{
selected->insert (f.get_filename ());
CONFIG->set_filesel_path (f.get_filename ());
DeckPlayerVectorIter iter = m_players.begin ();
while (iter != m_players.end ())
{
if (selected == dynamic_cast<Deck*>((*iter)->get_deck ())) {
m_players.erase (iter);
break;
}
iter++;
}
m_players.push_back (new DeckPlayer (*selected));
}
}
}
/** Import cards from CSV-formatted file
*/
void
DeckManager::
import_deck_cb ()
{
trace_with_mask("DeckManager::import_deck_cb", GUITRACE);
Deck* selected = DECKLIST->get_selected ();
if (selected == NULL) {
Gtk::MessageDialog w (_(DECK_REQEUST), false, MESSAGE_WARNING);
w.run ();
return;
}
FileOpenDialog f (_("Select File To Import from"), MAINWIN);
/** If response is OK and conversion was successful,
* recreate the DeckPlayer (which is a View of the Deck).
* Find the old one and nuke it. Then create a new DeckPlayer
*/
if (f.run () == Gtk::RESPONSE_OK)
{
f.hide ();
if (selected->import_from_csv (f.get_filename ()) == false) {
return;
}
CONFIG->set_filesel_path (f.get_filename ());
DeckPlayerVectorIter iter = m_players.begin ();
while (iter != m_players.end ()) {
if (selected == dynamic_cast<Deck*>((*iter)->get_deck ())) {
m_players.erase (iter);
break;
}
iter++;
}
m_players.push_back (new DeckPlayer (*selected));
/** This is a rather sour spot based purely on speculations:
*
* When importing from a CSV file, if the second card has a
* UTF-8 2 byte string, then going from the first card to the
* second without first flipping the first card kills the
* application with:
*
* glibmm-CRITICAL **:
* unhandled exception (type Glib::Error) in signal handler:
* domain: g_convert_error
* code : 1
* what : Invalid byte sequence in conversion input
*
* If, however, the first card is flipped, then going to the
* next does no harm. I suspect, some Glib events are queued and
* not processed.
*
* Flushing the event queue seems to cure this problem.
*/
while (Gtk::Main::instance ()->events_pending ()) {
Gtk::Main::instance ()->iteration ();
}
}
}
/** Export cards to CSV-formatted file
*/
void
DeckManager::
export_deck_cb ()
{
trace_with_mask("DeckManager::export_deck_cb", GUITRACE);
Deck* selected = DECKLIST->get_selected ();
if (selected == NULL) {
Gtk::MessageDialog w (_(DECK_REQEUST), false, MESSAGE_WARNING);
w.run ();
return;
}
FileSaveDialog f (_("Select File To Export to"), MAINWIN);
std::string::size_type idx;
std::string fname = GRANULE->get_basename (selected->get_name ().c_str ());
if ((idx = fname.find (DECK_FILE_EXT)) != string::npos) {
fname.replace (idx, idx+3, ".csv");
}
else {
fname += ".csv";
}
f.set_current_name (fname);
if (f.run () == Gtk::RESPONSE_OK) {
selected->export_to_csv (f.get_filename ());
CONFIG->set_filesel_path (f.get_filename ());
}
}
void
DeckManager::
save_deck_cb ()
{
trace_with_mask("DeckManager::save_deck_cb", GUITRACE);
Deck* selected = DECKLIST->get_selected ();
if (selected != NULL) {
if (selected->needs_renaming ()) {
save_as_deck_cb ();
}
else {
selected->save ();
}
}
else {
Gtk::MessageDialog w (_(DECK_REQEUST), false, MESSAGE_WARNING);
w.run ();
}
}
void
DeckManager::
save_all_deck_cb ()
{
trace_with_mask("DeckManager::save_all_deck_cb", GUITRACE);
bool save_all = false;
DeckEditStatus dstat;
DeckList::iterator iter = DECKLIST->begin ();
string fname;
DL((GRAPP,"Number of Decks in DeckList=%d\n", DECKLIST->size ()));
while (iter != DECKLIST->end ())
{
dstat = DECKLIST->get_deck_status (iter);
if (DECKLIST->get_deck (iter)->size () == 0 && dstat == CLEAN_DECK)
{
goto next; // skip empty decks
}
if (DECKLIST->deck_needs_renaming (iter))
{
FileSaveDialog f (_("Save Untitled Deck"), MAINWIN,
"DeckFile", "*" DECK_FILE_EXT);
fname = GRANULE->get_basename (
DECKLIST->get_deck (iter)->get_name ().c_str ());
f.set_current_name (fname);
if (f.run () == Gtk::RESPONSE_OK)
{
fname = f.get_filename ();
if (fname.find (DECK_FILE_EXT) == string::npos) {
fname += DECK_FILE_EXT;
}
DECKLIST->get_deck (iter)->save_as (fname);
CONFIG->set_filesel_path (f.get_filename ());
}
}
else {
if (dstat == MODIFIED_DECK)
{
if (save_all) {
DECKLIST->save (iter);
goto next;
}
Gtk::MessageDialog qm (
"Deck \"" + DECKLIST->get_name (iter) +
"\" has been modified."
"\nWould you like to save it?",
false,
Gtk::MESSAGE_QUESTION,
Gtk::BUTTONS_NONE,
true);
qm.add_button ("Save", Gtk::RESPONSE_OK );
qm.add_button ("Save All", Gtk::RESPONSE_YES);
qm.add_button ("Don't Save", Gtk::RESPONSE_NO );
int ret = qm.run ();
if (ret == Gtk::RESPONSE_OK || ret == Gtk::RESPONSE_YES)
{
DECKLIST->save (iter);
if (ret == Gtk::RESPONSE_YES) {
save_all = true;
}
}
}
}
next:
iter++;
}
}
void
DeckManager::
save_as_deck_cb ()
{
trace_with_mask("DeckManager::save_as_deck_cb", GUITRACE);
Deck* selected = DECKLIST->get_selected ();
string ext (DECK_FILE_EXT);
if (selected != NULL)
{
FileSaveDialog f (_("Save Deck As"), MAINWIN,
"DeckFile", "*" DECK_FILE_EXT);
std::string::size_type idx;
std::string fname = GRANULE->get_basename(selected->get_name().c_str());
if ((idx = fname.find (ext)) != string::npos) {
fname.replace (idx, idx+3, "(copy).dkf");
}
else {
fname += "(copy).dkf";
}
f.set_current_name (fname);
if (f.run () == Gtk::RESPONSE_OK) {
fname = f.get_filename ();
if (fname.find (ext) == string::npos) {
fname += ext;
}
selected->save_as (fname);
DECKLIST->refresh_selected_name ();
CONFIG->set_filesel_path (f.get_filename ());
}
}
else {
Gtk::MessageDialog w (_(DECK_REQEUST), false, MESSAGE_WARNING);
w.run ();
}
}
/**
Add currently selected Deck in the DeckList (TreeView)
to the CardDeck idx_.
This is the callback from Deck->Add_to->{1,..,5} menu.
*/
void
DeckManager::
add_deck_to_box (int idx_)
{
trace_with_mask("DeckManager::add_deck_to_box", GUITRACE);
Deck* selected = DECKLIST->get_selected ();
if (selected) {
CARDBOX->add_deck_to_box (idx_, selected);
}
else {
Gtk::MessageDialog w (_(DECK_REQEUST), false, MESSAGE_WARNING);
w.run ();
}
}
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Deck management functions
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
void
DeckManager::
on_deck_loaded (Deck* deck_, DeckLoadStatus status_)
{
trace_with_mask("DeckManager::on_deck_loaded", GUITRACE);
if (status_ == LOAD_FAILED) {
string msg ("Failed to load deck file\n");
msg += "\"" + deck_->get_name () + "\"!\n";
msg += "Check your file syntax with\n";
msg += "\"xmllint --valid <fname>\"";
Gtk::MessageDialog emsg (msg, false, MESSAGE_ERROR);
emsg.run ();
delete deck_;
}
else {
DECKLIST->push_back (deck_);
m_players.push_back (new DeckPlayer (*deck_));
}
}
void
DeckManager::
repaint_deck_players ()
{
trace_with_mask("DeckManager::repain_deck_players", GUITRACE);
DeckPlayerVectorIter iter = m_players.begin ();
while (iter != m_players.end ()) {
(*iter)->repaint ();
iter++;
}
}