<commit_before>//=====================================================================//
/*! @file
    @brief  NET sample (GR-KAEDE) メイン @n
			Copyright 2017 Kunihito Hiramatsu
    @author 平松邦仁 (hira@rvf-rc45.net)
*/
//=====================================================================//
#include "common/renesas.hpp"
#include "common/cmt_io.hpp"
#include "common/fifo.hpp"
#include "common/sci_io.hpp"
#include "common/rspi_io.hpp"
#include "common/sdc_io.hpp"

#include "GR/core/ethernet.hpp"
#include "GR/core/ethernet_server.hpp"

namespace {

	class cmt_task {

		void (*task_10ms_)();

		volatile unsigned long millis_;
		volatile unsigned long delay_;
		volatile uint32_t millis10x_;
		volatile uint32_t cmtdiv_;

	public:
		cmt_task() : task_10ms_(nullptr),
			millis_(0), delay_(0), millis10x_(0), cmtdiv_(0) { }

		void operator() () {
			++millis_;
			++cmtdiv_;
			if(cmtdiv_ >= 10) {
				if(task_10ms_ != nullptr) (*task_10ms_)();
				cmtdiv_ = 0;
				++millis10x_;
			}
			if(delay_) {
				--delay_;
			}

		}

		void set_task_10ms(void (*task)(void)) {
			task_10ms_ = task;
		}

		void sync_100hz()
		{
			volatile uint32_t tmp = millis10x_;
			while(tmp == millis10x_) ;
		}

		volatile unsigned long get_millis() const { return millis_; }
		volatile unsigned long get_delay() const { return delay_; }

		void set_delay(volatile unsigned long n) { delay_ = n; }
	};

	typedef device::cmt_io<device::CMT0, cmt_task> CMT0;
	CMT0	cmt0_;

	typedef utils::fifo<uint8_t, 2048> BUFFER;
	typedef device::sci_io<device::SCI7, BUFFER, BUFFER> SCI;
	SCI		sci_;

	// SDC 用　SPI 定義（RSPI）
	typedef device::rspi_io<device::RSPI> SPI;
	typedef device::PORT<device::PORTC, device::bitpos::B4> SDC_SELECT;	///< カード選択信号
	typedef device::NULL_PORT  SDC_POWER;	///< カード電源制御（常に電源ＯＮ）
	typedef device::PORT<device::PORTB, device::bitpos::B7> SDC_DETECT;	///< カード検出
	SPI		spi_;

	typedef utils::sdc_io<SPI, SDC_SELECT, SDC_POWER, SDC_DETECT> SDC;
	SDC		sdc_(spi_, 20000000);

	typedef utils::rtc_io RTC;
	RTC		rtc_;

	net::ethernet ethernet_;
	net::ethernet_server server_(ethernet_);
}

extern "C" {

	// Ethernet driver の割り込みタスク
	void INT_Excep_ICU_GROUPAL1(void);

	//-----------------------------------------------------------------//
	/*!
		@brief  システム・文字出力
		@param[in]	ch	文字
	*/
	//-----------------------------------------------------------------//
	void sci_putch(char ch)
	{
		sci_.putch(ch);
	}

	//-----------------------------------------------------------------//
	/*!
		@brief  システム・文字列出力
		@param[in]	s	文字列
	*/
	//-----------------------------------------------------------------//
	void sci_puts(const char* s)
	{
		sci_.puts(s);
	}

	//-----------------------------------------------------------------//
	/*!
		@brief  システム・文字入力
		@return	文字
	*/
	//-----------------------------------------------------------------//
	char sci_getch(void)
	{
		return sci_.getch();
	}

	//-----------------------------------------------------------------//
	/*!
		@brief  システム・文字列長の取得
		@return	文字列長
	*/
	//-----------------------------------------------------------------//
	uint16_t sci_length(void)
	{
		return sci_.recv_length();
	}

	//-----------------------------------------------------------------//
	/*!
		@brief  GMT 時間の取得
		@return GMT 時間
	*/
	//-----------------------------------------------------------------//
	time_t get_time()
	{
		time_t t = 0;
		rtc_.get_time(t);
		return t;
	}

	//-----------------------------------------------------------------//
	/*!
		@brief	FatFs へ初期化関数を提供
		@param[in]	drv		Physical drive nmuber (0)
		@return ステータス
	 */
	//-----------------------------------------------------------------//
	DSTATUS disk_initialize(BYTE drv) {
		return sdc_.at_mmc().disk_initialize(drv);
	}

	//-----------------------------------------------------------------//
	/*!
		@brief	FatFs へステータスを提供
		@param[in]	drv		Physical drive nmuber (0)
	 */
	//-----------------------------------------------------------------//
	DSTATUS disk_status(BYTE drv) {
		return sdc_.at_mmc().disk_status(drv);
	}

	//-----------------------------------------------------------------//
	/*!
		@brief	FatFs へリード・セクターを提供
		@param[in]	drv		Physical drive nmuber (0)
		@param[out]	buff	Pointer to the data buffer to store read data
		@param[in]	sector	Start sector number (LBA)
		@param[in]	count	Sector count (1..128)
		@return リザルト
	 */
	//-----------------------------------------------------------------//
	DRESULT disk_read(BYTE drv, BYTE* buff, DWORD sector, UINT count) {
		return sdc_.at_mmc().disk_read(drv, buff, sector, count);
	}

	//-----------------------------------------------------------------//
	/*!
		@brief	FatFs へライト・セクターを提供
		@param[in]	drv		Physical drive nmuber (0)
		@param[in]	buff	Pointer to the data to be written
		@param[in]	sector	Start sector number (LBA)
		@param[in]	count	Sector count (1..128)
		@return リザルト
	 */
	//-----------------------------------------------------------------//
	DRESULT disk_write(BYTE drv, const BYTE* buff, DWORD sector, UINT count) {
		return sdc_.at_mmc().disk_write(drv, buff, sector, count);
	}

	//-----------------------------------------------------------------//
	/*!
		@brief	FatFs へI/O コントロールを提供
		@param[in]	drv		Physical drive nmuber (0)
		@param[in]	ctrl	Control code
		@param[in]	buff	Buffer to send/receive control data
		@return リザルト
	 */
	//-----------------------------------------------------------------//
	DRESULT disk_ioctl(BYTE drv, BYTE ctrl, void* buff) {
		return sdc_.at_mmc().disk_ioctl(drv, ctrl, buff);
	}

	//-----------------------------------------------------------------//
	/*!
		@brief	FatFs へ時間を提供
		@return FatFs 時間
	 */
	//-----------------------------------------------------------------//
	DWORD get_fattime(void) {
		auto t = get_time();
		return utils::str::get_fattime(t);
	}

	//-----------------------------------------------------------------//
	/*!
		@brief	UTF-8 から ShiftJIS への変換
		@param[in]	src	UTF-8 文字列ソース
		@param[out]	dst	ShiftJIS 文字列出力
	 */
	//-----------------------------------------------------------------//
	void utf8_to_sjis(const char* src, char* dst) {
		utils::str::utf8_to_sjis(src, dst);
	}

	//-----------------------------------------------------------------//
	/*!
		@brief	NET で必要なミリ秒タイマー
	 */
	//-----------------------------------------------------------------//
	unsigned long millis(void)
	{
		return cmt0_.at_task().get_millis();
	}

	//-----------------------------------------------------------------//
	/*!
		@brief	NET で必要なミリ秒ディレイ
	 */
	//-----------------------------------------------------------------//
	void delay(unsigned long ms)
	{
		cmt0_.at_task().set_delay(ms);
		while(cmt0_.at_task().get_delay() != 0) ;
	}

	//-----------------------------------------------------------------//
	/*!
		@brief	NET で必要な１０ミリ秒タスク登録
		@param[in]	task	１０ミリ秒毎に呼ばれるタスク
	 */
	//-----------------------------------------------------------------//
	void set_task_10ms(void (*task)(void)) {
		cmt0_.at_task().set_task_10ms(task);
	}
}

int main(int argc, char** argv);

int main(int argc, char** argv)
{
	device::PORT3::PCR.B5 = 1; // P35(NMI) pull-up

	device::SYSTEM::PRCR = 0xA50B;	// クロック、低消費電力、関係書き込み許可

	device::SYSTEM::MOSCWTCR = 9;	// 1ms wait
	// メインクロック強制発振とドライブ能力設定
	device::SYSTEM::MOFCR = device::SYSTEM::MOFCR.MODRV2.b(0b10)
						  | device::SYSTEM::MOFCR.MOFXIN.b(1);
	device::SYSTEM::MOSCCR.MOSTP = 0;		// メインクロック発振器動作
	while(device::SYSTEM::OSCOVFSR.MOOVF() == 0) asm("nop");

	// Base Clock 12MHz
	// PLLDIV: 1/1, STC: 16 倍(198MHz)
	device::SYSTEM::PLLCR = device::SYSTEM::PLLCR.PLIDIV.b(0) |
							device::SYSTEM::PLLCR.STC.b(0b011111);
	device::SYSTEM::PLLCR2.PLLEN = 0;			// PLL 動作
	while(device::SYSTEM::OSCOVFSR.PLOVF() == 0) asm("nop");

	device::SYSTEM::SCKCR = device::SYSTEM::SCKCR.FCK.b(2)		// 1/2 (198/4=48)
						  | device::SYSTEM::SCKCR.ICK.b(1)		// 1/2 (198/2=96)
						  | device::SYSTEM::SCKCR.BCK.b(2)		// 1/2 (198/4=48)
						  | device::SYSTEM::SCKCR.PCKA.b(1)		// 1/2 (198/2=96)
						  | device::SYSTEM::SCKCR.PCKB.b(2)		// 1/4 (198/4=48)
						  | device::SYSTEM::SCKCR.PCKC.b(2)		// 1/4 (198/4=48)
						  | device::SYSTEM::SCKCR.PCKD.b(2);	// 1/4 (198/4=48)
	device::SYSTEM::SCKCR2 = device::SYSTEM::SCKCR2.UCK.b(0b0011) | 1;  // USB Clock: 1/4 (198/4=48)
	device::SYSTEM::SCKCR3.CKSEL = 0b100;	///< PLL 選択

	{  // タイマー設定、１０００Ｈｚ（１ｍｓ）
		uint8_t int_level = 1;
		cmt0_.start(1000, int_level);
	}

	{  // SCI 設定
		uint8_t int_level = 2;
		sci_.start(115200, int_level);
	}

	// SD カード・クラスの初期化
	sdc_.start();

	{  // インサーネット開始
		device::power_cfg::turn(device::peripheral::ETHERC0);
		device::port_map::turn(device::peripheral::ETHERC0);

		set_interrupt_task(INT_Excep_ICU_GROUPAL1, static_cast<uint32_t>(device::icu_t::VECTOR::GROUPAL1));

		ethernet_.start();

		static const uint8_t mac[6] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };

		if(ethernet_.begin(mac) == 0) {
			utils::format("Ethernet Fail: begin (DHCP)...\n");
			utils::format("SetIP: ");
			net::ip_address ipa(192, 168, 3, 20);
			ethernet_.begin(mac, ipa);
		} else {
			utils::format("DHCP: ");
		}

#if 0
		utils::format("SetIP: ");
			net::ip_address ipa(192, 168, 1, 10);
			ethernet_.begin(mac, ipa);
		}
		ethernet_.localIP().print();
		utils::format("\n");
#endif

		server_.begin(80);
		utils::format("Start server: ");
		ethernet_.get_local_ip().print();
		utils::format("  port(%d)\n") % static_cast<int>(server_.get_port());
	}

	uint32_t cnt = 0;
	while(1) {
		cmt0_.at_task().sync_100hz();

		ethernet_.service();

		++cnt;
		if(cnt >= 30) {
			cnt = 0;
		}
		device::PORTC::PDR.B0 = 1; // GR-KAEDE LED0 output
		device::PORTC::PODR.B0 = (cnt < 10) ? 0 : 1;
	}
}
<commit_msg>update http server conection<commit_after>//=====================================================================//
/*! @file
    @brief  NET sample (GR-KAEDE) メイン @n
			Copyright 2017 Kunihito Hiramatsu
    @author 平松邦仁 (hira@rvf-rc45.net)
*/
//=====================================================================//
#include "common/renesas.hpp"
#include "common/cmt_io.hpp"
#include "common/fifo.hpp"
#include "common/sci_io.hpp"
#include "common/rspi_io.hpp"
#include "common/sdc_io.hpp"

#include "GR/core/ethernet.hpp"
#include "GR/core/ethernet_server.hpp"
#include "GR/core/ethernet_client.hpp"

namespace {

