/* KPilot ** ** Copyright (C) 1998-2001 by Dan Pilone ** Copyright (C) 2001 by David Bishop (XML stuff) ** Copyright (C) 2004 by Adriaan de Groot ** ** This is the memo-viewing widget (internal conduit) used by KPilot. */ /* ** 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 in a file called COPYING; if not, write to ** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, ** MA 02110-1301, USA. */ /* ** Bug reports and questions can be sent to kde-pim@kde.org */ #include "options.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kpilot.h" #include "kpilotConfig.h" #include "listItems.h" #include "pilotLocalDatabase.h" #include "pilotMemo.h" #include "memoWidget.moc" class MemoWidget::Private { public: Private() : fMemoAppInfo(0L) { } ; ~Private() { KPILOT_DELETE(fMemoAppInfo); } ; PilotMemoInfo *fMemoAppInfo; TQPtrList fMemoList; } ; MemoWidget::MemoWidget(TQWidget * parent, const TQString & path) : PilotComponent(parent, "component_memo", path), fTextWidget(0L), d(new Private()), lastSelectedMemo(-1) { FUNCTIONSETUP; setGeometry(0, 0, parent->geometry().width(), parent->geometry().height()); setupWidget(); d->fMemoList.setAutoDelete(true); slotUpdateButtons(); } MemoWidget::~MemoWidget() { FUNCTIONSETUP; saveChangedMemo(); KPILOT_DELETE(d); } // void MemoWidget::initializeMemos(PilotDatabase *memoDB) // // Reads all the memos from the local database and places them // in the selection screen. // void MemoWidget::initializeMemos(PilotDatabase * memoDB) { FUNCTIONSETUP; // ShowSecrets tells us to also list memos with an attribute of "Secret" // or "Private" // bool showSecrets = KPilotSettings::showSecrets(); d->fMemoList.clear(); int currentRecord = 0; PilotRecord *pilotRec; PilotMemo *memo; while ((pilotRec = memoDB->readRecordByIndex(currentRecord)) != NULL) { if (!pilotRec->isDeleted()) { if ((!pilotRec->isSecret()) || showSecrets) { memo = new PilotMemo(pilotRec); d->fMemoList.append(memo); #ifdef DEBUG DEBUGKPILOT << fname << ": Added memo " << currentRecord << endl; #endif } else { #ifdef DEBUG DEBUGKPILOT << fname << ": Skipped secret record " << currentRecord << endl; #endif } } else { #ifdef DEBUG DEBUGKPILOT << fname << ": Skipped deleted record " << currentRecord << endl; #endif } delete pilotRec; currentRecord++; } } void MemoWidget::showComponent() { FUNCTIONSETUP; if (!shown) return; // Get the local database - assume the call may fail and return // NULL, or the database object may be returned but unopened. // // PilotLocalDatabase *memoDB = new PilotLocalDatabase(dbPath(), CSL1("MemoDB")); if (memoDB == NULL || !memoDB->isOpen()) { WARNINGKPILOT << "Can't open local database MemoDB\n"; populateCategories(fCatList, 0L); updateWidget(); return; } KPILOT_DELETE(d->fMemoAppInfo); d->fMemoAppInfo = new PilotMemoInfo(memoDB); d->fMemoAppInfo->dump(); populateCategories(fCatList, d->fMemoAppInfo->categoryInfo()); initializeMemos(memoDB); KPILOT_DELETE( memoDB ); updateWidget(); } void MemoWidget::hideComponent() { FUNCTIONSETUP; saveChangedMemo(); fCatList->clear(); fTextWidget->clear(); d->fMemoList.clear(); fListBox->clear(); lastSelectedMemo = -1; } void MemoWidget::postHotSync() { FUNCTIONSETUP; d->fMemoList.clear(); showComponent(); } // void MemoWidget::setupWidget() // // Setup all the GUI components by allocating them. // // void MemoWidget::setupWidget() { FUNCTIONSETUP; TQLabel *label = NULL; TQPushButton *button = NULL; TQGridLayout *grid = new TQGridLayout(this, 5, 4, SPACING); TQString wt; fCatList = new TQComboBox(this); grid->addWidget(fCatList, 0, 1); connect(fCatList, TQT_SIGNAL(activated(int)), this, TQT_SLOT(slotSetCategory(int))); TQWhatsThis::add(fCatList, i18n("Select the category of addresses\n" "to display here.")); (void) i18n("Memos:"); label = new TQLabel(i18n("Category:"), this); label->setBuddy(fCatList); grid->addWidget(label, 0, 0); fListBox = new TQListBox(this); grid->addMultiCellWidget(fListBox, 1, 1, 0, 1); connect(fListBox, TQT_SIGNAL(highlighted(int)), this, TQT_SLOT(slotShowMemo(int))); connect(fListBox, TQT_SIGNAL(selectionChanged()), this,TQT_SLOT(slotUpdateButtons())); TQWhatsThis::add(fListBox, i18n("This list displays all the memos\n" "in the selected category. Click on\n" "one to display it to the right.")); label = new TQLabel(i18n("Memo text:"), this); grid->addWidget(label, 0, 2); fTextWidget = new KTextEdit(this, "textArea"); fTextWidget->setWordWrap(KTextEdit::WidgetWidth); fTextWidget->setTextFormat(TQt::PlainText); grid->addMultiCellWidget(fTextWidget, 1, 4, 2, 2); TQWhatsThis::add(fTextWidget, i18n("The text of the selected memo appears here.")); fTextWidget->setReadOnly(!KPilotSettings::internalEditors()); button = new TQPushButton(i18n("Import Memo..."), this); grid->addWidget(button, 2, 0); connect(button, TQT_SIGNAL(clicked()), this, TQT_SLOT(slotImportMemo())); wt = KPilotSettings::internalEditors() ? i18n ("Read a text file and add it to the Pilot's memo database.") : i18n("Import is disabled by the 'internal editors' setting."); TQWhatsThis::add(button,wt); fExportButton = new TQPushButton(i18n("Export Memo..."), this); grid->addWidget(fExportButton, 2, 1); connect(fExportButton, TQT_SIGNAL(clicked()), this, TQT_SLOT(slotExportMemo())); TQWhatsThis::add(fExportButton, i18n("Write the selected memo to a file.")); fDeleteButton = new TQPushButton(i18n("Delete Memo"), this); grid->addWidget(fDeleteButton, 3, 1); connect(fDeleteButton, TQT_SIGNAL(clicked()), this, TQT_SLOT(slotDeleteMemo())); wt = KPilotSettings::internalEditors() ? i18n("Delete the selected memo.") : i18n("Deleting is disabled by the 'internal editors' setting.") ; TQWhatsThis::add(fDeleteButton, wt); button = new TQPushButton(i18n("Add Memo"), this); grid->addWidget(button, 3, 0); connect(button, TQT_SIGNAL(clicked()), this, TQT_SLOT(slotAddMemo())); TQWhatsThis::add(button,i18n("Add a new memo to the database.")); } void MemoWidget::slotUpdateButtons() { FUNCTIONSETUP; bool highlight = false; if ((fListBox->currentItem() != -1) && (fListBox->isSelected(fListBox->currentItem()))) highlight=true; #ifdef DEBUG DEBUGKPILOT << fname << ": Selected items " << highlight << endl; #endif if (fExportButton) { fExportButton->setEnabled(highlight); } // The remaining buttons are relevant only if the // internal editors are editable. highlight &= KPilotSettings::internalEditors() ; if (fDeleteButton) { fDeleteButton->setEnabled(highlight); } } void MemoWidget::slotSetCategory(int) { FUNCTIONSETUP; updateWidget(); } void MemoWidget::slotDeleteMemo() { FUNCTIONSETUP; if (!shown) return; int item = fListBox->currentItem(); if (item == -1) { #ifdef DEBUG DEBUGKPILOT << fname << ": No current item selected\n"; #endif return; } if (KMessageBox::questionYesNo(this, i18n("Delete currently selected memo?"), i18n("Delete Memo?"), KStdGuiItem::del(), KStdGuiItem::cancel()) != KMessageBox::Yes) { #ifdef DEBUG DEBUGKPILOT << fname << ": User decided not to delete memo.\n"; #endif return; } PilotListItem *p = (PilotListItem *) fListBox->item(item); PilotMemo *selectedMemo = (PilotMemo *) p->rec(); if (selectedMemo->id() == 0x0) { #ifdef DEBUG DEBUGKPILOT << fname << ": Searching for record to delete (it's fresh)" << endl; #endif PilotLocalDatabase *memoDB = new PilotLocalDatabase(dbPath(), CSL1("MemoDB")); if (!memoDB || (!memoDB->isOpen())) { // Err.. peculiar. WARNINGKPILOT << "Can't open MemoDB" << endl; KMessageBox::sorry(this, i18n("Cannot open MemoDB to delete record."), i18n("Cannot Delete Memo")); return; } memoDB->resetDBIndex(); #ifdef DEBUG DEBUGKPILOT << fname << ": Searching for new record." << endl; #endif const PilotRecord *r = 0L; while ((r = memoDB->findNextNewRecord())) { #ifdef DEBUG DEBUGKPILOT << fname << ": got record " << (void *) r << endl; #endif PilotMemo m(r); if (m.text() == selectedMemo->text()) { #ifdef DEBUG DEBUGKPILOT << fname << ": I think I found the memo." << endl; #endif (const_cast(r))->setDeleted(true); break; } } delete memoDB; } else { selectedMemo->setDeleted(true); writeMemo(selectedMemo); } d->fMemoList.remove(selectedMemo); delete p; } void MemoWidget::updateWidget() { FUNCTIONSETUP; if (!shown || !d->fMemoAppInfo ) return; if (fCatList->currentItem() == -1) { #ifdef DEBUG DEBUGKPILOT << fname << ": No category selected.\n"; #endif return; } int listIndex = 0; int currentCatID = findSelectedCategory(fCatList, d->fMemoAppInfo->categoryInfo(), false); fListBox->clear(); d->fMemoList.first(); // Iterate through all the memos and insert each memo // only if the category of the memo matches the selected category // (using -1 to mean "All") // // while (d->fMemoList.current()) { PilotMemo *curr = d->fMemoList.current(); if ((curr->category() == currentCatID) || (currentCatID == -1)) { PilotListItem *p = new PilotListItem(curr->shortTitle(), listIndex, curr); // List will delete the title of the memo, // so there's no memory leak here. // // fListBox->insertItem(p); #ifdef DEBUG DEBUGKPILOT << fname << ": Added memo " << curr->getTitle() << endl; #endif } else { #ifdef DEBUG DEBUGKPILOT << fname << ": Skipped memo " << curr->getTitle() << endl; #endif } listIndex++; d->fMemoList.next(); } fTextWidget->clear(); slotUpdateButtons(); lastSelectedMemo=-1; } void MemoWidget::showMemo(const PilotMemo *m) { FUNCTIONSETUP; int index = fListBox->count(); for (int x = 0; x < index; x++) { PilotMemo *p = (PilotMemo *) ((PilotListItem *)fListBox->item(x))->rec(); #ifdef DEBUG DEBUGKPILOT << fname << ": Memo @" << (void *) p <item(x)->text() << endl; #endif if (m==p) { fListBox->setSelected(x,true); slotShowMemo(x); break; } } } void MemoWidget::slotShowMemo(int which) { FUNCTIONSETUP; if ( which == -1 ) return; if (!shown) return; slotUpdateButtons(); if ( !fListBox->isSelected(which) ) { // Handle unselecting a memo. This is easy. fTextWidget->blockSignals(true); fTextWidget->clear(); fTextWidget->blockSignals(false); return; } #ifdef DEBUG DEBUGKPILOT << fname << ": Displaying memo " << which << endl; #endif fTextWidget->blockSignals(true); PilotListItem *p = (PilotListItem *) fListBox->item(which); PilotMemo *theMemo = (PilotMemo *) p->rec(); fTextWidget->setText(theMemo->text()); fTextWidget->blockSignals(false); } void MemoWidget::writeMemo(PilotMemo * which) { FUNCTIONSETUP; if (!shown) return; PilotRecord *pilotRec = which->pack(); PilotDatabase *memoDB = new PilotLocalDatabase(dbPath(), CSL1("MemoDB")); memoDB->writeRecord(pilotRec); markDBDirty(CSL1("MemoDB")); KPILOT_DELETE( memoDB ); KPILOT_DELETE( pilotRec ); } void MemoWidget::saveChangedMemo() { FUNCTIONSETUP; if (!shown) return; if (-1 == lastSelectedMemo) return; if (!fTextWidget->isModified()) return; #ifdef DEBUG DEBUGKPILOT << fname << ": Saving changed memo " << lastSelectedMemo << endl; #endif PilotListItem *p = (PilotListItem *) fListBox->item(lastSelectedMemo); PilotMemo *currentMemo = (PilotMemo *) p->rec(); // TODO: overload setText in PilotMemo currentMemo->setText(Pilot::toPilot(fTextWidget->text())); writeMemo(currentMemo); } /* virtual */ bool MemoWidget::preHotSync(TQString &) { FUNCTIONSETUP; saveChangedMemo(); return true; } bool MemoWidget::addMemo(const TQString &s, int category) { FUNCTIONSETUP; if (s.length() >= MemoWidget::MAX_MEMO_LEN) { return false; } if ((category<0) || (category>=(int)Pilot::CATEGORY_COUNT)) { category=Pilot::Unfiled; } PilotMemo *aMemo = new PilotMemo(); aMemo->setCategory(category); aMemo->setText(s); d->fMemoList.append(aMemo); writeMemo(aMemo); updateWidget(); #ifdef DEBUG DEBUGKPILOT << fname << ": New memo @" << (void *)aMemo << endl; #endif showMemo(aMemo); return true; } void MemoWidget::slotAddMemo() { FUNCTIONSETUP; int currentCatID = findSelectedCategory(fCatList, d->fMemoAppInfo->categoryInfo(), true); addMemo(TQDateTime::currentDateTime().toString(), currentCatID); } void MemoWidget::slotImportMemo() { FUNCTIONSETUP; if (!shown || !d->fMemoAppInfo ) return; int currentCatID = findSelectedCategory(fCatList, d->fMemoAppInfo->categoryInfo(), true); TQString fileName = KFileDialog::getOpenFileName(); if (!fileName.isEmpty()) { TQFile importFile(fileName); if (importFile.open(IO_ReadOnly) == FALSE) { // show error! return; } if (importFile.size() > MemoWidget::MAX_MEMO_LEN) { // Perhaps read first 64k? return; } TQTextStream stream(&importFile); TQString memoText = stream.read(); addMemo(memoText, currentCatID); } } void MemoWidget::slotExportMemo() { FUNCTIONSETUP; if (!shown) return; int index = fListBox->numRows(); if (index == 0) return; TQString data; const TQString filter = CSL1("*|Plain text output\n*.xml|XML output"); TQString fileName; KFileDialog kfile( TQString() , filter, fExportButton , "memoSave" , true ); kfile.setOperationMode( KFileDialog::Saving ); if ( kfile.exec() == TQDialog::Accepted ) { fileName = kfile.selectedFile(); } if (fileName.isEmpty()) return; TQPtrList menu_items; for (int x = 0; x < index; x++){ if (fListBox->item(x)->isSelected()){ menu_items.append((PilotListItem *) fListBox->item(x)); } } if (kfile.currentFilter() == CSL1("*.xml") ) { MemoWidget::saveAsXML( fileName , menu_items ); } else { MemoWidget::saveAsText( fileName , menu_items ); } return; } bool MemoWidget::saveAsText(const TQString &fileName,const TQPtrList &memo_list) { TQFile f( fileName ); TQTextStream stream(&f); if ( TQFile::exists( fileName ) ) { if( !f.open(IO_ReadWrite | IO_Append) ) { return false; } } else { if( !f.open(IO_WriteOnly) ) { return false; } } TQPtrListIterator it(memo_list); for ( ; it.current(); ++it ) { PilotListItem *p = it.current(); PilotMemo *theMemo = (PilotMemo *) p->rec(); stream << theMemo->text() << endl; } return true; } bool MemoWidget::saveAsXML(const TQString &fileName,const TQPtrList &memo_list) { TQDomDocument doc( CSL1("kpilotmemos") ); TQFile f( fileName ); TQTextStream stream( &f ); TQDomElement memos; int append = 0; if ( f.exists() ) { if ( !f.open(IO_ReadOnly ) ) return false; if ( doc.setContent( &f ) ) { // // //Only if TQDom can read the .xml file and set the doc object to be populated with it's contents memos = doc.documentElement(); if ( memos.tagName()!= CSL1("memos") ) { return false; } // // //This is an XML Document but it isn't a valid KPilot-Memo xml document else { append = 1; } // // //This is a valid KPilot memo, and we will append the current memo to the xml } else { // // //We *couldn't* understand the xml. Return false! return false; } } else { if ( !f.open(IO_ReadWrite ) ) return false; // // //If there's no such file, we are not appending, just opening the file to read/write. } f.close(); // These are temporary, and should be retrieved from the pilot stuff TQString mpilotid; mpilotid = "1"; // End of temp variables if (append == 1) { memos = doc.documentElement(); } else { memos = doc.createElement( CSL1("memos") ); doc.appendChild ( memos ); } TQPtrListIterator it(memo_list); for ( ; it.current(); ++it ) { PilotListItem *p = it.current(); PilotMemo *theMemo = (PilotMemo *) p->rec(); TQDomElement memo = doc.createElement( CSL1("memo") ); memo.setAttribute ( CSL1("pilotid") , mpilotid ); memos.appendChild ( memo ); //TQDomElement category = doc.createElement( "category" ); //head.appendChild ( category ); // //TQDomText categorytext = doc.createTextNode( memo->category() ); //category.appendChild ( categorytext ); //FIXME TQDomElement title = doc.createElement(CSL1("title" )); memo.appendChild ( title ); TQDomText titletext = doc.createTextNode( theMemo->shortTitle() ); title.appendChild ( titletext ); TQDomText body = doc.createTextNode( theMemo->text() ); memo.appendChild ( body ); } if ( !f.open(IO_WriteOnly ) ) return false; stream << doc.toString(); return true; }