//
//  kjots
//
//  Copyright (C) 1997 Christoph Neerfeld
//  Copyright (C) 2002-2004 Aaron J. Seigo
//  Copyright (C) 2003 Stanislav Kljuhhin
//
//  This program is free software; you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation; either version 2 of the License, or
//  (at your option) any later version.
//
//  This program 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 General Public License
//  along with this program; if not, write to the Free Software
//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
//
#include <qheader.h>
#include <qwidgetstack.h>

#include <kio/netaccess.h>

#include <kaction.h>
#include <kapplication.h>
#include <kbookmarkmenu.h>
#include <kconfigdialog.h>
#include <kdebug.h>
#include <keditcl.h>
#include <kfiledialog.h>
#include <kglobalsettings.h>
#include <kinputdialog.h>
#include <kmessagebox.h>
#include <kpopupmenu.h>
#include <krun.h>
#include <kstandarddirs.h>
#include <kstdaccel.h>
#include <ktextbrowser.h>

#include "KJotsMain.h"
#include "KJotsSettings.h"
#include "kjotsbookmarks.h"
#include "kjotsedit.h"
#include "kjotsentry.h"
#include "confpagemisc.h"
#include "confpagefont.h"

#define KJOTSVERSION "0.6"

extern KIconLoader* global_pix_loader;