	class cmt_task {

		void (*task_10ms_)();

		volatile unsigned long millis_;
		volatile unsigned long delay_;
		volatile uint32_t millis10x_;
		volatile uint32_t cmtdiv_;

	public:
		cmt_task() : task_10ms_(nullptr),
			millis_(0), delay_(0), millis10x_(0), cmtdiv_(0) { }

		void operator() () {
			++millis_;
			++cmtdiv_;
			if(cmtdiv_ >= 10) {
				if(task_10ms_ != nullptr) (*task_10ms_)();
				cmtdiv_ = 0;
				++millis10x_;
			}
			if(delay_) {
				--delay_;
			}

		}

		void set_task_10ms(void (*task)(void)) {
			task_10ms_ = task;
		}

		void sync_100hz()
		{
			volatile uint32_t tmp = millis10x_;
			while(tmp == millis10x_) ;
		}

		volatile unsigned long get_millis() const { return millis_; }
		volatile unsigned long get_delay() const { return delay_; }

		void set_delay(volatile unsigned long n) { delay_ = n; }
	};

	typedef device::cmt_io<device::CMT0, cmt_task> CMT0;
	CMT0	cmt0_;

	typedef utils::fifo<uint8_t, 2048> BUFFER;
	typedef device::sci_io<device::SCI7, BUFFER, BUFFER> SCI;
	SCI		sci_;

	// SDC 用　SPI 定義（RSPI）
	typedef device::rspi_io<device::RSPI> SPI;
	typedef device::PORT<device::PORTC, device::bitpos::B4> SDC_SELECT;	///< カード選択信号
	typedef device::NULL_PORT  SDC_POWER;	///< カード電源制御（常に電源ＯＮ）
	typedef device::PORT<device::PORTB, device::bitpos::B7> SDC_DETECT;	///< カード検出
	SPI		spi_;

	typedef utils::sdc_io<SPI, SDC_SELECT, SDC_POWER, SDC_DETECT> SDC;
	SDC		sdc_(spi_, 20000000);

	typedef utils::rtc_io RTC;
	RTC		rtc_;

	net::ethernet ethernet_;
	net::ethernet_server server_(ethernet_);
	net::ethernet_client client_(ethernet_);
}

extern "C" {

	// Ethernet driver の割り込みタスク
	void INT_Excep_ICU_GROUPAL1(void);

	//-----------------------------------------------------------------//
	/*!
		@brief  システム・文字出力
		@param[in]	ch	文字
	*/
	//-----------------------------------------------------------------//
	void sci_putch(char ch)
	{
		sci_.putch(ch);
	}

	//-----------------------------------------------------------------//
	/*!
		@brief  システム・文字列出力
		@param[in]	s	文字列
	*/
	//-----------------------------------------------------------------//
	void sci_puts(const char* s)
	{
		sci_.puts(s);
	}

	//-----------------------------------------------------------------//
	/*!
		@brief  システム・文字入力
		@return	文字
	*/
	//-----------------------------------------------------------------//
	char sci_getch(void)
	{
		return sci_.getch();
	}

	//-----------------------------------------------------------------//
	/*!
		@brief  システム・文字列長の取得
		@return	文字列長
	*/
	//-----------------------------------------------------------------//
	uint16_t sci_length(void)
	{
		return sci_.recv_length();
	}

	//-----------------------------------------------------------------//
	/*!
		@brief  GMT 時間の取得
		@return GMT 時間
	*/
	//-----------------------------------------------------------------//
	time_t get_time()
	{
		time_t t = 0;
		rtc_.get_time(t);
		return t;
	}

	//-----------------------------------------------------------------//
	/*!
		@brief	FatFs へ初期化関数を提供
		@param[in]	drv		Physical drive nmuber (0)
		@return ステータス
	 */
	//-----------------------------------------------------------------//
	DSTATUS disk_initialize(BYTE drv) {
		return sdc_.at_mmc().disk_initialize(drv);
	}

	//-----------------------------------------------------------------//
	/*!
		@brief	FatFs へステータスを提供
		@param[in]	drv		Physical drive nmuber (0)
	 */
	//-----------------------------------------------------------------//
	DSTATUS disk_status(BYTE drv) {
		return sdc_.at_mmc().disk_status(drv);
	}

	//-----------------------------------------------------------------//
	/*!
		@brief	FatFs へリード・セクターを提供
		@param[in]	drv		Physical drive nmuber (0)
		@param[out]	buff	Pointer to the data buffer to store read data
		@param[in]	sector	Start sector number (LBA)
		@param[in]	count	Sector count (1..128)
		@return リザルト
	 */
	//-----------------------------------------------------------------//
	DRESULT disk_read(BYTE drv, BYTE* buff, DWORD sector, UINT count) {
		return sdc_.at_mmc().disk_read(drv, buff, sector, count);
	}

	//-----------------------------------------------------------------//
	/*!
		@brief	FatFs へライト・セクターを提供
		@param[in]	drv		Physical drive nmuber (0)
		@param[in]	buff	Pointer to the data to be written
		@param[in]	sector	Start sector number (LBA)
		@param[in]	count	Sector count (1..128)
		@return リザルト
	 */
	//-----------------------------------------------------------------//
	DRESULT disk_write(BYTE drv, const BYTE* buff, DWORD sector, UINT count) {
		return sdc_.at_mmc().disk_write(drv, buff, sector, count);
	}

	//-----------------------------------------------------------------//
	/*!
		@brief	FatFs へI/O コントロールを提供
		@param[in]	drv		Physical drive nmuber (0)
		@param[in]	ctrl	Control code
		@param[in]	buff	Buffer to send/receive control data
		@return リザルト
	 */
	//-----------------------------------------------------------------//
	DRESULT disk_ioctl(BYTE drv, BYTE ctrl, void* buff) {
		return sdc_.at_mmc().disk_ioctl(drv, ctrl, buff);
	}

	//-----------------------------------------------------------------//
	/*!
		@brief	FatFs へ時間を提供
		@return FatFs 時間
	 */
	//-----------------------------------------------------------------//
	DWORD get_fattime(void) {
		auto t = get_time();
		return utils::str::get_fattime(t);
	}

	//-----------------------------------------------------------------//
	/*!
		@brief	UTF-8 から ShiftJIS への変換
		@param[in]	src	UTF-8 文字列ソース
		@param[out]	dst	ShiftJIS 文字列出力
	 */
	//-----------------------------------------------------------------//
	void utf8_to_sjis(const char* src, char* dst) {
		utils::str::utf8_to_sjis(src, dst);
	}

	//-----------------------------------------------------------------//
	/*!
		@brief	NET で必要なミリ秒タイマー
	 */
	//-----------------------------------------------------------------//
	unsigned long millis(void)
	{
		return cmt0_.at_task().get_millis();
	}

	//-----------------------------------------------------------------//
	/*!
		@brief	NET で必要なミリ秒ディレイ
	 */
	//-----------------------------------------------------------------//
	void delay(unsigned long ms)
	{
		cmt0_.at_task().set_delay(ms);
		while(cmt0_.at_task().get_delay() != 0) ;
	}

	//-----------------------------------------------------------------//
	/*!
		@brief	NET で必要な１０ミリ秒タスク登録
		@param[in]	task	１０ミリ秒毎に呼ばれるタスク
	 */
	//-----------------------------------------------------------------//
	void set_task_10ms(void (*task)(void)) {
		cmt0_.at_task().set_task_10ms(task);
	}
}

int main(int argc, char** argv);

int main(int argc, char** argv)
{
	device::PORT3::PCR.B5 = 1; // P35(NMI) pull-up

	device::SYSTEM::PRCR = 0xA50B;	// クロック、低消費電力、関係書き込み許可

	device::SYSTEM::MOSCWTCR = 9;	// 1ms wait
	// メインクロック強制発振とドライブ能力設定
	device::SYSTEM::MOFCR = device::SYSTEM::MOFCR.MODRV2.b(0b10)
						  | device::SYSTEM::MOFCR.MOFXIN.b(1);
	device::SYSTEM::MOSCCR.MOSTP = 0;		// メインクロック発振器動作
	while(device::SYSTEM::OSCOVFSR.MOOVF() == 0) asm("nop");

	// Base Clock 12MHz
	// PLLDIV: 1/1, STC: 16 倍(198MHz)
	device::SYSTEM::PLLCR = device::SYSTEM::PLLCR.PLIDIV.b(0) |
							device::SYSTEM::PLLCR.STC.b(0b011111);
	device::SYSTEM::PLLCR2.PLLEN = 0;			// PLL 動作
	while(device::SYSTEM::OSCOVFSR.PLOVF() == 0) asm("nop");

	device::SYSTEM::SCKCR = device::SYSTEM::SCKCR.FCK.b(2)		// 1/2 (198/4=48)
						  | device::SYSTEM::SCKCR.ICK.b(1)		// 1/2 (198/2=96)
						  | device::SYSTEM::SCKCR.BCK.b(2)		// 1/2 (198/4=48)
						  | device::SYSTEM::SCKCR.PCKA.b(1)		// 1/2 (198/2=96)
						  | device::SYSTEM::SCKCR.PCKB.b(2)		// 1/4 (198/4=48)
						  | device::SYSTEM::SCKCR.PCKC.b(2)		// 1/4 (198/4=48)
						  | device::SYSTEM::SCKCR.PCKD.b(2);	// 1/4 (198/4=48)
	device::SYSTEM::SCKCR2 = device::SYSTEM::SCKCR2.UCK.b(0b0011) | 1;  // USB Clock: 1/4 (198/4=48)
	device::SYSTEM::SCKCR3.CKSEL = 0b100;	///< PLL 選択

	{  // タイマー設定、１０００Ｈｚ（１ｍｓ）
		uint8_t int_level = 1;
		cmt0_.start(1000, int_level);
	}

	{  // SCI 設定
		uint8_t int_level = 2;
		sci_.start(115200, int_level);
	}

	// SD カード・クラスの初期化
	sdc_.start();

	{  // インサーネット開始
		device::power_cfg::turn(device::peripheral::ETHERC0);
		device::port_map::turn(device::peripheral::ETHERC0);

		set_interrupt_task(INT_Excep_ICU_GROUPAL1, static_cast<uint32_t>(device::icu_t::VECTOR::GROUPAL1));

		ethernet_.start();

		static const uint8_t mac[6] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };

		if(ethernet_.begin(mac) == 0) {
			utils::format("Ethernet Fail: begin (DHCP)...\n");
			utils::format("SetIP: ");
			net::ip_address ipa(192, 168, 3, 20);
			ethernet_.begin(mac, ipa);
		} else {
			utils::format("DHCP: ");
		}

#if 0
		utils::format("SetIP: ");
			net::ip_address ipa(192, 168, 1, 10);
			ethernet_.begin(mac, ipa);
		}
		ethernet_.localIP().print();
		utils::format("\n");
#endif

		server_.begin(80);
		utils::format("Start server: ");
		ethernet_.get_local_ip().print();
		utils::format("  port(%d)\n") % static_cast<int>(server_.get_port());
	}

	uint32_t cnt = 0;
	while(1) {
		cmt0_.at_task().sync_100hz();

		ethernet_.service();

		++cnt;
		if(cnt >= 30) {
			cnt = 0;
		}
		device::PORTC::PDR.B0 = 1; // GR-KAEDE LED0 output
		device::PORTC::PODR.B0 = (cnt < 10) ? 0 : 1;
	}
}
<|endoftext|>


<commit_before>/***************************************************************************
**
** Copyright (C) 2010, 2011 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (directui@nokia.com)
**
** This file is part of libmeegotouch.
**
** If you have questions regarding the use of this file, please contact
** Nokia at directui@nokia.com.
**
** This library is free software; you can redistribute it and/or
** modify it under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation
** and appearing in the file LICENSE.LGPL included in the packaging
** of this file.
**
****************************************************************************/

#include "contentitemspage.h"
#include "utils.h"

#include <MApplication>
#include <MAbstractCellCreator>
#include <MImageWidget>
#include <MLayout>
#include <MLinearLayoutPolicy>
#include <MList>
#include <MListItem>
#include <MProgressIndicator>

#include <MBasicListItem>
#include <MDetailedListItem>
#include <MAdvancedListItem>

class BasicListItemCreator : public MAbstractCellCreator<MBasicListItem>
{
public:
    BasicListItemCreator(MBasicListItem::ItemStyle style)
        : itemStyle(style),
        defaultImage(Utils::imagesDir() + "DefaultAvatar.png")
    {
    }

