Codebase list granule / HEAD src / CardDeck.cpp
HEAD

Tree @HEAD (Download .tar.gz)

CardDeck.cpp @HEADraw · history · blame

// -*- c++ -*-
//------------------------------------------------------------------------------
//                              CardDeck.cpp
//------------------------------------------------------------------------------
// $Id: CardDeck.cpp,v 1.32 2008/08/04 02:54:31 vlg Exp $
//
//  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: Apr 24 2004
//------------------------------------------------------------------------------

#include "Granule.h"
#include "CardDeck.h"
#include "Granule-main.h"
#include "Intern.h"

#include <sstream>
#include <fstream>
#include <algorithm>

#include <glib.h>

void
CardDeck::
set_name (int idx_)
{
	std::ostringstream os;
	os << "Card Box " << idx_;
	m_cdname = os.str ();
	m_index = idx_;
}

bool
CardDeck::
erase (VCard* vcard_)
{
	trace_with_mask("CardDeck::erase(VCard)",GUITRACE|DECK);

	cardlist_iterator p = m_cards.begin ();

	while (p != m_cards.end ()) 
	{
		if ((*p) == vcard_) {
			erase (p);
			return true;
		}
		p++;
	}
	return false;
}

void 
CardDeck::
play_card (SideSelection side_, VDeck::cardlist_iterator& iter_)
{
	CardRef* cref = dynamic_cast<CardRef*> (*iter_);
	Assure_exit (cref);

	if (!Deck::audio_playback_active ()) {
		cref->get_deck ().play_card (side_, iter_);
	}
	else {
		DL ((GRAPP,"Already playing an audio clip.\n"));
	}
}

void 
CardDeck::
remove_duplicates ()
{
	trace_with_mask("CardDeck::remove_duplicates",GUITRACE);

	CardRef* cr_p;
	CardRef* cr_q;

	cardlist_iterator p;
	cardlist_iterator q;

	DL ((GRAPP,"cards before = %d\n", m_cards.size ()));
	std::sort (m_cards.begin (), m_cards.end (), Compare_CardRefs ());

	p = m_cards.begin ();
	if (p == m_cards.end ()) {
		goto done;
	}
	q = p + 1;

	while (q != m_cards.end ()) 
	{
		cr_p = dynamic_cast<CardRef*> (*p);
		cr_q = dynamic_cast<CardRef*> (*q);
		Assure_exit (cr_p && cr_q);

		if (cr_p->get_card().get_id() == cr_q->get_card().get_id() &&
			cr_p->get_reversed() == cr_q->get_reversed ())
		{
			delete (*q);
			m_cards.erase (q);
			q = p;
		}
		else {
			p = q;
		}
		q++;
	}
  done:
	DL ((GRAPP,"cards after = %d\n", m_cards.size ()));
}

/** 
 * Report the ratio of cards already reviewed vs. total and percentage-wise.
 *
 * The size of the deck is a constant determined at the moment when
 * DeckPlayer starts playing it. It can change when a card is moved
 * to the upper or lower box. It can also remain constant when
 * the card is sent to the end of the deck for repetition (fringe
 * cases). The important concept here is that we want to show the user
 * how many cards he has seen so far and how many remains to be seen
 * at least once.
 *
 * We, therefore, consider initial Deck size and count our advance
 * through the Deck (first sweep) ourselves.
 */
void
CardDeck::
get_progress (float& pct_done_, std::string& msg_, int /* idx_ */)
{
	trace_with_mask("CardDeck::get_progress",GUITRACE);
	std::ostringstream os;

	DL((GRAPP,"m_init_size = %d; m_progress_idx = %d\n",
		m_init_size, m_progress_idx));

	if (m_init_size == 0) {
		pct_done_ = 0;
		msg_ = "";
		return;
	}
	if (m_progress_idx == m_init_size) {
		pct_done_ = 1.0;
		os << m_progress_idx << "/" << m_init_size;
		return;
	}

	m_progress_idx++;
	os << (m_progress_idx) << "/" << m_init_size;
	msg_ = os.str ();

	if (m_cards.size () <= 1) {
		pct_done_ = 0;
	}
	else {
		pct_done_ = (m_progress_idx) * 1.0 / m_init_size;
	}

}

