Embedding: use a vector to store multiple ParConstIterator of multiple insets referring to the same embedded file, polish the internal logics of EmbeddedFile(s)

git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@20137 a592a061-630c-0410-9148-cb99ea01b6c8
This commit is contained in:
Bo Peng 2007-09-08 04:10:43 +00:00
parent 95aa2be7b3
commit cf84439502
6 changed files with 198 additions and 111 deletions

View File

@ -69,8 +69,11 @@ using support::makedir;
EmbeddedFile::EmbeddedFile(string const & file, string const & inzip_name,
bool embed, ParConstIterator const & pit)
: DocFileName(file, true), inzip_name_(inzip_name), embedded_(embed),
valid_(true), par_it_(pit)
{}
valid_(true), par_it_()
{
if (pit != ParConstIterator())
par_it_.push_back(pit);
}
string EmbeddedFile::embeddedFile(Buffer const * buf) const
@ -79,98 +82,119 @@ string EmbeddedFile::embeddedFile(Buffer const * buf) const
}
int const EmbeddedFile::parID() const
void EmbeddedFile::addParIter(ParConstIterator const & pit)
{
// some embedded file do not have a valid par iterator
return par_it_ == ParConstIterator() ? 0 : par_it_->id();
par_it_.push_back(pit);
}
void EmbeddedFile::setParIter(ParConstIterator const & pit)
int EmbeddedFile::parID(int idx) const
{
par_it_ = pit;
BOOST_ASSERT(idx < refCount());
// some embedded file do not have a valid par iterator
return par_it_[idx]->id();
}
string EmbeddedFile::availableFile(Buffer const * buf) const
{
if (embedded_)
if (embedded())
return embeddedFile(buf);
else
return absFilename();
}
void EmbeddedFile::invalidate()
{
// Clear par_it_ because they will be registered again.
par_it_.clear();
valid_ = false;
}
bool EmbeddedFile::extract(Buffer const * buf) const
{
if (!embedded_)
return true;
string ext_file = absFilename();
string emb_file = embeddedFile(buf);
bool copyFile = false;
// both files exist, are different
if (fs::exists(ext_file) && fs::exists(emb_file)
&& sum(*this) != sum(FileName(emb_file))) {
int const ret = Alert::prompt(
if (!fs::exists(emb_file))
return false;
// if external file already exists ...
if (fs::exists(ext_file)) {
// no need to copy if the files are the same
if (sum(*this) == sum(FileName(emb_file)))
return true;
// otherwise, ask if overwrite
int ret = Alert::prompt(
_("Overwrite external file?"),
bformat(_("External file %1$s already exists, do you want to overwrite it"),
from_utf8(ext_file)), 1, 1, _("&Overwrite"), _("&Cancel"));
copyFile = ret == 0;
}
// copy file in the previous case, and a new case
if (copyFile || (!fs::exists(ext_file) && fs::exists(emb_file))) {
try {
// need to make directory?
string path = onlyPath(ext_file);
if (!fs::is_directory(path))
makedir(const_cast<char*>(path.c_str()), 0755);
fs::copy_file(emb_file, ext_file, false);
if (ret != 0)
// if the user does not want to overwrite, we still consider it
// a successful operation.
return true;
} catch (fs::filesystem_error const & fe) {
Alert::error(_("Copy file failure"),
bformat(_("Cannot copy file %1$s to %2$s.\n"
"Please check whether the directory exists and is writeable."),
from_utf8(emb_file), from_utf8(ext_file)));
LYXERR(Debug::DEBUG) << "Fs error: " << fe.what() << endl;
}
}
// copy file
try {
// need to make directory?
string path = onlyPath(ext_file);
if (!fs::is_directory(path))
makedir(const_cast<char*>(path.c_str()), 0755);
fs::copy_file(emb_file, ext_file, false);
return true;
} catch (fs::filesystem_error const & fe) {
Alert::error(_("Copy file failure"),
bformat(_("Cannot copy file %1$s to %2$s.\n"
"Please check whether the directory exists and is writeable."),
from_utf8(emb_file), from_utf8(ext_file)));
LYXERR(Debug::DEBUG) << "Fs error: " << fe.what() << endl;
}
return false;
}
bool EmbeddedFile::update(Buffer const * buf) const
bool EmbeddedFile::updateFromExternalFile(Buffer const * buf) const
{
string ext_file = absFilename();
string emb_file = embeddedFile(buf);
bool copyFile = false;
// both files exist, are different
if (fs::exists(ext_file) && fs::exists(emb_file)
&& sum(*this) != sum(FileName(emb_file))) {
if (!fs::exists(ext_file))
return false;
// if embedded file already exists ...
if (fs::exists(emb_file)) {
// no need to copy if the files are the same
if (sum(*this) == sum(FileName(emb_file)))
return true;
// other wise, ask if overwrite
int const ret = Alert::prompt(
_("Update embedded file?"),
bformat(_("Embeddedl file %1$s already exists, do you want to overwrite it"),
from_utf8(ext_file)), 1, 1, _("&Overwrite"), _("&Cancel"));
copyFile = ret == 0;
if (ret != 0)
// if the user does not want to overwrite, we still consider it
// a successful operation.
return true;
}
// copy file in the previous case, and a new case
if (copyFile || (fs::exists(ext_file) && !fs::exists(emb_file))) {
try {
// need to make directory?
string path = onlyPath(emb_file);
if (!fs::is_directory(path))
makedir(const_cast<char*>(path.c_str()), 0755);
fs::copy_file(ext_file, emb_file, false);
} catch (fs::filesystem_error const & fe) {
Alert::error(_("Copy file failure"),
bformat(_("Cannot copy file %1$s to %2$s.\n"
"Please check whether the directory exists and is writeable."),
from_utf8(ext_file), from_utf8(emb_file)));
LYXERR(Debug::DEBUG) << "Fs error: " << fe.what() << endl;
return false;
}
// copy file
try {
// need to make directory?
string path = onlyPath(emb_file);
if (!fs::is_directory(path))
makedir(const_cast<char*>(path.c_str()), 0755);
fs::copy_file(ext_file, emb_file, false);
return true;
} catch (fs::filesystem_error const & fe) {
Alert::error(_("Copy file failure"),
bformat(_("Cannot copy file %1$s to %2$s.\n"
"Please check whether the directory exists and is writeable."),
from_utf8(ext_file), from_utf8(emb_file)));
LYXERR(Debug::DEBUG) << "Fs error: " << fe.what() << endl;
}
return true;
return false;
}
@ -180,15 +204,16 @@ bool EmbeddedFiles::enabled() const
}
void EmbeddedFiles::enable(bool flag)
bool EmbeddedFiles::enable(bool flag)
{
if (enabled() != flag) {
// if disable embedding, first extract all embedded files
if (flag || (!flag && extractAll())) {
// file will be changed
buffer_->markDirty();
buffer_->params().embedded = flag;
}
// if enable, copy all files to temppath()
// if disable, extract all files
if ((flag && !updateFromExternalFile()) || (!flag && !extract()))
return false;
// if operation is successful
buffer_->markDirty();
buffer_->params().embedded = flag;
}
}
@ -196,6 +221,7 @@ void EmbeddedFiles::enable(bool flag)
void EmbeddedFiles::registerFile(string const & filename,
bool embed, ParConstIterator const & pit)
{
// filename can be relative or absolute, translate to absolute filename
string abs_filename = makeAbsPath(filename, buffer_->filePath()).absFilename();
// try to find this file from the list
EmbeddedFileList::iterator it = file_list_.begin();
@ -203,16 +229,21 @@ void EmbeddedFiles::registerFile(string const & filename,
for (; it != it_end; ++it)
if (it->absFilename() == abs_filename || it->embeddedFile(buffer_) == abs_filename)
break;
// find this filename
// find this filename, keep the original embedding status
if (it != file_list_.end()) {
it->setParIter(pit);
it->update(buffer_);
it->setEmbed(true);
it->addParIter(pit);
// if the file is embedded, the embedded file should have exist
// check for this to ensure that our logic is correct
if (it->embedded())
BOOST_ASSERT(fs::exists(it->embeddedFile(buffer_)));
it->validate();
return;
}
// try to be more careful
file_list_.push_back(EmbeddedFile(abs_filename,
getInzipName(abs_filename), embed, pit));
// validate if things are OK
BOOST_ASSERT(fs::exists(file_list_.back().availableFile(buffer_)));
}
@ -225,7 +256,9 @@ void EmbeddedFiles::update()
EmbeddedFileList::iterator it = file_list_.begin();
EmbeddedFileList::iterator it_end = file_list_.end();
for (; it != it_end; ++it)
it->invalidate();
// we do not update items that are manually inserted
if (it->refCount() > 0)
it->invalidate();
ParIterator pit = buffer_->par_iterator_begin();
ParIterator pit_end = buffer_->par_iterator_end();
@ -306,16 +339,26 @@ EmbeddedFiles::EmbeddedFileList::const_iterator EmbeddedFiles::find(std::string
}
bool EmbeddedFiles::extractAll() const
bool EmbeddedFiles::extract() const
{
EmbeddedFileList::const_iterator it = file_list_.begin();
EmbeddedFileList::const_iterator it_end = file_list_.end();
// FIXME: the logic here is hard to decide, we should allow cancel for
// 'do not overwrite' this file, and cancel for 'cancel extract all files'.
// I am not sure how to do this now.
for (; it != it_end; ++it)
if (it->valid() && it->embedded())
it->extract(buffer_);
if(!it->extract(buffer_))
return false;
return true;
}
bool EmbeddedFiles::updateFromExternalFile() const
{
EmbeddedFileList::const_iterator it = file_list_.begin();
EmbeddedFileList::const_iterator it_end = file_list_.end();
for (; it != it_end; ++it)
if (it->valid() && it->embedded())
if (!it->updateFromExternalFile(buffer_))
return false;
return true;
}

View File

@ -122,13 +122,18 @@ public:
std::string availableFile(Buffer const * buf) const;
/// paragraph id
void setParIter(ParConstIterator const & pit);
int const parID() const;
void addParIter(ParConstIterator const & pit);
int parID(int idx) const;
/// Number of Insets this file item is referred
/// If refCount() == 0, this file must be manually inserted.
/// This fact is used by the update() function to skip updating
/// such items.
int refCount() const { return par_it_.size(); }
/// embedding status of this file
bool embedded() const { return embedded_; }
/// set embedding status. update() should be called before this
/// to sync the embedded file with external one.
/// set embedding status. updateFromExternal() should be called before this
/// to copy or sync the embedded file with external one.
bool setEmbed(bool embed) { embedded_ = embed; }
// A flag indicating whether or not this filename is valid.
@ -139,12 +144,12 @@ public:
// status setting untouched.
bool valid() const { return valid_; }
void validate() { valid_ = true; }
void invalidate() { valid_ = false; }
void invalidate();
/// extract file, does not change embedding status
bool extract(Buffer const * buf) const;
/// update embedded file from external file, does not change embedding status
bool update(Buffer const * buf) const;
bool updateFromExternalFile(Buffer const * buf) const;
private:
/// filename in zip file
@ -153,10 +158,9 @@ private:
bool embedded_;
///
bool valid_;
/// Current position of the item, used to locate the files
/// A figure may be referred by several items. In this case
/// only the last location is recorded.
ParConstIterator par_it_;
/// Current position of the item, used to locate the files. Because one
/// file item can be referred by several Insets, a vector is used.
std::vector<ParConstIterator> par_it_;
};
@ -171,10 +175,16 @@ public:
/// return buffer params embedded flag
bool enabled() const;
/// set buffer params embedded flag
void enable(bool flag);
/// set buffer params embedded flag. Files will be updated or extracted
/// if such an operation fails, enable will fail.
bool enable(bool flag);
/// add a file item
/// add a file item.
/* \param filename filename to add
* \param embed embedding status. For a new file item, this is always true.
* If the file already exists, this parameter is ignored.
* \param pit paragraph id.
*/
void registerFile(std::string const & filename, bool embed = false,
ParConstIterator const & pit = ParConstIterator());
@ -196,8 +206,10 @@ public:
EmbeddedFileList::const_iterator end() const { return file_list_.end(); }
// try to locate filename, using either absFilename() or embeddedFile()
EmbeddedFileList::const_iterator find(std::string filename) const;
///
bool extractAll() const;
/// extract all file items, used when disable embedding
bool extract() const;
/// update all files from external, used when enable embedding
bool updateFromExternalFile() const;
///
friend std::istream & operator>> (std::istream & is, EmbeddedFiles &);

View File

@ -67,13 +67,11 @@ void ControlEmbeddedFiles::dispatchMessage(string const & msg)
}
void ControlEmbeddedFiles::goTo(EmbeddedFile const & item)
void ControlEmbeddedFiles::goTo(EmbeddedFile const & item, int idx)
{
int id = item.parID();
if (id != 0) {
string const tmp = convert<string>(item.parID());
kernel().lyxview().dispatch(FuncRequest(LFUN_PARAGRAPH_GOTO, tmp));
}
BOOST_ASSERT(idx < item.refCount());
string const tmp = convert<string>(item.parID(idx));
kernel().lyxview().dispatch(FuncRequest(LFUN_PARAGRAPH_GOTO, tmp));
}
@ -85,8 +83,9 @@ void ControlEmbeddedFiles::view(EmbeddedFile const & item)
void ControlEmbeddedFiles::setEmbed(EmbeddedFile & item, bool embed)
{
// FIXME: updateFromExternalFile() or extract() may fail...
if (embed)
item.update(&kernel().buffer());
item.updateFromExternalFile(&kernel().buffer());
else
item.extract(&kernel().buffer());
item.setEmbed(embed);
@ -106,13 +105,19 @@ docstring const ControlEmbeddedFiles::browseFile()
bool ControlEmbeddedFiles::extract(EmbeddedFile const & item)
{
return item.extract(&kernel().buffer());
if (item.embedded())
return item.extract(&kernel().buffer());
else
return false;
}
bool ControlEmbeddedFiles::update(EmbeddedFile const & item)
{
return item.update(&kernel().buffer());
if (item.embedded())
return item.updateFromExternalFile(&kernel().buffer());
else
return false;
}
} // namespace frontend

View File

@ -44,7 +44,7 @@ public:
///
void dispatchParams() {};
///
void goTo(EmbeddedFile const & item);
void goTo(EmbeddedFile const & item, int idx);
///
void view(EmbeddedFile const & item);
///

View File

@ -12,9 +12,12 @@
#include "GuiEmbeddedFiles.h"
#include "debug.h"
#include "support/convert.h"
using std::string;
namespace lyx {
namespace frontend {
@ -44,9 +47,13 @@ void GuiEmbeddedFilesDialog::on_filesLW_itemChanged(QListWidgetItem* item)
{
EmbeddedFiles & files = controller().embeddedFiles();
if (item->checkState() == Qt::Checked) {
if (files[filesLW->row(item)].embedded())
return;
controller().setEmbed(files[filesLW->row(item)], true);
controller().dispatchMessage("Embed file " + fromqstr(item->text()));
} else {
if (!files[filesLW->row(item)].embedded())
return;
controller().setEmbed(files[filesLW->row(item)], false);
controller().dispatchMessage("Stop embedding file " + fromqstr(item->text()));
}
@ -71,17 +78,10 @@ void GuiEmbeddedFilesDialog::on_filesLW_itemSelectionChanged()
fullpathLE->setEnabled(selection.size() == 1);
// try to find a common embedding status
QList<QListWidgetItem*>::iterator it = selection.begin();
QList<QListWidgetItem*>::iterator it_end = selection.end();
// if the selection is not empty
if (it != it_end) {
int idx = filesLW->row(*it);
fullpathLE->setText(toqstr(files[idx].absFilename()));
controller().goTo(files[idx]);
}
// are the items all selected or unselected?
bool hasSelected = false;
bool hasUnselected = false;
QList<QListWidgetItem*>::iterator it = selection.begin();
QList<QListWidgetItem*>::iterator it_end = selection.end();
for (; it != it_end; ++it) {
if ((*it)->checkState() == Qt::Checked)
hasSelected = true;
@ -93,11 +93,32 @@ void GuiEmbeddedFilesDialog::on_filesLW_itemSelectionChanged()
}
void GuiEmbeddedFilesDialog::on_filesLW_itemDoubleClicked()
void GuiEmbeddedFilesDialog::on_filesLW_itemClicked(QListWidgetItem* item)
{
EmbeddedFiles & files = controller().embeddedFiles();
QList<QListWidgetItem *> selection = filesLW->selectedItems();
controller().view(files[filesLW->row(*selection.begin())]);
int idx = filesLW->row(item);
fullpathLE->setText(toqstr(files[idx].absFilename()));
if (files[idx].refCount() > 1) {
// if multiple insets are referred, click again will move
// to another inset
int k = item->data(Qt::UserRole).toInt();
controller().goTo(files[idx], k);
k = (k + 1) % files[idx].refCount();
item->setData(Qt::UserRole, k);
// update label
string label = files[idx].inzipName() + " ("
+ convert<string>(k + 1) + "/"
+ convert<string>(files[idx].refCount()) + ")";
item->setText(toqstr(label));
} else
controller().goTo(files[idx], 0);
}
void GuiEmbeddedFilesDialog::on_filesLW_itemDoubleClicked(QListWidgetItem* item)
{
EmbeddedFiles & files = controller().embeddedFiles();
controller().view(files[filesLW->row(item)]);
}
@ -109,7 +130,10 @@ void GuiEmbeddedFilesDialog::updateView()
EmbeddedFiles::EmbeddedFileList::const_iterator it = files.begin();
EmbeddedFiles::EmbeddedFileList::const_iterator it_end = files.end();
for (; it != it_end; ++it) {
QListWidgetItem * item = new QListWidgetItem(toqstr(it->inzipName()));
string label = it->inzipName();
if (it->refCount() > 1)
label += " (1/" + convert<string>(it->refCount()) + ")";
QListWidgetItem * item = new QListWidgetItem(toqstr(label));
Qt::ItemFlags flag = Qt::ItemIsUserCheckable | Qt::ItemIsSelectable;
if (it->valid())
flag |= Qt::ItemIsEnabled;
@ -118,6 +142,8 @@ void GuiEmbeddedFilesDialog::updateView()
item->setCheckState(Qt::Checked);
else
item->setCheckState(Qt::Unchecked);
// index of the currently used ParConstIterator
item->setData(Qt::UserRole, 0);
filesLW->addItem(item);
}
}

View File

@ -31,7 +31,8 @@ public Q_SLOTS:
///
void on_filesLW_itemChanged(QListWidgetItem* item);
void on_filesLW_itemSelectionChanged();
void on_filesLW_itemDoubleClicked();
void on_filesLW_itemClicked(QListWidgetItem* item);
void on_filesLW_itemDoubleClicked(QListWidgetItem* item);
///
void updateView();
///