    MWidget *createCell(const QModelIndex &index, MWidgetRecycler &recycler) const {
        Q_UNUSED(index);
        MBasicListItem *cell = dynamic_cast<MBasicListItem*>(recycler.take(MBasicListItem::staticMetaObject.className()));

        if (cell == NULL) {
            cell = new MBasicListItem(itemStyle);
            if (MApplication::instance()->objectName() == "widgetsgallery") {
                cell->setStyleName("CommonBasicListItem");
            } else {
                cell->setStyleName("CommonBasicListItemInverted");
            }
            cell->setLayoutPosition(M::CenterPosition);
        }
        updateCell(index, cell);

        return cell;
    }

    void updateCell(const QModelIndex &index, MWidget *cell) const {
        MBasicListItem *item = dynamic_cast<MBasicListItem*>(cell);
        if (!item)
            return;

        switch (itemStyle) {
        case MBasicListItem::SingleTitle: {
            item->setTitle(index.data().toString());
            break;
        }
        case MBasicListItem::TitleWithSubtitle: {
            item->setTitle(index.data().toString());
            //% "Subtitle"
            item->setSubtitle(qtTrId("xx_wg_contentitemspage_subtitle"));
            break;
        }
        case MBasicListItem::IconWithTitle: {
            item->setTitle(index.data().toString());
            item->imageWidget()->setImage(defaultImage);
            break;
        }
        case MBasicListItem::IconWithTitleAndSubtitle: {
            item->setTitle(index.data().toString());
            //% "Subtitle"
            item->setSubtitle(qtTrId("xx_wg_contentitemspage_subtitle"));
            item->imageWidget()->setImage(defaultImage);
            break;
        }
        default:
            break;
        };

    }

private:
    MBasicListItem::ItemStyle itemStyle;
    QImage defaultImage;
};

class DetailedListItemCreator : public MAbstractCellCreator<MDetailedListItem>
{
public:
    DetailedListItemCreator(MDetailedListItem::ItemStyle style)
        : itemStyle(style),
        defaultImage(Utils::imagesDir() + "DefaultAvatar.png")
    {
    }

    MWidget *createCell(const QModelIndex &index, MWidgetRecycler &recycler) const {
        Q_UNUSED(index);
        MDetailedListItem *cell = dynamic_cast<MDetailedListItem*>(recycler.take(MDetailedListItem::staticMetaObject.className()));

        if (cell == NULL) {
            cell = new MDetailedListItem(itemStyle);
            if (MApplication::instance()->objectName() == "widgetsgallery") {
                cell->setStyleName("CommonDetailedListItem");
            } else {
                cell->setStyleName("CommonDetailedListItemInverted");
            }
            cell->setLayoutPosition(M::CenterPosition);
        }
        updateCell(index, cell);

        return cell;
    }

    void updateCell(const QModelIndex &index, MWidget *cell) const {
        MDetailedListItem *item = dynamic_cast<MDetailedListItem*>(cell);
        if (!item)
            return;

        switch (itemStyle) {
        case MDetailedListItem::IconTitleSubtitleAndTwoSideIcons: {
            item->setTitle(index.data().toString());
            //% "Subtitle"
            item->setSubtitle(qtTrId("xx_wg_contentitemspage_subtitle"));
            item->imageWidget()->setImage(defaultImage);
            item->sideTopImageWidget()->setImage(defaultImage);
            item->sideBottomImageWidget()->setImage(defaultImage);
            break;
        }
        case MDetailedListItem::IconTitleSubtitleAndSideIconWithLabel: {
            item->setTitle(index.data().toString());
            //% "Subtitle"
            item->setSubtitle(qtTrId("xx_wg_contentitemspage_subtitle"));
            item->imageWidget()->setImage(defaultImage);
            item->sideTopImageWidget()->setImage(defaultImage);
            //% "Side"
            item->setSideBottomTitle(qtTrId("xx_wg_contentitemspage_sidetitle"));
            break;
        }
        case MDetailedListItem::ThumbnailTitleSubtitleAndTwoSideIcons: {
            item->setTitle(index.data().toString());
            //% "Subtitle"
            item->setSubtitle(qtTrId("xx_wg_contentitemspage_subtitle"));
            item->imageWidget()->setImage(defaultImage);
            item->sideTopImageWidget()->setImage(defaultImage);
            item->sideBottomImageWidget()->setImage(defaultImage);
            break;
        }
        case MDetailedListItem::ThumbnailTitleAndTwoSideIcons: {
            item->setTitle(index.data().toString());
            item->imageWidget()->setImage(defaultImage);
            item->sideTopImageWidget()->setImage(defaultImage);
            item->sideBottomImageWidget()->setImage(defaultImage);
            break;
        }
        default:
            break;
        };

    }

private:
    MDetailedListItem::ItemStyle itemStyle;
    QImage defaultImage;
};

class AdvancedListItemCreator : public MAbstractCellCreator<MAdvancedListItem>
{
public:
    AdvancedListItemCreator(MAdvancedListItem::ItemStyle style)
        : itemStyle(style),
        defaultImage(Utils::imagesDir() + "DefaultAvatar.png")
    {
    }

    MWidget *createCell(const QModelIndex &index, MWidgetRecycler &recycler) const {
        Q_UNUSED(index);
        MAdvancedListItem *cell = dynamic_cast<MAdvancedListItem*>(recycler.take(MAdvancedListItem::staticMetaObject.className()));

        if (cell == NULL) {
            cell = new MAdvancedListItem(itemStyle);
            if (MApplication::instance()->objectName() == "widgetsgallery") {
                cell->setStyleName("CommonAdvancedListItem");
            } else {
                cell->setStyleName("CommonAdvancedListItemInverted");
            }
            cell->setLayoutPosition(M::CenterPosition);
        }
        updateCell(index, cell);

        return cell;
    }

    void updateCell(const QModelIndex &index, MWidget *cell) const {
        MAdvancedListItem *item = dynamic_cast<MAdvancedListItem*>(cell);
        if (!item)
            return;

        switch (itemStyle) {
        case MAdvancedListItem::IconWithTitleProgressIndicatorAndTwoSideIcons: {
                item->setTitle(index.data().toString());
                item->imageWidget()->setImage(defaultImage);
                item->sideTopImageWidget()->setImage(defaultImage);
                item->sideBottomImageWidget()->setImage(defaultImage);
                item->progressIndicator()->setValue(55);
                break;
            }
        case MAdvancedListItem::IconWithTitleProgressIndicatorAndTopSideIcon: {
                item->setTitle(index.data().toString());
                item->imageWidget()->setImage(defaultImage);
                item->sideTopImageWidget()->setImage(defaultImage);
                item->progressIndicator()->setUnknownDuration(true);
                break;
            }
        default:
            break;
        };

    }

private:
    MAdvancedListItem::ItemStyle itemStyle;
    QImage defaultImage;
};

class ContentItemsPageModel : public QAbstractItemModel {
public:
    ContentItemsPageModel(QObject *parent, const QString &title)
        : QAbstractItemModel(parent),
        headerTitle(title)
    {
    }

    QModelIndex parent(const QModelIndex &child) const {
        Q_UNUSED(child);

        if (child.internalId() == 0)
            return index(0, 0);

        return QModelIndex();
    }

    QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const {
        if (parent.isValid())
            return createIndex(row, column, 0);
        else
            return createIndex(row, column, 1);
    }

    int rowCount(const QModelIndex &parent) const {
        if (parent.isValid())
            return 2;

        return 1;
    }

    int columnCount(const QModelIndex &parent) const {
        Q_UNUSED(parent);

        return 1;
    }

    QVariant data(const QModelIndex &index, int role) const {
        if (role == Qt::DisplayRole) {
            if (!index.parent().isValid())
                return QVariant::fromValue(headerTitle);
            else
                return QVariant::fromValue(QString(headerTitle + " %1").arg(index.row()));
        }

        return QVariant();
    }

private:
    QString headerTitle;
};

ContentItemsPage::ContentItemsPage()
    : TemplatePage(TemplatePage::ListsGridsAndPopups),
      contentItemLists(),
      policy(0)
{
}

QString ContentItemsPage::timedemoTitle()
{
    return "ContentItems";
}

void ContentItemsPage::createContent()
{
    MApplicationPage::createContent();

    setStyleName(inv("CommonApplicationPage"));

    QGraphicsWidget *panel = centralWidget();

    MLayout *layout = new MLayout(panel);
    layout->setContentsMargins(0, 0, 0, 0);
    panel->setLayout(layout);
    policy = new MLinearLayoutPolicy(layout, Qt::Vertical);
    policy->setContentsMargins(0, 0, 0, 0);
    policy->setSpacing(0);

    populateLayout();

    retranslateUi();
}

void ContentItemsPage::populateLayout()
{    //Basic list items
    //% "Single Title"
    contentItemLists.append(createList(qtTrId("xx_wg_contentitemspage_single_title"), new BasicListItemCreator(MBasicListItem::SingleTitle)));
    //% "Title with Subtitle"
    contentItemLists.append(createList(qtTrId("xx_wg_contentitemspage_title_with_subtitle"), new BasicListItemCreator(MBasicListItem::TitleWithSubtitle)));
    //% "Icon with Title"
    contentItemLists.append(createList(qtTrId("xx_wg_contentitemspage_icon_with_title"),  new BasicListItemCreator(MBasicListItem::IconWithTitle)));
    //% "Icon with Title and Subtitle"
    contentItemLists.append(createList(qtTrId("xx_wg_contentitemspage_icon_with_title_and_subtitle"),  new BasicListItemCreator(MBasicListItem::IconWithTitleAndSubtitle)));

    //Detailed list items
    //% "Icon with Title Subtitle and Two Side Icons"
    contentItemLists.append(createList(qtTrId("xx_wg_contentitemspage_icon_with_title_subtitle_and_two_side_icons"),
                                       new DetailedListItemCreator(MDetailedListItem::IconTitleSubtitleAndTwoSideIcons)));
    //% "Icon with Title Subtitle and Side Icon with Label"
    contentItemLists.append(createList(qtTrId("xx_wg_contentitemspage_icon_with_title_subtitle_and_side_icon_with_label"),
                                       new DetailedListItemCreator(MDetailedListItem::IconTitleSubtitleAndSideIconWithLabel)));
    //% "Thumbnail with Title Subtitle and Two Side Icons"
    contentItemLists.append(createList(qtTrId("xx_wg_contentitemspage_thumbnail_with_title_subtitle_and_two_side_icons"),
                                       new DetailedListItemCreator(MDetailedListItem::ThumbnailTitleSubtitleAndTwoSideIcons)));
    //% "Thumbnail with Title and Two Side Icons"
    contentItemLists.append(createList(qtTrId("xx_wg_contentitemspage_thumbnail_with_title_and_two_side_icons"),
                                       new DetailedListItemCreator(MDetailedListItem::ThumbnailTitleAndTwoSideIcons)));

    //Advanced list items
    //% "Icon with Title Progress Indicator and Two Side Icons"
    contentItemLists.append(createList(qtTrId("xx_wg_contentitemspage_icon_with_title_progress_indicator_and_two_side_icons"),
                                       new AdvancedListItemCreator(MAdvancedListItem::IconWithTitleProgressIndicatorAndTwoSideIcons)));
    //% "Icon with Title Progress Indicator and Top Side Icon"
    contentItemLists.append(createList(qtTrId("xx_wg_contentitemspage_icon_with_title_progress_indicator_and_top_side_icon"),
                                       new AdvancedListItemCreator(MAdvancedListItem::IconWithTitleProgressIndicatorAndTopSideIcon)));
}

MList *ContentItemsPage::createList(const QString &title, MCellCreator *creator)
{
    QAbstractItemModel *model = new ContentItemsPageModel(this, title);
    model->setObjectName("contentItemsPageModel");

    MList *list = new MList(centralWidget());
    list->setObjectName("list");
    list->setStyleName(inv("CommonList"));
    list->setCellCreator(creator);
    list->setItemModel(model);
    list->setShowGroups(true);
    policy->addItem(list, Qt::AlignCenter);

    return list;
}

void ContentItemsPage::retranslateUi()
{
    //% "Content Items"
    setTitle(qtTrId("xx_contentitemspage_title"));
    if (!isContentCreated())
        return;
    // this file has no other calls to qtTrId() except for the title
    // at the moment. If more qtTrId() calls are needed they should
    // be added here.
}
<commit_msg>Changes: Add a sample to widgetsgallery how the label preferred line count infulences on list item height.<commit_after>/***************************************************************************
**
** Copyright (C) 2010, 2011 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (directui@nokia.com)
**
** This file is part of libmeegotouch.
**
** If you have questions regarding the use of this file, please contact
** Nokia at directui@nokia.com.
**
** This library is free software; you can redistribute it and/or
** modify it under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation
** and appearing in the file LICENSE.LGPL included in the packaging
** of this file.
**
****************************************************************************/