//----------------------------------------------------------------------
// KJOTSMAIN
//----------------------------------------------------------------------
KJotsMain::KJotsMain(const char* name)
    : KMainWindow(0,name),
      currentEntry(0)
{
    //
    // Main widget
    //
    QWidget* f_main = new QFrame( this, "Frame_0" );
    setCentralWidget(f_main);

    splitter = new QSplitter(f_main);
    splitter->setOpaqueResize( KGlobalSettings::opaqueResize() );

    // the subject list
    subjectList = new KListView(splitter, "subjectList");
    subjectList->setRootIsDecorated(true);
    subjectList->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding));
    subjectList->setMinimumWidth(subjectList->fontMetrics().maxWidth() * 10 + 5);
    subjectList->addColumn(i18n("Pages"));
    subjectList->setFullWidth(true);
    subjectList->setHScrollBarMode(QScrollView::AlwaysOff);
    subjectList->setDefaultRenameAction(QListView::Accept);
    subjectList->setSorting(-1);
    subjectList->setAlternateBackground(QColor());
    subjectList->header()->hide();
    subjectList->setDragEnabled(true);
    subjectList->setAcceptDrops(true);
    subjectList->setItemsMovable(true);
    subjectList->setItemsRenameable(true);
    subjectList->setRenameable(0,true);

    textStack = new QWidgetStack(splitter);

    me_text = new KJotsEdit(textStack, "me_text");
    me_text->setFocusPolicy(QWidget::StrongFocus);
    me_text->setEnabled(false);
    textStack->addWidget(me_text);
    connect(me_text, SIGNAL(findSuccessful()), this, SLOT(slotFindSuccessful()));

    roTextView = new KTextBrowser(textStack, "roTextView", true);
    textStack->addWidget(roTextView);

    QVBoxLayout *bookGrid = new QVBoxLayout(f_main, KDialog::marginHint(), KDialog::spacingHint());
    bookGrid->addWidget(splitter, 0, 0);
    bookGrid->setMargin(0);
    splitter->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding, 2, 1));

    // create actions
    KAction *action = new KAction(i18n("Next Book"), QString::null, CTRL + Key_D, this,
                                  SLOT(nextBook()), actionCollection(), "go_next_book");
    action = new KAction(i18n("Previous Book"), QString::null, CTRL + SHIFT + Key_D, this,
                         SLOT(prevBook()), actionCollection(), "go_prev_book");
    action = new KAction(i18n("Next Page"), QString::null, CTRL + Key_PageDown, this,
                         SLOT(nextPage()), actionCollection(), "go_next_page");
    action = new KAction(i18n("Previous Page"), QString::null, CTRL + Key_PageUp, this,
                         SLOT(prevPage()), actionCollection(), "go_prev_page");

    action = KStdAction::openNew(this, SLOT(newEntry()), actionCollection());
    action->setText(i18n("&New Page"));
    action = new KAction(i18n("New &Book..."), "contents", CTRL + SHIFT + Key_N, this,
            SLOT(createFolder()), actionCollection(), "new_book");

    KActionMenu* actionMenu = new KActionMenu(i18n("Export Page"), actionCollection(), "save_page_to");
    action = new KAction(i18n("To Text File..."), QString::null, 0, this,
            SLOT(writePage()), actionCollection(), "save_page_to_ascii");
    action->plug(actionMenu->popupMenu());
    action = new KAction(i18n("To HTML File..."), QString::null, 0, this,
            SLOT(writePageToHTML()), actionCollection(), "save_page_to_html");
    action->plug(actionMenu->popupMenu());

    actionMenu = new KActionMenu(i18n("Export Book"), actionCollection(), "save_book_to");
    action = new KAction(i18n("To Text File..."), QString::null, 0, this,
            SLOT(writeBook()), actionCollection(), "save_book_to_ascii");
    action->plug(actionMenu->popupMenu());
    action = new KAction(i18n("To HTML File..."), QString::null, 0, this,
            SLOT(writeBookToHTML()), actionCollection(), "save_book_to_html");
    action->plug(actionMenu->popupMenu());

    action = new KAction(i18n("&Delete Page"), "editdelete", 0, this,
            SLOT(deleteEntry()), actionCollection(), "del_page");
    action = new KAction(i18n("Delete Boo&k"), "editdelete", 0, this,
            SLOT(deleteFolder()), actionCollection(), "del_folder");

    action = new KAction(i18n("Manual Save"), 0, KStdAccel::save(),
                         this, SLOT(autoSave()), actionCollection(), "manual_save");
    action = KStdAction::print(this, SLOT(slotPrint()), actionCollection());
    action = KStdAction::quit(this, SLOT(slotQuit()), actionCollection());

    action = KStdAction::cut(me_text, SLOT(cut()), actionCollection());
    connect(me_text, SIGNAL(copyAvailable(bool)), action, SLOT(setEnabled(bool)));
    action->setEnabled(false);
    action  = KStdAction::copy(me_text, SLOT(copy()), actionCollection());
    connect(me_text, SIGNAL(copyAvailable(bool)), action, SLOT(setEnabled(bool)));
    action->setEnabled(false);
    action = new KAction(i18n("Copy &into Page Title"), "editcopy", CTRL+Key_T, this,
            SLOT(copySelection()), actionCollection(), "copyIntoTitle");
    connect(me_text, SIGNAL(copyAvailable(bool)), action, SLOT(setEnabled(bool)));
    action->setEnabled(false);
    action  = KStdAction::pasteText(me_text, SLOT(paste()), actionCollection());

    action = KStdAction::find( this, SLOT( slotSearch() ), actionCollection() );
    action = KStdAction::findNext( this, SLOT( slotRepeatSearch() ), actionCollection() );
    action->setEnabled(false);
    action = KStdAction::replace( this, SLOT( slotReplace() ), actionCollection() );

    action = new KAction(i18n("Rename..."), QString::null, CTRL + Key_M, this,
            SLOT(slotRenameEntry()), actionCollection(), "rename_entry");
    action = new KAction(i18n("Insert Date"), "date", CTRL + Key_I, this,
            SLOT(insertDate()), actionCollection(), "insert_date");

    action = KStdAction::preferences(this, SLOT(configure()), actionCollection());

    KActionMenu* bookmarkMenu = new KActionMenu(i18n("&Bookmarks"), "bookmarks", actionCollection(), "bookmarks");
    KJotsBookmarks* bookmarks = new KJotsBookmarks(this);
    connect(bookmarks, SIGNAL(openPage(const QString&)), SLOT(jumpToBookmark(const QString&)));
    new KBookmarkMenu(KBookmarkManager::managerForFile(locateLocal("appdata","bookmarks.xml")),
                      bookmarks, bookmarkMenu->popupMenu(),
                      actionCollection(), true);
    setupGUI();
    /*
     * TODO: add a bookmark bar, something like this
    KToolBar* bar = dynamic_cast<KToolBar *>(child("bookmarkToolBar", "KToolBar"));
    new KBookmarkBar(KBookmarkManager::managerForFile(locateLocal("appdata","bookmarks.xml")),
                     bookmarks, bar, actionCollection(), this, "BookmarkBar");
    if (bar->count() == 0)
    {
        bar->hide();
    }
    */

    m_autosaveTimer = new QTimer(this, "autosave_timer");

    //
    // Set startup size.
    //
    if (!KJotsSettings::splitterSizes().isEmpty())
    {
        splitter->setSizes(KJotsSettings::splitterSizes());
    }

    //
    // Now load the books
    //
    loadBooks();

    updateConfiguration();
    updateMenu();

    QTimer::singleShot(0, this, SLOT(slotSetupInterface()));

    //connect new slots
    connect(subjectList, SIGNAL(selectionChanged(QListViewItem*)),
            this, SLOT(jumpToEntry(QListViewItem*)));
    connect(subjectList, SIGNAL(moved(QListViewItem*, QListViewItem*, QListViewItem*)),
            this, SLOT(slotItemMoved(QListViewItem*, QListViewItem*, QListViewItem*)));
    connect(subjectList, SIGNAL(expanded(QListViewItem*)),
            this, SLOT(slotExpandBook(QListViewItem*)));
    connect(subjectList, SIGNAL(collapsed(QListViewItem*)),
            this, SLOT(jumpToEntry(QListViewItem*)));
    connect(subjectList, SIGNAL(contextMenu(KListView*,QListViewItem*,const QPoint&)),
            this, SLOT(showListviewContextMenu(KListView*,QListViewItem*,const QPoint&)));
    connect(subjectList, SIGNAL(itemRenamed(QListViewItem*,const QString&,int)),
            this, SLOT(slotItemRenamed(QListViewItem*,const QString&,int)));
    connect(roTextView, SIGNAL(linkClicked(const QString&)),
            this, SLOT(bookLinkClicked(const QString&)));
    connect(m_autosaveTimer, SIGNAL(timeout()),
            this, SLOT(autoSave()));
}