void
CardDeck::
reset_progress () 
{ 
	trace_with_mask("CardDeck::reset_progress",GUITRACE|DECK);

	m_init_size = size ();
	m_progress_idx = 0;
}

void
CardDeck::
clear ()
{
	trace_with_mask("CardDeck::clear",GUITRACE);
	
	cardlist_iterator p = m_cards.begin ();
	while (p != m_cards.end ()) {
		delete (*p);
		p++;
	}
	m_cards.clear ();
}

void
CardDeck::
shuffle_subsets ()
{
	trace_with_mask("CardDeck::shuffle_subsets",GUITRACE);

	if (m_cards.size () <= 1) {
		return;
	}
	/** Algorithm
	 */
	bool inset = false;			// are we inside the subset?
	cardlist_iterator first;	
	cardlist_iterator second;	
	first = second = m_cards.begin ();

	while (second + 1 != m_cards.end ()) {
		second++;
		if ((**first) == (**second)) {
			inset = true;
			continue;
		}
		else {
			if (inset) {
				random_shuffle (first, second);
				inset = false;
			}
			first = second;
		}
	}
	if (inset) {
		random_shuffle (first, second+1);
	}
}

void
CardDeck::
dump_to_file ()
{
	VCard* vcard;
	std::ostringstream fname;

	fname << "/tmp/cardbox" << get_index () << ".dat";

	::unlink (fname.str ().c_str ());
	std::ofstream fdump (fname.str ().c_str ());

	if (!fdump) {
		DL ((DECK,"Failed to open dump file \"%s\"\n", fname.str ().c_str ()));
		return;
	}
	DL ((DECK,"Dumping to file \"%s\"\n", fname.str ().c_str ()));

	cardlist_const_iterator iter = begin ();
	while (iter != end ()) {
		vcard = *iter;
		fdump << Granule::locale_from_utf8 (
			Granule::trim_multiline (vcard->get_question ()));
		fdump << " ";
		fdump << vcard->get_expiration ().fmtString ().c_str ();
		fdump << '\n';
		iter++;
	}
	fdump.close ();
}

/** For every card in the file, create a subdirectory
	with the name of the first letter of the word in the card
	and put the word in it.
*/
void 
CardDeck::
export_sound_bits (const std::string& path_)
{
	trace_with_mask("CardDeck::export_sound_bits",GUITRACE);

#if !defined (IS_WIN32)
	string word;
	string to;
	string from;
	string from_path;
	vector<string> v;
	
	from_path = Granule::filename_to_utf8 (CONFIG->get_snd_archive_path ());
	from_path += G_DIR_SEPARATOR_S;

	cardlist_const_iterator citer = m_cards.begin ();
	while (citer != m_cards.end ()) {
		word = Granule::locale_from_utf8 ((*citer)->get_question ());
		ASSA::Utils::split (word.c_str (), v);
		if (v.size () > 1) {
			word = v[0] != "to" ? v[0] : v[1];
		}

		from = from_path + word[0] + G_DIR_SEPARATOR_S + word + ".wav";
		if (!g_file_test (from.c_str (), G_FILE_TEST_IS_REGULAR)) {
			citer++;
			continue;
		}

		to = path_ + G_DIR_SEPARATOR_S + word[0];
		if (!g_file_test (to.c_str (), G_FILE_TEST_IS_DIR)) 
		{
#if defined (IS_WIN32)
			::mkdir (to.c_str ());
#else
			::mkdir (to.c_str (), 0755);
#endif
		}
		to += G_DIR_SEPARATOR_S + word + ".wav";

		/** Uber-fast file copy
		 */
		std::ifstream ifs (from.c_str (), std::ios::in  | std::ios::binary);
		std::ofstream ofs (to.c_str (),   std::ios::out | std::ios::binary);

        ofs << ifs.rdbuf();

		ifs.close ();
		ofs.close ();

		citer++;
	}
#endif	// !def IS_WIN32
}