#include "contentitemspage.h"
#include "utils.h"

#include <MApplication>
#include <MAbstractCellCreator>
#include <MImageWidget>
#include <MLayout>
#include <MLinearLayoutPolicy>
#include <MList>
#include <MListItem>
#include <MProgressIndicator>
#include <MLabel>

#include <MBasicListItem>
#include <MDetailedListItem>
#include <MAdvancedListItem>

#include <QGraphicsGridLayout>

namespace {

    class CustomListItem : public MListItem
    {
    public:
        CustomListItem(QGraphicsItem *parent = 0) : MListItem(parent) {
            setObjectName("CommonLargePanel");

            // Layout
            layoutGrid = new QGraphicsGridLayout(this);

            // Title
            titleLabel = new MLabel(this);
            titleLabel->setTextElide(true);
            titleLabel->setObjectName("CommonTitle");

            // Subtitle
            subtitleLabel = new MLabel(this);
            subtitleLabel->setTextElide(true);
            subtitleLabel->setWordWrap(true);
            subtitleLabel->setPreferredLineCount(3);
            subtitleLabel->setObjectName("CommonSubTitle");

            // Icon
            image = new MImageWidget(this);
            image->setObjectName("CommonMainIcon");

            // Construct layout
            layoutGrid->addItem(image, 0, 0, 2, 1);
            layoutGrid->addItem(titleLabel, 0, 1);
            layoutGrid->addItem(subtitleLabel, 1, 1);
        }

        void setTitle(const QString &title)
        {
            titleLabel->setText(title);
        }

        void setSubtitle(const QString &subtitle)
        {
            subtitleLabel->setText(subtitle);
        }

        void setImage(const QImage &picture)
        {
            image->setImage(picture);
        }

    private:
        MLabel *titleLabel;
        MLabel *subtitleLabel;
        MImageWidget *image;
        QGraphicsGridLayout *layoutGrid;
    };

    class CustomListItemCreator : public MAbstractCellCreator<CustomListItem>
    {
    public:
        CustomListItemCreator()
            : defaultImage(Utils::imagesDir() + "DefaultAvatar.png")
        {
            veryLongSubtitle = "This is multi-line elided text.";

            for (int i = 10; i > 0; i--) {
                veryLongSubtitle += " " + veryLongSubtitle;
            }
        }

        MWidget *createCell(const QModelIndex &index, MWidgetRecycler &recycler) const {
            Q_UNUSED(index);
            CustomListItem *cell = dynamic_cast<CustomListItem*>(recycler.take(CustomListItem::staticMetaObject.className()));

            if (cell == NULL) {
                cell = new CustomListItem;
                if (MApplication::instance()->objectName() == "widgetsgallery") {
                    cell->setStyleName("CommonBasicListItem");
                } else {
                    cell->setStyleName("CommonBasicListItemInverted");
                }
                cell->setLayoutPosition(M::CenterPosition);
            }
            updateCell(index, cell);

            return cell;
        }

        void updateCell(const QModelIndex &index, MWidget *cell) const {
            CustomListItem *item = dynamic_cast<CustomListItem*>(cell);
            if (!item)
                return;

            item->setTitle(index.data().toString());
            item->setSubtitle(veryLongSubtitle);
            item->setImage(defaultImage);
        }

    private:
        QString veryLongSubtitle;
        QImage defaultImage;
    };

    class BasicListItemCreator : public MAbstractCellCreator<MBasicListItem>
    {
    public:
        BasicListItemCreator(MBasicListItem::ItemStyle style)
            : itemStyle(style),
            defaultImage(Utils::imagesDir() + "DefaultAvatar.png")
        {
        }

        MWidget *createCell(const QModelIndex &index, MWidgetRecycler &recycler) const {
            Q_UNUSED(index);
            MBasicListItem *cell = dynamic_cast<MBasicListItem*>(recycler.take(MBasicListItem::staticMetaObject.className()));

            if (cell == NULL) {
                cell = new MBasicListItem(itemStyle);
                if (MApplication::instance()->objectName() == "widgetsgallery") {
                    cell->setStyleName("CommonBasicListItem");
                } else {
                    cell->setStyleName("CommonBasicListItemInverted");
                }
                cell->setLayoutPosition(M::CenterPosition);
            }
            updateCell(index, cell);

            return cell;
        }

        void updateCell(const QModelIndex &index, MWidget *cell) const {
            MBasicListItem *item = dynamic_cast<MBasicListItem*>(cell);
            if (!item)
                return;

            switch (itemStyle) {
            case MBasicListItem::SingleTitle: {
                item->setTitle(index.data().toString());
                break;
            }
            case MBasicListItem::TitleWithSubtitle: {
                item->setTitle(index.data().toString());
                //% "Subtitle"
                item->setSubtitle(qtTrId("xx_wg_contentitemspage_subtitle"));
                break;
            }
            case MBasicListItem::IconWithTitle: {
                item->setTitle(index.data().toString());
                item->imageWidget()->setImage(defaultImage);
                break;
            }
            case MBasicListItem::IconWithTitleAndSubtitle: {
                item->setTitle(index.data().toString());
                //% "Subtitle"
                item->setSubtitle(qtTrId("xx_wg_contentitemspage_subtitle"));
                item->imageWidget()->setImage(defaultImage);
                break;
            }
            default:
                break;
            };

        }

    private:
        MBasicListItem::ItemStyle itemStyle;
        QImage defaultImage;
    };

    class DetailedListItemCreator : public MAbstractCellCreator<MDetailedListItem>
    {
    public:
        DetailedListItemCreator(MDetailedListItem::ItemStyle style)
            : itemStyle(style),
            defaultImage(Utils::imagesDir() + "DefaultAvatar.png")
        {
        }

        MWidget *createCell(const QModelIndex &index, MWidgetRecycler &recycler) const {
            Q_UNUSED(index);
            MDetailedListItem *cell = dynamic_cast<MDetailedListItem*>(recycler.take(MDetailedListItem::staticMetaObject.className()));

            if (cell == NULL) {
                cell = new MDetailedListItem(itemStyle);
                if (MApplication::instance()->objectName() == "widgetsgallery") {
                    cell->setStyleName("CommonDetailedListItem");
                } else {
                    cell->setStyleName("CommonDetailedListItemInverted");
                }
                cell->setLayoutPosition(M::CenterPosition);
            }
            updateCell(index, cell);

            return cell;
        }

        void updateCell(const QModelIndex &index, MWidget *cell) const {
            MDetailedListItem *item = dynamic_cast<MDetailedListItem*>(cell);
            if (!item)
                return;

            switch (itemStyle) {
            case MDetailedListItem::IconTitleSubtitleAndTwoSideIcons: {
                item->setTitle(index.data().toString());
                //% "Subtitle"
                item->setSubtitle(qtTrId("xx_wg_contentitemspage_subtitle"));
                item->imageWidget()->setImage(defaultImage);
                item->sideTopImageWidget()->setImage(defaultImage);
                item->sideBottomImageWidget()->setImage(defaultImage);
                break;
            }
            case MDetailedListItem::IconTitleSubtitleAndSideIconWithLabel: {
                item->setTitle(index.data().toString());
                //% "Subtitle"
                item->setSubtitle(qtTrId("xx_wg_contentitemspage_subtitle"));
                item->imageWidget()->setImage(defaultImage);
                item->sideTopImageWidget()->setImage(defaultImage);
                //% "Side"
                item->setSideBottomTitle(qtTrId("xx_wg_contentitemspage_sidetitle"));
                break;
            }
            case MDetailedListItem::ThumbnailTitleSubtitleAndTwoSideIcons: {
                item->setTitle(index.data().toString());
                //% "Subtitle"
                item->setSubtitle(qtTrId("xx_wg_contentitemspage_subtitle"));
                item->imageWidget()->setImage(defaultImage);
                item->sideTopImageWidget()->setImage(defaultImage);
                item->sideBottomImageWidget()->setImage(defaultImage);
                break;
            }
            case MDetailedListItem::ThumbnailTitleAndTwoSideIcons: {
                item->setTitle(index.data().toString());
                item->imageWidget()->setImage(defaultImage);
                item->sideTopImageWidget()->setImage(defaultImage);
                item->sideBottomImageWidget()->setImage(defaultImage);
                break;
            }
            default:
                break;
            };

        }

    private:
        MDetailedListItem::ItemStyle itemStyle;
        QImage defaultImage;
    };

    class AdvancedListItemCreator : public MAbstractCellCreator<MAdvancedListItem>
    {
    public:
        AdvancedListItemCreator(MAdvancedListItem::ItemStyle style)
            : itemStyle(style),
            defaultImage(Utils::imagesDir() + "DefaultAvatar.png")
        {
        }

        MWidget *createCell(const QModelIndex &index, MWidgetRecycler &recycler) const {
            Q_UNUSED(index);
            MAdvancedListItem *cell = dynamic_cast<MAdvancedListItem*>(recycler.take(MAdvancedListItem::staticMetaObject.className()));

            if (cell == NULL) {
                cell = new MAdvancedListItem(itemStyle);
                if (MApplication::instance()->objectName() == "widgetsgallery") {
                    cell->setStyleName("CommonAdvancedListItem");
                } else {
                    cell->setStyleName("CommonAdvancedListItemInverted");
                }
                cell->setLayoutPosition(M::CenterPosition);
            }
            updateCell(index, cell);

            return cell;
        }

        void updateCell(const QModelIndex &index, MWidget *cell) const {
            MAdvancedListItem *item = dynamic_cast<MAdvancedListItem*>(cell);
            if (!item)
                return;

            switch (itemStyle) {
            case MAdvancedListItem::IconWithTitleProgressIndicatorAndTwoSideIcons: {
                    item->setTitle(index.data().toString());
                    item->imageWidget()->setImage(defaultImage);
                    item->sideTopImageWidget()->setImage(defaultImage);
                    item->sideBottomImageWidget()->setImage(defaultImage);
                    item->progressIndicator()->setValue(55);
                    break;
                }
            case MAdvancedListItem::IconWithTitleProgressIndicatorAndTopSideIcon: {
                    item->setTitle(index.data().toString());
                    item->imageWidget()->setImage(defaultImage);
                    item->sideTopImageWidget()->setImage(defaultImage);
                    item->progressIndicator()->setUnknownDuration(true);
                    break;
                }
            default:
                break;
            };

        }

    private:
        MAdvancedListItem::ItemStyle itemStyle;
        QImage defaultImage;
    };

}

class ContentItemsPageModel : public QAbstractItemModel {
public:
    ContentItemsPageModel(QObject *parent, const QString &title)
        : QAbstractItemModel(parent),
        headerTitle(title)
    {
    }

    QModelIndex parent(const QModelIndex &child) const {
        Q_UNUSED(child);

        if (child.internalId() == 0)
            return index(0, 0);

        return QModelIndex();
    }

    QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const {
        if (parent.isValid())
            return createIndex(row, column, 0);
        else
            return createIndex(row, column, 1);
    }

    int rowCount(const QModelIndex &parent) const {
        if (parent.isValid())
            return 2;

        return 1;
    }

    int columnCount(const QModelIndex &parent) const {
        Q_UNUSED(parent);

        return 1;
    }

    QVariant data(const QModelIndex &index, int role) const {
        if (role == Qt::DisplayRole) {
            if (!index.parent().isValid())
                return QVariant::fromValue(headerTitle);
            else
                return QVariant::fromValue(QString(headerTitle + " %1").arg(index.row()));
        }

        return QVariant();
    }

private:
    QString headerTitle;
};

ContentItemsPage::ContentItemsPage()
    : TemplatePage(TemplatePage::ListsGridsAndPopups),
      contentItemLists(),
      policy(0)
{
}

QString ContentItemsPage::timedemoTitle()
{
    return "ContentItems";
}

void ContentItemsPage::createContent()
{
    MApplicationPage::createContent();

    setStyleName(inv("CommonApplicationPage"));

    QGraphicsWidget *panel = centralWidget();

    MLayout *layout = new MLayout(panel);
    layout->setContentsMargins(0, 0, 0, 0);
    panel->setLayout(layout);
    policy = new MLinearLayoutPolicy(layout, Qt::Vertical);
    policy->setContentsMargins(0, 0, 0, 0);
    policy->setSpacing(0);

    populateLayout();

    retranslateUi();
}