void KJotsMain::slotSetupInterface()
{
    if (subjectList->childCount() == 0)
        createFolder();
}

void KJotsMain::loadBooks()
{
    QDir dir(locateLocal("appdata",""));
    QStringList files = dir.entryList(QDir::Files|QDir::Readable);
    QStringList openBooks = KJotsSettings::openBooks();
    QString currentBookName = KJotsSettings::currentBook();
    int currentPageNo = KJotsSettings::currentPage();

    for (QStringList::Iterator it = files.begin(); it != files.end(); it++)
    {
        if (!KJotsBook::isBookFile(dir.absFilePath(*it)))
        {
            continue;
        }

        QString decodedFilename = (*it);
        KJotsBook* book = new KJotsBook(subjectList, decodedFilename, 0);
        if (openBooks.find(decodedFilename) != openBooks.end())
        {
            book->openBook();
            book->setOpen(true);
        }

        if (decodedFilename == currentBookName)
        {
            /*
             * this shouldn't be necessary, but in case something goes awry
             * and the current book doesn't end up in the open books list,
             * we may as well be safer than sorry
             */
            book->openBook();

            if (currentPageNo == -1)
            {
                subjectList->setSelected(book, true);
                jumpToEntry(book);
            }
            else
            {
                book->setOpen(true); //again, shouldn't be necessary but who knows?

                QListViewItem* page = book->firstChild();
                for (int i = 0; i < currentPageNo && page; ++i)
                {
                    page = page->nextSibling();
                }

                if (!page)
                {
                    page = book->firstChild();
                }

                if (page)
                {
                    subjectList->setSelected(page, true);
                }

                jumpToEntry(page);
            }
        }
    }
}

bool KJotsMain::createFolder()
{
    bool ok;

    QString name = KInputDialog::getText( i18n( "New Book" ),
        i18n( "Book name:" ), QString::null, &ok, this );

    if (!ok)
        return false;

    if (subjectList->findItem(name,0))
    {
        KMessageBox::sorry(topLevelWidget(),i18n("A book with this name already exists."),"KJots");
        return false;
    }

    KJotsBook* book = new KJotsBook(subjectList, name);
    book->setOpen(true);
    subjectList->setSelected(book->firstChild(), true);
    return true;
}

void KJotsMain::slotRenameEntry()
{
    KJotsEntryBase* entry = dynamic_cast<KJotsEntryBase*>(subjectList->currentItem());

    if (entry)
    {
        entry->rename();
        jumpToEntry(entry, true); //reflect the changes on screen
    }
}

void KJotsMain::deleteFolder()
{
    KJotsBook* b = currentBook();

    if (!b)
        return;

    QString msg = i18n("<qt>Are you sure you want to delete the <strong>%1</strong> book?</qt>");
    int result = KMessageBox::warningContinueCancel(topLevelWidget(), msg.arg(b->subject()), i18n("Delete Book"),KStdGuiItem::del());

    if (result!=KMessageBox::Continue)
        return;

    KJotsBook* nxt=dynamic_cast<KJotsBook*>(b->nextSibling());

    b->setDeleteFlag();
    currentEntry = 0L;
    delete b;

    if (!nxt)
       nxt=dynamic_cast<KJotsBook*>(subjectList->firstChild());

    if (nxt)
    {
        subjectList->setSelected(nxt, true);
    }
    else
    {
        roTextView->clear();
        textStack->raiseWidget(roTextView);

        updateMenu();
    }
}

