//
// I have used all persistancy classes for some time.
// all provide consistant error-free behaviour and sufficient performance
//
// (dis)advantages of each method:
//
// REGISTRY / DATABASE FILE SYSTEM
// -------------------------------------- ------------------------------------------
// will not survive hard reset will survive hard reset
// not easy to corrupt visible via file explorer, hence prone to
// deletion/tampering/corruption
// independant of media storage requires storage card. Is created on largest
// capacity storage card (if more than 1 present)
// can have only 1 book collection resides on media storage, hence one can have
// multiple book collections each residing on a
// different card with its own library
// adequate mechanism very rudamentary directory/file mechanism
//
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Collections;
using System.IO;
using System.Reflection;
namespace abPlayer
{
public partial class frmLibrary : Form
{
// constants: the listview is used to display alternatively books and files
// with different font sizes (so a book can be selected without the stylus)
private const float BOOKS_FONT_SIZE = 14.0F;
private const float FILES_FONT_SIZE = 10.0F;
private const String FONT_NAME = "Tahoma";
// icon constants
private const int BOOK_ICON = 0;
private const int BACK_ICON = 1;
private const int BOOKSHELF_ICON = 2;
// menu constants
private const String MENU_BOOK = "Delete Book";
private const String MENU_BOOKSHELF = "Delete Bookshelf";
private const String MENU_ROOT = "... (Root)";
// form used to create a new book and add files to an existing book
public frmFileSelector FileSelector = null;
// class used for persistant storage
public cBasePersistancy BookStore = null;
// when the listview displays books, the selected index is used.
// when switching to file view - SelectedBook is used to maintain
// the relevant book
public String SelectedBook = "";
private String ActiveBookShelf = "";
private String InfoText = "";
public frmLibrary()
{
InitializeComponent();
// ============= choose one persistancy system =======================================
//BookStore = new cXMLPersistancy(Assembly.GetExecutingAssembly().GetName().Name);
BookStore = new cFileSystemPersistantcy(Assembly.GetExecutingAssembly().GetName().Name);
//BookStore = new cRegistryPersistancy(Assembly.GetExecutingAssembly().GetName().Name);
//BookStore = new cDatabasePersistantcy(Assembly.GetExecutingAssembly().GetName().Name);
// ===================================================================================
// form used to create a new book and add files to an existing book
FileSelector = new frmFileSelector();
}
// override the ShowDialog function so we can assure we always
// start on books view
public new DialogResult ShowDialog()
{
ActiveBookShelf = BookStore.GetBooksBookshelf(BookStore.ActiveBookName);
ShowBooks();
lsv.Focus();
return base.ShowDialog();
}
// we switch between books/files display
private void picSwitch_Click(object sender, EventArgs e)
{
FileSelector.FlashControl((Control)sender);
// we can switch to files only if there is a selected book
if (lsv.Font.Size == BOOKS_FONT_SIZE)
{
ListViewItem lsvi = lsvSelectedItem;
if (lsvi != null)
if (lsvi.ImageIndex == BOOK_ICON)
{
// save the book that we are going to show its files
SelectedBook = lsvSelectedItem.Text;
// switch display to files
ShowFiles(SelectedBook);
}
}
else
ShowBooks();
}
void ShowBooks()
{
// set font size
lsv.Items.Clear();
lsv.Font = new Font(FONT_NAME, BOOKS_FONT_SIZE, FontStyle.Regular);
lsv.SmallImageList = imlBooks;
LoadLsv((SelectedBook == "") ? BookStore.ActiveBookName :
SelectedBook);
SelectedBook = "";
}
// load listview with relevant items (books & bookshelves)
// and select a book
void LoadLsv(String selectedBook)
{
lsv.Items.Clear();
if (ActiveBookShelf.Length == 0)
{
foreach(String bookshelf in BookStore.Bookshelves)
{
lsv.Items.Add(new ListViewItem(bookshelf)).ImageIndex = BOOKSHELF_ICON;
}
foreach (String book in BookStore.GetBookshelfBooks(""))
{
lsv.Items.Add(new ListViewItem(book)).ImageIndex = BOOK_ICON;
}
}
else
{
lsv.Items.Add(new ListViewItem("... [" +
ActiveBookShelf + "]")).ImageIndex = BACK_ICON;
foreach (String book in BookStore.GetBookshelfBooks(ActiveBookShelf))
{
lsv.Items.Add(new ListViewItem(book)).ImageIndex = BOOK_ICON;
}
}
lsvSelectItem(selectedBook);
}
// select a listviewitem
void lsvSelectItem(String name)
{
foreach (ListViewItem item in lsv.Items)
item.Selected = (item.Text == name);
}
void ShowFiles(String book)
{
lsv.Items.Clear();
// set relevant font size
lsv.Font = new Font(FONT_NAME, FILES_FONT_SIZE, FontStyle.Regular);
lsv.SmallImageList = imlFiles;
// on books with a large number of files this may take some time -
// we show the wait cursor to indicate it
Cursor.Current = Cursors.WaitCursor;
// extract the file name from the file path and load into the listview
foreach (String file in BookStore.GetBookFiles(book))
{
lsv.Items.Add(new ListViewItem(new FileInfo(file).Name)).ImageIndex = 0;
}
int n = BookStore.GetBookProperty(eBookProperty.BOOKMARK_ENTRY, book);
lsv.Items[n].Selected = true;
// wait is over
Cursor.Current = Cursors.Default;
// edt is used to rename a book so is irelevant in file display
edt.Enabled = false;
DrawStatusBar(book, BOOK_ICON);
}
private void picMenu_Click(object sender, EventArgs e)
{
FileSelector.FlashControl((Control)sender);
// if we are in books diaplay - show books context menu
// otherwise show files context menu
if (lsv.Font.Size == BOOKS_FONT_SIZE)
pumBooks.Show((Control)sender, new Point(picMenu.Left,
pnlTop.Height));
else
pumEntries.Show((Control)sender, new Point(picMenu.Left,
pnlTop.Height));
}
private void pumBooks_Popup(object sender, EventArgs e)
{
// new bookshelf only on top level
mnuNewBookshelf.Enabled = (ActiveBookShelf.Length == 0);
// delete entry only available if an item is selected
ListViewItem lsvi = lsvSelectedItem;
mnuDel.Enabled = mnuMove.Enabled = false;
// set enabled, text and submenues depending
// on the type of the selected item
if(lsvi != null)
switch (lsvi.ImageIndex)
{
case BOOKSHELF_ICON:
mnuDel.Enabled = true;
mnuDel.Text = MENU_BOOKSHELF;
mnuMove.Enabled = false;
break;
case BOOK_ICON:
mnuDel.Enabled = true;
mnuDel.Text = MENU_BOOK;
mnuMove.Enabled = true;
// populate mnuMove submenu items (bbokshelves)
// make all except the current enabled
String oldBookshelf = BookStore.GetBooksBookshelf(lsvi.Text);
mnuMove.MenuItems.Clear();
MenuItem mnu = new MenuItem();
mnuMove.MenuItems.Add(mnu);
mnu.Text = MENU_ROOT;
mnu.Enabled = (oldBookshelf.Length > 0);
mnu.Click += new EventHandler(mnu_Click);
foreach (String bookshelf in BookStore.Bookshelves)
{
mnu = new MenuItem();
mnuMove.MenuItems.Add(mnu);
mnu.Text = bookshelf;
mnu.Enabled = (oldBookshelf != bookshelf);
mnu.Click += new EventHandler(mnu_Click);
}
break;
default: // must be a "go one level up" item
mnuDel.Enabled = false;
mnuMove.Enabled = false;
break;
}
}
void mnu_Click(object sender, EventArgs e)
{
// move book
ListViewItem lsvi = lsvSelectedItem;
if (lsvi == null) return;
if (lsvi.ImageIndex != BOOK_ICON) return;
BookStore.SetBooksBookshelf(lsvi.Text,
(((MenuItem)sender).Text == MENU_ROOT)? "": ((MenuItem)sender).Text);
LoadLsv(lsvi.Text);
}
private void pumEntries_Popup(object sender, EventArgs e)
{
// delete entery relevant only if a file is selected
mnuDelE.Enabled = ((lsv.Items.Count > 0) &&
(lsvSelectedItem != null));
// delete all relevant only if there are files
mnuDelAll.Enabled = (lsv.Items.Count > 0);
}
private void lsv_SelectedIndexChanged(object sender, EventArgs e)
{
ListViewItem lsvi = lsvSelectedItem;
if (lsvi != null)
lsv.EnsureVisible(lsvi.Index);
// if we are not in books display - nothing to do
if (lsv.Font.Size != BOOKS_FONT_SIZE)
{
picPlay.Enabled = (lsvSelectedItem != null);
return;
}
// edt and status bar erased
InfoText = "";
// play button enabled only if there is a selected book
// rename enabled only if there is a selected item
if (lsvi == null)
picPlay.Enabled = edt.Enabled = false;
else
{
edt.Enabled = (lsvi.ImageIndex != BACK_ICON);
picPlay.Enabled = (lsvi.ImageIndex == BOOK_ICON);
}
// if we have a renameable item ...
if (edt.Enabled)
{
edt.Text = lsvi.Text;
DrawStatusBar(lsvi.Text, lsvi.ImageIndex);
}
else
{
edt.Text = "";
pnl.Refresh();
}
}
// get listview selected item
ListViewItem lsvSelectedItem
{
get
{
try
{
return lsv.Items[lsv.SelectedIndices[0]];
}
catch{return null;}
}
}
// new bookshelf
private void mnuNewBookshelf_Click(object sender, EventArgs e)
{
// set default new book name (directory name)
String bookshelf = BookStore.MakeUnique("BOOKSHELF");
// commit to persistant storage
BookStore.NewBookshelf(bookshelf);
LoadLsv(bookshelf);
lsv.Focus();
}
// new book function
private void mnuNewBook_Click(object sender, EventArgs e)
{
// show file selector and do it if user presed the OK button
if (FileSelector.ShowDialog() == DialogResult.OK)
{
// get default new book name (directory name)
String book = BookStore.MakeUnique(FileSelector.DefaultBookName);
// commit to persistant storage
BookStore.NewBook(book, ActiveBookShelf);
ArrayList files = new ArrayList();
// get the files of the new book
files.AddRange(FileSelector.BookFiles);
// commit to persistent storage
BookStore.SetBookFiles((String[])files.ToArray(typeof(string)), book);
// load available books into the listview
LoadLsv(book);
lsv.Focus();
}
}
// delete entry (book/bookshelf)
private void mnuDel_Click(object sender, EventArgs e)
{
// only if a deleteable item is selected
ListViewItem lsvi = lsvSelectedItem;
if (lsvi != null)
{
// remove from persistant storage and listview
if (lsvi.ImageIndex == BOOK_ICON)
{
BookStore.DeleteBook(lsvi.Text);
lsv.Items.RemoveAt(lsvi.Index);
}
if (lsvi.ImageIndex == BOOKSHELF_ICON)
{
BookStore.DeleteBookshelf(lsvi.Text);
LoadLsv(BookStore.ActiveBookName);
}
}
}
// handles entry in the rename book edit box
private void edt_KeyPress(object sender, KeyPressEventArgs e)
{
// if we pressed while no book is selected
if (lsvSelectedItem == null)
{
// erase it and make the edit box loose focus
edt.Text = "";
e.Handled = true;
lsv.Focus();
}
// if Enter/Return was presed - signifies end of entry
if ((e.KeyChar == (char)Keys.Return) || (e.KeyChar == (char)Keys.Enter))
{
e.Handled = true;
// do the book rename
DoRename();
}
}
private void DoRename()
{
// if no book selected - can't rename
ListViewItem lsvi = lsvSelectedItem;
if(lsvi == null) return;
if (lsvi.ImageIndex == BACK_ICON) return;
// trim leading and trailing blanks
String item = edt.Text.Trim();
// can't tell when, but on occasions the last character of
// the book name was a null character - we remove it
int p = item.IndexOf('\0');
if (p != -1)
item = item.Substring(0, p);
// if new name same as old - nothing to do
// in file system and registry persistancies
// "ab" = "aB" = "Ab" = "AB"
if (item.ToUpper() == lsvi.Text.ToUpper())
{
lsv.Focus();
return;
}
// if we are left with an actual string (not empty)- go do it
if (item.Length > 0)
{
// ensure uniqueness
item = BookStore.MakeUnique(item);
// rename book in persistant storage
if (lsvi.ImageIndex == BOOK_ICON)
BookStore.RenameBook(item, lsvi.Text);
else
BookStore.RenameBookShelf(item, lsvi.Text);
// reload listview to maintain sort order
LoadLsv(item);
}
// put back in the edit box (in case we had to make some changes)
lsvi = lsvSelectedItem;
if (lsvi != null)
{
edt.Text = lsvi.Text;
// reflect in status bar
DrawStatusBar(lsvi.Text, lsvi.ImageIndex);
}
lsv.Focus();
}
// Add files to a book
private void mnuAddFiles_Click(object sender, EventArgs e)
{
// show file selection screen
if (FileSelector.ShowDialog() == DialogResult.OK)
{
// add the new files to the existing ones
BookStore.SetBookFiles((String[])FileSelector.BookFiles.ToArray(typeof(string)), SelectedBook);
// extract the file name from the file path and add to listview
foreach (String file in FileSelector.BookFiles)
{
lsv.Items.Add(new ListViewItem(new FileInfo(file).Name)).ImageIndex = 0;
}
// reflect in status bar
DrawStatusBar(SelectedBook, BOOK_ICON);
}
}
// delete entry
private void mnuDelE_Click(object sender, EventArgs e)
{
BookStore.DeleteBookFile(lsvSelectedItem.Index, SelectedBook);
// delete from listview
lsv.Items.RemoveAt(lsvSelectedItem.Index);
// reflect in status bar
DrawStatusBar(SelectedBook, BOOK_ICON);
}
// delete all files
private void mnuDelAll_Click(object sender, EventArgs e)
{
// clear listview
lsv.Items.Clear();
// commit
BookStore.DeleteBookFiles(SelectedBook);
DrawStatusBar(SelectedBook, BOOK_ICON);
}
// show info on status bar:
// book name, file count and current position - index and timeline
private void DrawStatusBar(String item, int itemType)
{
InfoText = (itemType == BOOK_ICON) ?
BookStore.GetBookFileCount(item) + " Files; Location: " +
(BookStore.GetBookProperty(eBookProperty.BOOKMARK_ENTRY, item) + 1).ToString() +
" / " +
LocationToString(BookStore.GetBookProperty(eBookProperty.BOOKMARK_LOCATION, item)) :
BookStore.GetBookshelfBooks(item).GetLength(0).ToString() + " Books;";
pnl.Refresh();
}
// convert timeline location to string HH:MM:SS
public String LocationToString(int loc)
{
int s = loc % 60;
int m = ((loc - s) / 60) % 60;
int h = (((loc - s) / 60) - m) / 60;
if (h > 0)
return h.ToString() + ":" +
((m < 10) ? "0" + m.ToString() + ":" : m.ToString() + ":") +
((s < 10) ? "0" + s.ToString() : s.ToString());
else
return m.ToString() + ":" +
((s < 10) ? "0" + s.ToString() : s.ToString());
}
// play selected book
private void picPlay_Click(object sender, EventArgs e)
{
FileSelector.FlashControl((Control)sender);
ActivateBook();
}
// activate a book
void ActivateBook()
{
// if we are on book display take selected index otherwise
// take book stored in SelectgedBook
// indicate to frmPlayer by OK result
if (lsv.Font.Size == BOOKS_FONT_SIZE)
{
ListViewItem lsvi = lsvSelectedItem;
if (lsvi != null)
if(lsvi.ImageIndex == BOOK_ICON)
{
BookStore.ActiveBookName = lsvi.Text;
DialogResult = DialogResult.OK;
}
}
else
{
if (BookStore.GetBookFileCount(SelectedBook) > 0)
{
ListViewItem lsvi = lsvSelectedItem;
if (lsvi != null)
{
BookStore.ActiveBookName = SelectedBook;
if (BookStore.GetBookProperty(eBookProperty.BOOKMARK_ENTRY, "") != lsvi.Index)
{
BookStore.SetBookProperty(eBookProperty.BOOKMARK_ENTRY, lsvi.Index, "");
BookStore.SetBookProperty(eBookProperty.BOOKMARK_LOCATION, 0, "");
}
}
DialogResult = DialogResult.OK;
}
}
}
// we show the input panel when the edit box (rename area) gets focus
private void edt_GotFocus(object sender, EventArgs e)
{
inp.Enabled = true;
}
// hide inp when we exit the edit box
private void NotEdt_GotFocus(object sender, EventArgs e)
{
inp.Enabled = false;
}
// cancel button pressed
// indicate by Cancel dialog result
private void picBack_Click(object sender, EventArgs e)
{
FileSelector.FlashControl((Control)sender);
SelectedBook = "";
DialogResult = DialogResult.Cancel;
}
// find uses a recursive function (findbooks) to scan the whole
// file system, it then looks for media files in each directory
// found and if exists - creates a new book for this directory
private void mnuFind_Click(object sender, EventArgs e)
{
Cursor.Current = Cursors.WaitCursor;
FindBooks("\\");
ShowBooks();
Cursor.Current = Cursors.Default;
}
private void FindBooks(String path)
{
String[] dirs = Directory.GetDirectories(path);
// sort them
Array.Sort(dirs);
foreach (String dir in dirs)
{
String[] files = FileSelector.GetMediaFiles(dir);
if (files.GetLength(0) > 0)
{
// add new book
String book = BookStore.MakeUnique("(Auto) " + new FileInfo(dir).Name);
BookStore.NewBook(book, ActiveBookShelf);
// sort
Array.Sort(files);
// add files
BookStore.SetBookFiles(files, book);
}
FindBooks(dir);
}
}
private void pnl_Paint(object sender, PaintEventArgs e)
{
// erase the whole control face with its backcolor
e.Graphics.FillRectangle(new SolidBrush(pnl.BackColor), 0, 0,
pnl.Width, pnl.Height);
// if no files are displayed - we are done
if (InfoText.Length == 0) return;
// get string sizes
SizeF strWH = e.Graphics.MeasureString(InfoText, this.Font);
// display string in mid horizontal and vertical point
e.Graphics.DrawString(InfoText, this.Font, new SolidBrush(Color.White),
4, 4);
}
private void lsv_KeyDown(object sender, KeyEventArgs e)
{
switch (e.KeyCode)
{
case Keys.Up:
NavigateLsv(-1);
break;
case Keys.Down:
NavigateLsv(1);
break;
case Keys.Enter:
ActivateEntry();
break;
}
e.Handled = true;
}
void ActivateEntry()
{
// only relevant in book display
if (lsv.Font.Size != BOOKS_FONT_SIZE) return;
ListViewItem lsvi = lsvSelectedItem;
if (lsvi == null) return;
switch (lsvi.ImageIndex)
{
case BOOKSHELF_ICON:
ActiveBookShelf = lsvi.Text;
LoadLsv(BookStore.ActiveBookName);
break;
case BACK_ICON:
ActiveBookShelf = "";
LoadLsv(BookStore.ActiveBookName);
break;
case BOOK_ICON:
ActivateBook();
break;
}
}
private void lsv_ItemActivate(object sender, EventArgs e)
{
ActivateEntry();
}
void NavigateLsv(int d)
{
if(lsv.Items.Count < 1) return;
if (lsvSelectedItem == null)
lsv.Items[0].Selected = true;
else
{
int i = lsvSelectedItem.Index + d;
if ((i >= 0) &&
(i < lsv.Items.Count))
lsv.Items[i].Selected = true;
}
}
}
}