void ContentItemsPage::populateLayout()
{    //Basic list items
    //% "Single Title"
    contentItemLists.append(createList(qtTrId("xx_wg_contentitemspage_single_title"), new BasicListItemCreator(MBasicListItem::SingleTitle)));
    //% "Title with Subtitle"
    contentItemLists.append(createList(qtTrId("xx_wg_contentitemspage_title_with_subtitle"), new BasicListItemCreator(MBasicListItem::TitleWithSubtitle)));
    //% "Icon with Title"
    contentItemLists.append(createList(qtTrId("xx_wg_contentitemspage_icon_with_title"),  new BasicListItemCreator(MBasicListItem::IconWithTitle)));
    //% "Icon with Title and Subtitle"
    contentItemLists.append(createList(qtTrId("xx_wg_contentitemspage_icon_with_title_and_subtitle"),  new BasicListItemCreator(MBasicListItem::IconWithTitleAndSubtitle)));

    //Detailed list items
    //% "Icon with Title Subtitle and Two Side Icons"
    contentItemLists.append(createList(qtTrId("xx_wg_contentitemspage_icon_with_title_subtitle_and_two_side_icons"),
                                       new DetailedListItemCreator(MDetailedListItem::IconTitleSubtitleAndTwoSideIcons)));
    //% "Icon with Title Subtitle and Side Icon with Label"
    contentItemLists.append(createList(qtTrId("xx_wg_contentitemspage_icon_with_title_subtitle_and_side_icon_with_label"),
                                       new DetailedListItemCreator(MDetailedListItem::IconTitleSubtitleAndSideIconWithLabel)));
    //% "Thumbnail with Title Subtitle and Two Side Icons"
    contentItemLists.append(createList(qtTrId("xx_wg_contentitemspage_thumbnail_with_title_subtitle_and_two_side_icons"),
                                       new DetailedListItemCreator(MDetailedListItem::ThumbnailTitleSubtitleAndTwoSideIcons)));
    //% "Thumbnail with Title and Two Side Icons"
    contentItemLists.append(createList(qtTrId("xx_wg_contentitemspage_thumbnail_with_title_and_two_side_icons"),
                                       new DetailedListItemCreator(MDetailedListItem::ThumbnailTitleAndTwoSideIcons)));

    //Advanced list items
    //% "Icon with Title Progress Indicator and Two Side Icons"
    contentItemLists.append(createList(qtTrId("xx_wg_contentitemspage_icon_with_title_progress_indicator_and_two_side_icons"),
                                       new AdvancedListItemCreator(MAdvancedListItem::IconWithTitleProgressIndicatorAndTwoSideIcons)));
    //% "Icon with Title Progress Indicator and Top Side Icon"
    contentItemLists.append(createList(qtTrId("xx_wg_contentitemspage_icon_with_title_progress_indicator_and_top_side_icon"),
                                       new AdvancedListItemCreator(MAdvancedListItem::IconWithTitleProgressIndicatorAndTopSideIcon)));

    //% "Icon with Title and Multiline Subtitle"
    contentItemLists.append(createList(qtTrId("xx_wg_contentitemspage_icon_with_title_and_multiline_subtitle"), new CustomListItemCreator));
}

MList *ContentItemsPage::createList(const QString &title, MCellCreator *creator)
{
    QAbstractItemModel *model = new ContentItemsPageModel(this, title);
    model->setObjectName("contentItemsPageModel");

    MList *list = new MList(centralWidget());
    list->setObjectName("list");
    list->setStyleName(inv("CommonList"));
    list->setCellCreator(creator);
    list->setItemModel(model);
    list->setShowGroups(true);
    policy->addItem(list, Qt::AlignCenter);

    return list;
}

void ContentItemsPage::retranslateUi()
{
    //% "Content Items"
    setTitle(qtTrId("xx_contentitemspage_title"));
    if (!isContentCreated())
        return;
    // this file has no other calls to qtTrId() except for the title
    // at the moment. If more qtTrId() calls are needed they should
    // be added here.
}
<|endoftext|>


<commit_before>/*
This file is part of Bohrium and copyright (c) 2012 the Bohrium
team <http://www.bh107.org>.

Bohrium is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3
of the License, or (at your option) any later version.

Bohrium is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the
GNU Lesser General Public License along with Bohrium.

If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef __BH_JITK_BLOCK_HPP
#define __BH_JITK_BLOCK_HPP

#include <set>
#include <vector>
#include <iostream>
#include <boost/variant/variant.hpp>
#include <boost/variant/get.hpp>

#include <bh_instruction.hpp>

namespace bohrium {
namespace jitk {

/* Design Overview

   A block can represent two things in Bohrium:
    * A for-loop, i.e. a traversal of arrays over an axis/dimension (class LoopB)
    * A single instruction such as BH_ADD or BH_MULTIPLY (class InstrB)

*/

// Forward declaration
class Block;

// We use a shared pointer of an const instruction. The idea is to never change an instruction inplace
// instead, create a whole new instruction.
typedef std::shared_ptr<const bh_instruction> InstrPtr;

// Representation of a for-loop, which contains a list of nested loops (_block_list)
class LoopB {
public:
    // The rank of this loop block
    int rank;
    // List of sub-blocks
    std::vector <Block> _block_list;
    // Size of the loop
    int64_t size;
    // Sweep instructions within this loop
    std::set<InstrPtr> _sweeps;
    // New arrays within this loop
    std::set<bh_base *> _news;
    // Freed arrays within this loop
    std::set<bh_base *> _frees;
    // Is this loop and all its sub-blocks reshapable
    bool _reshapable = false;

    // Unique id of this block
    int _id;

    // Default Constructor
    LoopB() { static int id_count = 0; _id = id_count++; }

    void replaceInstr(InstrPtr subject, const bh_instruction &replacement);

    // Is this block innermost? (not counting instruction blocks)
    bool isInnermost() const;

    // Return all sub-blocks (incl. nested blocks)
    void getAllSubBlocks(std::vector<const LoopB *> &out) const;

    // Return all sub-blocks (excl. nested blocks)
    std::vector<const Block*> getLocalSubBlocks() const;

    // Return all instructions in the block (incl. nested blocks)
    void getAllInstr(std::vector<InstrPtr> &out) const;
    std::vector<InstrPtr> getAllInstr() const;

    // Return instructions in the block (excl. nested blocks)
    void getLocalInstr(std::vector<InstrPtr> &out) const;
    std::vector<InstrPtr> getLocalInstr() const;

    // Return all bases accessed by this block
    std::set<const bh_base*> getAllBases() const {
        std::set<const bh_base*> ret;
        for (InstrPtr instr: getAllInstr()) {
            std::set<const bh_base*> t = instr->get_bases();
            ret.insert(t.begin(), t.end());
        }
        return ret;
    }

    // Return all new arrays in this block (incl. nested blocks)
    void getAllNews(std::set<bh_base*> &out) const;
    std::set<bh_base*> getAllNews() const;

    // Return all freed arrays in this block (incl. nested blocks)
    void getAllFrees(std::set<bh_base*> &out) const;
    std::set<bh_base*> getAllFrees() const;

    // Return all local temporary arrays in this block (excl. nested blocks)
    // NB: The returned temporary arrays are the arrays that this block should declare
    void getLocalTemps(std::set<bh_base*> &out) const;
    std::set<bh_base*> getLocalTemps() const;

    // Return all temporary arrays in this block (incl. nested blocks)
    void getAllTemps(std::set<bh_base*> &out) const;
    std::set<bh_base*> getAllTemps() const;

    // Returns true when all instructions within the block is system or if the block is empty()
    bool isSystemOnly() const;

    // Validation check of this block
    bool validation() const;

    // Finds the block and instruction that accesses 'base' last. If 'base' is NULL, any data access
    // is accepted thus the last instruction is returned.
    // Returns the block and the index of the instruction (or NULL and -1 if not accessed)
    std::pair<LoopB*, int64_t> findLastAccessBy(const bh_base *base);

    // Insert the system instruction 'instr' after the instruction that accesses 'base' last.
    // If no instruction accesses 'base' we insert it after the last instruction.
    // NB: Force reshape the instruction to match the instructions accesses 'base' last.
    void insert_system_after(InstrPtr instr, const bh_base *base);

    // Pretty print this block
    std::string pprint(const char *newline="\n") const;

    // Equality test based on the unique ID
    bool operator==(const LoopB &loop_block) const {
        return this->_id == loop_block._id;
    }
};

// Representation of a single instruction
class InstrB {
public:
    InstrPtr instr;
    int rank;
};

// A block can represent a for-loop (LoopB) or an instruction (InstrB)
class Block {
private:
    // This is the variant that contains either a LoopB or a InstrB.
    // Note, boost::blank is only used to catch programming errors
    boost::variant<boost::blank, LoopB, InstrB> _var;

public:

    // Default Constructor
    Block() {}

    // Loop Block Constructor
    explicit Block(const LoopB &loop_block) {
        assert(_var.which() == 0);
        _var = loop_block;
    }
    explicit Block(LoopB &&loop_block) {
        assert(_var.which() == 0);
        _var = loop_block;
    }

    // Instruction Block Constructor
    // Note, the rank is only to make pretty printing easier
    Block(const bh_instruction &instr, int rank) {
        assert(_var.which() == 0);
        InstrB _instr{std::make_shared<bh_instruction>(instr), rank};
        _var = std::move(_instr);
    }

    // Returns true if this block is an instruction block
    bool isInstr() const {
        return _var.which() == 2; // Notice, the third type in '_var' is 'InstrB'
    }

    // Retrieve the Loop Block
    LoopB &getLoop() {return boost::get<LoopB>(_var);}
    const LoopB &getLoop() const {return boost::get<LoopB>(_var);}

    // Retrieve the instruction within the instruction block
    const InstrPtr getInstr() const {return boost::get<InstrB>(_var).instr;}
    InstrPtr getInstr() {return boost::get<InstrB>(_var).instr;}
    void setInstr(const bh_instruction &instr) {
        assert(_var.which() == 0 or _var.which() == 2);
        boost::get<InstrB>(_var).instr.reset(new bh_instruction(instr));
    }

    // Return the rank of this block
    int rank() const {
        if (isInstr()) {
            return boost::get<InstrB>(_var).rank;
        } else {
            return getLoop().rank;
        }
    }

    // Validation check of this block
    bool validation() const;

    // Return all instructions in the block (incl. nested blocks)
    void getAllInstr(std::vector<InstrPtr> &out) const;
    std::vector<InstrPtr> getAllInstr() const;

    // Return all bases accessed by this block
    std::set<const bh_base*> getAllBases() const {
        std::set<const bh_base*> ret;
        for (InstrPtr instr: getAllInstr()) {
            std::set<const bh_base*> t = instr->get_bases();
            ret.insert(t.begin(), t.end());
        }
        return ret;
    }

    // Returns true when all instructions within this block is system or if the block is empty()
    bool isSystemOnly() const {
        if (isInstr()) {
            return bh_opcode_is_system(getInstr()->opcode);
        }
        for (const Block &b: getLoop()._block_list) {
            if (not b.isSystemOnly()) {
                return false;
            }
        }
        return true;
    }

    // Returns true when all instructions within this block is reshapable
    bool isReshapable() const {
        if (isInstr()) {
            return getInstr()->reshapable();
        } else {
            return getLoop()._reshapable;
        }
    }

    // Determines whether this block must be executed after 'other'
    bool depend_on(const Block &other) const {
        const std::vector<InstrPtr> this_instr_list = getAllInstr();
        const std::vector<InstrPtr> other_instr_list = other.getAllInstr();
        for (const InstrPtr this_instr: this_instr_list) {
            for (const InstrPtr other_instr: other_instr_list) {
                if (*this_instr != *other_instr and
                    bh_instr_dependency(this_instr.get(), other_instr.get())) {
                    return true;
                }
            }
        }
        return false;
    }

    // Pretty print this block
    std::string pprint(const char *newline="\n") const;
};

// Merge the two loop blocks, 'a' and 'b', in that order.
LoopB merge(const LoopB &a, const LoopB &b);

// Create a nested block based on 'instr_list' with the sets of new, free, and temp arrays given.
// The dimensions from zero to 'rank-1' are ignored.
// The 'size_of_rank_dim' specifies the size of the dimension 'rank'.
Block create_nested_block(const std::vector<InstrPtr> &instr_list, int rank, int64_t size_of_rank_dim);

// Returns the blocks that can be parallelized in 'block' (incl. 'block' and its sub-blocks)
// and the total amount of parallelism (in number of possible parallel threads)
std::pair<std::vector<const LoopB *>, uint64_t> find_threaded_blocks(const LoopB &block);