void KJotsMain::newEntry()
{
    KJotsBook* b = currentBook();

    if (b)
    {
        KJotsPage* s = b->addPage();
        subjectList->setSelected(s, true);
    }
}

void KJotsMain::deleteEntry()
{
    KJotsPage* cur = currentPage();

    if (!cur ||
        KMessageBox::warningContinueCancel(topLevelWidget(),
                                  i18n("<qt>Are you sure you want to delete the <strong>%1</strong> page?</qt>")
                                  .arg(cur->subject()),
                                  i18n("Delete Page"),KStdGuiItem::del()) != KMessageBox::Continue)
    {
        return;
    }

    KJotsBook* bk = dynamic_cast<KJotsBook*>(cur->KListViewItem::parent());
    KJotsPage* nxt = dynamic_cast<KJotsPage*>(cur->nextSibling());

    me_text->setEntry(0);
    delete cur;
    currentEntry = 0;

    if (!nxt)
        nxt = dynamic_cast<KJotsPage*>(bk->firstChild());

    if (nxt)
        subjectList->setSelected(nxt, true);
    else
    {
        setCaption(kapp->makeStdCaption(QString::null));
    }
    updateMenu();
}

void KJotsMain::configure()
{
    if(KConfigDialog::showDialog("settings"))
        return;

    KConfigDialog* dialog = new KConfigDialog(this, "settings", KJotsSettings::self());

    confPageFont* cpFont = new confPageFont(0, "Font");
    dialog->addPage(cpFont, i18n("Font"), "fonts");

    confPageMisc* cpMisc = new confPageMisc(0, "Autosave");
    dialog->addPage(cpMisc, i18n("Autosave"), "misc");

    connect(dialog, SIGNAL(settingsChanged()), this, SLOT(updateConfiguration()));

    dialog->show();
}

void KJotsMain::slotSearch()
{
    me_text->search();
}

void KJotsMain::slotRepeatSearch()
{
    me_text->repeatSearch();
}

void KJotsMain::slotReplace()
{
    me_text->replace();
}

void KJotsMain::updateConfiguration()
{
    me_text->setFont(KJotsSettings::font());
    if (KJotsSettings::autoSave())
        m_autosaveTimer->changeInterval(KJotsSettings::autoSaveInterval()*1000*60);
    else
        m_autosaveTimer->stop();
}

// the "isDirty()" check only works for edits. Use "force" for things like renames
// and reorders, or any other time that you want to be really, really sure.
void KJotsMain::autoSave(bool force)
{
    QListViewItem* b = subjectList->firstChild();
    while (b)
    {
        KJotsBook *book = dynamic_cast<KJotsBook*>(b);

        if (book)
        {
            if ( book->isDirty() || force )
            {
                book->closeBook(true);
            }
        }

        b = b->nextSibling();
    }
}

void KJotsMain::writeBook()
{
    saveBookToFile(true);
}

void KJotsMain::writeBookToHTML()
{
    saveBookToFile(false);
}

void KJotsMain::saveBookToFile(bool plainText)
{
    KJotsBook* b = currentBook();

    if (!b)
        return;

    KURL url;
    bool tryAgain = true;
    while (tryAgain)
    {
        url = KFileDialog::getSaveURL();

        if (url.isEmpty())
        {
            tryAgain = false;
        }

        if (!KIO::NetAccess::exists(url, true, this) ||
            KMessageBox::warningYesNo(this, "<qt>" + i18n("The file <strong>%1</strong> already exists. Do you wish to overwrite it?").arg(url.prettyURL()) + "</qt>", i18n("File Exists")) == KMessageBox::Yes)
        {
            tryAgain = false;
        }
    }

    if (!url.isEmpty())
    {
        b->saveToFile(url, plainText);
    }
}

void KJotsMain::writePage()
{
    savePageToFile(true);
}

void KJotsMain::writePageToHTML()
{
    savePageToFile(false);
}

void KJotsMain::savePageToFile(bool plainText)
{
    KJotsPage* e = currentPage();

    if (!e)
        return;

    KURL url;
    bool tryAgain = true;
    while (tryAgain)
    {
        url = KFileDialog::getSaveURL();

        if (url.isEmpty())
        {
            tryAgain = false;
        }

        if (!KIO::NetAccess::exists(url, true, this) ||
            KMessageBox::warningYesNo(this, "<qt>" + i18n("The file <strong>%1</strong> already exists. Do you wish to overwrite it?").arg(url.prettyURL()) + "</qt>", i18n("File Exists")) == KMessageBox::Yes)
        {
            tryAgain = false;
        }
    }

    if (!url.isEmpty())
    {
        e->saveToFile(url, plainText);
    }
}