// Merges the two blocks 'a' and 'a' (in that order) if they are fusible.
// NB: 'a' or 'b' might be reshaped in order to make the merge legal
// Returns the new block and a flag indicating whether the merge was performed
std::pair<Block, bool> merge_if_possible(const Block &a, const Block &b);

//Implements pprint of block
std::ostream& operator<<(std::ostream& out, const LoopB& b);

//Implements pprint of block
std::ostream& operator<<(std::ostream& out, const Block& b);

//Implements pprint of a vector of blocks
std::ostream& operator<<(std::ostream& out, const std::vector<Block>& b);

} // jit
} // bohrium

#endif
<commit_msg>jitk: added a missing include<commit_after>/*
This file is part of Bohrium and copyright (c) 2012 the Bohrium
team <http://www.bh107.org>.

Bohrium is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3
of the License, or (at your option) any later version.

Bohrium is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the
GNU Lesser General Public License along with Bohrium.

If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef __BH_JITK_BLOCK_HPP
#define __BH_JITK_BLOCK_HPP

#include <set>
#include <vector>
#include <iostream>
#include <memory>
#include <boost/variant/variant.hpp>
#include <boost/variant/get.hpp>

#include <bh_instruction.hpp>

namespace bohrium {
namespace jitk {

/* Design Overview

   A block can represent two things in Bohrium:
    * A for-loop, i.e. a traversal of arrays over an axis/dimension (class LoopB)
    * A single instruction such as BH_ADD or BH_MULTIPLY (class InstrB)

*/

// Forward declaration
class Block;

// We use a shared pointer of an const instruction. The idea is to never change an instruction inplace
// instead, create a whole new instruction.
typedef std::shared_ptr<const bh_instruction> InstrPtr;

// Representation of a for-loop, which contains a list of nested loops (_block_list)
class LoopB {
public:
    // The rank of this loop block
    int rank;
    // List of sub-blocks
    std::vector <Block> _block_list;
    // Size of the loop
    int64_t size;
    // Sweep instructions within this loop
    std::set<InstrPtr> _sweeps;
    // New arrays within this loop
    std::set<bh_base *> _news;
    // Freed arrays within this loop
    std::set<bh_base *> _frees;
    // Is this loop and all its sub-blocks reshapable
    bool _reshapable = false;

    // Unique id of this block
    int _id;

    // Default Constructor
    LoopB() { static int id_count = 0; _id = id_count++; }

    void replaceInstr(InstrPtr subject, const bh_instruction &replacement);

    // Is this block innermost? (not counting instruction blocks)
    bool isInnermost() const;

    // Return all sub-blocks (incl. nested blocks)
    void getAllSubBlocks(std::vector<const LoopB *> &out) const;

    // Return all sub-blocks (excl. nested blocks)
    std::vector<const Block*> getLocalSubBlocks() const;

    // Return all instructions in the block (incl. nested blocks)
    void getAllInstr(std::vector<InstrPtr> &out) const;
    std::vector<InstrPtr> getAllInstr() const;

    // Return instructions in the block (excl. nested blocks)
    void getLocalInstr(std::vector<InstrPtr> &out) const;
    std::vector<InstrPtr> getLocalInstr() const;

    // Return all bases accessed by this block
    std::set<const bh_base*> getAllBases() const {
        std::set<const bh_base*> ret;
        for (InstrPtr instr: getAllInstr()) {
            std::set<const bh_base*> t = instr->get_bases();
            ret.insert(t.begin(), t.end());
        }
        return ret;
    }

    // Return all new arrays in this block (incl. nested blocks)
    void getAllNews(std::set<bh_base*> &out) const;
    std::set<bh_base*> getAllNews() const;

    // Return all freed arrays in this block (incl. nested blocks)
    void getAllFrees(std::set<bh_base*> &out) const;
    std::set<bh_base*> getAllFrees() const;

    // Return all local temporary arrays in this block (excl. nested blocks)
    // NB: The returned temporary arrays are the arrays that this block should declare
    void getLocalTemps(std::set<bh_base*> &out) const;
    std::set<bh_base*> getLocalTemps() const;

    // Return all temporary arrays in this block (incl. nested blocks)
    void getAllTemps(std::set<bh_base*> &out) const;
    std::set<bh_base*> getAllTemps() const;

    // Returns true when all instructions within the block is system or if the block is empty()
    bool isSystemOnly() const;

    // Validation check of this block
    bool validation() const;

    // Finds the block and instruction that accesses 'base' last. If 'base' is NULL, any data access
    // is accepted thus the last instruction is returned.
    // Returns the block and the index of the instruction (or NULL and -1 if not accessed)
    std::pair<LoopB*, int64_t> findLastAccessBy(const bh_base *base);

    // Insert the system instruction 'instr' after the instruction that accesses 'base' last.
    // If no instruction accesses 'base' we insert it after the last instruction.
    // NB: Force reshape the instruction to match the instructions accesses 'base' last.
    void insert_system_after(InstrPtr instr, const bh_base *base);

    // Pretty print this block
    std::string pprint(const char *newline="\n") const;

    // Equality test based on the unique ID
    bool operator==(const LoopB &loop_block) const {
        return this->_id == loop_block._id;
    }
};

// Representation of a single instruction
class InstrB {
public:
    InstrPtr instr;
    int rank;
};

// A block can represent a for-loop (LoopB) or an instruction (InstrB)
class Block {
private:
    // This is the variant that contains either a LoopB or a InstrB.
    // Note, boost::blank is only used to catch programming errors
    boost::variant<boost::blank, LoopB, InstrB> _var;

public:

    // Default Constructor
    Block() {}

    // Loop Block Constructor
    explicit Block(const LoopB &loop_block) {
        assert(_var.which() == 0);
        _var = loop_block;
    }
    explicit Block(LoopB &&loop_block) {
        assert(_var.which() == 0);
        _var = loop_block;
    }

    // Instruction Block Constructor
    // Note, the rank is only to make pretty printing easier
    Block(const bh_instruction &instr, int rank) {
        assert(_var.which() == 0);
        InstrB _instr{std::make_shared<bh_instruction>(instr), rank};
        _var = std::move(_instr);
    }

    // Returns true if this block is an instruction block
    bool isInstr() const {
        return _var.which() == 2; // Notice, the third type in '_var' is 'InstrB'
    }

    // Retrieve the Loop Block
    LoopB &getLoop() {return boost::get<LoopB>(_var);}
    const LoopB &getLoop() const {return boost::get<LoopB>(_var);}

    // Retrieve the instruction within the instruction block
    const InstrPtr getInstr() const {return boost::get<InstrB>(_var).instr;}
    InstrPtr getInstr() {return boost::get<InstrB>(_var).instr;}
    void setInstr(const bh_instruction &instr) {
        assert(_var.which() == 0 or _var.which() == 2);
        boost::get<InstrB>(_var).instr.reset(new bh_instruction(instr));
    }

    // Return the rank of this block
    int rank() const {
        if (isInstr()) {
            return boost::get<InstrB>(_var).rank;
        } else {
            return getLoop().rank;
        }
    }

    // Validation check of this block
    bool validation() const;

    // Return all instructions in the block (incl. nested blocks)
    void getAllInstr(std::vector<InstrPtr> &out) const;
    std::vector<InstrPtr> getAllInstr() const;

    // Return all bases accessed by this block
    std::set<const bh_base*> getAllBases() const {
        std::set<const bh_base*> ret;
        for (InstrPtr instr: getAllInstr()) {
            std::set<const bh_base*> t = instr->get_bases();
            ret.insert(t.begin(), t.end());
        }
        return ret;
    }

    // Returns true when all instructions within this block is system or if the block is empty()
    bool isSystemOnly() const {
        if (isInstr()) {
            return bh_opcode_is_system(getInstr()->opcode);
        }
        for (const Block &b: getLoop()._block_list) {
            if (not b.isSystemOnly()) {
                return false;
            }
        }
        return true;
    }

    // Returns true when all instructions within this block is reshapable
    bool isReshapable() const {
        if (isInstr()) {
            return getInstr()->reshapable();
        } else {
            return getLoop()._reshapable;
        }
    }

    // Determines whether this block must be executed after 'other'
    bool depend_on(const Block &other) const {
        const std::vector<InstrPtr> this_instr_list = getAllInstr();
        const std::vector<InstrPtr> other_instr_list = other.getAllInstr();
        for (const InstrPtr this_instr: this_instr_list) {
            for (const InstrPtr other_instr: other_instr_list) {
                if (*this_instr != *other_instr and
                    bh_instr_dependency(this_instr.get(), other_instr.get())) {
                    return true;
                }
            }
        }
        return false;
    }

    // Pretty print this block
    std::string pprint(const char *newline="\n") const;
};

// Merge the two loop blocks, 'a' and 'b', in that order.
LoopB merge(const LoopB &a, const LoopB &b);

// Create a nested block based on 'instr_list' with the sets of new, free, and temp arrays given.
// The dimensions from zero to 'rank-1' are ignored.
// The 'size_of_rank_dim' specifies the size of the dimension 'rank'.
Block create_nested_block(const std::vector<InstrPtr> &instr_list, int rank, int64_t size_of_rank_dim);

// Returns the blocks that can be parallelized in 'block' (incl. 'block' and its sub-blocks)
// and the total amount of parallelism (in number of possible parallel threads)
std::pair<std::vector<const LoopB *>, uint64_t> find_threaded_blocks(const LoopB &block);

// Merges the two blocks 'a' and 'a' (in that order) if they are fusible.
// NB: 'a' or 'b' might be reshaped in order to make the merge legal
// Returns the new block and a flag indicating whether the merge was performed
std::pair<Block, bool> merge_if_possible(const Block &a, const Block &b);

//Implements pprint of block
std::ostream& operator<<(std::ostream& out, const LoopB& b);

//Implements pprint of block
std::ostream& operator<<(std::ostream& out, const Block& b);

//Implements pprint of a vector of blocks
std::ostream& operator<<(std::ostream& out, const std::vector<Block>& b);

} // jit
} // bohrium

#endif
<|endoftext|>


<commit_before>/**
 * \file tests.cpp
 * \author Matt Hammerly
 * \brief This file contains int main() which will run tests as they're written
 */
#include <iostream>
#include <cassert>
#include "Library.h"
#include "Track.h"
#include "Playlist.h"
#include "tests.h"

using std::cout; using std::endl;

/// File location of one track
const std::string track1 = "/home/matt/hdd/dev/cpp/musicmanager/test_data/Gregory And The Hawk/The Boats & Birds EP/01 Boats & Birds.mp3";

/// File location of another track
const std::string track2 = "/home/matt/hdd/dev/cpp/musicmanager/test_data/Beirut/The Flying Club Cup/05 la banlieue.m4a";

/// Title of one playlist
const std::string playlist1 = "test1";

/**
 * \brief Main entry point of program, where tests will be run
 */
int main()
{

    Test_Library_Constructor();

    Test_Library_PrepareDatabase();

    Test_Library_DestroyDatabase();

    Test_Library_AddTrack();

    Test_Library_AddPlaylist();

    Test_Library_RemoveTrack();

    Test_Library_RemovePlaylist();

    Test_Playlist_Constructors();

    Test_Playlist_AppendTrack();

    // So I can poke around manually after running tests
    //CLibrary library;
    //library.PrepareDatabase();

    return 0;
}

/**
 * \brief I mean, there's constructor logic, let's test it.
 */
void Test_Library_Constructor()
{
    cout << "Test_Library_Constructor... ";
    CLibrary library;
    assert(library.GetStatus() == CONNECTION_OK);
    cout << "OK" << endl;
}

/**
 * \brief Ensure all expected database objects are created
 */
void Test_Library_PrepareDatabase()
{
    cout << "Test_Library_PrepareDatabase... ";
    CLibrary library;

    library.PrepareDatabase();

    // We need the unwrapped connection object for arbitrary queries to test
    PGconn *conn = library.GetConnection();

    // Check to see if tables exist
    // Three rows should be returned; one for tracks, one for playlists, one for tracks_playlists
    PGresult* res_tables = PQexec(conn, "SELECT table_name FROM information_schema.tables WHERE table_name IN ('tracks', 'playlists', 'tracks_playlists');");
    assert(PQntuples(res_tables) == 3);
    PQclear(res_tables);

    // Check to see if the tracks_playlists_insert_func procedure exists
    PGresult* res_func = PQexec(conn, "SELECT routine_name FROM information_schema.routines WHERE routine_name = 'tracks_playlists_insert_func';");
    assert(PQntuples(res_func) == 1);
    PQclear(res_func);

    // Check to see if the tracks_playlists_insert_trg trigger exists
    // Two records should be returned; one for ON INSERT, one for ON DELETE
    PGresult* res_trg = PQexec(conn, "SELECT trigger_name FROM information_schema.triggers WHERE trigger_name = 'tracks_playlists_insert_trg';");
    assert(PQntuples(res_trg) == 2);
    PQclear(res_trg);

    // Check to see if the default library playlist was properly created
    PGresult *res_playlist = PQexec(conn, "SELECT id, title, length FROM playlists WHERE id=1");
    assert(PQntuples(res_playlist) == 1);
    PQclear(res_playlist);

    // clean up, I guess
    library.DestroyDatabase();

    cout << "OK" << endl;
}