void KJotsMain::copySelection()
{
    //TODO: Strip HTML tags
    KJotsPage* cur = currentPage();

    if (cur && me_text->hasSelectedText())
        cur->setSubject(me_text->selectedText());
}

void KJotsMain::slotPrint()
{
    autoSave();
    KJotsEntryBase* entry = dynamic_cast<KJotsEntryBase*>(subjectList->currentItem());

    if (entry)
    {
        entry->print(m_font);
    }
}

void KJotsMain::slotQuit()
{
    close();
}

void KJotsMain::insertDate()
{
  me_text->insert(KGlobal::locale()->formatDateTime(QDateTime::currentDateTime(), true) + " ");
}

void KJotsMain::updateMenu()
{
    KJotsEntryBase* cur = dynamic_cast<KJotsEntryBase*>(subjectList->currentItem());

    if (!cur)
    {
        // no (meaningful?) selection
        actionCollection()->action("del_folder")->setEnabled(false);
        actionCollection()->action("del_page")->setEnabled(false);
        actionCollection()->action(KStdAction::stdName(KStdAction::New))->setEnabled(false);
        actionCollection()->action("save_book_to_ascii")->setEnabled(false);
        actionCollection()->action("save_book_to_html")->setEnabled(false);
        actionCollection()->action("save_page_to_ascii")->setEnabled(false);
        actionCollection()->action("save_page_to_html")->setEnabled(false);
        actionCollection()->action("rename_entry")->setEnabled(false);
        actionCollection()->action("insert_date")->setEnabled(false);
        actionCollection()->action("bookmarks")->setEnabled(false);
    }
    else if (dynamic_cast<KJotsBook*>(cur))
    {
        // book is selected
        actionCollection()->action("del_folder")->setEnabled(true);
        actionCollection()->action("del_page")->setEnabled(false);
        actionCollection()->action(KStdAction::stdName(KStdAction::New))->setEnabled(true);
        actionCollection()->action("save_book_to_ascii")->setEnabled(true);
        actionCollection()->action("save_book_to_html")->setEnabled(true);
        actionCollection()->action("save_page_to_ascii")->setEnabled(false);
        actionCollection()->action("save_page_to_html")->setEnabled(false);
        actionCollection()->action("rename_entry")->setEnabled(true);
        actionCollection()->action("insert_date")->setEnabled(true);
        actionCollection()->action("bookmarks")->setEnabled(true);
    }
    else
    {
        // subject is selected
        actionCollection()->action("del_folder")->setEnabled(true);
        actionCollection()->action("del_page")->setEnabled(true);
        actionCollection()->action(KStdAction::stdName(KStdAction::New))->setEnabled(true);
        actionCollection()->action("save_book_to_ascii")->setEnabled(true);
        actionCollection()->action("save_book_to_html")->setEnabled(true);
        actionCollection()->action("save_page_to_ascii")->setEnabled(true);
        actionCollection()->action("save_page_to_html")->setEnabled(true);
        actionCollection()->action("rename_entry")->setEnabled(true);
        actionCollection()->action("insert_date")->setEnabled(true);
        actionCollection()->action("bookmarks")->setEnabled(true);
    }
}

void KJotsMain::slotFindSuccessful()
{
    actionCollection()->action(KStdAction::stdName(KStdAction::FindNext))->setEnabled(true);
}

void KJotsMain::showListviewContextMenu(KListView*, QListViewItem* i, const QPoint& p)
{
    KActionMenu* am = new KActionMenu(this);

    if (!i)
    {
        // clicked on empty space
        am->insert(actionCollection()->action("new_book"));
    }
    else if (dynamic_cast<KJotsBook*>(i))
    {
        // clicked on book
        am->insert(actionCollection()->action(KStdAction::stdName(KStdAction::New)));
        am->insert(actionCollection()->action("rename_entry"));
        am->insert(actionCollection()->action("save_book_to_ascii"));
        am->insert(new KActionSeparator(am));
        am->insert(actionCollection()->action("del_folder"));
    }
    else
    {
        //clicked on page
        am->insert(actionCollection()->action(KStdAction::stdName(KStdAction::New)));
        am->insert(actionCollection()->action("rename_entry"));
        am->insert(actionCollection()->action("save_page_to_ascii"));
        am->insert(new KActionSeparator(am));
        am->insert(actionCollection()->action("del_page"));
    }

    am->popup(p);
}