/**
 * \brief Ensure database objects are deleted when asked
 */
void Test_Library_DestroyDatabase()
{
    cout << "Test_Library_DestroyDatabase... ";
    CLibrary library;

    // Make sure all tables and such exist
    library.PrepareDatabase();

    library.DestroyDatabase();

    // We need the unwrapped connection object for arbitrary queries to test
    PGconn *conn = library.GetConnection();

    // Check to see if tables exist
    PGresult* res_tables = PQexec(conn, "SELECT table_name FROM information_schema.tables WHERE table_name IN ('tracks', 'playlists', 'tracks_playlists');");
    assert(PQntuples(res_tables) == 0);
    PQclear(res_tables);

    // Check to see if the tracks_playlists_insert_func procedure exists
    PGresult* res_func = PQexec(conn, "SELECT routine_name FROM information_schema.routines WHERE routine_name = 'tracks_playlists_insert_func';");
    assert(PQntuples(res_func) == 0);
    PQclear(res_func);

    // Check to see if the tracks_playlists_insert_trg trigger exists
    PGresult* res_trg = PQexec(conn, "SELECT trigger_name FROM information_schema.triggers WHERE trigger_name = 'tracks_playlists_insert_trg';");
    assert(PQntuples(res_trg) == 0);
    PQclear(res_trg);

    cout << "OK" << endl;

}

/**
 * \brief Ensure tracks can be added properly
 *
 * Maybe later I'll figure out how to verify that the date is correct
 */
void Test_Library_AddTrack()
{
    cout << "Test_Library_AddTrack... ";
    CLibrary library;

    // Make sure all tables and such exist
    library.PrepareDatabase();

    std::string id = library.AddTrack(track1);

    // We need the unwrapped connection object for arbitrary queries to test
    PGconn* conn = library.GetConnection();

    std::string query = "SELECT * FROM tracks WHERE id=";

    // id should never not be a numeric string but safety first
    char escaped_id[30];
    PQescapeStringConn(conn, escaped_id, id.c_str(), 30, 0);
    query.append(escaped_id);

    PGresult* res = PQexec(conn, query.c_str());

    // Is there actually a row inserted with our id?
    std::string new_id(PQgetvalue(res, 0, 0));
    assert(new_id == id);

    // Is it the same one (same filepath) that we entered?
    std::string filepath(PQgetvalue(res, 0, 1));
    assert(filepath == track1);

    PQclear(res);

    library.DestroyDatabase();

    cout << "OK" << endl;
}

/**
 * \brief Ensure playlists can be added properly
 */
void Test_Library_AddPlaylist()
{
    cout << "Test_Library_AddPlaylist... ";
    CLibrary library;

    // Make sure all tables and such exist
    library.PrepareDatabase();

    std::string id = library.AddPlaylist(playlist1);

    // We need the unwrapped connection object for arbitrary queries to test
    PGconn *conn = library.GetConnection();

    std::string query = "SELECT * FROM playlists WHERE id=";

    // id should never not be a numeric string but safety first
    char escaped_id[30];
    PQescapeStringConn(conn, escaped_id, id.c_str(), 30, 0);
    query.append(escaped_id);

    PGresult *res = PQexec(conn, query.c_str());

    // Is there actually a row inserted with our id?
    std::string new_id(PQgetvalue(res, 0, 0));
    assert(new_id == id);

    // Is it the same one (same title) that we entered?
    std::string title(PQgetvalue(res, 0, 1));
    assert(title == playlist1);

    PQclear(res);

    library.DestroyDatabase();

    cout << "OK" << endl;
}

/**
 * \brief Ensure tracks are removed from the database properly
 * This includes removal from all playlists they have been added to
 */
void Test_Library_RemoveTrack()
{
    cout << "Test_Library_RemoveTrack... ";
    CLibrary library;

    // Make sure all tables and such exist
    library.PrepareDatabase();

    std::string track1_id = library.AddTrack(track1);
    std::string track2_id = library.AddTrack(track2);

    std::string playlist_id = library.AddPlaylist("test");
    CPlaylist playlist(&library, playlist_id);
    playlist.AppendTrack(track1_id);
    playlist.AppendTrack(track2_id);

    PGconn *conn = library.GetConnection();

    std::string query = "SELECT * FROM tracks_playlists WHERE track_id = ";
    char escaped_track1_id[30];
    char escaped_track2_id[30];
    PQescapeStringConn(conn, escaped_track1_id, track1_id.c_str(), 30, 0);
    PQescapeStringConn(conn, escaped_track2_id, track2_id.c_str(), 30, 0);

    std::string query1 = query + escaped_track1_id;
    std::string query2 = query + escaped_track2_id;
    PGresult *res1 = PQexec(conn, query1.c_str());
    PGresult *res2 = PQexec(conn, query2.c_str());

    assert(PQntuples(res1) == 2);
    assert(PQntuples(res2) == 2);

    PQclear(res1);
    PQclear(res2);

    library.RemoveTrack(track1_id);
    library.RemoveTrack(track2_id);

    std::string query3 = "SELECT * FROM tracks WHERE id = ";
    query3.append(escaped_track1_id);
    std::string query4 = "SELECT * FROM tracks WHERE id = ";
    query3.append(escaped_track2_id);
    PGresult *res3 = PQexec(conn, query3.c_str());
    PGresult *res4 = PQexec(conn, query4.c_str());

    res1 = PQexec(conn, query1.c_str());
    res2 = PQexec(conn, query2.c_str());

    assert(PQntuples(res1) == 0);
    assert(PQntuples(res2) == 0);
    assert(PQntuples(res3) == 0);
    assert(PQntuples(res4) == 0);

    PQclear(res1);
    PQclear(res2);
    PQclear(res3);
    PQclear(res4);

    library.DestroyDatabase();

    cout << "OK" << endl;
}

void Test_Library_RemovePlaylist()
{
    cout << "Test_Library_RemovePlaylist... ";
    CLibrary library;

    // Make sure all tables and such exist
    library.PrepareDatabase();

    std::string track1_id = library.AddTrack(track1);
    std::string track2_id = library.AddTrack(track2);

    std::string playlist_id = library.AddPlaylist("test");
    CPlaylist playlist(&library, playlist_id);
    playlist.AppendTrack(track1_id);
    playlist.AppendTrack(track2_id);

    PGconn *conn = library.GetConnection();

    std::string query = "SELECT * FROM tracks_playlists WHERE playlist_id = ";
    char escaped_playlist_id[30];
    PQescapeStringConn(conn, escaped_playlist_id, playlist_id.c_str(), 30, 0);

    query.append(escaped_playlist_id);
    PGresult *res = PQexec(conn, query.c_str());

    assert(PQntuples(res) == 2);

    PQclear(res);

    library.RemovePlaylist(playlist_id);

    res = PQexec(conn, query.c_str());

    assert(PQntuples(res) == 0);

    PQclear(res);

    library.DestroyDatabase();

    cout << "OK" << endl;
}

/**
 * \brief Ensure playlist objects are constructed properly
 *
 * Note: This isn't done. Verify that non-empty playlists are properly
 * populated with Track objects when the Track class is implemented
 */
void Test_Playlist_Constructors()
{
    cout << "Test_Playlist_Constructors... ";
    CLibrary library;

    // Make sure all tables and such exist
    library.PrepareDatabase();

    // Should create an empty playlist
    CPlaylist playlist(&library);
    assert(playlist.GetId() == "temp");
    assert(playlist.GetTitle() == "working playlist");
    assert(playlist.GetLength() == "0");
    assert(playlist.GetLibrary() == &library);

    // Should create an object for the default playlist in db
    CPlaylist playlist2(&library, "1");
    assert(playlist2.GetId() == "1");
    assert(playlist2.GetTitle() == "library");
    assert(playlist2.GetLength() == "0");
    assert(playlist2.GetLibrary() == &library);

    library.DestroyDatabase();

    cout << "OK" << endl;
}

/**
 * \brief Ensure tracks can be properly appended to playlists
 *
 * Note: This isn't done. Verify that Track objects are properly appended
 * to their containers when the Track Class is implemented
 */
void Test_Playlist_AppendTrack()
{
    cout << "Test_Playlist_AppendTrack... ";
    CLibrary library;

    // Make sure all tables and such exist
    library.PrepareDatabase();

    // Adding tracks that can be added to playlists
    std::string track1_id = library.AddTrack(track1);
    std::string track2_id = library.AddTrack(track2);

    CPlaylist db_playlist(&library, "1"); // a playlist from the database
    CPlaylist temp_playlist(&library);    // a temp playlist not in the database

    std::string association1 = db_playlist.AppendTrack(track1_id);
    std::string association2 = db_playlist.AppendTrack(track2_id);

    std::string association3 = temp_playlist.AppendTrack(track1_id);
    std::string association4 = temp_playlist.AppendTrack(track2_id);

    assert(association3 == "temp");
    assert(association4 == "temp");

    // Insert logic to make sure Track objects were appropriately added to the container
    // for both playlists

    // Verify that the proper database records were created
    if (db_playlist.GetId() != "temp")
    {
        PGconn *conn = library.GetConnection();

        std::string query = "SELECT id, playlist_id, track_id, position FROM tracks_playlists WHERE playlist_id = ";

        char escaped_playlist_id[30];
        PQescapeStringConn(conn, escaped_playlist_id, db_playlist.GetId().c_str(), 30, 0);

        query.append(escaped_playlist_id);

        PGresult *res = PQexec(conn, query.c_str());

        // 0 and 1 should be auto-created when tracks are added
        // 2 and 3 are the new records we just added
        std::string association1_id(PQgetvalue(res, 2, 0));
        std::string association1_playlist_id(PQgetvalue(res, 2, 1));
        std::string association1_track_id(PQgetvalue(res, 2, 2));
        std::string association1_position(PQgetvalue(res, 2, 3));
        std::string association2_id(PQgetvalue(res, 3, 0));
        std::string association2_playlist_id(PQgetvalue(res, 3, 1));
        std::string association2_track_id(PQgetvalue(res, 3, 2));
        std::string association2_position(PQgetvalue(res, 3, 3));

        assert(association1 == association1_id);
        assert(db_playlist.GetId() == association1_playlist_id);
        assert(track1_id == association1_track_id);
        assert("3" == association1_position);
        assert(association2 == association2_id);
        assert(db_playlist.GetId() == association2_playlist_id);
        assert(track2_id == association2_track_id);
        assert("4" == association2_position);

        PQclear(res);
    }
    else
    {
        assert(association1 == "temp");
        assert(association2 == "temp");
    }

    library.DestroyDatabase();

    cout << "OK" << endl;
}
<commit_msg>added a section to the library addtrack test<commit_after>/**
 * \file tests.cpp
 * \author Matt Hammerly
 * \brief This file contains int main() which will run tests as they're written
 */
#include <iostream>
#include <cassert>
#include "Library.h"
#include "Track.h"
#include "Playlist.h"
#include "tests.h"

using std::cout; using std::endl;

/// File location of one track
const std::string track1 = "/home/matt/hdd/dev/cpp/musicmanager/test_data/Gregory And The Hawk/The Boats & Birds EP/01 Boats & Birds.mp3";

/// File location of another track
const std::string track2 = "/home/matt/hdd/dev/cpp/musicmanager/test_data/Beirut/The Flying Club Cup/05 la banlieue.m4a";

/// Title of one playlist
const std::string playlist1 = "test1";

/**
 * \brief Main entry point of program, where tests will be run
 */
int main()
{

    Test_Library_Constructor();

    Test_Library_PrepareDatabase();

    Test_Library_DestroyDatabase();

    Test_Library_AddTrack();

    Test_Library_AddPlaylist();

    Test_Library_RemoveTrack();

    Test_Library_RemovePlaylist();

    Test_Playlist_Constructors();

    Test_Playlist_AppendTrack();

    // So I can poke around manually after running tests
    //CLibrary library;
    //library.PrepareDatabase();

    return 0;
}

/**
 * \brief I mean, there's constructor logic, let's test it.
 */