void KJotsMain::bookLinkClicked(const QString& link)
{
    KJotsBook* book = currentBook();

    if (!book)
    {
        return;
    }

    if (link[0] == 'p')
    {
        // internal link from TOC
        roTextView->scrollToAnchor(link);
        return;
    }

    int pageNo = link.toInt();

    if (pageNo < 1)
    {
        return;
    }

    QListViewItem* child = book->firstChild();
    int i = 1;
    while (child)
    {
        if (i == pageNo)
        {
            subjectList->setSelected(child, true);
            return;
        }
        child = child->nextSibling();
        ++i;
    }
}

bool KJotsMain::queryClose()
{
    autoSave(true);
    saveProperties();
    return true;
}

void KJotsMain::saveProperties()
{
    KJotsSettings::setSplitterSizes(splitter->sizes());

    KJotsBook* selBook = currentBook();
    KJotsPage* selSubj = currentPage();
    int pageNo = -1;

    if (selBook && (!selSubj || static_cast<KJotsEntryBase*>(selBook) == selSubj))
    {
        selSubj = dynamic_cast<KJotsPage*>(selBook->firstChild());
    }
    else if (selSubj)
    {
        QListViewItem* page = selBook->firstChild();
        while (page)
        {
            ++pageNo;

            if (page == selSubj)
            {
                break;
            }

            page = page->nextSibling();
        }
    }

    KJotsSettings::setCurrentBook( selBook ? selBook->subject() : QString::null);
    KJotsSettings::setCurrentPage( pageNo );

    QStringList list;

    QListViewItem* citem = subjectList->firstChild();
    while (citem)
    {
        if (citem->isOpen())
        {
            KJotsBook* book = dynamic_cast<KJotsBook*>(citem);

            if (book)
            {
                list << book->subject();
            }
        }

        citem = citem->nextSibling();
    }

    KJotsSettings::setOpenBooks( list );
    KJotsSettings::writeConfig();
}

void KJotsMain::slotExpandBook(QListViewItem* item)
{
    void(dynamic_cast<KJotsBook*>(item)->openBook());
}

QString KJotsMain::currentTitle()
{
    QListViewItem* entry = subjectList->currentItem();

    if (entry == 0)
    {
        return QString::null;
    }
    else if (dynamic_cast<KJotsBook*>(entry))
    {
        KJotsBook* book = static_cast<KJotsBook*>(entry);
        return book->subject();
    }
    else
    {
        KJotsPage* page = static_cast<KJotsPage*>(entry);
        // TODO: this will have to be fixed if/when we allow arbitrary depth pages
        return static_cast<KJotsEntryBase*>(page->KListViewItem::parent())->subject() +
               ": " + page->subject();
    }
}

QString KJotsMain::currentBookmarkURL()
{
    QListViewItem* entry = subjectList->currentItem();

    if (entry == 0)
    {
        return QString::null;
    }
    else if (dynamic_cast<KJotsBook*>(entry))
    {
        KJotsBook* book = static_cast<KJotsBook*>(entry);
        return book->subject() + "/";
    }
    else
    {
        KJotsPage* page = static_cast<KJotsPage*>(entry);
        // TODO: this will have to be fixed if/when we allow arbitrary depth pages
        return dynamic_cast<KJotsEntryBase*>(page->KListViewItem::parent())->subject() +
               "/" + QString::number(page->id());
    }
}

void KJotsMain::jumpToBookmark(const QString& page)
{
    int slash = page.findRev('/');

    if (slash == -1)
    {
        return;
    }

    if (static_cast<uint>(slash) == page.length() - 1)
    {
        // we have a bookmark to a book
        QString bookName = page.left(page.length() - 1);
        QListViewItem* item = subjectList->firstChild();

        while (item)
        {
            KJotsBook* book = dynamic_cast<KJotsBook*>(item);

            if (book && book->subject() == bookName)
            {
                subjectList->setSelected(item, true);
                jumpToEntry(item);
                return;
            }

            item = item->nextSibling();
        }
    }
    else
    {
        // we have a bookmark to a page
        QString bookName = page.left(slash);
        int pageID = page.right(page.length() - slash - 1).toInt();
        QListViewItem* item = subjectList->firstChild();

        while (item)
        {
            KJotsBook* book = dynamic_cast<KJotsBook*>(item);

            if (book && book->subject() == bookName)
            {
                item = book->firstChild();

                while (item)
                {
                    KJotsPage* page = dynamic_cast<KJotsPage*>(item);

                    if (page && page->id() == pageID)
                    {
                        book->setOpen(true);
                        subjectList->ensureItemVisible(item);
                        subjectList->setSelected(item, true);
                        jumpToEntry(item);
                        return;
                    }

                    item = item->nextSibling();
                }
                return;
            }

            item = item->nextSibling();
        }
    }
}