void Test_Library_Constructor()
{
    cout << "Test_Library_Constructor... ";
    CLibrary library;
    assert(library.GetStatus() == CONNECTION_OK);
    cout << "OK" << endl;
}

/**
 * \brief Ensure all expected database objects are created
 */
void Test_Library_PrepareDatabase()
{
    cout << "Test_Library_PrepareDatabase... ";
    CLibrary library;

    library.PrepareDatabase();

    // We need the unwrapped connection object for arbitrary queries to test
    PGconn *conn = library.GetConnection();

    // Check to see if tables exist
    // Three rows should be returned; one for tracks, one for playlists, one for tracks_playlists
    PGresult* res_tables = PQexec(conn, "SELECT table_name FROM information_schema.tables WHERE table_name IN ('tracks', 'playlists', 'tracks_playlists');");
    assert(PQntuples(res_tables) == 3);
    PQclear(res_tables);

    // Check to see if the tracks_playlists_insert_func procedure exists
    PGresult* res_func = PQexec(conn, "SELECT routine_name FROM information_schema.routines WHERE routine_name = 'tracks_playlists_insert_func';");
    assert(PQntuples(res_func) == 1);
    PQclear(res_func);

    // Check to see if the tracks_playlists_insert_trg trigger exists
    // Two records should be returned; one for ON INSERT, one for ON DELETE
    PGresult* res_trg = PQexec(conn, "SELECT trigger_name FROM information_schema.triggers WHERE trigger_name = 'tracks_playlists_insert_trg';");
    assert(PQntuples(res_trg) == 2);
    PQclear(res_trg);

    // Check to see if the default library playlist was properly created
    PGresult *res_playlist = PQexec(conn, "SELECT id, title, length FROM playlists WHERE id=1");
    assert(PQntuples(res_playlist) == 1);
    PQclear(res_playlist);

    // clean up, I guess
    library.DestroyDatabase();

    cout << "OK" << endl;
}

/**
 * \brief Ensure database objects are deleted when asked
 */
void Test_Library_DestroyDatabase()
{
    cout << "Test_Library_DestroyDatabase... ";
    CLibrary library;

    // Make sure all tables and such exist
    library.PrepareDatabase();

    library.DestroyDatabase();

    // We need the unwrapped connection object for arbitrary queries to test
    PGconn *conn = library.GetConnection();

    // Check to see if tables exist
    PGresult* res_tables = PQexec(conn, "SELECT table_name FROM information_schema.tables WHERE table_name IN ('tracks', 'playlists', 'tracks_playlists');");
    assert(PQntuples(res_tables) == 0);
    PQclear(res_tables);

    // Check to see if the tracks_playlists_insert_func procedure exists
    PGresult* res_func = PQexec(conn, "SELECT routine_name FROM information_schema.routines WHERE routine_name = 'tracks_playlists_insert_func';");
    assert(PQntuples(res_func) == 0);
    PQclear(res_func);

    // Check to see if the tracks_playlists_insert_trg trigger exists
    PGresult* res_trg = PQexec(conn, "SELECT trigger_name FROM information_schema.triggers WHERE trigger_name = 'tracks_playlists_insert_trg';");
    assert(PQntuples(res_trg) == 0);
    PQclear(res_trg);

    cout << "OK" << endl;

}

/**
 * \brief Ensure tracks can be added properly
 *
 * Maybe later I'll figure out how to verify that the date is correct
 */
void Test_Library_AddTrack()
{
    cout << "Test_Library_AddTrack... ";
    CLibrary library;

    // Make sure all tables and such exist
    library.PrepareDatabase();

    std::string id = library.AddTrack(track1);

    // We need the unwrapped connection object for arbitrary queries to test
    PGconn* conn = library.GetConnection();

    std::string query = "SELECT * FROM tracks WHERE id=";

    // id should never not be a numeric string but safety first
    char escaped_id[30];
    PQescapeStringConn(conn, escaped_id, id.c_str(), 30, 0);
    query.append(escaped_id);

    PGresult* res = PQexec(conn, query.c_str());

    // Is there actually a row inserted with our id?
    std::string new_id(PQgetvalue(res, 0, 0));
    assert(new_id == id);

    // Is it the same one (same filepath) that we entered?
    std::string filepath(PQgetvalue(res, 0, 1));
    assert(filepath == track1);

    PQclear(res);

    std::string query2 = "SELECT * FROM tracks_playlists WHERE track_id = " + id;
    res = PQexec(conn, query2.c_str());
    assert(PQntuples(res) == 1);

    PQclear(res);

    library.DestroyDatabase();

    cout << "OK" << endl;
}

/**
 * \brief Ensure playlists can be added properly
 */
void Test_Library_AddPlaylist()
{
    cout << "Test_Library_AddPlaylist... ";
    CLibrary library;

    // Make sure all tables and such exist
    library.PrepareDatabase();

    std::string id = library.AddPlaylist(playlist1);

    // We need the unwrapped connection object for arbitrary queries to test
    PGconn *conn = library.GetConnection();

    std::string query = "SELECT * FROM playlists WHERE id=";

    // id should never not be a numeric string but safety first
    char escaped_id[30];
    PQescapeStringConn(conn, escaped_id, id.c_str(), 30, 0);
    query.append(escaped_id);

    PGresult *res = PQexec(conn, query.c_str());

    // Is there actually a row inserted with our id?
    std::string new_id(PQgetvalue(res, 0, 0));
    assert(new_id == id);

    // Is it the same one (same title) that we entered?
    std::string title(PQgetvalue(res, 0, 1));
    assert(title == playlist1);

    PQclear(res);

    library.DestroyDatabase();

    cout << "OK" << endl;
}

/**
 * \brief Ensure tracks are removed from the database properly
 * This includes removal from all playlists they have been added to
 */
void Test_Library_RemoveTrack()
{
    cout << "Test_Library_RemoveTrack... ";
    CLibrary library;

    // Make sure all tables and such exist
    library.PrepareDatabase();

    std::string track1_id = library.AddTrack(track1);
    std::string track2_id = library.AddTrack(track2);

    std::string playlist_id = library.AddPlaylist("test");
    CPlaylist playlist(&library, playlist_id);
    playlist.AppendTrack(track1_id);
    playlist.AppendTrack(track2_id);

    PGconn *conn = library.GetConnection();

    std::string query = "SELECT * FROM tracks_playlists WHERE track_id = ";
    char escaped_track1_id[30];
    char escaped_track2_id[30];
    PQescapeStringConn(conn, escaped_track1_id, track1_id.c_str(), 30, 0);
    PQescapeStringConn(conn, escaped_track2_id, track2_id.c_str(), 30, 0);

    std::string query1 = query + escaped_track1_id;
    std::string query2 = query + escaped_track2_id;
    PGresult *res1 = PQexec(conn, query1.c_str());
    PGresult *res2 = PQexec(conn, query2.c_str());

    assert(PQntuples(res1) == 2);
    assert(PQntuples(res2) == 2);

    PQclear(res1);
    PQclear(res2);

    library.RemoveTrack(track1_id);
    library.RemoveTrack(track2_id);

    std::string query3 = "SELECT * FROM tracks WHERE id = ";
    query3.append(escaped_track1_id);
    std::string query4 = "SELECT * FROM tracks WHERE id = ";
    query3.append(escaped_track2_id);
    PGresult *res3 = PQexec(conn, query3.c_str());
    PGresult *res4 = PQexec(conn, query4.c_str());

    res1 = PQexec(conn, query1.c_str());
    res2 = PQexec(conn, query2.c_str());

    assert(PQntuples(res1) == 0);
    assert(PQntuples(res2) == 0);
    assert(PQntuples(res3) == 0);
    assert(PQntuples(res4) == 0);

    PQclear(res1);
    PQclear(res2);
    PQclear(res3);
    PQclear(res4);

    library.DestroyDatabase();

    cout << "OK" << endl;
}

void Test_Library_RemovePlaylist()
{
    cout << "Test_Library_RemovePlaylist... ";
    CLibrary library;

    // Make sure all tables and such exist
    library.PrepareDatabase();

    std::string track1_id = library.AddTrack(track1);
    std::string track2_id = library.AddTrack(track2);

    std::string playlist_id = library.AddPlaylist("test");
    CPlaylist playlist(&library, playlist_id);
    playlist.AppendTrack(track1_id);
    playlist.AppendTrack(track2_id);

    PGconn *conn = library.GetConnection();

    std::string query = "SELECT * FROM tracks_playlists WHERE playlist_id = ";
    char escaped_playlist_id[30];
    PQescapeStringConn(conn, escaped_playlist_id, playlist_id.c_str(), 30, 0);

    query.append(escaped_playlist_id);
    PGresult *res = PQexec(conn, query.c_str());

    assert(PQntuples(res) == 2);

    PQclear(res);

    library.RemovePlaylist(playlist_id);

    res = PQexec(conn, query.c_str());

    assert(PQntuples(res) == 0);

    PQclear(res);

    library.DestroyDatabase();

    cout << "OK" << endl;
}

/**
 * \brief Ensure playlist objects are constructed properly
 *
 * Note: This isn't done. Verify that non-empty playlists are properly
 * populated with Track objects when the Track class is implemented
 */
void Test_Playlist_Constructors()
{
    cout << "Test_Playlist_Constructors... ";
    CLibrary library;

    // Make sure all tables and such exist
    library.PrepareDatabase();

    // Should create an empty playlist
    CPlaylist playlist(&library);
    assert(playlist.GetId() == "temp");
    assert(playlist.GetTitle() == "working playlist");
    assert(playlist.GetLength() == "0");
    assert(playlist.GetLibrary() == &library);

    // Should create an object for the default playlist in db
    CPlaylist playlist2(&library, "1");
    assert(playlist2.GetId() == "1");
    assert(playlist2.GetTitle() == "library");
    assert(playlist2.GetLength() == "0");
    assert(playlist2.GetLibrary() == &library);

    library.DestroyDatabase();

    cout << "OK" << endl;
}

/**
 * \brief Ensure tracks can be properly appended to playlists
 *
 * Note: This isn't done. Verify that Track objects are properly appended
 * to their containers when the Track Class is implemented
 */
void Test_Playlist_AppendTrack()
{
    cout << "Test_Playlist_AppendTrack... ";
    CLibrary library;

    // Make sure all tables and such exist
    library.PrepareDatabase();

    // Adding tracks that can be added to playlists
    std::string track1_id = library.AddTrack(track1);
    std::string track2_id = library.AddTrack(track2);

    CPlaylist db_playlist(&library, "1"); // a playlist from the database
    CPlaylist temp_playlist(&library);    // a temp playlist not in the database

    std::string association1 = db_playlist.AppendTrack(track1_id);
    std::string association2 = db_playlist.AppendTrack(track2_id);

    std::string association3 = temp_playlist.AppendTrack(track1_id);
    std::string association4 = temp_playlist.AppendTrack(track2_id);

    assert(association3 == "temp");
    assert(association4 == "temp");

    // Insert logic to make sure Track objects were appropriately added to the container
    // for both playlists

    // Verify that the proper database records were created
    if (db_playlist.GetId() != "temp")
    {
        PGconn *conn = library.GetConnection();

        std::string query = "SELECT id, playlist_id, track_id, position FROM tracks_playlists WHERE playlist_id = ";

        char escaped_playlist_id[30];
        PQescapeStringConn(conn, escaped_playlist_id, db_playlist.GetId().c_str(), 30, 0);

        query.append(escaped_playlist_id);

        PGresult *res = PQexec(conn, query.c_str());

        // 0 and 1 should be auto-created when tracks are added
        // 2 and 3 are the new records we just added
        std::string association1_id(PQgetvalue(res, 2, 0));
        std::string association1_playlist_id(PQgetvalue(res, 2, 1));
        std::string association1_track_id(PQgetvalue(res, 2, 2));
        std::string association1_position(PQgetvalue(res, 2, 3));
        std::string association2_id(PQgetvalue(res, 3, 0));
        std::string association2_playlist_id(PQgetvalue(res, 3, 1));
        std::string association2_track_id(PQgetvalue(res, 3, 2));
        std::string association2_position(PQgetvalue(res, 3, 3));

        assert(association1 == association1_id);
        assert(db_playlist.GetId() == association1_playlist_id);
        assert(track1_id == association1_track_id);
        assert("3" == association1_position);
        assert(association2 == association2_id);
        assert(db_playlist.GetId() == association2_playlist_id);
        assert(track2_id == association2_track_id);
        assert("4" == association2_position);

        PQclear(res);
    }
    else
    {
        assert(association1 == "temp");
        assert(association2 == "temp");
    }

    library.DestroyDatabase();

    cout << "OK" << endl;
}
<|endoftext|>