QListViewItem* KJotsMain::findNextBookItem()
{
    QListViewItem* book = currentBook();

    if (!book)
    {
        book = subjectList->firstChild();
    }
    else
    {
        book = book->nextSibling();
        if (!book)
        {
            book = subjectList->firstChild();
        }
    }

    return book;
}

void KJotsMain::nextBook()
{
    QListViewItem* book = findNextBookItem();
    subjectList->setSelected(book, true);
    jumpToEntry(book);
}

QListViewItem* KJotsMain::findPrevBookItem()
{
    QListViewItem* book = currentBook();

    if (!book)
    {
        book = subjectList->firstChild();
    }
    else
    {
        QListViewItem* item = subjectList->firstChild();
        while (item && item->nextSibling() && item->nextSibling() != book)
        {
            item = item->nextSibling();
        }

        book = item;
    }

    return book;
}

void KJotsMain::prevBook()
{
    QListViewItem* book = findPrevBookItem();
    subjectList->setSelected(book, true);
    jumpToEntry(book);
}

void KJotsMain::nextPage()
{
    QListViewItem* page = currentPage();

    if (!page)
    {
        QListViewItem* book = findNextBookItem();
        if (!book || !book->firstChild())
        {
            nextBook();
            return;
        }

        page = book->firstChild();
        book->setOpen(true);
        subjectList->setSelected(page, true);
        jumpToEntry(page);
    }
    else if (page->nextSibling())
    {
        subjectList->setSelected(page->nextSibling(), true);
        jumpToEntry(page->nextSibling());
    }
    else
    {
        QListViewItem* book = findNextBookItem();
        if (!book || !book->firstChild())
        {
            nextBook();
            return;
        }

        book->setOpen(true);
        if (book->firstChild())
        {
            subjectList->setSelected(book->firstChild(), true);
            jumpToEntry(book->firstChild());
        }
        else
        {
            subjectList->setSelected(book, true);
            jumpToEntry(book);
        }
    }
}

void KJotsMain::prevPage()
{
    QListViewItem* page = currentPage();

    if (!page)
    {
        QListViewItem* book = findPrevBookItem();
        book->setOpen(true);
        page = book->firstChild();

        while (page && page->nextSibling())
        {
            page = page->nextSibling();
        }

        if (page)
        {
            subjectList->setSelected(page, true);
            jumpToEntry(page);
        }
        else
        {
            subjectList->setSelected(book, true);
            jumpToEntry(book);
        }
        return;
    }

    QListViewItem* book = page->parent();

    if (!book)
    {
        return;
    }

    QListViewItem* item = book->firstChild();

    if (item == page)
    {
        // yay for code duplication! ;-P
        book = findPrevBookItem();
        book->setOpen(true);
        page = book->firstChild();

        while (page && page->nextSibling())
        {
            page = page->nextSibling();
        }

        if (page)
        {
            subjectList->setSelected(page, true);
            jumpToEntry(page);
        }
        else
        {
            subjectList->setSelected(book, true);
        }
        return;
    }
    while (item && item->nextSibling() != page)
    {
        item = item->nextSibling();
    }

    if (!item)
    {
        prevBook();
        return;
    }

    subjectList->setSelected(item, true);
    jumpToEntry(item);
}

void KJotsMain::jumpToEntry(QListViewItem* entry, bool force)
{
    //This won't lose data or anything; but it does reset the cursor postion and waste cycles.
    if (currentEntry == entry && !force)
        return;

    if (entry == 0)
    {
        me_text->setEntry(0);
        currentEntry = 0;
    }
    else if (dynamic_cast<KJotsBook*>(entry))
    {
        me_text->setEntry(0);
        KJotsBook* book = static_cast<KJotsBook*>(entry);

        book->openBook(); //making sure. doesn't hurt if book is already open

        QString TOC;
        QString pageText;
        QListViewItem* child = entry->firstChild();
        int i = 1;
        while (child)
        {
            KJotsPage* page = dynamic_cast<KJotsPage*>(child);
            if (page)
            {
                TOC += QString("<li><a href=\"p%1\">").arg(i) + page->subject() + "</a></li>";
                QString text = page->subject();
                text.replace("<", "&lt;");
                text.replace(">", "&gt;");
                pageText += QString("<h3><a name=\"p%1\" href=\"%2\">%3</a></h3>").arg(i).arg(i).arg(text);
                text = page->body();
                text.replace("<", "&lt;");
                text.replace(">", "&gt;");
                text.replace("\n", "<br>");
                pageText += text + "<hr>";
            }
            child = child->nextSibling();
            ++i;
        }

        if (i > 2)
        {
            pageText = "<h3>" + i18n("Table of Contents") + "</h3><ul>" + TOC + "</ul><hr>" + pageText;
        }

        roTextView->setText(QString("<h1>%1</h1>").arg(book->subject()) + pageText);
        roTextView->setCursorPosition(0, 0);
        textStack->raiseWidget(roTextView);
        currentEntry = 0;
    }
    else
    {
        textStack->raiseWidget(me_text);
        me_text->setEntry(dynamic_cast<KJotsPage*>(entry));
        currentEntry = static_cast<KJotsEntryBase*>(entry);
    }

    updateCaption();
    updateMenu();
}

void KJotsMain::updateCaption()
{
    // TODO: this will have to be fixed if/when we allow arbitrary depth pages
    
    QListViewItem* e = subjectList->currentItem();
   
    if (!e)
    {
        setCaption(kapp->makeStdCaption(QString::null));

        return;
    }

    if (dynamic_cast<KJotsBook*>(e))
    {
        setCaption(kapp->makeStdCaption(static_cast<KJotsBook*>(e)->subject()));
    }
    else if (dynamic_cast<KJotsPage*>(e))
    {
        QString caption = static_cast<KJotsEntryBase*>(e->parent())->subject() +
                          " / " +
                          static_cast<KJotsEntryBase*>(e)->subject();

        setCaption(kapp->makeStdCaption(caption));
    }
}

void KJotsMain::slotItemRenamed(QListViewItem* item, const QString& str, int  /*col*/)
{
    KJotsEntryBase* entry = dynamic_cast<KJotsEntryBase*>(item);

    if (entry)
    {
        entry->setSubject(str);
        updateCaption();
        jumpToEntry(entry, true); //reflect the changes on screen
    }
}

void KJotsMain::slotItemMoved(QListViewItem *item, QListViewItem * afterFirst, QListViewItem * /* afterNow */)
{
    KJotsEntryBase *p_item = dynamic_cast<KJotsEntryBase*>(item);
    KJotsEntryBase *p_item_parent = dynamic_cast<KJotsEntryBase*>(item->parent());

    if (p_item_parent)
    {
        if (dynamic_cast<KJotsBook*>(p_item) && dynamic_cast<KJotsBook*>(p_item_parent))
        {
            // we don't accept books inside books right now
            // move the book outside
            KMessageBox::sorry(this,i18n("You cannot have a book inside another book."));
            p_item_parent->takeItem(p_item);
            subjectList->insertItem(p_item);

            if (afterFirst)
            {
                if (!dynamic_cast<KJotsBook*>(afterFirst))
                    p_item->moveItem(afterFirst->parent());
                else
                    p_item->moveItem(afterFirst);
            }
        }
    }
    else if (!dynamic_cast<KJotsBook*>(p_item))
    {
        KJotsPage *p_aux = dynamic_cast<KJotsPage*>(item);

        if (!p_aux)
        {
            return;
        }

        // we don't accept pages in the root
        // create a new book
        if (KMessageBox::questionYesNo(this,
                                       i18n("All pages must be inside a book. "
                                            "Would you like to create a new book to put the page in, "
                                            "or would you prefer to not move the page at all?"),
                                       QString::null,
                                       i18n("Create New Book"), i18n("Do Not Move The Page")) ==
            KMessageBox::No ||
            !createFolder())
        {
            //Careful! afterFirst only works here if this page was after another page.
            if ( dynamic_cast<KJotsPage*>(afterFirst) )
                subjectList->moveItem(p_aux, currentBook(), afterFirst);
            else
                subjectList->moveItem(p_aux, currentBook(), 0);
            return;
        }

        delete currentBook()->firstChild();
        currentBook()->insertItem(p_aux);
        me_text->setEntry(p_aux);
        currentEntry = p_item;
    }

    autoSave(true); //flush changes to disk
}

KJotsBook* KJotsMain::currentBook()
{
    KJotsEntryBase* e = dynamic_cast<KJotsEntryBase*>(subjectList->currentItem());

    while (e)
    {
        if (dynamic_cast<KJotsBook*>(e))
        {
            return dynamic_cast<KJotsBook*>(e);
        }
        e = dynamic_cast<KJotsEntryBase*>(e->KListViewItem::parent());
    }

    return 0;
}

KJotsPage* KJotsMain::currentPage()
{
    return dynamic_cast<KJotsPage*>(subjectList->currentItem());
}

void addBookmark()
{
}

void editBookmarks()
{
}

#include "KJotsMain.moc"
/* ex: set tabstop=4 softtabstop=4 shiftwidth=4 expandtab: */
