The graphics inset now has:

* lazy loading (don't try and load the image until a request to draw it
  is received).
* asynchronous conversion to a loadable format.
* asynchronous loading if the image loader supports it (it doesn't).
* "simple" cropping, rotating and scaling (in that order) of the image
  on the LyX screen.
* display in color, grayscale or monochrome.

We also have a forked calls dialog, although it isn't very exciting
yet because only the graphics cache makes use of the forked call controller.


git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@3591 a592a061-630c-0410-9148-cb99ea01b6c8
This commit is contained in:
Angus Leeming 2002-02-27 09:59:52 +00:00
parent af8dccce0f
commit 607ad8d3a7
51 changed files with 4754 additions and 869 deletions

View File

@ -1,3 +1,7 @@
2002-02-19 Angus Leeming <a.leeming@ic.ac.uk>
* ui/default.ui: add a show-forks item to the two File menus.
2002-02-21 Jean-Marc Lasgouttes <lasgouttes@freesurf.fr>
* ui/default.ui: change Layout>LaTeX Preamble to Layout>Preamble.

View File

@ -31,6 +31,7 @@ Menuset
Item "Open...|O" "file-open"
Separator
Submenu "Import|I" "file_import"
Item "Child processes|h" "show-forks"
Separator
Item "Exit|x" "lyx-quit"
Separator
@ -52,6 +53,7 @@ Menuset
Submenu "Export|E" "file_export"
Item "Print...|P" "buffer-print"
OptItem "Fax...|F" "buffer-export fax"
Item "Child processes|h" "show-forks"
Separator
Item "Exit|x" "lyx-quit"
Separator

View File

@ -1,3 +1,12 @@
2002-02-20 Angus Leeming <a.leeming@ic.ac.uk>
* lyxfunc.C (dispatch): act on LFUN_FORKS_SHOW and LFUN_FORKS_KILL.
also call grfx::GCache::changeDisplay if the graphicsbg color changes.
* PainterBase.h (image):
* Painter.[Ch] (image): now accepts a grfx::GImage const & rather than
a LyXImage const *.
2002-02-26 John Levon <moz@compsoc.man.ac.uk>
* Makefile.am:

View File

@ -25,7 +25,7 @@
#include "language.h"
#include "frontends/GUIRunTime.h"
#include "frontends/support/LyXImage.h"
#include "graphics/GraphicsImage.h"
#include "support/LAssert.h"
#include "support/lstrings.h"
@ -171,11 +171,10 @@ PainterBase & Painter::pixmap(int x, int y, int w, int h, Pixmap bitmap)
}
PainterBase & Painter::image(int x, int y, int w, int h, LyXImage const * image)
PainterBase & Painter::image(int x, int y, int w, int h,
grfx::GImage const & image)
{
Pixmap bitmap = image->getPixmap();
return pixmap(x, y, w, h, bitmap);
return pixmap(x, y, w, h, image.getPixmap());
}

View File

@ -76,7 +76,8 @@ public:
LColor::color);
/// For the graphics inset.
PainterBase & image(int x, int y, int w, int h, LyXImage const * image);
PainterBase & image(int x, int y, int w, int h,
grfx::GImage const & image);
/// For the figinset
PainterBase & pixmap(int x, int y, int w, int h, Pixmap bitmap);

View File

@ -20,7 +20,9 @@
class WorkArea;
class LyXFont;
class LyXImage;
namespace grfx {
class GImage;
}
/** A painter class to encapsulate all graphics parameters and operations
@ -147,8 +149,8 @@ public:
// For the figure inset
virtual PainterBase & image(int x, int y, int w, int h, LyXImage const * image) = 0;
virtual PainterBase & image(int x, int y, int w, int h,
grfx::GImage const & image) = 0;
/// Draw a string at position x, y (y is the baseline)
virtual PainterBase & text(int x, int y,

View File

@ -1,3 +1,16 @@
2002-02-20 Angus Leeming <a.leeming@ic.ac.uk>
* ControlForks.[Ch]: new files. A controller for the Forked Child
processes dialog, enabling the user to see what forked processes
are running, and, if he so desires, to kill them.
* GUI.h: add class GUIForks.
* Makefile.am: add ControlForks.[Ch].
* ControlGraphics.C (getParams): small change due to change in
insetgraphics.
2002-02-21 Herbert Voss <voss@lyx.org>
* biblio.C: fix bug with commentlines in a bibentry
@ -8,7 +21,7 @@
2002-02-18 Herbert Voss <voss@lyx.org>
* ControlGraphics.[C]: remove help-file call
* ControlGraphics.[Ch]: remove help-file call
2002-02-18 Angus Leeming <a.leeming@ic.ac.uk>

View File

@ -0,0 +1,96 @@
/**
* \file ControlForks.C
* Copyright 2001 The LyX Team
* Read COPYING
*
* \author Angus Leeming
*/
#include <config.h>
#ifdef __GNUG__
#pragma implementation
#endif
#include "ControlForks.h"
#include "ButtonControllerBase.h"
#include "ViewBase.h"
#include "BufferView.h"
#include "LyXView.h"
#include "lyxfunc.h"
#include "frontends/Dialogs.h"
#include "support/forkedcontr.h"
#include "support/lstrings.h"
using std::vector;
using SigC::slot;
ControlForks::ControlForks(LyXView & lv, Dialogs & d)
: ControlDialogBI(lv, d)
{
d_.showForks.connect(slot(this, &ControlForks::show));
}
vector<pid_t> const ControlForks::getPIDs() const
{
ForkedcallsController const & fcc = ForkedcallsController::get();
return fcc.getPIDs();
}
string const ControlForks::getCommand(pid_t pid) const
{
ForkedcallsController const & fcc = ForkedcallsController::get();
return fcc.getCommand(pid);
}
void ControlForks::kill(pid_t pid)
{
pids_.push_back(tostr(pid));
}
void ControlForks::apply()
{
if (!lv_.view()->available())
return;
view().apply();
// Nothing to apply?
if (pids_.empty())
return;
for (vector<string>::const_iterator it = pids_.begin();
it != pids_.end(); ++it) {
lv_.getLyXFunc()->dispatch(LFUN_FORKS_KILL, *it);
}
pids_.clear();
}
void ControlForks::setParams()
{
if (childrenChanged_.connected())
return;
pids_.clear();
ForkedcallsController & fcc = ForkedcallsController::get();
childrenChanged_ =
fcc.childrenChanged.connect(slot(this, &ControlForks::update));
}
void ControlForks::clearParams()
{
pids_.clear();
childrenChanged_.disconnect();
}

View File

@ -0,0 +1,49 @@
// -*- C++ -*-
/**
* \file ControlForks.h
* Copyright 2001 The LyX Team
* Read COPYING
*
* \author Angus Leeming
*/
#ifndef CONTROLFORKS_H
#define CONTROLFORKS_H
#ifdef __GNUG__
#pragma interface
#endif
#include "ControlDialog_impl.h"
#include "LString.h"
#include <sys/types.h>
#include <vector>
/** A controller for dialogs that display the child processes forked by LyX.
Also provides an interface enabling them to be killed prematurely.
*/
class ControlForks : public ControlDialogBI {
public:
///
ControlForks(LyXView &, Dialogs &);
///
std::vector<pid_t> const getPIDs() const;
///
string const getCommand(pid_t) const;
///
void kill(pid_t);
private:
///
virtual void apply();
/// disconnect from the ForkedcallsController
virtual void clearParams();
/// connect to the ForkedcallsController
virtual void setParams();
/// Connection to the ForkedcallsController signal
SigC::Connection childrenChanged_;
/// The list of PIDs to kill
std::vector<string> pids_;
};
#endif // CONTROLFORKS_H

View File

@ -58,7 +58,7 @@ InsetGraphicsParams const ControlGraphics::getParams(string const &)
InsetGraphicsParams const
ControlGraphics::getParams(InsetGraphics const & inset)
{
return inset.getParams();
return inset.params();
}

View File

@ -22,6 +22,7 @@
#include "ControlERT.h"
#include "ControlExternal.h"
#include "ControlFloat.h"
#include "ControlForks.h"
#include "ControlGraphics.h"
#include "insets/insetgraphicsParams.h"
#include "ControlInclude.h"
@ -161,6 +162,17 @@ public:
};
/** Specialization for Forks dialog
*/
template <class GUIview, class GUIbc>
class GUIForks :
public GUI<ControlForks, GUIview, OkApplyCancelPolicy, GUIbc> {
public:
///
GUIForks(LyXView & lv, Dialogs & d)
: GUI<ControlForks, GUIview, OkApplyCancelPolicy, GUIbc>(lv, d) {}
};
/** Specialization for Graphics dialog
*/
template <class GUIview, class GUIbc>

View File

@ -40,16 +40,18 @@ libcontrollers_la_SOURCES= \
ControlDialog.h \
ControlDialog_impl.C \
ControlDialog_impl.h \
ControlError.h \
ControlError.C \
ControlERT.h \
ControlError.h \
ControlERT.C \
ControlExternal.h \
ControlERT.h \
ControlExternal.C \
ControlFloat.h \
ControlExternal.h \
ControlFloat.C \
ControlGraphics.h \
ControlFloat.h \
ControlForks.C \
ControlForks.h \
ControlGraphics.C \
ControlGraphics.h \
ControlInclude.C \
ControlInclude.h \
ControlIndex.C \

View File

@ -1,3 +1,19 @@
2002-02-20 Angus Leeming <a.leeming@ic.ac.uk>
* FormForks.[Ch]:
* forms/form_forks.fd: new files. A view for the Forked Child
processes dialog, enabling the user to see what forked processes
are running, and, if he so desires, to kill them.
* Dialogs.C: add the class Forked Child dialog.
* Makefile.am: add FormForks.[Ch], form_forks.[Ch].
* forms/makefile: add form_forks.fd.
* FormPreferences.C (LnFmisc::apply): rather ugly: call
grfx::GCache::changeDisplay if the lyxrc.display_graphics changes.
2002-02-24 Juergen Spitzmueller <j.spitzmueller@gmx.de>
* forms/form_graphics.fd: Enlarge Restore button.

View File

@ -31,6 +31,7 @@
#include "form_ert.h"
#include "form_external.h"
#include "form_float.h"
#include "form_forks.h"
#include "form_graphics.h"
#include "form_include.h"
#include "form_index.h"
@ -55,6 +56,7 @@
#include "FormERT.h"
#include "FormExternal.h"
#include "FormFloat.h"
#include "FormForks.h"
#include "FormGraphics.h"
#include "FormInclude.h"
#include "FormIndex.h"
@ -93,6 +95,7 @@ Dialogs::Dialogs(LyXView * lv)
add(new GUIError<FormError, xformsBC>(*lv, *this));
add(new GUIERT<FormERT, xformsBC>(*lv, *this));
add(new GUIExternal<FormExternal, xformsBC>(*lv, *this));
add(new GUIForks<FormForks, xformsBC>(*lv, *this));
add(new GUIGraphics<FormGraphics, xformsBC>(*lv, *this));
add(new GUIInclude<FormInclude, xformsBC>(*lv, *this));
add(new GUIIndex<FormIndex, xformsBC>(*lv, *this));

View File

@ -0,0 +1,419 @@
/**
* \file FormForks.C
* Copyright 2001 the LyX Team
* Read the file COPYING
*
* \author Angus Leeming
* \date 2001-10-22
*/
#include <config.h>
#ifdef __GNUG__
#pragma implementation
#endif
#include "xformsBC.h"
#include "FormForks.h"
#include "ControlForks.h"
#include "form_forks.h"
#include "gettext.h"
#include "helper_funcs.h"
#include "xforms_helpers.h"
#include "support/lstrings.h"
using std::vector;
using std::find;
using std::find_if;
typedef FormCB<ControlForks, FormDB<FD_form_forks> > base_class;
FormForks::FormForks(ControlForks & c)
: base_class(c, _("Child processes"))
{}
void FormForks::build() {
dialog_.reset(build_forks());
// It appears that the browsers aren't initialised properly.
// This fudge fixes tings.
fl_add_browser_line(dialog_->browser_children, " ");
fl_add_browser_line(dialog_->browser_kill, " ");
fl_clear_browser(dialog_->browser_children);
fl_clear_browser(dialog_->browser_kill);
// Manage the ok, apply, restore and cancel/close buttons
bc().setOK(dialog_->button_ok);
bc().setApply(dialog_->button_apply);
bc().setCancel(dialog_->button_close);
bc().invalid();
// Set up the tooltip mechanism
setTooltipHandler(dialog_->browser_children);
setTooltipHandler(dialog_->browser_kill);
setTooltipHandler(dialog_->button_all);
setTooltipHandler(dialog_->button_add);
setTooltipHandler(dialog_->button_remove);
}
void FormForks::update()
{
if (!form())
return;
string const current_pid_str =
getSelectedStringFromBrowser(dialog_->browser_kill);
pid_t const current_pid = strToInt(current_pid_str);
vector<pid_t> pids = controller().getPIDs();
// No child processes.
if (pids.empty()) {
if (fl_get_browser_maxline(dialog_->browser_kill) > 0)
fl_clear_browser(dialog_->browser_kill);
if (fl_get_browser_maxline(dialog_->browser_children) > 0)
fl_clear_browser(dialog_->browser_children);
setEnabled(dialog_->browser_children, false);
setEnabled(dialog_->browser_kill, false);
setEnabled(dialog_->button_all, false);
setEnabled(dialog_->button_add, false);
setEnabled(dialog_->button_remove, false);
return;
}
// Remove any processes from the kill browser that aren't in the
// vector of existing PIDs.
for (int i = 1; i <= fl_get_browser_maxline(dialog_->browser_kill);
++i) {
string const pid_str =
getStringFromBrowser(dialog_->browser_kill, i);
pid_t const pid = strToInt(pid_str);
vector<pid_t>::const_iterator it =
find(pids.begin(), pids.end(), pid);
if (it == pids.end())
fl_delete_browser_line(dialog_->browser_kill, i);
}
// Build the children browser from scratch.
if (fl_get_browser_maxline(dialog_->browser_children) > 0)
fl_clear_browser(dialog_->browser_children);
int i = 1;
for (vector<pid_t>::const_iterator it = pids.begin();
it != pids.end(); ++it) {
string const pid_str = tostr(*it);
string const command = controller().getCommand(*it);
string const line = pid_str + '\t' + command;
fl_add_browser_line(dialog_->browser_children, line.c_str());
if (*it == current_pid)
fl_select_browser_line(dialog_->browser_children, i);
++i;
}
setEnabled(dialog_->browser_children, true);
setEnabled(dialog_->button_all, true);
setEnabled(dialog_->button_add, true);
}
void FormForks::apply()
{
// Get the list of all processes to kill.
vector<string> const kill_vec =
getVectorFromBrowser(dialog_->browser_kill);
if (kill_vec.empty())
return;
// Remove these items from the vector of child processes.
for (int i = 1; i <= fl_get_browser_maxline(dialog_->browser_children);
++i) {
string const selection =
getStringFromBrowser(dialog_->browser_children, i);
string pid_str;
split(selection, pid_str, '\t');
vector<string>::const_iterator it =
find(kill_vec.begin(), kill_vec.end(), pid_str);
if (it != kill_vec.end())
fl_delete_browser_line(dialog_->browser_children, i);
}
// Clear the kill browser and deactivate appropriately.
fl_clear_browser(dialog_->browser_kill);
setEnabled(dialog_->browser_kill, false);
setEnabled(dialog_->button_remove, false);
// Pass these pids to the controller for destruction.
for (vector<string>::const_iterator it = kill_vec.begin();
it != kill_vec.end(); ++it) {
pid_t const pid = strToInt(*it);
controller().kill(pid);
}
}
ButtonPolicy::SMInput FormForks::input(FL_OBJECT * ob, long)
{
ButtonPolicy::SMInput activate = ButtonPolicy::SMI_NOOP;
if (ob == dialog_->browser_children) {
activate = input_browser_children();
} else if (ob == dialog_->browser_kill) {
activate = input_browser_kill();
} else if (ob == dialog_->button_all) {
activate = input_button_all();
} else if (ob == dialog_->button_add) {
activate = input_button_add();
} else if (ob == dialog_->button_remove) {
activate = input_button_remove();
}
return activate;
}
ButtonPolicy::SMInput FormForks::input_browser_children()
{
// Selected an item in the browser containing a list of all child
// processes.
// 1. Highlight this item in the browser of processes to kill
// if it is already there.
// 2. If it is there, enable the remove button so that it can
// be removed from this list, if so desired.
// 3. If it isn't there, activate the add button so that it can
// be added to this list if so desired.
string const selection =
getSelectedStringFromBrowser(dialog_->browser_children);
string pid_str;
split(selection, pid_str, '\t');
vector<string> const kill_vec =
getVectorFromBrowser(dialog_->browser_kill);
vector<string>::const_iterator it =
find(kill_vec.begin(), kill_vec.end(), pid_str);
fl_deselect_browser(dialog_->browser_kill);
if (it != kill_vec.end()) {
int const n = int(it - kill_vec.begin());
fl_select_browser_line(dialog_->browser_kill, n+1);
fl_set_browser_topline(dialog_->browser_kill, n+1);
}
setEnabled(dialog_->button_remove, it != kill_vec.end());
setEnabled(dialog_->button_add, it == kill_vec.end());
return ButtonPolicy::SMI_NOOP;
}
namespace {
class FindPID {
public:
FindPID(string const & pid) : pid_(pid) {}
bool operator()(string const & line)
{
if (line.empty())
return false;
string pid_str;
split(line, pid_str, '\t');
return pid_str == pid_;
}
private:
string pid_;
};
} // namespace anon
ButtonPolicy::SMInput FormForks::input_browser_kill()
{
// Selected an item in the browser containing a list of processes
// to kill.
// 1. Highlight this item in the browser of all child processes.
// 2. Enable the remove button so that it can removed from this list,
// if so desired.
// 3. Disable the add button.
string const pid_str =
getSelectedStringFromBrowser(dialog_->browser_kill);
// Find this string in the list of all child processes
vector<string> const child_vec =
getVectorFromBrowser(dialog_->browser_children);
vector<string>::const_iterator it =
find_if(child_vec.begin(), child_vec.end(), FindPID(pid_str));
fl_deselect_browser(dialog_->browser_children);
if (it != child_vec.end()) {
int const n = int(it - child_vec.begin());
fl_select_browser_line(dialog_->browser_children, n+1);
fl_set_browser_topline(dialog_->browser_children, n+1);
}
setEnabled(dialog_->button_remove, true);
setEnabled(dialog_->button_add, false);
return ButtonPolicy::SMI_NOOP;
}
namespace {
vector<string> const getPIDvector(FL_OBJECT * ob)
{
vector<string> vec = getVectorFromBrowser(ob);
if (vec.empty())
return vec;
for (vector<string>::iterator it = vec.begin(); it != vec.end(); ++it) {
string pid_str;
split(*it, pid_str, '\t');
*it = pid_str;
}
return vec;
}
} // namespace anon
ButtonPolicy::SMInput FormForks::input_button_all()
{
// Pressed the "All" button.
// 1. Check that the browser of processes to kill doesn't already
// contain the entire list.
// 2. If it doesn't, copy the PIDs of all child processes into the
// browser of processes to kill.
// 3. Deactivate the "children" browser and the "add" and "all" buttons
// 4. Activate the "kill" browser and the "remove" button"
ButtonPolicy::SMInput activate = ButtonPolicy::SMI_NOOP;
vector<string> const pid_vec = getPIDvector(dialog_->browser_children);
if (fl_get_browser_maxline(dialog_->browser_kill) != pid_vec.size()) {
activate = ButtonPolicy::SMI_VALID;
fl_clear_browser(dialog_->browser_kill);
for (vector<string>::const_iterator it = pid_vec.begin();
it != pid_vec.end(); ++it) {
fl_add_browser_line(dialog_->browser_kill, it->c_str());
}
if (fl_get_browser_maxline(dialog_->browser_kill) >= 1)
fl_set_browser_topline(dialog_->browser_kill, 1);
}
setEnabled(dialog_->browser_children, false);
setEnabled(dialog_->button_add, false);
setEnabled(dialog_->button_all, false);
setEnabled(dialog_->browser_kill, true);
setEnabled(dialog_->button_remove, true);
return activate;
}
ButtonPolicy::SMInput FormForks::input_button_add()
{
// Pressed the "Add" button.
// 1. Copy the PID of the selected item in the browser of all child
// processes over into the browser of processes to kill.
// 2. Activate the "kill" browser and the "remove" button.
// 3. Deactivate the "add" button.
string const selection =
getSelectedStringFromBrowser(dialog_->browser_children);
string pid_str;
split(selection, pid_str, '\t');
vector<string> const kill_vec =
getVectorFromBrowser(dialog_->browser_kill);
vector<string>::const_iterator it =
find(kill_vec.begin(), kill_vec.end(), pid_str);
if (it == kill_vec.end()) {
fl_add_browser_line(dialog_->browser_kill, pid_str.c_str());
int const n = fl_get_browser_maxline(dialog_->browser_kill);
fl_select_browser_line(dialog_->browser_kill, n);
}
setEnabled(dialog_->browser_kill, true);
setEnabled(dialog_->button_remove, true);
setEnabled(dialog_->button_add, false);
return ButtonPolicy::SMI_VALID;
}
ButtonPolicy::SMInput FormForks::input_button_remove()
{
// Pressed the "Remove" button.
// 1. Remove the selected item in the browser of processes to kill.
// 2. Activate the "add" button and "all" buttons.
// 3. Deactivate the "remove" button.
int const sel = fl_get_browser(dialog_->browser_kill);
fl_delete_browser_line(dialog_->browser_kill, sel);
setEnabled(dialog_->button_add, true);
setEnabled(dialog_->button_all, true);
setEnabled(dialog_->button_remove, false);
return ButtonPolicy::SMI_VALID;
}
string const FormForks::getVerboseTooltip(FL_OBJECT const * ob) const
{
string str;
if (ob == dialog_->browser_children) {
str = _("All currently running child processes forked by LyX.");
} else if (ob == dialog_->browser_kill) {
str = _("A list of all child processes to kill.");
} else if (ob == dialog_->button_all) {
str = _("Add all processes to the list of processes to kill.");
} else if (ob == dialog_->button_add) {
str = _("Add the currently selected child process to the list of processes to kill.");
} else if (ob == dialog_->button_remove) {
str = _("Remove the currently selected item from the list of processes to kill.");
}
return str;
}

View File

@ -0,0 +1,51 @@
// -*- C++ -*-
/**
* \file FormForks.h
* Copyright 2001 the LyX Team
* Read the file COPYING
*
* \author Angus Leeming
*/
#ifndef FORMFORKS_H
#define FORMFORKS_H
#ifdef __GNUG__
#pragma interface
#endif
#include "FormBase.h"
struct FD_form_forks;
class ControlForks;
class FormForks : public FormCB<ControlForks, FormDB<FD_form_forks> > {
public:
///
FormForks(ControlForks &);
/// preemptive handler for feedback messages
void feedbackCB(FL_OBJECT *, int);
private:
/// Return the list of PIDs to kill to the controller.
virtual void apply();
/// Build the dialog.
virtual void build();
/// Update the dialog.
virtual void update();
/// Filter the inputs on callback from xforms
virtual ButtonPolicy::SMInput input(FL_OBJECT *, long);
/// tooltips
string const getVerboseTooltip(FL_OBJECT const * ob) const;
/// Fdesign generated method
FD_form_forks * build_forks();
ButtonPolicy::SMInput input_browser_children();
ButtonPolicy::SMInput input_browser_kill();
ButtonPolicy::SMInput input_button_all();
ButtonPolicy::SMInput input_button_add();
ButtonPolicy::SMInput input_button_remove();
};
#endif // FORMFORKS_H

View File

@ -48,6 +48,8 @@
#include "support/filetools.h"
#include "support/LAssert.h"
#include "graphics/GraphicsCache.h"
using std::endl;
using std::pair;
using std::make_pair;
@ -1836,6 +1838,7 @@ void FormPreferences::LnFmisc::apply() const
lyxrc.wheel_jump = static_cast<unsigned int>
(fl_get_counter_value(dialog_->counter_wm_jump));
string const old_value = lyxrc.display_graphics;
if (fl_get_button(dialog_->radio_display_monochrome)) {
lyxrc.display_graphics = "mono";
} else if (fl_get_button(dialog_->radio_display_grayscale)) {
@ -1845,6 +1848,10 @@ void FormPreferences::LnFmisc::apply() const
} else {
lyxrc.display_graphics = "no";
}
if (old_value != lyxrc.display_graphics) {
grfx::GCache & gc = grfx::GCache::get();
gc.changeDisplay();
}
}

View File

@ -76,6 +76,10 @@ libxforms_la_SOURCES = \
FormFloat.h \
form_float.C \
form_float.h \
FormForks.C \
FormForks.h \
form_forks.C \
form_forks.h \
FormGraphics.C \
FormGraphics.h \
form_graphics.C \
@ -178,10 +182,10 @@ libxforms_la_SOURCES = \
form_url.h \
FormVCLog.C \
FormVCLog.h \
input_validators.h \
input_validators.C \
MathsSymbols.h \
input_validators.h \
MathsSymbols.C \
MathsSymbols.h \
Menubar_pimpl.C \
Menubar_pimpl.h \
RadioButtonGroup.C \

View File

@ -0,0 +1,80 @@
// File modified by fdfix.sh for use by lyx (with xforms >= 0.88) and gettext
#include <config.h>
#include "xforms_helpers.h"
#include "gettext.h"
/* Form definition file generated with fdesign. */
#include FORMS_H_LOCATION
#include <stdlib.h>
#include "form_forks.h"
#include "FormForks.h"
FD_form_forks::~FD_form_forks()
{
if ( form->visible ) fl_hide_form( form );
fl_free_form( form );
}
FD_form_forks * FormForks::build_forks()
{
FL_OBJECT *obj;
FD_form_forks *fdui = new FD_form_forks;
fdui->form = fl_bgn_form(FL_NO_BOX, 650, 390);
fdui->form->u_vdata = this;
obj = fl_add_box(FL_UP_BOX, 0, 0, 650, 390, "");
{
char const * const dummy = N_("Forked child processes|#F");
fdui->browser_children = obj = fl_add_browser(FL_HOLD_BROWSER, 20, 30, 400, 290, idex(_(dummy)));
fl_set_button_shortcut(obj, scex(_(dummy)), 1);
}
fl_set_object_lsize(obj, FL_NORMAL_SIZE);
fl_set_object_lalign(obj, FL_ALIGN_TOP);
fl_set_object_callback(obj, C_FormBaseInputCB, 0);
{
char const * const dummy = N_("Kill processes|#K");
fdui->browser_kill = obj = fl_add_browser(FL_HOLD_BROWSER, 510, 30, 125, 290, idex(_(dummy)));
fl_set_button_shortcut(obj, scex(_(dummy)), 1);
}
fl_set_object_lsize(obj, FL_NORMAL_SIZE);
fl_set_object_lalign(obj, FL_ALIGN_TOP);
fl_set_object_callback(obj, C_FormBaseInputCB, 0);
fdui->button_all = obj = fl_add_button(FL_NORMAL_BUTTON, 432, 30, 65, 30, _("All ->"));
fl_set_object_lsize(obj, FL_NORMAL_SIZE);
fl_set_object_callback(obj, C_FormBaseInputCB, 0);
fdui->button_add = obj = fl_add_button(FL_NORMAL_BUTTON, 450, 70, 30, 30, _("@->"));
fl_set_object_lsize(obj, FL_NORMAL_SIZE);
fl_set_object_callback(obj, C_FormBaseInputCB, 0);
fdui->button_remove = obj = fl_add_button(FL_NORMAL_BUTTON, 450, 110, 30, 30, _("@4->"));
fl_set_object_lsize(obj, FL_NORMAL_SIZE);
fl_set_object_callback(obj, C_FormBaseInputCB, 0);
fdui->button_ok = obj = fl_add_button(FL_RETURN_BUTTON, 355, 350, 90, 30, _("OK"));
fl_set_object_lsize(obj, FL_NORMAL_SIZE);
fl_set_object_gravity(obj, FL_SouthEast, FL_SouthEast);
fl_set_object_callback(obj, C_FormBaseOKCB, 0);
{
char const * const dummy = N_("Apply|#A");
fdui->button_apply = obj = fl_add_button(FL_NORMAL_BUTTON, 450, 350, 90, 30, idex(_(dummy)));
fl_set_button_shortcut(obj, scex(_(dummy)), 1);
}
fl_set_object_lsize(obj, FL_NORMAL_SIZE);
fl_set_object_gravity(obj, FL_SouthEast, FL_SouthEast);
fl_set_object_callback(obj, C_FormBaseApplyCB, 0);
{
char const * const dummy = N_("Close|^[");
fdui->button_close = obj = fl_add_button(FL_NORMAL_BUTTON, 545, 350, 90, 30, idex(_(dummy)));
fl_set_button_shortcut(obj, scex(_(dummy)), 1);
}
fl_set_object_lsize(obj, FL_NORMAL_SIZE);
fl_set_object_gravity(obj, FL_SouthEast, FL_SouthEast);
fl_set_object_callback(obj, C_FormBaseCancelCB, 0);
fl_end_form();
fdui->form->fdui = fdui;
return fdui;
}
/*---------------------------------------*/

View File

@ -0,0 +1,29 @@
// File modified by fdfix.sh for use by lyx (with xforms >= 0.88) and gettext
/** Header file generated with fdesign **/
#ifndef FD_form_forks_h_
#define FD_form_forks_h_
/** Callbacks, globals and object handlers **/
extern "C" void C_FormBaseInputCB(FL_OBJECT *, long);
extern "C" void C_FormBaseOKCB(FL_OBJECT *, long);
extern "C" void C_FormBaseApplyCB(FL_OBJECT *, long);
extern "C" void C_FormBaseCancelCB(FL_OBJECT *, long);
/**** Forms and Objects ****/
struct FD_form_forks {
~FD_form_forks();
FL_FORM *form;
FL_OBJECT *browser_children;
FL_OBJECT *browser_kill;
FL_OBJECT *button_all;
FL_OBJECT *button_add;
FL_OBJECT *button_remove;
FL_OBJECT *button_ok;
FL_OBJECT *button_apply;
FL_OBJECT *button_close;
};
#endif /* FD_form_forks_h_ */

View File

@ -0,0 +1,178 @@
Magic: 13000
Internal Form Definition File
(do not change)
Number of forms: 1
Unit of measure: FL_COORD_PIXEL
=============== FORM ===============
Name: form_forks
Width: 650
Height: 390
Number of Objects: 9
--------------------
class: FL_BOX
type: UP_BOX
box: 0 0 650 390
boxtype: FL_UP_BOX
colors: FL_COL1 FL_COL1
alignment: FL_ALIGN_CENTER
style: FL_NORMAL_STYLE
size: FL_DEFAULT_SIZE
lcol: FL_BLACK
label:
shortcut:
resize: FL_RESIZE_ALL
gravity: FL_NoGravity FL_NoGravity
name:
callback:
argument:
--------------------
class: FL_BROWSER
type: HOLD_BROWSER
box: 20 30 400 290
boxtype: FL_DOWN_BOX
colors: FL_COL1 FL_YELLOW
alignment: FL_ALIGN_TOP
style: FL_NORMAL_STYLE
size: FL_NORMAL_SIZE
lcol: FL_BLACK
label: Forked child processes|#F
shortcut:
resize: FL_RESIZE_ALL
gravity: FL_NoGravity FL_NoGravity
name: browser_children
callback: C_FormBaseInputCB
argument: 0
--------------------
class: FL_BROWSER
type: HOLD_BROWSER
box: 510 30 125 290
boxtype: FL_DOWN_BOX
colors: FL_COL1 FL_YELLOW
alignment: FL_ALIGN_TOP
style: FL_NORMAL_STYLE
size: FL_NORMAL_SIZE
lcol: FL_BLACK
label: Kill processes|#K
shortcut:
resize: FL_RESIZE_ALL
gravity: FL_NoGravity FL_NoGravity
name: browser_kill
callback: C_FormBaseInputCB
argument: 0
--------------------
class: FL_BUTTON
type: NORMAL_BUTTON
box: 432 30 65 30
boxtype: FL_UP_BOX
colors: FL_COL1 FL_COL1
alignment: FL_ALIGN_CENTER
style: FL_NORMAL_STYLE
size: FL_NORMAL_SIZE
lcol: FL_BLACK
label: All ->
shortcut:
resize: FL_RESIZE_ALL
gravity: FL_NoGravity FL_NoGravity
name: button_all
callback: C_FormBaseInputCB
argument: 0
--------------------
class: FL_BUTTON
type: NORMAL_BUTTON
box: 450 70 30 30
boxtype: FL_UP_BOX
colors: FL_COL1 FL_COL1
alignment: FL_ALIGN_CENTER
style: FL_NORMAL_STYLE
size: FL_NORMAL_SIZE
lcol: FL_BLACK
label: @->
shortcut:
resize: FL_RESIZE_ALL
gravity: FL_NoGravity FL_NoGravity
name: button_add
callback: C_FormBaseInputCB
argument: 0
--------------------
class: FL_BUTTON
type: NORMAL_BUTTON
box: 450 110 30 30
boxtype: FL_UP_BOX
colors: FL_COL1 FL_COL1
alignment: FL_ALIGN_CENTER
style: FL_NORMAL_STYLE
size: FL_NORMAL_SIZE
lcol: FL_BLACK
label: @4->
shortcut:
resize: FL_RESIZE_ALL
gravity: FL_NoGravity FL_NoGravity
name: button_remove
callback: C_FormBaseInputCB
argument: 0
--------------------
class: FL_BUTTON
type: RETURN_BUTTON
box: 355 350 90 30
boxtype: FL_UP_BOX
colors: FL_COL1 FL_COL1
alignment: FL_ALIGN_CENTER
style: FL_NORMAL_STYLE
size: FL_NORMAL_SIZE
lcol: FL_BLACK
label: OK
shortcut: ^M
resize: FL_RESIZE_ALL
gravity: FL_SouthEast FL_SouthEast
name: button_ok
callback: C_FormBaseOKCB
argument: 0
--------------------
class: FL_BUTTON
type: NORMAL_BUTTON
box: 450 350 90 30
boxtype: FL_UP_BOX
colors: FL_COL1 FL_COL1
alignment: FL_ALIGN_CENTER
style: FL_NORMAL_STYLE
size: FL_NORMAL_SIZE
lcol: FL_BLACK
label: Apply|#A
shortcut:
resize: FL_RESIZE_ALL
gravity: FL_SouthEast FL_SouthEast
name: button_apply
callback: C_FormBaseApplyCB
argument: 0
--------------------
class: FL_BUTTON
type: NORMAL_BUTTON
box: 545 350 90 30
boxtype: FL_UP_BOX
colors: FL_COL1 FL_COL1
alignment: FL_ALIGN_CENTER
style: FL_NORMAL_STYLE
size: FL_NORMAL_SIZE
lcol: FL_BLACK
label: Close|^[
shortcut:
resize: FL_RESIZE_ALL
gravity: FL_SouthEast FL_SouthEast
name: button_close
callback: C_FormBaseCancelCB
argument: 0
==============================
create_the_forms

View File

@ -30,6 +30,7 @@ SRCS = form_aboutlyx.fd \
form_external.fd \
form_filedialog.fd \
form_float.fd \
form_forks.fd \
form_graphics.fd \
form_include.fd \
form_index.fd \

View File

@ -1,3 +1,21 @@
2002-02-15 Angus Leeming <a.leeming@ic.ac.uk>
* ImageLoader.[Ch]:
* ImageLoaderXPM.[Ch]: removed.
* GraphicsConverter.[Ch]:
* GraphicsImage.[Ch]:
* GraphicsImageXPM.[Ch]:
* GraphicsParams.[Ch]:
* GraphicsTypes.h: new files.
* All files. A total re-write of the graphics cache. The cache now
supports asynchronous file conversion and file loading. Images
can be cropped, rotated and scaled for display on the LyX screen.
The old LyXImage and ImageLoader have been combined in a new class
GImage. Ditto, ImageLoaderXPM's functionality has been moved into
GImageXPM.
2002-02-07 Herbert Voss <voss@lyx.org>
* GraphicsCacheItem.C: use unzipFile() from support/filetools

View File

@ -1,12 +1,11 @@
/* This file is part of
* =================================================
*
* LyX, The Document Processor
* Copyright 1995 Matthias Ettrich.
* Copyright 1995-2001 The LyX Team.
/*
* \file GraphicsCache.C
* Copyright 2002 the LyX Team
* Read the file COPYING
*
* This file Copyright 2000 Baruch Even
* ================================================= */
* \author Baruch Even <baruch.even@writeme.com>
* \author Angus Leeming <a.leeming@ic.ac.uk>
*/
#include <config.h>
@ -16,51 +15,174 @@
#include "GraphicsCache.h"
#include "GraphicsCacheItem.h"
#include "GraphicsImage.h"
#include "GraphicsParams.h"
#include "insets/insetgraphics.h"
#include "support/LAssert.h"
// I think that graphicsInit should become Dialogs::graphicsInit.
// These #includes would then be moved there.
// Angus 25 Feb 2002
#include "GraphicsImageXPM.h"
//#include "xformsGraphicsImage.h"
GraphicsCache &
GraphicsCache::getInstance()
namespace {
void graphicsInit()
{
static GraphicsCache singleton;
using namespace grfx;
using SigC::slot;
// connect the image loader based on the XPM library
GImage::newImage.connect(slot(&GImageXPM::newImage));
GImage::loadableFormats.connect(slot(&GImageXPM::loadableFormats));
// connect the image loader based on the xforms library
// GImage::newImage.connect(slot(&xformsGImage::newImage));
// GImage::loadableFormats.connect(slot(&xformsGImage::loadableFormats));
}
} // namespace anon
namespace grfx {
GCache & GCache::get()
{
static bool start = true;
if (start) {
start = false;
graphicsInit();
}
// Now return the cache
static GCache singleton;
return singleton;
}
GraphicsCache::~GraphicsCache()
GCache::GCache()
{
// All elements are destroyed by the shared_ptr's in the map.
cache = new CacheType;
}
GraphicsCache::shared_ptr_item
GraphicsCache::addFile(string const & filename)
// all elements are destroyed by the shared_ptr's in the map.
GCache::~GCache()
{
CacheType::iterator it = cache.find(filename);
if (it != cache.end()) {
return it->second;
delete cache;
}
void GCache::update(InsetGraphics const & inset)
{
// A subset only of InsetGraphicsParams is needed for display purposes.
// The GraphicsParams c-tor also interrogates lyxrc to ascertain whether
// to display or not.
GParams params(inset.params());
// Each inset can reference only one file, so check the cache for any
// graphics files referenced by inset. If the name of this file is
// different from that in params, then remove the reference.
CacheType::iterator it = find(inset);
if (it != cache->end()) {
CacheItemType item = it->second;
if (item->filename() != params.filename) {
item->remove(inset);
if (item->empty())
cache->erase(it);
}
}
// Are we adding a new file or modifying the display of an existing one?
it = cache->find(params.filename);
if (it != cache->end()) {
it->second->modify(inset, params);
return;
}
CacheItemType item(new GCacheItem(inset, params));
if (item.get() != 0)
(*cache)[params.filename] = item;
}
void GCache::remove(InsetGraphics const & inset)
{
CacheType::iterator it = find(inset);
if (it == cache->end())
return;
CacheItemType item = it->second;
item->remove(inset);
if (item->empty()) {
cache->erase(it);
}
}
void GCache::startLoading(InsetGraphics const & inset)
{
CacheType::iterator it = find(inset);
if (it == cache->end())
return;
it->second->startLoading(inset);
}
ImagePtr const GCache::image(InsetGraphics const & inset) const
{
CacheType::const_iterator it = find(inset);
if (it == cache->end())
return ImagePtr();
return it->second->image(inset);
}
ImageStatus GCache::status(InsetGraphics const & inset) const
{
CacheType::const_iterator it = find(inset);
if (it == cache->end())
return ErrorUnknown;
return it->second->status(inset);
}
void GCache::changeDisplay(bool changed_background)
{
CacheType::iterator it = cache->begin();
CacheType::iterator end = cache->end();
for(; it != end; ++it)
it->second->changeDisplay(changed_background);
}
GCache::CacheType::iterator
GCache::find(InsetGraphics const & inset)
{
CacheType::iterator it = cache->begin();
for (; it != cache->end(); ++it) {
if (it->second->referencedBy(inset))
return it;
}
shared_ptr_item cacheItem(new GraphicsCacheItem(filename));
if (cacheItem.get() == 0)
return cacheItem;
cache[filename] = cacheItem;
// GraphicsCacheItem_ptr is a shared_ptr and thus reference counted,
// it is safe to return it directly.
return cacheItem;
return cache->end();
}
void
GraphicsCache::removeFile(string const & filename)
GCache::CacheType::const_iterator
GCache::find(InsetGraphics const & inset) const
{
// We do not destroy the GraphicsCacheItem since we are here because
// the last copy of it is being erased.
CacheType::iterator it = cache.find(filename);
if (it != cache.end())
cache.erase(it);
CacheType::const_iterator it = cache->begin();
for (; it != cache->end(); ++it) {
if (it->second->referencedBy(inset))
return it;
}
return cache->end();
}
} // namespace grfx

View File

@ -1,13 +1,18 @@
// -*- C++ -*-
/* This file is part of
* =================================================
*
* LyX, The Document Processor
* Copyright 1995 Matthias Ettrich.
* Copyright 1995-2001 The LyX Team.
/**
* \file GraphicsCache.h
* Copyright 2002 the LyX Team
* Read the file COPYING
*
* This file Copyright 2000 Baruch Even
* ================================================= */
* \author Baruch Even <baruch.even@writeme.com>
* \author Angus Leeming <a.leeming@ic.ac.uk>
*
* grfx::GCache is the manager of the image cache.
* It is responsible for creating the grfx::GCacheItem's and maintaining them.
*
* grfx::GCache is a singleton class. It is possible to have only one
* instance of it at any moment.
*/
#ifndef GRAPHICSCACHE_H
#define GRAPHICSCACHE_H
@ -16,48 +21,80 @@
#pragma interface
#endif
#include "GraphicsTypes.h"
#include <map>
#include "LString.h"
#include "GraphicsCacheItem.h"
#include <boost/utility.hpp>
#include <boost/smart_ptr.hpp>
class GraphicsCacheItem;
class InsetGraphics;
/** GraphicsCache is the manager of the image cache.
It is responsible of create the GraphicsCacheItem's and maintain them.
GraphicsCache is a singleton class, there should be only one instance of
it at any moment.
*/
class GraphicsCache : boost::noncopyable {
namespace grfx {
class GCacheItem;
class GCache : boost::noncopyable {
public:
/// Get the instance of the class.
static GraphicsCache & getInstance();
/// Public destructor due to compiler warnings.
~GraphicsCache();
typedef boost::shared_ptr<GraphicsCacheItem> shared_ptr_item;
/// This is a singleton class. Get the instance.
static GCache & get();
/// Add a file to the cache.
shared_ptr_item addFile(string const & filename);
///
~GCache();
/// Add a file to the cache (or modify an existing image).
void update(InsetGraphics const &);
/** Remove the data associated with this inset.
* Called from the InsetGraphics d-tor.
*/
void remove(InsetGraphics const &);
/** No processing of the image will take place until this call is
* received.
*/
void startLoading(InsetGraphics const &);
/** If (changed_background == true), then the background color of the
* graphics inset has changed. Update all images.
* Else, the preferred display type has changed.
* Update the view of all insets whose display type is DEFAULT.
*/
void changeDisplay(bool changed_background = false);
/// Get the image referenced by a particular inset.
ImagePtr const image(InsetGraphics const &) const;
/// How far have we got in loading the image?
ImageStatus status(InsetGraphics const &) const;
private:
/// Remove a cache item if it's count has gone to zero.
void removeFile(string const & filename);
/// Private c-tor so we can control how many objects are instantiated.
GraphicsCache() {}
/** Make the c-tor private so we can control how many objects
* are instantiated.
*/
GCache();
/// The cache contains data of this type.
typedef boost::shared_ptr<GCacheItem> CacheItemType;
/** The cache contains one item per file, so use a map to find the
* cache item quickly by filename.
* Note that each cache item can have multiple views, potentially one
* per inset that references the original file.
*/
typedef std::map<string, CacheItemType> CacheType;
/// Search the cache by inset.
CacheType::const_iterator find(InsetGraphics const &) const;
///
typedef std::map<string, shared_ptr_item> CacheType;
///
CacheType cache;
/** We need this so that an Item can tell the cache that it should be
deleted. (to call removeFile).
It also helps removing a warning gcc emits. */
friend class GraphicsCacheItem;
CacheType::iterator find(InsetGraphics const &);
/** Store a pointer to the cache so that we can forward declare
* GCacheItem.
*/
CacheType * cache;
};
#endif
} // namespace grfx
#endif // GRAPHICSCACHE_H

View File

@ -1,13 +1,12 @@
/* This file is part of
* =================================================
*
* LyX, The Document Processor
* Copyright 1995 Matthias Ettrich.
* Copyright 1995-2001 The LyX Team.
/*
* \file GraphicsCacheItem.C
* Copyright 2002 the LyX Team
* Read the file COPYING
*
* \author Baruch Even
* \author Baruch Even <baruch.even@writeme.com>
* \author Herbert Voss <voss@lyx.org>
* ================================================= */
* \author Angus Leeming <a.leeming@ic.ac.uk>
*/
#include <config.h>
@ -15,90 +14,380 @@
#pragma implementation
#endif
#include "graphics/GraphicsCacheItem.h"
#include "graphics/GraphicsCache.h"
#include "graphics/ImageLoaderXPM.h"
#include "converter.h"
#include "lyx_gui_misc.h"
#include "graphics/GraphicsCacheItem.h"
#include "graphics/GraphicsImage.h"
#include "graphics/GraphicsParams.h"
#include "graphics/GraphicsConverter.h"
#include "insets/insetgraphics.h"
#include "BufferView.h"
#include "debug.h"
#include "support/LAssert.h"
#include "gettext.h"
#include "lyxfunc.h"
#include "frontends/support/LyXImage.h"
#include "lyx_main.h" // for global dispatch method
#include "support/LAssert.h"
#include "support/filetools.h"
#include "support/lyxlib.h"
#include "frontends/Alert.h"
// Very, Very UGLY!
extern BufferView * current_view;
using std::endl;
namespace grfx {
/*
* The order of conversion:
*
* The c-tor calls convertImage()
*
* convertImage() verifies that we need to do conversion, if not it will just
* call the loadImage()
* if conversion is needed, it will initiate the conversion.
*
* When the conversion is completed imageConverted() is called, which in turn
* calls loadImage().
*
* Since we currently do everything synchronously, convertImage() calls
* imageConverted() right after it does the call to the conversion process.
*/
GraphicsCacheItem::GraphicsCacheItem(string const & filename)
: filename_(filename), imageStatus_(GraphicsCacheItem::Loading)
GCacheItem::GCacheItem(InsetGraphics const & inset, GParams const & params)
: filename_(params.filename), zipped_(false),
remove_loaded_file_(false), status_(WaitingToLoad)
{
bool success = convertImage(filename);
if (!success) // Conversion failed miserably (couldn't even start).
ModifiedItemPtr item(new ModifiedItem(inset, params, image_));
modified_images.push_back(item);
}
namespace {
typedef GCacheItem::ModifiedItemPtr ModifiedItemPtr;
class Compare_Params {
public:
Compare_Params(GParams const & p) : p_(p) {}
bool operator()(ModifiedItemPtr const & ptr)
{
if (!ptr.get())
return false;
return ptr->params() == p_;
}
private:
GParams const & p_;
};
class Find_Inset {
public:
Find_Inset(InsetGraphics const & i) : i_(i) {}
bool operator()(ModifiedItemPtr const & ptr)
{
if (!ptr.get())
return false;
return ptr->referencedBy(i_);
}
private:
InsetGraphics const & i_;
};
} // namespace anon
void GCacheItem::modify(InsetGraphics const & inset, GParams const & params)
{
// Does this inset currently reference an existing ModifiedItem with
// different params?
// If so, remove the inset from the ModifiedItem's internal list
// of insets
ListType::iterator begin = modified_images.begin();
ListType::iterator end = modified_images.end();
ListType::iterator it = begin;
while (it != end) {
it = std::find_if(it, end, Find_Inset(inset));
if (it == end)
break;
if ((*it)->params() != params) {
(*it)->remove(inset);
if ((*it)->empty())
it = modified_images.erase(it);
}
++it;
}
// Is there an existing ModifiedItem with these params?
// If so, add inset to the list of insets referencing this ModifiedItem
begin = modified_images.begin();
end = modified_images.end();
it = std::find_if(begin, end, Compare_Params(params));
if (it != end) {
(*it)->add(inset);
return;
}
// If no ModifiedItem exists with these params, then create one.
ModifiedItemPtr item(new ModifiedItem(inset, params, image_));
modified_images.push_back(item);
return;
}
void GCacheItem::remove(InsetGraphics const & inset)
{
// search the list of ModifiedItems for one referenced by this inset.
// If it is found, remove the reference.
// If the ModifiedItem is now referenced by no insets, remove it.
ListType::iterator begin = modified_images.begin();
ListType::iterator end = modified_images.end();
ListType::iterator it = std::find_if(begin, end, Find_Inset(inset));
if (it == end)
return;
(*it)->remove(inset);
if ((*it)->empty()) {
modified_images.clear();
}
}
void GCacheItem::startLoading(InsetGraphics const & inset)
{
if (status() != WaitingToLoad)
return;
// Check that the image is referenced by this inset
ListType::const_iterator begin = modified_images.begin();
ListType::const_iterator end = modified_images.end();
ListType::const_iterator it =
std::find_if(begin, end, Find_Inset(inset));
if (it == end)
return;
if ((*it)->params().display == GParams::NONE)
return;
convertToDisplayFormat();
}
bool GCacheItem::empty() const
{
return modified_images.empty();
}
bool GCacheItem::referencedBy(InsetGraphics const & inset) const
{
// Is one of the list of ModifiedItems referenced by this inset?
ListType::const_iterator begin = modified_images.begin();
ListType::const_iterator end = modified_images.end();
return std::find_if(begin, end, Find_Inset(inset)) != end;
}
string const & GCacheItem::filename() const
{
return filename_;
}
ImagePtr const GCacheItem::image(InsetGraphics const & inset) const
{
// find a ModifiedItem that is referenced by this inset.
ListType::const_iterator begin = modified_images.begin();
ListType::const_iterator end = modified_images.end();
ListType::const_iterator it =
std::find_if(begin, end, Find_Inset(inset));
// Someone's being daft.
if (it == end)
return ImagePtr();
// We are expressly requested to not render the image
if ((*it)->params().display == GParams::NONE)
return ImagePtr();
// If the original image has been loaded, return what's going on
// in the ModifiedItem
if (status() == Loaded)
return (*it)->image();
return ImagePtr();
}
ImageStatus GCacheItem::status(InsetGraphics const & inset) const
{
// find a ModifiedItem that is referenced by this inset.
ListType::const_iterator begin = modified_images.begin();
ListType::const_iterator end = modified_images.end();
ListType::const_iterator it =
std::find_if(begin, end, Find_Inset(inset));
// Someone's being daft.
if (it == end)
return ErrorUnknown;
if (status() == Loaded)
return (*it)->status();
return status();
}
// Called internally only. Use to ascertain the status of the loading of the
// original image. No scaling etc.
ImageStatus GCacheItem::status() const
{
return status_;
}
void GCacheItem::setStatus(ImageStatus new_status)
{
status_ = new_status;
// Loop over all insets and tell the BufferView that it has changed.
typedef ModifiedItem::ListType::const_iterator inset_iterator;
ListType::const_iterator it = modified_images.begin();
ListType::const_iterator end = modified_images.end();
for (; it != end; ++it) {
inset_iterator it2 = (*it)->insets.begin();
inset_iterator end2 = (*it)->insets.end();
for (; it2 != end2; ++it2) {
InsetGraphics * inset =
const_cast<InsetGraphics *>(*it2);
// Use of current_view is very, very Evil!!
current_view->updateInset(inset, false);
}
}
}
void GCacheItem::changeDisplay(bool changed_background)
{
ListType::iterator begin = modified_images.begin();
ListType::iterator end = modified_images.end();
// The background has changed. Change all modified images.
if (changed_background) {
for (ListType::iterator it = begin; it != end; ++it) {
(*it)->setPixmap();
}
return;
}
ListType temp_list;
for (ListType::iterator it = begin; it != end; ++it) {
// ModifiedItem::changeDisplay returns a full
// ModifiedItemPtr if any of the insets have display=DEFAULT
// and if that DEFAULT value has changed
ModifiedItemPtr new_item = (*it)->changeDisplay();
if (!new_item.get())
continue;
temp_list.push_back(new_item);
// The original store may now be empty
if ((*it)->insets.empty()) {
it = modified_images.erase(it);
}
}
if (temp_list.empty())
return;
// Recombine new_list and modified_images.
begin = modified_images.begin();
end = modified_images.end();
ListType::const_iterator tbegin = temp_list.begin();
ListType::const_iterator tend = temp_list.end();
ListType append_list;
for (ListType::const_iterator tit = tbegin; tit != tend; ++tit) {
GParams const & params = (*tit)->params();
ListType::iterator it =
std::find_if(begin, end, Compare_Params(params));
if (it == end)
append_list.push_back(*tit);
else
(*it)->insets.merge((*tit)->insets);
}
if (append_list.empty())
return;
modified_images.splice(modified_images.end(), append_list);
}
void GCacheItem::imageConverted(string const & file_to_load)
{
bool const success =
(!file_to_load.empty() && IsFileReadable(file_to_load));
string const text = success ? "succeeded" : "failed";
lyxerr[Debug::GRAPHICS] << "Image conversion " << text << "." << endl;
if (!success) {
setStatus(ErrorConverting);
}
if (zipped_)
lyx::unlink(unzipped_filename_);
GraphicsCacheItem::~GraphicsCacheItem()
{}
GraphicsCacheItem::ImageStatus
GraphicsCacheItem::getImageStatus() const
{
return imageStatus_;
}
void GraphicsCacheItem::setStatus(ImageStatus new_status)
{
imageStatus_ = new_status;
}
LyXImage *
GraphicsCacheItem::getImage() const
{
return image_.get();
}
void GraphicsCacheItem::imageConverted(bool success)
{
// Debug output
string text = "succeeded";
if (!success)
text = "failed";
lyxerr << "imageConverted, conversion " << text << "." << endl;
if (! success) {
lyxerr << "(GraphicsCacheItem::imageConverter) "
"Error converting image." << endl;
setStatus(GraphicsCacheItem::ErrorConverting);
return;
}
cc_.disconnect();
// Do the actual image loading from file to memory.
loadImage();
file_to_load_ = file_to_load;
loadImage();
}
// This function gets called from the callback after the image has been
// converted successfully.
void GCacheItem::loadImage()
{
setStatus(Loading);
lyxerr[Debug::GRAPHICS] << "Loading image." << endl;
// Connect a signal to this->imageLoaded and pass this signal to
// GImage::loadImage.
SignalLoadTypePtr on_finish;
on_finish.reset(new SignalLoadType);
cl_ = on_finish->connect(SigC::slot(this, &GCacheItem::imageLoaded));
image_ = GImage::newImage();
image_->load(file_to_load_, on_finish);
}
void GCacheItem::imageLoaded(bool success)
{
string const text = success ? "succeeded" : "failed";
lyxerr[Debug::GRAPHICS] << "Image loading " << text << "." << endl;
// Clean up after loading.
if (zipped_)
lyx::unlink(unzipped_filename_);
if (remove_loaded_file_ && unzipped_filename_ != file_to_load_)
lyx::unlink(file_to_load_);
cl_.disconnect();
if (!success) {
setStatus(ErrorLoading);
return;
}
setStatus(Loaded);
// Loop over the list of modified images and create them.
ListType::iterator it = modified_images.begin();
ListType::iterator end = modified_images.end();
for (; it != end; ++it) {
(*it)->modify(image_);
}
}
@ -106,35 +395,44 @@ namespace {
string const findTargetFormat(string const & from)
{
typedef ImageLoader::FormatList FormatList;
FormatList formats = ImageLoaderXPM().loadableFormats();
lyx::Assert(formats.size() > 0); // There must be a format to load from.
typedef GImage::FormatList FormatList;
FormatList const & formats = GImage::loadableFormats();
FormatList::const_iterator iter = formats.begin();
FormatList::const_iterator end = formats.end();
// There must be a format to load from.
lyx::Assert(!formats.empty());
for (; iter != end; ++iter) {
if (converters.isReachable(from, *iter))
grfx::GConverter const & graphics_converter = grfx::GConverter::get();
FormatList::const_iterator it = formats.begin();
FormatList::const_iterator end = formats.end();
for (; it != end; ++it) {
if (graphics_converter.isReachable(from, *it))
break;
}
if (iter == end) {
// We do not know how to convert the image to something loadable.
lyxerr << "ERROR: Do not know how to convert image." << endl;
if (it == end)
return string();
}
return (*iter);
return *it;
}
} // anon namespace
bool GraphicsCacheItem::convertImage(string const & filename)
void GCacheItem::convertToDisplayFormat()
{
setStatus(GraphicsCacheItem::Converting);
#warning shadowing class variable (Lgb)
// Is this needed at all?
string filename_ = string(filename);
lyxerr << "try to convert image file: " << filename_ << endl;
setStatus(Converting);
string filename = filename_; // Make a local copy in case we unzip it
string const displayed_filename = MakeDisplayPath(filename_);
// First, check that the file exists!
if (!IsFileReadable(filename)) {
Alert::alert(_("File ") + displayed_filename,
_("\nisn't readable or doesn't exist!"));
setStatus(ErrorNoFile);
return;
}
// maybe that other zip extensions also be useful, especially the
// ones that may be declared in texmf/tex/latex/config/graphics.cfg.
// for example:
@ -146,61 +444,213 @@ bool GraphicsCacheItem::convertImage(string const & filename)
\DeclareGraphicsRule{.eps.gz}{eps}{.eps.bb}{}}}%
-----------snip-------------*/
lyxerr << "GetExtension: " << GetExtension(filename_) << endl;
bool const zipped = zippedFile(filename_);
if (zipped)
filename_ = unzipFile(filename_);
string const from = getExtFromContents(filename_); // get the type
lyxerr << "GetExtFromContents: " << from << endl;
string const to = findTargetFormat(from);
lyxerr << "from: " << from << " -> " << to << endl;
if (to.empty())
return false;
// manage zipped files. unzip them first into the tempdir
lyxerr[Debug::GRAPHICS]
<< "Attempting to convert image file: " << displayed_filename
<< "\nwith recognised extension: " << GetExtension(filename)
<< "." << endl;
zipped_ = zippedFile(filename);
if (zipped_) {
filename = unzipFile(filename);
unzipped_filename_ = filename;
}
string const from = getExtFromContents(filename);
string const to = grfx::findTargetFormat(from);
lyxerr[Debug::GRAPHICS]
<< "The file contains " << from << " format data." << endl;
if (to.empty()) {
Alert::alert(_("Unable to convert file ") +
displayed_filename +
_(" to a loadable format."));
setStatus(ErrorConverting);
return;
}
if (from == to) {
// No conversion needed!
// Saves more than just time: prevents the deletion of
// the "to" file after loading when it's the same as the "from"!
tempfile = filename_;
loadImage();
return true;
lyxerr[Debug::GRAPHICS] << "No conversion needed!" << endl;
file_to_load_ = filename;
loadImage();
return;
}
lyxerr[Debug::GRAPHICS] << "Converting it to " << to << " format." << endl;
// Take only the filename part of the file, without path or extension.
string temp = OnlyFilename(filename_);
temp = ChangeExtension(filename_, string());
string const temp = ChangeExtension(OnlyFilename(filename), string());
// Add some stuff to have it a unique temp file.
// This tempfile is deleted in loadImage after it is loaded to memory.
tempfile = lyx::tempName(string(), temp);
// Add some stuff to create a uniquely named temporary file.
// This file is deleted in loadImage after it is loaded into memory.
string const to_file_base = lyx::tempName(string(), temp);
remove_loaded_file_ = true;
// Remove the temp file, we only want the name...
lyx::unlink(tempfile);
bool result = converters.convert(0, filename_, tempfile, from, to);
tempfile.append(".xpm");
// For now we are synchronous
imageConverted(result);
// Cleanup after the conversion.
lyx::unlink(tempfile);
if (zipped)
lyx::unlink(filename_);
tempfile.erase();
return true;
lyx::unlink(to_file_base);
// Connect a signal to this->imageConverted and pass this signal to
// the graphics converter so that we can load the modified file
// on completion of the conversion process.
SignalConvertTypePtr on_finish;
on_finish.reset(new SignalConvertType);
cc_ = on_finish->connect(SigC::slot(this, &GCacheItem::imageConverted));
GConverter & graphics_converter = GConverter::get();
graphics_converter.convert(filename, to_file_base, from, to, on_finish);
}
// This function gets called from the callback after the image has been
// converted successfully.
void
GraphicsCacheItem::loadImage()
ModifiedItem::ModifiedItem(InsetGraphics const & new_inset,
GParams const & new_params,
ImagePtr const & new_image)
: status_(ScalingEtc)
{
lyxerr << "Loading XPM Image... ";
p_.reset(new GParams(new_params));
insets.push_back(&new_inset);
modify(new_image);
}
ImageLoaderXPM imageLoader;
if (imageLoader.loadImage(tempfile) == ImageLoader::OK) {
lyxerr << "Success." << endl;
image_.reset(imageLoader.getImage());
setStatus(GraphicsCacheItem::Loaded);
void ModifiedItem::add(InsetGraphics const & inset)
{
insets.push_back(&inset);
insets.sort();
}
void ModifiedItem::remove(InsetGraphics const & inset)
{
ListType::iterator begin = insets.begin();
ListType::iterator end = insets.end();
ListType::iterator it = std::remove(begin, end, &inset);
insets.erase(it, end);
}
bool ModifiedItem::referencedBy(InsetGraphics const & inset) const
{
ListType::const_iterator begin = insets.begin();
ListType::const_iterator end = insets.end();
return std::find(begin, end, &inset) != end;
}
ImagePtr const ModifiedItem::image() const
{
if (modified_image_.get())
return modified_image_;
return original_image_;
}
void ModifiedItem::modify(ImagePtr const & new_image)
{
if (!new_image.get())
return;
original_image_ = new_image;
modified_image_.reset(original_image_->clone());
if (params().display == GParams::NONE) {
setStatus(Loaded);
return;
}
setStatus(ScalingEtc);
modified_image_->clip(params());
modified_image_->rotate(params());
modified_image_->scale(params());
setPixmap();
}
void ModifiedItem::setPixmap()
{
if (!modified_image_.get())
return;
if (params().display == GParams::NONE) {
setStatus(Loaded);
return;
}
bool const success = modified_image_->setPixmap(params());
if (success) {
setStatus(Loaded);
} else {
lyxerr << "Loading " << tempfile << "Failed" << endl;
setStatus(GraphicsCacheItem::ErrorReading);
modified_image_.reset();
setStatus(ErrorScalingEtc);
}
}
void ModifiedItem::setStatus(ImageStatus new_status)
{
status_ = new_status;
// Tell the BufferView that the inset has changed.
// Very, Very Ugly!!
ListType::const_iterator it = insets.begin();
ListType::const_iterator end = insets.end();
for (; it != end; ++it) {
InsetGraphics * inset = const_cast<InsetGraphics *>(*it);
current_view->updateInset(inset, false);
}
}
namespace {
struct Params_Changed {
Params_Changed(GParams const & p) : p_(p) {}
bool operator()(InsetGraphics const * inset)
{
return GParams(inset->params()) != p_;
}
private:
GParams p_;
};
} // namespace anon
// changeDisplay returns an initialised ModifiedItem if any of the insets
// have display == DEFAULT and if that DEFAULT value has changed.
// If this occurs, then (this) has these insets removed.
ModifiedItemPtr ModifiedItem::changeDisplay()
{
// Loop over the list of insets. Compare the updated params for each
// with params(). If different, move into a new list.
ListType::iterator begin = insets.begin();
ListType::iterator end = insets.end();
ListType::iterator it =
std::remove_if(begin, end, Params_Changed(params()));
if (it == end) {
// No insets have changed params
return ModifiedItemPtr();
}
// it -> end have params that are changed. Move to the new list.
ListType new_insets;
new_insets.insert(new_insets.begin(), it, end);
insets.erase(it, end);
// Create a new ModifiedItem with these new params. Note that
// the only params that have changed are the display ones,
// so we don't need to crop, rotate, scale.
ModifiedItemPtr new_item(new ModifiedItem(*this));
new_item->insets = new_insets;
*(new_item->p_) = GParams((*new_insets.begin())->params());
new_item->setPixmap();
return new_item;
}
} // namespace grfx

View File

@ -1,13 +1,30 @@
// -*- C++ -*-
/* This file is part of
* =================================================
*
* LyX, The Document Processor
* Copyright 1995 Matthias Ettrich.
* Copyright 1995-2001 The LyX Team.
/*
* \file GraphicsCacheItem.h
* Copyright 2002 the LyX Team
* Read the file COPYING
*
* This file Copyright 2000 Baruch Even
* ================================================= */
* \author Baruch Even <baruch.even@writeme.com>
* \author Angus Leeming <a.leeming@ic.ac.uk>
*
* The graphics cache is a container of GCacheItems. Each GCacheItem, defined
* here represents a separate image file. However, each file can be viewed in
* different ways (different sizes, rotations etc), so each GCacheItem itself
* contains a list of ModifiedItems, also defined here. Each ModifiedItem
* has a GParams variable that defines the way it will be viewed. It also
* contains a list of the graphics insets that refer to it, so calls through
* the GCache to GCacheItem ultimately return the loading status and image
* for that particular graphics inset.
*
* The graphics cache supports fully asynchronous:
* file conversion to a loadable format;
* file loading.
*
* Whether you get that, of course, depends on grfx::GConverter and on the
* grfx::GImage-derived image class.
*
* Image modification (scaling, rotation etc) is blocking.
*/
#ifndef GRAPHICSCACHEITEM_H
#define GRAPHICSCACHEITEM_H
@ -16,78 +33,220 @@
#pragma interface
#endif
#include XPM_H_LOCATION
#include "GraphicsTypes.h"
#include <list>
#include "LString.h"
#include <boost/utility.hpp>
#include <boost/smart_ptr.hpp>
#include <sigc++/signal_system.h>
class LyXImage;
class InsetGraphics;
/// A GraphicsCache item holder.
class GraphicsCacheItem : boost::noncopyable {
namespace grfx {
class GParams;
class ModifiedItem;
/// A grfx::GCache item holder.
class GCacheItem : boost::noncopyable, public SigC::Object {
public:
/// c-tor
GraphicsCacheItem(string const & filename);
/// d-tor, frees the image structures.
~GraphicsCacheItem();
/// Return a pixmap that can be displayed on X server.
LyXImage * getImage() const;
///
enum ImageStatus {
///
Loading = 1,
///
Converting,
///
ErrorConverting,
///
ErrorReading,
///
UnknownError,
///
Loaded
};
/// Is the pixmap ready for display?
ImageStatus getImageStatus() const;
/// the GCacheItem contains data of this type.
typedef boost::shared_ptr<ModifiedItem> ModifiedItemPtr;
/** Get a notification when the image conversion is done.
used by an internal callback mechanism.
*/
void imageConverted(bool success);
///
GCacheItem(InsetGraphics const &, GParams const &);
/// The params have changed (but still refer to this file).
void modify(InsetGraphics const &, GParams const &);
/// Remove the reference to this inset.
void remove(InsetGraphics const &);
/// It's in the cache. Now start the loading process.
void startLoading(InsetGraphics const &);
/// Is the cache item referenced by any insets at all?
bool empty() const;
/// The name of the original image file.
string const & filename() const;
/// Is this image file referenced by this inset?
bool referencedBy(InsetGraphics const &) const;
/** Returns the image referenced by this inset (or an empty container
* if it's not yet loaded.
*/
ImagePtr const image(InsetGraphics const &) const;
/// The loading status of the image referenced by this inset.
ImageStatus status(InsetGraphics const &) const;
/** If (changed_background == true), then the background color of the
* graphics inset has changed. Update all images.
* Else, the preferred display type has changed.
* Update the view of all insets whose display type is DEFAULT.
*/
void changeDisplay(bool changed_background);
private:
/** Start image conversion process, checks first that it is necessary
* if necessary will start an (a)synchronous task and notify upon
* completion by calling imageConverted(bool) where true is for success
* and false is for a failure.
/** Start the image conversion process, checking first that it is
* necessary. If it is necessary, then a conversion task is started.
* GCacheItem asumes that the conversion is asynchronous and so
* passes a Signal to the converting routine. When the conversion
* is finished, this Signal is emitted, returning the converted
* file to this->imageConverted.
*
* Returns a bool to denote success or failure of starting the conversion
* task.
* If no file conversion is needed, then convertToDisplayFormat() calls
* loadImage() directly.
*
* convertToDisplayFormat() will set the loading status flag as
* approriate through calls to setStatus().
*/
bool convertImage(string const & filename);
void convertToDisplayFormat();
/// Load the image into memory, this gets called from imageConverted(bool).
/** Load the image into memory. This is called either from
* convertToDisplayFormat() direct or from imageConverted().
*/
void loadImage();
/// Sets the status of the image, in the future will also notify listeners
/// that the status is updated.
/** Get a notification when the image conversion is done.
* Connected to a signal on_finish_ which is passed to
* GConverter::convert.
*/
void imageConverted(string const & file_to_load);
/** Get a notification when the image loading is done.
* Connected to a signal on_finish_ which is passed to
* GImage::loadImage.
*/
void imageLoaded(bool);
/// How far have we got in loading the original, unmodified image?
ImageStatus status() const;
/** Sets the status of the loading process. Also notifies
* listeners that the status has chacnged.
*/
void setStatus(ImageStatus new_status);
/** The filename we refer too.
This is used when removing ourselves from the cache.
*/
/// The filename we refer too.
string filename_;
/// The temporary file that we use
string tempfile;
/// The image status
ImageStatus imageStatus_;
/// The image (if it got loaded)
boost::scoped_ptr<LyXImage> image_;
/// Is the file compressed?
bool zipped_;
/// If so, store the uncompressed file in this temporary file.
string unzipped_filename_;
/// What file are we trying to load?
string file_to_load_;
/** Should we delete the file after loading? True if the file is
* the result of a conversion process.
*/
bool remove_loaded_file_;
/// The original, unmodified image and its loading status.
ImagePtr image_;
///
ImageStatus status_;
/** A SignalLoadTypePtr is connected to this->imageLoaded and
* then passed to ImagePtr::load.
* When the image has been loaded, the signal is emitted.
*
* We pass a shared_ptr because it is eminently possible for the
* ModifiedItem to be destructed before the loading is complete and
* the signal must remain in scope. It doesn't matter if the slot
* disappears, SigC takes care of that.
*/
typedef SigC::Signal1<void, bool> SignalLoadType;
///
typedef boost::shared_ptr<SignalLoadType> SignalLoadTypePtr;
/// The connection of the signal passed to ImagePtr::loadImage.
SigC::Connection cl_;
/** A SignalConvertTypePtr is connected to this->imageConverted and
* then passed to GConverter::convert.
* When the image has been converted to a loadable format, the signal
* is emitted, returning the name of the loadable file to
* imageConverted.
*/
typedef SigC::Signal1<void, string const &> SignalConvertType;
///
typedef boost::shared_ptr<SignalConvertType> SignalConvertTypePtr;
/// The connection of the signal passed to GConverter::convert.
SigC::Connection cc_;
/// The list of all modified images.
typedef std::list<ModifiedItemPtr> ListType;
///
ListType modified_images;
};
#endif
class ModifiedItem {
public:
///
ModifiedItem(InsetGraphics const &, GParams const &, ImagePtr const &);
///
GParams const & params() { return *p_.get(); }
/// Add inset to the list of insets.
void add(InsetGraphics const & inset);
/// Remove inset from the list of insets.
void remove(InsetGraphics const & inset);
///
bool empty() const { return insets.empty(); }
/// Is this ModifiedItem referenced by inset?
bool referencedBy(InsetGraphics const & inset) const;
///
ImagePtr const image() const;
/// How far have we got in loading the modified image?
ImageStatus status() const { return status_; }
/** Called from GCacheItem once the raw image is loaded.
* Modifies the image in accord with p_.
*/
void modify(ImagePtr const &);
/// Updates the pixmap.
void setPixmap();
/** changeDisplay returns a full ModifiedItemPtr if any of the
* insets have display=DEFAULT and if that DEFAULT value has
* changed.
* If this occurs, then this has these insets removed.
*/
boost::shared_ptr<ModifiedItem> changeDisplay();
///
typedef std::list<InsetGraphics const *> ListType;
/// Make these accessible for changeDisplay.
ListType insets;
private:
/** Sets the status of the loading process. Also notifies
* listeners that the status has changed.
*/
void setStatus(ImageStatus new_status);
/// The original and modified images and its loading status.
ImagePtr original_image_;
///
ImagePtr modified_image_;
///
ImageStatus status_;
///
boost::shared_ptr<GParams> p_;
};
} // namespace grfx
#endif // GRAPHICSCACHEITEM_H

View File

@ -0,0 +1,293 @@
/*
* \file GraphicsConverter.C
* Copyright 2002 the LyX Team
* Read the file COPYING
*
* \author Angus Leeming <a.leeming@ic.ac.uk>
*/
#include <config.h>
#ifdef __GNUG__
#pragma implementation
#endif
#include "GraphicsConverter.h"
#include "converter.h"
#include "debug.h"
#include "gettext.h"
#include "frontends/Alert.h"
#include "support/filetools.h"
#include "support/forkedcall.h"
#include "support/path.h"
#include <fstream>
namespace {
string const move_file(string const & from_file, string const & to_file)
{
if (from_file == to_file)
return string();
ostringstream command;
command << "fromfile=" << from_file << "\n"
<< "tofile=" << to_file << "\n\n"
<< "'mv' -f ${fromfile} ${tofile}\n"
<< "if [ $? -ne 0 ]; then\n"
<< "\t'cp' -f ${fromfile} ${tofile}\n"
<< "\tif [ $? -ne 0 ]; then\n"
<< "\t\texit 1\n"
<< "\tfi\n"
<< "\t'rm' -f ${fromfile}\n"
<< "fi\n";
return command.str().c_str();
}
} // namespace anon
namespace grfx {
GConverter & GConverter::get()
{
static GConverter singleton;
return singleton;
}
bool GConverter::isReachable(string const & from_format_name,
string const & to_format_name) const
{
return converters.isReachable(from_format_name, to_format_name);
}
void GConverter::convert(string const & from_file, string const & to_file_base,
string const & from_format, string const & to_format,
SignalTypePtr on_finish)
{
// The conversion commands are stored in a stringstream
ostringstream script;
script << "#!/bin/sh\n";
bool const success = build_script(from_file, to_file_base,
from_format, to_format, script);
if (!success) {
lyxerr[Debug::GRAPHICS]
<< "Unable to build the conversion script" << std::endl;
on_finish->emit(string());
return;
}
lyxerr[Debug::GRAPHICS] << "Conversion script:\n\n"
<< script.str().c_str() << "\n" << std::endl;
// Output the script to file.
static int counter = 0;
string const script_file = OnlyPath(to_file_base) + "lyxconvert" +
tostr(counter++) + ".sh";
std::ofstream fs(script_file.c_str());
if (!fs.good()) {
// Unable to output the conversion script to file.
on_finish->emit(string());
return;
}
fs << script.str().c_str();
fs.close();
// Create a dummy command for ease of understanding of the
// list of forked processes.
// Note that 'sh ' is absolutely essential, or execvp will fail.
string const script_command =
"sh " + script_file + " " +
OnlyFilename(from_file) + " " + to_format;
string const to_file =
ChangeExtension(to_file_base, formats.extension(to_format));
// Launch the conversion process.
ConvProcessPtr shared_ptr;
shared_ptr.reset(new ConvProcess(script_file, script_command,
to_file, on_finish));
all_processes_.push_back(shared_ptr);
}
namespace {
typedef boost::shared_ptr<ConvProcess> ConvProcessPtr;
class Find_Ptr {
public:
Find_Ptr(ConvProcess * ptr) : ptr_(ptr) {}
bool operator()(ConvProcessPtr const & ptr)
{
return ptr.get() == ptr_;
}
private:
ConvProcess * ptr_;
};
} // namespace anon
void GConverter::erase(ConvProcess * process)
{
std::list<ConvProcessPtr>::iterator begin = all_processes_.begin();
std::list<ConvProcessPtr>::iterator end = all_processes_.end();
std::list<ConvProcessPtr>::iterator it =
std::find_if(begin, end, Find_Ptr(process));
if (it == end)
return;
all_processes_.erase(it);
}
bool GConverter::build_script(string const & from_file,
string const & to_file_base,
string const & from_format,
string const & to_format,
ostringstream & script) const
{
typedef Converters::EdgePath EdgePath;
string const to_file = ChangeExtension(to_file_base,
formats.extension(to_format));
if (from_format == to_format) {
script << move_file(QuoteName(from_file), QuoteName(to_file));
return true;
}
EdgePath edgepath = converters.getPath(from_format, to_format);
if (edgepath.empty()) {
Alert::alert(_("Cannot convert file"),
_("No information for converting from ")
+ formats.prettyName(from_format) + _(" to ")
+ formats.prettyName(to_format));
return false;
}
// Create a temporary base file-name for all intermediate steps.
// Remember to remove the temp file because we only want the name...
static int counter = 0;
string const tmp = "gconvert" + tostr(counter++);
string const to_base = lyx::tempName(string(), tmp);
lyx::unlink(to_base);
string outfile = from_file;
// The conversion commands may contain these tokens that need to be
// changed to infile, infile_base, outfile respectively.
string const token_from("$$i");
string const token_base("$$b");
string const token_to("$$o");
EdgePath::const_iterator it = edgepath.begin();
EdgePath::const_iterator end = edgepath.end();
for (; it != end; ++it) {
Converter const & conv = converters.get(*it);
// Build the conversion command
string const infile = outfile;
string const infile_base = ChangeExtension(infile, string());
outfile = ChangeExtension(to_base, conv.To->extension());
// Store these names in the shell script
script << "infile=" << QuoteName(infile) << '\n'
<< "infile_base=" << QuoteName(infile_base) << '\n'
<< "outfile=" << QuoteName(outfile) << '\n';
string command = conv.command;
command = subst(command, token_from, "${infile}");
command = subst(command, token_base, "${infile_base}");
command = subst(command, token_to, "${outfile}");
// Store in the shell script
script << "\n" << command << "\n\n";
// Test that this was successful. If not, remove
// ${outfile} and exit the shell script
script << "if [ $? -ne 0 ]; then\n"
<< "\t'rm' -f ${outfile}\n"
<< "\texit 1\n"
<< "fi\n\n";
// Test that the outfile exists.
// ImageMagick's convert will often create ${outfile}.0,
// ${outfile}.1.
// If this occurs, move ${outfile}.0 to ${outfile}
// and delete ${outfile}.?
script << "if [ ! -f ${outfile} ]; then\n"
<< "\tif [ -f ${outfile}.0 ]; then\n"
<< "\t\t'mv' -f ${outfile}.0 ${outfile}\n"
<< "\t\t'rm' -f ${outfile}.?\n"
<< "\telse\n"
<< "\t\texit 1\n"
<< "\tfi\n"
<< "fi\n\n";
// Delete the infile, if it isn't the original, from_file.
if (infile != from_file) {
script << "'rm' -f ${infile}\n\n";
}
}
// Move the final outfile to to_file
script << move_file("${outfile}", QuoteName(to_file));
return true;
}
ConvProcess::ConvProcess(string const & script_file,
string const & script_command,
string const & to_file, SignalTypePtr on_finish)
: script_file_(script_file), to_file_(to_file), on_finish_(on_finish)
{
Forkedcall::SignalTypePtr convert_ptr;
convert_ptr.reset(new Forkedcall::SignalType);
convert_ptr->connect(SigC::slot(this, &ConvProcess::converted));
Forkedcall call;
int retval = call.startscript(script_command, convert_ptr);
if (retval > 0) {
// Unable to even start the script, so clean-up the mess!
converted(string(), 0, 1);
}
}
void ConvProcess::converted(string /* cmd */, pid_t /* pid */, int retval)
{
// Clean-up behind ourselves
lyx::unlink(script_file_);
if (retval > 0) {
lyx::unlink(to_file_);
to_file_.erase();
}
if (on_finish_.get()) {
on_finish_->emit(to_file_);
}
grfx::GConverter::get().erase(this);
}
} // namespace grfx

View File

@ -0,0 +1,121 @@
// -*- C++ -*-
/*
* \file GraphicsConverter.h
* Copyright 2002 the LyX Team
* Read the file COPYING
*
* \author Angus Leeming <a.leeming@ic.ac.uk>
*
* class grfx::GConverter enables graphics files to be converted asynchronously
* to a loadable format. It does this by building a shell script of all
* the conversion commands needed for the transformation. This script is then
* sent to the forked calls controller for non-blocking execution. When it
* is finished a signal is emitted, thus informing us to proceed with the
* loading of the image.
*
* Ultimately, this class should be wrapped back into Dekel's converter class.
*/
#ifndef GRAPHICSCONVERTER_H
#define GRAPHICSCONVERTER_H
#include "LString.h"
#include "Lsstream.h"
#include <boost/smart_ptr.hpp>
#include <boost/utility.hpp>
#include <sigc++/signal_system.h>
#include <list>
#ifdef __GNUG__
#pragma interface
#endif
namespace grfx {
class GConverter : boost::noncopyable {
public:
/// This is a singleton class. Get the instance.
static GConverter & get();
/// Can the conversion be performed?
bool isReachable(string const & from_format_name,
string const & to_format_name) const;
/** Convert the file and at the end return it by emitting this signal
* If successful, the returned string will be the name of the
* converted file (to_file_base + extension(to_format_name)).
* If unsuccessful, the string will be empty.
*/
typedef SigC::Signal1<void, string const &> SignalType;
///
typedef boost::shared_ptr<SignalType> SignalTypePtr;
///
void convert(string const & from_file, string const & to_file_base,
string const & from_format, string const & to_format,
SignalTypePtr on_finish);
private:
/** Make the c-tor private so we can control how many objects
* are instantiated.
*/
GConverter() {}
/** Build the conversion script, returning true if able to build it.
* The script is output to the ostringstream 'script'.
*/
bool build_script(string const & from_file, string const & to_file_base,
string const & from_format, string const & to_format,
ostringstream & script) const;
/** Remove the ConvProcess from the list of all processes.
* Called by ConvProcess::converted.
*/
friend class ConvProcess;
///
void erase(ConvProcess *);
/// The list of all conversion processs
typedef boost::shared_ptr<ConvProcess> ConvProcessPtr;
///
std::list<ConvProcessPtr> all_processes_;
};
/// Each ConvProcess represents a single conversion process.
struct ConvProcess : public SigC::Object
{
///
typedef GConverter::SignalTypePtr SignalTypePtr;
/** Each ConvProcess represents a single conversion process.
* It is passed :
* 1. The name of the script_file, which it deletes once the
* conversion is comlpeted;
* 2. The script command itself, which it passes on to the forked
* call process;
* 3. The name of the output file, which it returns to the calling
* process on successfull completion, by emitting
* 4. The signal on_finish.
*/
ConvProcess(string const & script_file, string const & script_command,
string const & to_file, SignalTypePtr on_finish);
/** This method is connected to a signal passed to the forked call
* class, passing control back here when the conversion is completed.
* Cleans-up the temporary files, emits the on_finish signal and
* removes the ConvProcess from the list of all processes.
*/
void converted(string cmd, pid_t pid, int retval);
///
string script_file_;
///
string to_file_;
///
SignalTypePtr on_finish_;
};
} // namespace grfx
#endif // GRAPHICSCONVERTER_H

View File

@ -0,0 +1,59 @@
/*
* \file GraphicsImage.C
* Copyright 2002 the LyX Team
* Read the file COPYING
*
* \author Baruch Even <baruch.even@writeme.com>
* \author Angus Leeming <a.leeming@ic.ac.uk>
*/
#include <config.h>
#ifdef __GNUG__
#pragma implementation
#endif
#include "GraphicsImage.h"
#include "GraphicsParams.h"
#include <iostream>
namespace grfx {
// This will be connected to a function that will return whichever
// whichever derived class we desire.
SigC::Signal0<ImagePtr> GImage::newImage;
/// Return the list of loadable formats.
SigC::Signal0<GImage::FormatList> GImage::loadableFormats;
std::pair<unsigned int, unsigned int>
GImage::getScaledDimensions(GParams const & params) const
{
if (params.scale == 0 && params.width == 0 && params.height == 0)
// No scaling
return std::make_pair(getWidth(), getHeight());
unsigned int width = 0;
unsigned int height = 0;
if (params.scale != 0) {
width = getWidth() * params.scale / 100.0;
height = getHeight() * params.scale / 100.0;
} else {
width = params.width;
height = params.height;
if (width == 0) {
width = height * getWidth() / getHeight();
} else if (height == 0) {
height = width * getHeight() / getWidth();
}
}
if (width == 0 || height == 0)
// Something is wrong!
return std::make_pair(getWidth(), getHeight());
return std::make_pair(width, height);
}
} // namespace grfx

View File

@ -0,0 +1,104 @@
// -*- C++ -*-
/**
* \file GraphicsImage.h
* Copyright 2002 the LyX Team
* Read the file COPYING
*
* \author Baruch Even <baruch.even@writeme.com>
* \author Angus Leeming <a.leeming@ic.ac.uk>
*
* An abstract base class for the images themselves.
* Allows the user to retrieve the pixmap, once loaded and to issue commands
* to modify it.
*
* The signals newImage and loadableFormats are connected to the approriate
* derived classes elsewhere, allowing the graphics cache to access them
* without knowing anything about their instantiation.
*
* The loading process can be asynchronous, but cropping, rotating and
* scaling block execution.
*/
#ifndef GRAPHICSIMAGE_H
#define GRAPHICSIMAGE_H
#include "GraphicsTypes.h"
#include "LString.h"
#include <boost/smart_ptr.hpp>
#include <sigc++/signal_system.h>
#include <vector>
#include <utility>
#include <X11/X.h>
#ifdef __GNUG__
#pragma interface
#endif
namespace grfx {
class GParams;
class GImage
{
public:
/// A list of supported formats.
typedef std::vector<string> FormatList;
/** This will be connected to a function that will return whichever
* derived class we desire.
*/
static SigC::Signal0<ImagePtr> newImage;
/// Return the list of loadable formats.
static SigC::Signal0<FormatList> loadableFormats;
///
virtual ~GImage() {}
/// Create a copy
virtual GImage * clone() const = 0;
///
virtual Pixmap getPixmap() const = 0;
/// Get the image width
virtual unsigned int getWidth() const = 0;
/// Get the image height
virtual unsigned int getHeight() const = 0;
/** At the end of the loading or modification process, return the new
* image by emitting this signal */
typedef SigC::Signal1<void, bool> SignalType;
///
typedef boost::shared_ptr<SignalType> SignalTypePtr;
/// Start loading the image file.
virtual void load(string const & filename, SignalTypePtr) = 0;
/** Generate the pixmap.
* Uses the params to decide on color, grayscale etc.
* Returns true if the pixmap is created.
*/
virtual bool setPixmap(GParams const & params) = 0;
/// Clip the image using params.
virtual void clip(GParams const & params) = 0;
/// Rotate the image using params.
virtual void rotate(GParams const & params) = 0;
/// Scale the image using params.
virtual void scale(GParams const & params) = 0;
protected:
/** Uses the params to ascertain the dimensions of the scaled image.
* Returned as make_pair(width, height).
* If something geso wrong, returns make_pair(getWidth(), getHeight())
*/
std::pair<unsigned int, unsigned int>
getScaledDimensions(GParams const & params) const;
};
} // namespace grfx
#endif // GRAPHICSIMAGE_H

View File

@ -0,0 +1,660 @@
/*
* \file GraphicsImageXPM.C
* Copyright 2002 the LyX Team
* Read the file COPYING
*
* \author Baruch Even <baruch.even@writeme.com>
* \author Angus Leeming <a.leeming@ic.ac.uk>
*/
#include <config.h>
#ifdef __GNUG__
#pragma implementation
#endif
#include "GraphicsImageXPM.h"
#include "GraphicsParams.h"
#include "ColorHandler.h"
#include "debug.h"
#include "frontends/GUIRunTime.h" // x11Display
#include "support/filetools.h" // IsFileReadable
#include "support/lstrings.h"
#include "Lsstream.h"
#include <iomanip> // std::setfill, etc
#include <cmath> // cos, sin
#include <cstdlib> // malloc, free
namespace grfx {
/// Access to this class is through this static method.
ImagePtr GImageXPM::newImage()
{
ImagePtr ptr;
ptr.reset(new GImageXPM());
return ptr;
}
/// Return the list of loadable formats.
GImage::FormatList GImageXPM::loadableFormats()
{
FormatList formats(1);
formats[0] = "xpm";
return formats;
}
GImageXPM::GImageXPM()
: pixmap_(0),
pixmap_status_(PIXMAP_UNINITIALISED)
{}
GImageXPM::GImageXPM(GImageXPM const & other)
: GImage(other),
image_(other.image_),
pixmap_(other.pixmap_),
pixmap_status_(other.pixmap_status_)
{}
GImageXPM::~GImageXPM()
{
if (pixmap_status_ == PIXMAP_SUCCESS)
XFreePixmap(GUIRunTime::x11Display(), pixmap_);
}
GImage * GImageXPM::clone() const
{
return new GImageXPM(*this);
}
unsigned int GImageXPM::getWidth() const
{
return image_.width();
}
unsigned int GImageXPM::getHeight() const
{
return image_.height();
}
Pixmap GImageXPM::getPixmap() const
{
if (!pixmap_status_ == PIXMAP_SUCCESS)
return 0;
return pixmap_;
}
void GImageXPM::load(string const & filename, GImage::SignalTypePtr on_finish)
{
if (filename.empty()) {
on_finish->emit(false);
return;
}
if (!image_.empty()) {
lyxerr[Debug::GRAPHICS]
<< "Image is loaded already!" << std::endl;
on_finish->emit(false);
return;
}
XpmImage * xpm_image = new XpmImage;
int const success =
XpmReadFileToXpmImage(const_cast<char *>(filename.c_str()),
xpm_image, 0);
switch (success) {
case XpmOpenFailed:
lyxerr[Debug::GRAPHICS]
<< "No XPM image file found." << std::endl;
break;
case XpmFileInvalid:
lyxerr[Debug::GRAPHICS]
<< "File format is invalid" << std::endl;
break;
case XpmNoMemory:
lyxerr[Debug::GRAPHICS]
<< "Insufficient memory to read in XPM file"
<< std::endl;
break;
}
if (success != XpmSuccess) {
XpmFreeXpmImage(xpm_image);
delete xpm_image;
lyxerr[Debug::GRAPHICS]
<< "Error reading XPM file '"
<< XpmGetErrorString(success) << "'"
<< std::endl;
} else {
image_.reset(*xpm_image);
}
on_finish->emit(success == XpmSuccess);
}
bool GImageXPM::setPixmap(GParams const & params)
{
if (image_.empty() || params.display == GParams::NONE) {
return false;
}
if (pixmap_status_ == PIXMAP_FAILED) {
return false;
}
if (pixmap_status_ == PIXMAP_SUCCESS) {
return true;
}
using namespace grfx;
Display * display = GUIRunTime::x11Display();
//(BE 2000-08-05)
// This might be a dirty thing, but I dont know any other solution.
Screen * screen = ScreenOfDisplay(display, GUIRunTime::x11Screen());
Pixmap pixmap;
Pixmap mask;
XpmAttributes attrib;
// Allow libXPM lots of leeway when trying to allocate colors.
attrib.closeness = 10000;
attrib.valuemask = XpmCloseness;
// The XPM file format allows multiple pixel colours to be defined
// as c_color, g_color or m_color.
switch (params.display) {
case GParams::MONOCHROME:
attrib.color_key = XPM_MONO;
break;
case GParams::GRAYSCALE:
attrib.color_key = XPM_GRAY;
break;
case GParams::COLOR:
default: // NONE cannot happen!
attrib.color_key = XPM_COLOR;
break;
}
attrib.valuemask |= XpmColorKey;
// Set the color "none" entry to the color of the background.
XpmColorSymbol xpm_col;
xpm_col.name = 0;
xpm_col.value = "none";
xpm_col.pixel = lyxColorHandler->colorPixel(LColor::graphicsbg);
attrib.numsymbols = 1;
attrib.colorsymbols = &xpm_col;
attrib.valuemask |= XpmColorSymbols;
// Load up the pixmap
XpmImage xpm_image = image_.get();
int const status =
XpmCreatePixmapFromXpmImage(display,
XRootWindowOfScreen(screen),
&xpm_image,
&pixmap, &mask, &attrib);
XpmFreeAttributes(&attrib);
if (status != XpmSuccess) {
lyxerr << "Error creating pixmap from xpm_image '"
<< XpmGetErrorString(status) << "'"
<< std::endl;
pixmap_status_ = PIXMAP_FAILED;
return false;
}
pixmap_ = pixmap;
pixmap_status_ = PIXMAP_SUCCESS;
return true;
}
void GImageXPM::clip(GParams const & params)
{
if (image_.empty())
return;
if (params.bb.empty())
// No clipping is necessary.
return;
int const new_width = params.bb.xr - params.bb.xl;
int const new_height = params.bb.yt - params.bb.yb;
if (new_width > image_.width() || new_height > image_.height())
// Bounds are invalid.
return;
if (new_width == image_.width() && new_height == image_.height())
// Bounds are unchanged.
return;
unsigned int * new_data = image_.initialisedData(new_width, new_height);
unsigned int const * old_data = image_.data();
unsigned int * it = new_data;
unsigned int const * start_row = old_data;
for (int row = params.bb.yb; row < params.bb.yt; ++row) {
unsigned int const * begin = start_row + params.bb.xl;
unsigned int const * end = start_row + params.bb.xr;
it = std::copy(begin, end, it);
start_row += image_.width();
}
image_.resetData(new_width, new_height, new_data);
}
void GImageXPM::rotate(GParams const & params)
{
if (image_.empty())
return ;
if (!params.angle)
// No rotation is necessary.
return;
// Ascertain the bounding box of the rotated image
// Rotate about the bottom-left corner
static double const pi = 3.14159265358979323846;
double const angle = double(params.angle) * pi / 180.0;
double const cos_a = cos(angle);
double const sin_a = sin(angle);
// (0, 0)
double max_x = 0; double min_x = 0;
double max_y = 0; double min_y = 0;
// (old_xpm->width, 0)
double x_rot = cos_a * image_.width();
double y_rot = sin_a * image_.width();
max_x = std::max(max_x, x_rot); min_x = std::min(min_x, x_rot);
max_y = std::max(max_y, y_rot); min_y = std::min(min_y, y_rot);
// (image_.width, image_.height)
x_rot = cos_a * image_.width() - sin_a * image_.height();
y_rot = sin_a * image_.width() + cos_a * image_.height();
max_x = std::max(max_x, x_rot); min_x = std::min(min_x, x_rot);
max_y = std::max(max_y, y_rot); min_y = std::min(min_y, y_rot);
// (0, image_.height)
x_rot = - sin_a * image_.height();
y_rot = cos_a * image_.height();
max_x = std::max(max_x, x_rot); min_x = std::min(min_x, x_rot);
max_y = std::max(max_y, y_rot); min_y = std::min(min_y, y_rot);
int const new_width = 1 + int(max_x - min_x); // round up!
int const new_height = 1 + int(max_y - min_y);
unsigned int * new_data = image_.initialisedData(new_width, new_height);
unsigned int const * old_data = image_.data();
// rotate the data
for (int y_old = 0; y_old < image_.height(); ++y_old) {
for (int x_old = 0; x_old < image_.width(); ++x_old) {
int x_new = int(cos_a * x_old - sin_a * y_old - min_x);
int y_new = int(sin_a * x_old + cos_a * y_old - min_y);
// ensure that there are no rounding errors
y_new = std::min(int(new_height - 1), y_new);
y_new = std::max(0, y_new);
x_new = std::min(int(new_width - 1), x_new);
x_new = std::max(0, x_new);
int const old_id = x_old + image_.width() * y_old;
int const new_id = x_new + new_width * y_new;
new_data[new_id] = old_data[old_id];
}
}
image_.resetData(new_width, new_height, new_data);
}
void GImageXPM::scale(GParams const & params)
{
if (image_.empty())
return;
// boost::tie produces horrible compilation errors on my machine
// Angus 25 Feb 2002
std::pair<unsigned int, unsigned int> d = getScaledDimensions(params);
int const new_width = d.first;
int const new_height = d.second;
if (new_width == getWidth() && new_height == getHeight())
// No scaling needed
return;
unsigned int * new_data = image_.initialisedData(new_width, new_height);
unsigned int const * old_data = image_.data();
double const x_scale = double(image_.width()) / double(new_width);
double const y_scale = double(image_.height()) / double(new_height);
// A very simple scaling routine.
// Ascertain the old pixel corresponding to the new one.
// There is no dithering at all here.
for (int x_new = 0; x_new < new_width; ++x_new) {
int x_old = int(x_new * x_scale);
for (int y_new = 0; y_new < new_height; ++y_new) {
int y_old = int(y_new * y_scale);
int const old_id = x_old + image_.width() * y_old;
int const new_id = x_new + new_width * y_new;
new_data[new_id] = old_data[old_id];
}
}
image_.resetData(new_width, new_height, new_data);
}
} // namespace grfx
namespace {
void free_color_table(XpmColor * colorTable, int ncolors);
void copy_color_table(XpmColor const * in, int size, XpmColor * out);
bool contains_color_none(XpmImage const & image);
string const unique_color_string(XpmImage const & image);
// create a copy (using malloc and strcpy). If (!in) return 0;
char * clone_c_string(char const * in);
// Given a string of the form #ff0571 create a string for the appropriate
// grayscale or monochrome color.
char * mapcolor(char * color, bool toGray);
} // namespace anon
namespace grfx {
GImageXPM::Data::Data()
: width_(0), height_(0), cpp_(0), ncolors_(0)
{}
GImageXPM::Data::~Data()
{
if (colorTable_.unique())
free_color_table(colorTable_.get(), ncolors_);
}
void GImageXPM::Data::reset(XpmImage & image)
{
width_ = image.width;
height_ = image.height;
cpp_ = image.cpp;
// Move the data ptr into this store and free up image.data
data_.reset(image.data);
image.data = 0;
// Don't just store the color table, but check first that it contains
// all that we require of it.
// The idea is to store the color table in a shared_ptr and for all
// modified images to use the same table.
// It must, therefore, have a c_color "none" entry and g_color and
// m_color entries corresponding to each and every c_color entry
// (except "none"!)
// 1. Create a copy of the color table.
// Add a c_color "none" entry to the table if it isn't already there.
bool const add_color = !contains_color_none(image);
if (add_color) {
ncolors_ = 1 + image.ncolors;
size_t const mem_size = sizeof(XpmColor) * ncolors_;
XpmColor * table = static_cast<XpmColor *>(malloc(mem_size));
copy_color_table(image.colorTable, image.ncolors, table);
XpmColor & color = table[ncolors_ - 1];
color.symbolic = 0;
color.m_color = 0;
color.g_color = 0;
color.g4_color = 0;
color.string =
clone_c_string(unique_color_string(image).c_str());
color.c_color = clone_c_string("none");
free_color_table(image.colorTable, image.ncolors);
colorTable_.reset(table);
} else {
// Just move the pointer across
ncolors_ = image.ncolors;
colorTable_.reset(image.colorTable);
image.colorTable = 0;
}
// Clean-up the remaining entries of image.
image.width = 0;
image.height = 0;
image.cpp = 0;
image.ncolors = 0;
// 2. Ensure that the color table has g_color and m_color entries
XpmColor * table = colorTable_.get();
for (int i = 0; i < ncolors_; ++i) {
// If the c_color is defined and the equivalent
// grayscale one is not, then define it.
if (table[i].c_color && !table[i].g_color)
table[i].g_color = mapcolor(table[i].c_color, true);
// If the c_color is defined and the equivalent
// monochrome one is not, then define it.
if (table[i].c_color && !table[i].m_color)
table[i].m_color = mapcolor(table[i].c_color, false);
}
}
XpmImage GImageXPM::Data::get() const
{
XpmImage image;
image.width = width_;
image.height = height_;
image.cpp = cpp_;
image.ncolors = ncolors_;
image.data = data_.get();
image.colorTable = colorTable_.get();
return image;
}
void GImageXPM::Data::resetData(int w, int h, unsigned int * d)
{
width_ = w;
height_ = h;
data_.reset(d);
}
unsigned int * GImageXPM::Data::initialisedData(int w, int h) const
{
size_t const data_size = w * h;
size_t const mem_size = sizeof(unsigned int) * data_size;
unsigned int * ptr = static_cast<unsigned int *>(malloc(mem_size));
unsigned int none_id = color_none_id();
std::fill(ptr, ptr + data_size, none_id);
return ptr;
}
unsigned int GImageXPM::Data::color_none_id() const
{
XpmColor * table = colorTable_.get();
for (int i = 0; i < ncolors_; ++i) {
char const * const color = table[i].c_color;
if (color && lowercase(color) == "none")
return i;
}
return 0;
}
} // namespace grfx
namespace {
// Given a string of the form #ff0571 create a string for the appropriate
// grayscale or monochrome color.
char * mapcolor(char * color, bool toGray)
{
if (!color)
return 0;
Display * display = GUIRunTime::x11Display();
Colormap cmap = GUIRunTime::x11Colormap();
XColor xcol;
XColor ccol;
if (XLookupColor(display, cmap, color, &xcol, &ccol) == 0)
return 0;
// Note that X stores the RGB values in the range 0 - 65535
// whilst we require them in the range 0 - 255.
int const r = xcol.red / 256;
int const g = xcol.green / 256;
int const b = xcol.blue / 256;
// This gives a good match to a human's RGB to luminance conversion.
// (From xv's Postscript code --- Mike Ressler.)
int mapped_color = int((0.32 * r) + (0.5 * g) + (0.18 * b));
if (!toGray) // monochrome
mapped_color = (mapped_color < 128) ? 0 : 255;
ostringstream ostr;
ostr << "#" << std::setbase(16) << std::setfill('0')
<< std::setw(2) << mapped_color
<< std::setw(2) << mapped_color
<< std::setw(2) << mapped_color;
// This string is going into an XpmImage struct, so create a copy that
// libXPM can free successfully.
return clone_c_string(ostr.str().c_str());
}
void copy_color_table(XpmColor const * in, int size, XpmColor * out)
{
for (int i = 0; i < size; ++i) {
out[i].string = clone_c_string(in[i].string);
out[i].symbolic = clone_c_string(in[i].symbolic);
out[i].m_color = clone_c_string(in[i].m_color);
out[i].g_color = clone_c_string(in[i].g_color);
out[i].g4_color = clone_c_string(in[i].g4_color);
out[i].c_color = clone_c_string(in[i].c_color);
}
}
void free_color_table(XpmColor * table, int size)
{
for (int i = 0; i < size; ++i) {
free(table[i].string);
free(table[i].symbolic);
free(table[i].m_color);
free(table[i].g_color);
free(table[i].g4_color);
free(table[i].c_color);
}
free(table);
}
char * clone_c_string(char const * in)
{
if (!in)
return 0;
// Don't forget the '\0'
char * out = static_cast<char *>(malloc(strlen(in) + 1));
return strcpy(out, in);
}
bool contains_color_none(XpmImage const & image)
{
for (int i = 0; i < image.ncolors; ++i) {
char const * const color = image.colorTable[i].c_color;
if (color && lowercase(color) == "none")
return true;
}
return false;
}
string const unique_color_string(XpmImage const & image)
{
string id;
for (int i = 0; i < image.cpp; ++i) {
id.push_back('A');
}
for(;;) {
bool found_it = false;
for (int i = 0; i < image.ncolors; ++i) {
string const c_id = image.colorTable[i].string;
if (c_id == id) {
found_it = true;
break;
}
}
if (!found_it)
return id;
// A base 57 counter!
// eg AAAz+1 = AABA, AABz+1 = AACA, AAzz+1 = ABAA
int current_index = int(id.size() - 1);
bool continue_loop = true;
while(continue_loop && current_index >= 0) {
continue_loop = false;
if (id[current_index] == 'z') {
id[current_index] = 'A';
current_index -= 1;
continue_loop = true;
} else {
id[current_index] += 1;
}
}
if (current_index < 0)
// Unable to find a unique string
return string();
}
}
} // namespace anon

View File

@ -0,0 +1,156 @@
// -*- C++ -*-
/**
* \file GraphicsImageXPM.h
* Copyright 2002 the LyX Team
* Read the file COPYING
*
* \author Baruch Even <baruch.even@writeme.com>
* \author Angus Leeming <a.leeming@ic.ac.uk>
*
* An instantiation of GImage that makes use of libXPM to load and store
* the image in memory.
*/
#ifndef GRAPHICSIMAGEXPM_H
#define GRAPHICSIMAGEXPM_H
#include "GraphicsImage.h"
#include XPM_H_LOCATION
#include "support/smart_ptr.h"
#ifdef __GNUG__
#pragma interface
#endif
namespace grfx {
class GImageXPM : public GImage
{
public:
/// Access to this class is through this static method.
static ImagePtr newImage();
/// Return the list of loadable formats.
static FormatList loadableFormats();
///
~GImageXPM();
/// Create a copy
GImage * clone() const;
///
Pixmap getPixmap() const;
/// Get the image width
unsigned int getWidth() const;
/// Get the image height
unsigned int getHeight() const;
/** Load the image file into memory.
* In this case (GImageXPM), the process is blocking.
*/
void load(string const & filename, SignalTypePtr);
/** Generate the pixmap, based on the current state of the
* xpm_image_ (clipped, rotated, scaled etc).
* Uses the params to decide on color, grayscale etc.
* Returns true if the pixmap is created.
*/
bool setPixmap(GParams const & params);
/// Clip the image using params.
void clip(GParams const & params);
/// Rotate the image using params.
void rotate(GParams const & params);
/// Scale the image using params.
void scale(GParams const & params);
private:
/// Access to the class is through newImage() and clone.
GImageXPM();
///
GImageXPM(GImageXPM const &);
/** Contains the data read from file.
* This class is a wrapper for a XpmImage struct, but all views
* of a single file's data will share the same color table.
* This is done by ensuring that the color table contains a "none"
* c_color together with g_color and m_color entries for each c_color
* entry when it is first stored.
*/
class Data
{
public:
/// Default c-tor. Initialise everything to zero.
Data();
~Data();
bool empty() const { return width_ == 0; }
/** Wrap an XpmImage in a nice, clean C++ interface.
* Empty the original XpmImage.
* Does some analysis of the color table to ensure that
* it is suitable for all future eventualities. (See above
* description.)
*/
void reset(XpmImage & image);
/// Reset the data struct with this data.
void resetData(int width, int height, unsigned int * data);
/** Returns a ptr to an initialised block of memory.
* the data is initialised to the color "none" entry.
*/
unsigned int * initialisedData(int width, int height) const;
/** Construct an XpmImage from the stored contents.
* To pass to XpmCreatePixmapFromXpmImage.
* Efficient, because we only copy the ptrs to the structs.
*/
XpmImage get() const;
int width() const { return width_; }
int height() const { return height_; }
int cpp() const { return cpp_; }
int ncolors() const { return ncolors_; }
unsigned int const * data() const
{ return data_.get(); }
XpmColor const * colorTable() const
{ return colorTable_.get(); }
private:
int width_;
int height_;
int cpp_;
int ncolors_;
lyx::shared_c_ptr<unsigned int> data_;
lyx::shared_c_ptr<XpmColor> colorTable_;
unsigned int color_none_id() const;
};
Data image_;
/// The pixmap itself.
Pixmap pixmap_;
/// Is the pixmap initialized?
enum PixmapStatus {
///
PIXMAP_UNINITIALISED,
///
PIXMAP_FAILED,
///
PIXMAP_SUCCESS
};
PixmapStatus pixmap_status_;
};
} // namespace grfx
#endif // GRAPHICSIMAGEXPM_H

View File

@ -0,0 +1,171 @@
/*
* \file GraphicsParams.C
* Copyright 2002 the LyX Team
* Read the file COPYING
*
* \author Angus Leeming <a.leeming@ic.ac.uk>
*/
#include <config.h>
#ifdef __GNUG__
#pragma implementation
#endif
#include "GraphicsParams.h"
#include "insets/insetgraphicsParams.h"
#include "lyxrc.h"
#include "support/lstrings.h"
namespace grfx {
GParams::GParams(InsetGraphicsParams const & iparams)
: filename(iparams.filename),
width(0),
height(0),
scale(0),
angle(0)
{
if (iparams.clip)
bb = iparams.bb;
if (iparams.rotate)
angle = int(iparams.rotateAngle);
if (iparams.display == InsetGraphicsParams::DEFAULT) {
if (lyxrc.display_graphics == "mono")
display = MONOCHROME;
else if (lyxrc.display_graphics == "gray")
display = GRAYSCALE;
else if (lyxrc.display_graphics == "color")
display = COLOR;
else
display = NONE;
} else if (iparams.display == InsetGraphicsParams::NONE) {
display = NONE;
} else if (iparams.display == InsetGraphicsParams::MONOCHROME) {
display = MONOCHROME;
} else if (iparams.display == InsetGraphicsParams::GRAYSCALE) {
display = GRAYSCALE;
} else if (iparams.display == InsetGraphicsParams::COLOR) {
display = COLOR;
}
// Override the above if we're not using a gui
if (!lyxrc.use_gui) {
display = NONE;
}
if (iparams.lyxsize_type == InsetGraphicsParams::SCALE) {
scale = iparams.lyxscale;
} else if (iparams.lyxsize_type == InsetGraphicsParams::WH) {
if (!iparams.lyxwidth.zero())
width = iparams.lyxwidth.inPixels(1, 1);
if (!iparams.lyxheight.zero())
height = iparams.lyxheight.inPixels(1, 1);
// inPixels returns a value scaled by lyxrc.zoom.
// We want, therefore, to undo this.
double const scaling_factor = 100.0 / double(lyxrc.zoom);
width = int(scaling_factor * width);
height = int(scaling_factor * height);
}
}
bool operator==(GParams const & a, GParams const & b)
{
return (a.filename == b.filename &&
a.display == b.display &&
a.bb == b.bb &&
a.width == b.width &&
a.height == b.height &&
a.scale == b.scale &&
a.angle == b.angle);
}
bool operator!=(GParams const & a, GParams const & b)
{
return !(a == b);
}
BoundingBox::BoundingBox()
: xl(0), yb(0), xr(0), yt(0)
{}
BoundingBox::BoundingBox(string const & bb)
{
if (bb.empty())
return;
string tmp1;
string tmp2 = split(bb, tmp1, ' ');
if (!isValidLength(tmp1))
return;
LyXLength const length_xl(tmp1);
tmp2 = split(tmp2, tmp1, ' ');
if (!isValidLength(tmp1))
return;
LyXLength const length_yb(tmp1);
tmp2 = split(tmp2, tmp1, ' ');
if (!isValidLength(tmp1) || !isValidLength(tmp2))
return;
LyXLength const length_xr(tmp1);
LyXLength const length_yt(tmp2);
// inPixels returns the length in inches, scaled by
// lyxrc.dpi and lyxrc.zoom.
// We want, therefore, to undo all this lyxrc nonsense because we
// want the bounding box in Postscript pixels.
// Note further that there are 72 Postscript pixels per inch.
double const scaling_factor = 7200.0 / (lyxrc.dpi * lyxrc.zoom);
xl = int(scaling_factor * length_xl.inPixels(1, 1));
yb = int(scaling_factor * length_yb.inPixels(1, 1));
xr = int(scaling_factor * length_xr.inPixels(1, 1));
yt = int(scaling_factor * length_yt.inPixels(1, 1));
if (xr <= xl || yt <= yb) {
xl = 0;
yb = 0;
xr = 0;
yt = 0;
return;
}
}
bool BoundingBox::empty() const
{
return (!xl && !yb && !xr && !yt);
}
bool operator==(BoundingBox const & a, BoundingBox const & b)
{
return (a.xl == b.xl &&
a.yb == b.yb &&
a.xr == b.xr &&
a.yt == b.yt);
}
bool operator!=(BoundingBox const & a, BoundingBox const & b)
{
return !(a == b);
}
} // namespace grfx

View File

@ -0,0 +1,98 @@
// -*- C++ -*-
/**
* \file GraphicsParams.h
* Copyright 2002 the LyX Team
* Read the file COPYING
*
* \author Angus Leeming <a.leeming@ic.ac.uk>
*
* Used internally by the GraphicsCache.
* Only a subset of InsetGraphicsParams is needed for display purposes.
* The GraphicsParams c-tor also interrogates lyxrc to ascertain whether
* to display or not.
*/
#ifndef GRAPHICSPARAMS_H
#define GRAPHICSPARAMS_H
#ifdef __GNUG__
#pragma interface
#endif
#include "LString.h"
#include "lyxlength.h"
class InsetGraphicsParams;
namespace grfx {
/** Parse a string of the form "200pt 500pt 300mm 5in" into a
* usable bounding box.
*/
struct BoundingBox {
///
BoundingBox();
///
BoundingBox(string const &);
/// 0 0 0 0 is empty!
bool empty() const;
///
int xl;
int yb;
int xr;
int yt;
};
///
bool operator==(BoundingBox const &, BoundingBox const &);
///
bool operator!=(BoundingBox const &, BoundingBox const &);
struct GParams
{
///
GParams(InsetGraphicsParams const &);
/// How is the image to be displayed on the LyX screen?
enum DisplayType {
///
COLOR,
///
GRAYSCALE,
///
MONOCHROME,
/// We aren't going to display it at all!
NONE
};
///
DisplayType display;
/// The image filename.
string filename;
///
BoundingBox bb;
/** The size of the view inside lyx in pixels or the scaling of the
* image.
*/
unsigned int width;
///
unsigned int height;
///
unsigned int scale;
/// Rotation angle.
int angle;
};
///
bool operator==(GParams const &, GParams const &);
///
bool operator!=(GParams const &, GParams const &);
} // namespace grfx
#endif // GRAPHICSPARAMS_H

View File

@ -0,0 +1,57 @@
// -*- C++ -*-
/**
* \file GraphicsTypes.h
* Copyright 2002 the LyX Team
* Read the file COPYING
*
* \author Angus Leeming <a.leeming@ic.ac.uk>
*
* All that header files outside the graphics subdirectory should need to
* access. That just leaves insetgraphics.C to access GraphicsCache.h.
* It also makes life easier for files inside the graphics subdirectory!
*/
#ifndef GRAPHICSTYPES_H
#define GRAPHICSTYPES_H
#include <boost/smart_ptr.hpp>
#ifdef __GNUG__
#pragma interface
#endif
namespace grfx {
///
class GImage;
///
typedef boost::shared_ptr<GImage> ImagePtr;
/// The status of the loading process
enum ImageStatus {
/** The data is in the cache, but no request to display it
* has been received.
*/
WaitingToLoad,
/// The image is in a loadable format and is being loaded.
Loading,
/// The image is being converted to a loadable format.
Converting,
/// The image is in memory and is being scaled, rotated, etc.
ScalingEtc,
/// All finished. Can display the image.
Loaded,
///
ErrorNoFile,
///
ErrorConverting,
///
ErrorLoading,
/// Fall back on the unmodified image?
ErrorScalingEtc,
/// The data is not in the cache at all!
ErrorUnknown
};
}
#endif // GRAPHICSTYPES_H

View File

@ -1,82 +0,0 @@
/* This file is part of
* =================================================
*
* LyX, The Document Processor
* Copyright 1995 Matthias Ettrich.
* Copyright 1995-2001 The LyX Team.
*
* ================================================= */
#ifdef __GNUG__
#pragma implementation
#endif
#include <config.h>
#include "debug.h"
#include "ImageLoader.h"
#include "frontends/support/LyXImage.h"
#include "support/filetools.h"
using std::endl;
ImageLoader::ImageLoader()
: image_(0)
{
}
ImageLoader::~ImageLoader()
{
freeImage();
}
void
ImageLoader::freeImage()
{
delete image_;
image_ = 0;
}
bool ImageLoader::isImageFormatOK(string const & /*filename*/) const
{
return false;
}
void ImageLoader::setImage(LyXImage * image)
{
image_ = image;
}
LyXImage * ImageLoader::getImage()
{
LyXImage * tmp = image_;
image_ = 0;
return tmp;
}
ImageLoader::FormatList const
ImageLoader::loadableFormats() const
{
return FormatList();
}
ImageLoader::Result
ImageLoader::loadImage(string const & filename)
{
// Make sure file exists and is readable.
if (! IsFileReadable(filename)) {
lyxerr << "No XPM file found." << endl;
return NoFile;
}
// Verify that the file format is correct.
if (! isImageFormatOK(filename)) {
lyxerr << "File format incorrect." << endl;
return ImageFormatUnknown;
}
freeImage();
return runImageLoader(filename);
}

View File

@ -1,87 +0,0 @@
// -*- C++ -*-
/* This file is part of
* =================================================
*
* LyX, The Document Processor
* Copyright 1995 Matthias Ettrich.
* Copyright 1995-2001 The LyX Team.
*
* ================================================= */
#ifndef IMAGELOADER_H
#define IMAGELOADER_H
#ifdef __GNUG__
#pragma interface
#endif
#include "LString.h"
#include "boost/utility.hpp"
#include <vector>
class LyXImage;
/** ImageLoader is a base class for all image loaders. An ImageLoader instance is
* platform dependent, and knows how to load some image formats into a memory
* representation (LyXImage).
*
* It may do the image loading asynchronously.
*
* @Author Baruch Even, <baruch.even@writeme.com>
*/
class ImageLoader : boost::noncopyable {
public:
/// Errors that can be returned from this class.
enum Result {
OK = 0,
ImageFormatUnknown, // This loader doesn't know how to load this file.
NoFile, // File doesn't exists.
ErrorWhileLoading // Unknown error when loading.
};
/// A list of supported formats.
typedef std::vector<string> FormatList;
/// c-tor.
ImageLoader();
/// d-tor.
virtual ~ImageLoader();
/// Start loading the image file.
ImageLoader::Result loadImage(string const & filename);
/** Get the last rendered pixmap. Returns 0 if no image is ready.
*
* It is a one time operation, that is, after you get the image
* you are completely responsible to destroy it and the ImageLoader
* will not know about the image.
*
* This way we avoid deleting the image if you still use it and the
* ImageLoader is destructed, and if you don't use it we get to
* destruct the image to avoid memory leaks.
*/
LyXImage * getImage();
/// Return the list of loadable formats.
virtual FormatList const loadableFormats() const;
protected:
/// Verify that the file is one that we can handle.
virtual bool isImageFormatOK(string const & filename) const = 0;
/// Do the actual image loading.
virtual Result runImageLoader(string const & filename) = 0;
/// Set the image that was loaded.
void setImage(LyXImage * image);
private:
/// Free the loaded image.
void freeImage();
/// The loaded image. An auto_ptr would be great here, but it's not
/// available everywhere (gcc 2.95.2 doesnt have it).
LyXImage * image_;
};
#endif

View File

@ -1,142 +0,0 @@
/* This file is part of
* =================================================
*
* LyX, The Document Processor
* Copyright 1995 Matthias Ettrich.
* Copyright 1995-2001 The LyX Team.
*
* ================================================= */
#include <config.h>
#ifdef __GNUG__
#pragma implementation
#endif
#include "ImageLoaderXPM.h"
#include "ColorHandler.h"
#include "lyxrc.h"
#include "debug.h"
#include "frontends/support/LyXImage.h"
#include "frontends/GUIRunTime.h"
#include "support/filetools.h"
#include "support/LAssert.h"
#include XPM_H_LOCATION
#include <iostream>
#include <fstream>
using std::ifstream;
using std::endl;
using std::ios;
bool ImageLoaderXPM::isImageFormatOK(string const & filename) const
{
ifstream is(filename.c_str(), ios::in);
// The signature of the file without the spaces.
static char const str[] = "/*XPM*/";
char const * ptr = str;
for (; *ptr != '\0'; ++ptr) {
char c;
is >> c;
if (c != *ptr)
return false;
}
return true;
}
ImageLoaderXPM::FormatList const
ImageLoaderXPM::loadableFormats() const
{
FormatList formats;
formats.push_back("xpm");
return formats;
}
ImageLoader::Result
ImageLoaderXPM::runImageLoader(string const & filename)
{
Display * display = GUIRunTime::x11Display();
//(BE 2000-08-05)
// This might be a dirty thing, but I dont know any other solution.
Screen * screen = ScreenOfDisplay(display, GUIRunTime::x11Screen());
Pixmap pixmap;
Pixmap mask;
// If the pixmap contains a transparent colour, then set it to the
// colour of the background (Angus 21 Sep 2001)
XpmColorSymbol xpm_col;
xpm_col.name = 0;
xpm_col.value = "none";
xpm_col.pixel = lyxColorHandler->colorPixel(LColor::graphicsbg);
XpmAttributes attrib;
attrib.valuemask = XpmCloseness | XpmColorSymbols;
attrib.closeness = 10000;
attrib.numsymbols = 1;
attrib.colorsymbols = &xpm_col;
// Set color_key to monochrome, grayscale or color
// (Angus 21 Sep 2001)
int color_key = 0;
if (lyxrc.display_graphics == "color") {
color_key = XPM_COLOR;
} else if (lyxrc.display_graphics == "gray") {
color_key = XPM_GRAY;
} else if (lyxrc.display_graphics == "mono") {
color_key = XPM_MONO;
}
// If setting color_key failed, then fail gracefully!
if (color_key != 0) {
attrib.valuemask |= XpmColorKey;
attrib.color_key = color_key;
} else {
lyxerr << "Warning in ImageLoaderXPM::runImageLoader"
<< "lyxrc.display_graphics == \""
<< lyxrc.display_graphics
<< "\""
<< endl;
}
// Load up the pixmap
int status = XpmReadFileToPixmap(
display,
XRootWindowOfScreen(screen),
const_cast<char *>(filename.c_str()),
&pixmap, &mask, &attrib);
if (status != XpmSuccess) {
lyxerr << "Error reading XPM file '"
<< XpmGetErrorString(status) << "'"
<< endl;
return ErrorWhileLoading;
}
// This should have been set by the XpmReadFileToPixmap call!
lyx::Assert(attrib.valuemask & XpmSize);
setImage(new LyXImage(pixmap, attrib.width, attrib.height));
XpmFreeAttributes(&attrib);
return OK;
}

View File

@ -1,43 +0,0 @@
// -*- C++ -*-
/* This file is part of
* =================================================
*
* LyX, The Document Processor
* Copyright 1995 Matthias Ettrich.
* Copyright 1995-2001 The LyX Team.
*
* ================================================= */
#ifndef IMAGELOADER_XPM_H
#define IMAGELOADER_XPM_H
#ifdef __GNUG__
#pragma interface
#endif
#include "graphics/ImageLoader.h"
/** ImageLoaderXPM is an implementation of ImageLoader that can load XPM images by
* using libXPM.
*
* @Author Baruch Even, <baruch.even@writeme.com>
*/
class ImageLoaderXPM : public ImageLoader {
public:
/// c-tor.
ImageLoaderXPM() {};
/// d-tor.
virtual ~ImageLoaderXPM() {};
/// Return the list of loadable formats.
virtual FormatList const loadableFormats() const;
protected:
/// Verify that the file is one that we can handle.
virtual bool isImageFormatOK(string const & filename) const;
/// Do the actual image loading.
virtual ImageLoader::Result runImageLoader(string const & filename);
};
#endif

View File

@ -12,10 +12,14 @@ libgraphics_la_SOURCES = \
GraphicsCache.C \
GraphicsCacheItem.h \
GraphicsCacheItem.C \
ImageLoaderXPM.h \
ImageLoaderXPM.C \
ImageLoader.h \
ImageLoader.C
GraphicsConverter.h \
GraphicsConverter.C \
GraphicsImage.h \
GraphicsImage.C \
GraphicsImageXPM.h \
GraphicsImageXPM.C \
GraphicsParams.C \
GraphicsParams.h
libgraphics.la: libgraphics.o

View File

@ -93,47 +93,40 @@ TODO Before initial production release:
#include "insets/insetgraphicsParams.h"
#include "graphics/GraphicsCache.h"
#include "graphics/GraphicsCacheItem.h"
#include "graphics/GraphicsImage.h"
#include "LyXView.h"
#include "buffer.h"
#include "BufferView.h"
#include "converter.h"
#include "Painter.h"
#include "lyx_gui_misc.h"
#include "lyxtext.h"
#include "lyxrc.h"
#include "font.h"
#include "font.h" // For the lyxfont class.
#include "debug.h"
#include "gettext.h"
#include "LaTeXFeatures.h"
#include "frontends/Dialogs.h"
#include "frontends/Alert.h"
#include "frontends/controllers/helper_funcs.h"
#include "frontends/support/LyXImage.h"
#include "frontends/controllers/helper_funcs.h" // getVectorFromString
#include "support/FileInfo.h"
#include "support/LAssert.h"
#include "support/filetools.h"
#include "support/lyxlib.h"
#include "support/lyxmanip.h"
#include "support/lyxalgo.h"
#include "support/lyxalgo.h" // lyx::count
#include <fstream>
#include <algorithm>
#include <algorithm> // For the std::max
extern string system_tempdir;
using std::ifstream;
using std::ostream;
using std::endl;
using std::max;
using std::vector;
///////////////////////////////////////////////////////////////////////////
int const VersionNumber = 1;
///////////////////////////////////////////////////////////////////////////
namespace {
// This function is a utility function
// ... that should be with ChangeExtension ...
inline
@ -141,6 +134,8 @@ string const RemoveExtension(string const & filename)
{
return ChangeExtension(filename, string());
}
} // namespace anon
namespace {
@ -159,19 +154,17 @@ string const unique_id()
} // namespace anon
// Initialize only those variables that do not have a constructor.
InsetGraphics::InsetGraphics()
: cacheHandle(0), imageLoaded(false), graphic_label(unique_id())
: cached_status_(grfx::ErrorUnknown), cache_filled_(false),
graphic_label(unique_id())
{}
InsetGraphics::InsetGraphics(InsetGraphics const & ig, bool same_id)
: Inset(), SigC::Object()
, cacheHandle(ig.cacheHandle)
, imageLoaded(ig.imageLoaded)
, graphic_label(unique_id())
: cached_status_(grfx::ErrorUnknown), cache_filled_(false),
graphic_label(unique_id())
{
setParams(ig.getParams());
setParams(ig.params());
if (same_id)
id_ = ig.id_;
}
@ -179,6 +172,10 @@ InsetGraphics::InsetGraphics(InsetGraphics const & ig, bool same_id)
InsetGraphics::~InsetGraphics()
{
cached_image_.reset(0);
grfx::GCache & gc = grfx::GCache::get();
gc.remove(*this);
// Emits the hide signal to the dialog connected (if any)
hideDialog();
}
@ -187,37 +184,70 @@ InsetGraphics::~InsetGraphics()
string const InsetGraphics::statusMessage() const
{
string msg;
if (cacheHandle.get()) {
switch (cacheHandle->getImageStatus()) {
case GraphicsCacheItem::UnknownError:
msg = _("Unknown Error");
break;
case GraphicsCacheItem::Loading:
msg = _("Loading...");
break;
case GraphicsCacheItem::ErrorReading:
msg = _("Error reading");
break;
case GraphicsCacheItem::Converting:
msg = _("Converting Image");
break;
case GraphicsCacheItem::ErrorConverting:
msg = _("Error converting");
break;
case GraphicsCacheItem::Loaded:
// No message to write.
break;
}
switch (cached_status_) {
case grfx::WaitingToLoad:
msg = _("Waiting for draw request to start loading...");
break;
case grfx::Loading:
msg = _("Loading...");
break;
case grfx::Converting:
msg = _("Converting to loadable format...");
break;
case grfx::ScalingEtc:
msg = _("Loaded. Scaling etc...");
break;
case grfx::ErrorNoFile:
msg = _("No file found!");
break;
case grfx::ErrorLoading:
msg = _("Error loading file into memory");
break;
case grfx::ErrorConverting:
msg = _("Error converting to loadable format");
break;
case grfx::ErrorScalingEtc:
msg = _("Error scaling etc");
break;
case grfx::ErrorUnknown:
msg = _("No image associated with this inset is in the cache!");
break;
case grfx::Loaded:
msg = _("Loaded but not displaying");
break;
}
return msg;
}
void InsetGraphics::setCache() const
{
if (cache_filled_)
return;
grfx::GCache & gc = grfx::GCache::get();
cached_status_ = gc.status(*this);
cached_image_ = gc.image(*this);
}
bool InsetGraphics::drawImage() const
{
setCache();
Pixmap const pixmap =
(cached_status_ == grfx::Loaded && cached_image_.get() != 0) ?
cached_image_->getPixmap() : 0;
return pixmap != 0;
}
int InsetGraphics::ascent(BufferView *, LyXFont const &) const
{
LyXImage * pixmap = 0;
if (cacheHandle.get() && (pixmap = cacheHandle->getImage()))
return pixmap->getHeight();
if (drawImage())
return cached_image_->getHeight();
else
return 50;
}
@ -225,24 +255,21 @@ int InsetGraphics::ascent(BufferView *, LyXFont const &) const
int InsetGraphics::descent(BufferView *, LyXFont const &) const
{
// this is not true if viewport is used and clip is not.
return 0;
}
int InsetGraphics::width(BufferView *, LyXFont const & font) const
{
LyXImage * pixmap = 0;
if (cacheHandle.get() && (pixmap = cacheHandle->getImage()))
return pixmap->getWidth();
if (drawImage())
return cached_image_->getWidth();
else {
int font_width = 0;
LyXFont msgFont(font);
msgFont.setFamily(LyXFont::SANS_FAMILY);
string const justname = OnlyFilename (params.filename);
string const justname = OnlyFilename (params().filename);
if (!justname.empty()) {
msgFont.setSize(LyXFont::SIZE_FOOTNOTE);
font_width = lyxfont::width(justname, msgFont);
@ -252,10 +279,10 @@ int InsetGraphics::width(BufferView *, LyXFont const & font) const
if (!msg.empty()) {
msgFont.setSize(LyXFont::SIZE_TINY);
int const msg_width = lyxfont::width(msg, msgFont);
font_width = max(font_width, msg_width);
font_width = std::max(font_width, msg_width);
}
return max(50, font_width + 15);
return std::max(50, font_width + 15);
}
}
@ -263,49 +290,47 @@ int InsetGraphics::width(BufferView *, LyXFont const & font) const
void InsetGraphics::draw(BufferView * bv, LyXFont const & font,
int baseline, float & x, bool) const
{
Painter & paint = bv->painter();
int ldescent = descent(bv, font);
int lascent = ascent(bv, font);
int lwidth = width(bv, font);
int lascent = ascent(bv, font);
int lwidth = width(bv, font);
// Make sure x is updated upon exit from this routine
// Make sure now that x is updated upon exit from this routine
int old_x = int(x);
x += lwidth;
// Initiate the loading of the graphics file
if (cached_status_ == grfx::WaitingToLoad) {
grfx::GCache & gc = grfx::GCache::get();
gc.startLoading(*this);
}
// This will draw the graphics. If the graphics has not been loaded yet,
// we draw just a rectangle.
if (imageLoaded) {
Painter & paint = bv->painter();
if (drawImage()) {
paint.image(old_x + 2, baseline - lascent,
lwidth - 4, lascent + ldescent,
cacheHandle->getImage());
*cached_image_.get());
} else {
// Get the image status, default to unknown error.
GraphicsCacheItem::ImageStatus status = GraphicsCacheItem::UnknownError;
if (lyxrc.use_gui && params.display != InsetGraphicsParams::NONE &&
cacheHandle.get())
status = cacheHandle->getImageStatus();
// Check if the image is now ready.
if (status == GraphicsCacheItem::Loaded) {
imageLoaded = true;
// Tell BufferView we need to be updated!
bv->text->status(bv, LyXText::CHANGED_IN_DRAW);
return;
}
paint.rectangle(old_x + 2, baseline - lascent,
lwidth - 4,
lascent + ldescent);
// Print the file name.
LyXFont msgFont(font);
msgFont.setFamily(LyXFont::SANS_FAMILY);
string const justname = OnlyFilename (params.filename);
string const justname = OnlyFilename (params().filename);
if (!justname.empty()) {
msgFont.setSize(LyXFont::SIZE_FOOTNOTE);
paint.text(old_x + 8,
baseline - lyxfont::maxAscent(msgFont) - 4,
justname, msgFont);
}
// Print the message.
string const msg = statusMessage();
if (!msg.empty()) {
@ -313,6 +338,21 @@ void InsetGraphics::draw(BufferView * bv, LyXFont const & font,
paint.text(old_x + 8, baseline - 4, msg, msgFont);
}
}
// Reset the cache, ready for the next draw request
cached_status_ = grfx::ErrorUnknown;
cached_image_.reset(0);
cache_filled_ = false;
}
// Update the inset after parameters changed (read from file or changed in
// dialog. The grfx::GCache makes the decisions about whether or not to draw
// (interogates lyxrc, ascertains whether file exists etc)
void InsetGraphics::updateInset() const
{
grfx::GCache & gc = grfx::GCache::get();
gc.update(*this);
}
@ -337,7 +377,7 @@ Inset::EDITABLE InsetGraphics::editable() const
void InsetGraphics::write(Buffer const * buf, ostream & os) const
{
os << "Graphics FormatVersion " << VersionNumber << '\n';
params.Write(buf, os);
params().Write(buf, os);
}
@ -364,7 +404,7 @@ void InsetGraphics::readInsetGraphics(Buffer const * buf, LyXLex & lex)
string const token = lex.getString();
lyxerr[Debug::GRAPHICS] << "Token: '" << token << '\''
<< endl;
<< std::endl;
if (token.empty()) {
continue;
@ -378,13 +418,13 @@ void InsetGraphics::readInsetGraphics(Buffer const * buf, LyXLex & lex)
<< "This document was created with a newer Graphics widget"
", You should use a newer version of LyX to read this"
" file."
<< endl;
<< std::endl;
// TODO: Possibly open up a dialog?
}
else {
if (! params.Read(buf, lex, token))
if (! params_.Read(buf, lex, token))
lyxerr << "Unknown token, " << token << ", skipping."
<< endl;
<< std::endl;
}
}
}
@ -392,18 +432,18 @@ void InsetGraphics::readInsetGraphics(Buffer const * buf, LyXLex & lex)
// FormatVersion < 1.0 (LyX < 1.2)
void InsetGraphics::readFigInset(Buffer const * buf, LyXLex & lex)
{
vector<string> const oldUnits =
std::vector<string> const oldUnits =
getVectorFromString("pt,cm,in,p%,c%");
bool finished = false;
// set the display default
if (lyxrc.display_graphics == "mono")
params.display = InsetGraphicsParams::MONOCHROME;
params_.display = InsetGraphicsParams::MONOCHROME;
else if (lyxrc.display_graphics == "gray")
params.display = InsetGraphicsParams::GRAYSCALE;
params_.display = InsetGraphicsParams::GRAYSCALE;
else if (lyxrc.display_graphics == "color")
params.display = InsetGraphicsParams::COLOR;
params_.display = InsetGraphicsParams::COLOR;
else
params.display = InsetGraphicsParams::NONE;
params_.display = InsetGraphicsParams::NONE;
while (lex.isOK() && !finished) {
lex.next();
@ -418,49 +458,49 @@ void InsetGraphics::readFigInset(Buffer const * buf, LyXLex & lex)
if (lex.next()) {
string const name = lex.getString();
string const path = buf->filePath();
params.filename = MakeAbsPath(name, path);
params_.filename = MakeAbsPath(name, path);
}
} else if (token == "extra") {
if (lex.next());
// kept for backwards compability. Delete in 0.13.x
} else if (token == "subcaption") {
if (lex.eatLine())
params.subcaptionText = lex.getString();
params.subcaption = true;
params_.subcaptionText = lex.getString();
params_.subcaption = true;
} else if (token == "label") {
if (lex.next());
// kept for backwards compability. Delete in 0.13.x
} else if (token == "angle") {
if (lex.next())
params.rotate = true;
params.rotateAngle = lex.getFloat();
params_.rotate = true;
params_.rotateAngle = lex.getFloat();
} else if (token == "size") {
if (lex.next())
params.lyxwidth = LyXLength(lex.getString()+"pt");
params_.lyxwidth = LyXLength(lex.getString()+"pt");
if (lex.next())
params.lyxheight = LyXLength(lex.getString()+"pt");
params_.lyxheight = LyXLength(lex.getString()+"pt");
} else if (token == "flags") {
if (lex.next())
switch (lex.getInteger()) {
case 1: params.display = InsetGraphicsParams::MONOCHROME;
case 1: params_.display = InsetGraphicsParams::MONOCHROME;
break;
case 2: params.display = InsetGraphicsParams::GRAYSCALE;
case 2: params_.display = InsetGraphicsParams::GRAYSCALE;
break;
case 3: params.display = InsetGraphicsParams::COLOR;
case 3: params_.display = InsetGraphicsParams::COLOR;
break;
}
} else if (token == "subfigure") {
params.subcaption = true;
params_.subcaption = true;
} else if (token == "width") {
if (lex.next()) {
int i = lex.getInteger();
if (lex.next()) {
if (i == 5) {
params.scale = lex.getInteger();
params.size_type = InsetGraphicsParams::SCALE;
params_.scale = lex.getInteger();
params_.size_type = InsetGraphicsParams::SCALE;
} else {
params.width = LyXLength(lex.getString()+oldUnits[i]);
params.size_type = InsetGraphicsParams::WH;
params_.width = LyXLength(lex.getString()+oldUnits[i]);
params_.size_type = InsetGraphicsParams::WH;
}
}
}
@ -468,8 +508,8 @@ void InsetGraphics::readFigInset(Buffer const * buf, LyXLex & lex)
if (lex.next()) {
int i = lex.getInteger();
if (lex.next()) {
params.height = LyXLength(lex.getString()+oldUnits[i]);
params.size_type = InsetGraphicsParams::WH;
params_.height = LyXLength(lex.getString()+oldUnits[i]);
params_.size_type = InsetGraphicsParams::WH;
}
}
}
@ -482,51 +522,52 @@ string const InsetGraphics::createLatexOptions() const
// stream since we might have a trailing comma that we would like to remove
// before writing it to the output stream.
ostringstream options;
if (!params.bb.empty())
options << " bb=" << strip(params.bb) << ",\n";
if (params.draft)
if (!params().bb.empty())
options << " bb=" << strip(params().bb) << ",\n";
if (params().draft)
options << " draft,\n";
if (params.clip)
if (params().clip)
options << " clip,\n";
if (params.size_type == InsetGraphicsParams::WH) {
if (!params.width.zero())
options << " width=" << params.width.asLatexString() << ",\n";
if (!params.height.zero())
options << " height=" << params.height.asLatexString() << ",\n";
} else if (params.size_type == InsetGraphicsParams::SCALE) {
if (params.scale > 0)
options << " scale=" << double(params.scale)/100.0 << ",\n";
if (params().size_type == InsetGraphicsParams::WH) {
if (!params().width.zero())
options << " width=" << params().width.asLatexString() << ",\n";
if (!params().height.zero())
options << " height=" << params().height.asLatexString() << ",\n";
} else if (params().size_type == InsetGraphicsParams::SCALE) {
if (params().scale > 0)
options << " scale=" << double(params().scale)/100.0 << ",\n";
}
if (params.keepAspectRatio)
if (params().keepAspectRatio)
options << " keepaspectratio,\n";
// Make sure it's not very close to zero, a float can be effectively
// zero but not exactly zero.
if (!lyx::float_equal(params.rotateAngle, 0, 0.001) && params.rotate) {
options << " angle=" << params.rotateAngle << ",\n";
if (!params.rotateOrigin.empty()) {
options << " origin=" << params.rotateOrigin[0];
if (contains(params.rotateOrigin,"Top"))
if (!lyx::float_equal(params().rotateAngle, 0, 0.001) && params().rotate) {
options << " angle=" << params().rotateAngle << ",\n";
if (!params().rotateOrigin.empty()) {
options << " origin=" << params().rotateOrigin[0];
if (contains(params().rotateOrigin,"Top"))
options << 't';
else if (contains(params.rotateOrigin,"Bottom"))
else if (contains(params().rotateOrigin,"Bottom"))
options << 'b';
else if (contains(params.rotateOrigin,"Baseline"))
else if (contains(params().rotateOrigin,"Baseline"))
options << 'B';
options << ",\n";
}
}
if (!params.special.empty())
options << params.special << ",\n";
if (!params().special.empty())
options << params().special << ",\n";
string opts = options.str().c_str();
return opts.substr(0,opts.size()-2); // delete last ",\n"
}
namespace {
string decideOutputImageFormat(string const & suffix)
string findTargetFormat(string const & suffix)
{
// lyxrc.pdf_mode means:
// Are we creating a PDF or a PS file?
// (Should actually mean, are we using latex or pdflatex).
lyxerr[Debug::GRAPHICS] << "decideOutput::lyxrc.pdf_mode = " << lyxrc.pdf_mode << "\n";
lyxerr[Debug::GRAPHICS] << "decideOutput: lyxrc.pdf_mode = "
<< lyxrc.pdf_mode << std::endl;
if (lyxrc.pdf_mode) {
if (contains(suffix,"ps") || suffix == "pdf")
return "pdf";
@ -545,6 +586,7 @@ string decideOutputImageFormat(string const & suffix)
} // Anon. namespace
string const InsetGraphics::prepareFile(Buffer const *buf) const
{
// do_convert = Do we need to convert the file?
@ -560,38 +602,36 @@ string const InsetGraphics::prepareFile(Buffer const *buf) const
// return original filename without the extension
//
// if it's a zipped one, than let LaTeX do the rest!!!
if ((zippedFile(params.filename) && params.noUnzip) || buf->niceFile) {
lyxerr[Debug::GRAPHICS] << "don't unzip file or export latex"
<< params.filename << endl;
return params.filename;
}
string filename_ = params.filename;
if (zippedFile(filename_))
filename_ = unzipFile(filename_);
// now we have unzipped files
// Get the extension (format) of the original file.
// we handle it like a virtual one, so we can have
// different extensions with the same type.
string const extension = getExtFromContents(filename_);
// are we usind latex ((e)ps) or pdflatex (pdf,jpg,png)
string const image_target = decideOutputImageFormat(extension);
if (extension == image_target) // :-)
string filename_ = params().filename;
bool const zipped = zippedFile(filename_);
if ((zipped && params().noUnzip) || buf->niceFile) {
lyxerr[Debug::GRAPHICS] << "don't unzip file or export latex"
<< filename_ << endl;
return filename_;
// commented out to check if the "not exist"bug is fixed.
// if (!IsFileReadable(filename_)) { // :-(
// Alert::alert(_("File") + params.filename,
// _("isn't readable or doesn't exists!"));
// return filename_;
// }
string outfile;
}
if (zipped)
filename_ = unzipFile(filename_);
string const from = getExtFromContents(filename_);
string const to = findTargetFormat(from);
if (from == to) {
// No conversion needed!
return filename_;
}
string const temp = AddName(buf->tmppath, filename_);
outfile = RemoveExtension(temp);
string const outfile_base = RemoveExtension(temp);
lyxerr[Debug::GRAPHICS] << "tempname = " << temp << "\n";
lyxerr[Debug::GRAPHICS] << "buf::tmppath = " << buf->tmppath << "\n";
lyxerr[Debug::GRAPHICS] << "filename_ = " << filename_ << "\n";
lyxerr[Debug::GRAPHICS] << "outfile = " << outfile << endl;
converters.convert(buf, filename_, outfile, extension, image_target);
return outfile;
lyxerr[Debug::GRAPHICS] << "outfile_base = " << outfile_base << endl;
converters.convert(buf, filename_, outfile_base, from, to);
return outfile_base;
}
@ -600,7 +640,7 @@ int InsetGraphics::latex(Buffer const *buf, ostream & os,
{
// If there is no file specified, just output a message about it in
// the latex output.
if (params.filename.empty()) {
if (params().filename.empty()) {
os << "\\fbox{\\rule[-0.5in]{0pt}{1in}"
<< _("empty figure path") << "}\n";
return 1; // One end of line marker added to the stream.
@ -610,8 +650,8 @@ int InsetGraphics::latex(Buffer const *buf, ostream & os,
string before;
string after;
// Do we want subcaptions?
if (params.subcaption) {
before += "\\subfigure[" + params.subcaptionText + "]{";
if (params().subcaption) {
before += "\\subfigure[" + params().subcaptionText + "]{";
after = '}';
}
// We never use the starred form, we use the "clip" option instead.
@ -626,9 +666,11 @@ int InsetGraphics::latex(Buffer const *buf, ostream & os,
// appropriate (when there are several versions in different formats)
string const latex_str = before + '{' + prepareFile(buf) + '}' + after;
os << latex_str;
// Return how many newlines we issued.
int const newlines =
int(lyx::count(latex_str.begin(), latex_str.end(),'\n') + 1);
// lyxerr << "includegraphics: " << newlines << " lines of text"
// << endl;
return newlines;
@ -642,7 +684,7 @@ int InsetGraphics::ascii(Buffer const *, ostream & os, int) const
// 1. Convert file to ascii using gifscii
// 2. Read ascii output file and add it to the output stream.
// at least we send the filename
os << '<' << _("Graphicfile:") << params.filename << ">\n";
os << '<' << _("Graphicfile:") << params().filename << ">\n";
return 0;
}
@ -670,59 +712,27 @@ int InsetGraphics::docbook(Buffer const *, ostream & os) const
void InsetGraphics::validate(LaTeXFeatures & features) const
{
// If we have no image, we should not require anything.
if (params.filename.empty())
if (params().filename.empty())
return ;
features.includeFile(graphic_label, RemoveExtension(params.filename));
features.includeFile(graphic_label, RemoveExtension(params_.filename));
features.require("graphicx");
if (params.subcaption)
if (params().subcaption)
features.require("subfigure");
}
// Update the inset after parameters changed (read from file or changed in
// dialog.
void InsetGraphics::updateInset() const
{
GraphicsCache & gc = GraphicsCache::getInstance();
boost::shared_ptr<GraphicsCacheItem> temp(0);
// We do it this way so that in the face of some error, we will still
// be in a valid state.
InsetGraphicsParams::DisplayType local_display = params.display;
if (local_display == InsetGraphicsParams::DEFAULT) {
if (lyxrc.display_graphics == "mono")
local_display = InsetGraphicsParams::MONOCHROME;
else if (lyxrc.display_graphics == "gray")
local_display = InsetGraphicsParams::GRAYSCALE;
else if (lyxrc.display_graphics == "color")
local_display = InsetGraphicsParams::COLOR;
else
local_display = InsetGraphicsParams::NONE;
}
if (!params.filename.empty() && lyxrc.use_gui &&
local_display != InsetGraphicsParams::NONE) {
temp = gc.addFile(params.filename);
}
// Mark the image as unloaded so that it gets updated.
imageLoaded = false;
cacheHandle = temp;
}
bool InsetGraphics::setParams(InsetGraphicsParams const & p)
{
// If nothing is changed, just return and say so.
if (params == p)
if (params() == p && !p.filename.empty()) {
return false;
}
// Copy the new parameters.
params = p;
params_ = p;
// Update the inset with the new parameters.
updateInset();
@ -732,9 +742,9 @@ bool InsetGraphics::setParams(InsetGraphicsParams const & p)
}
InsetGraphicsParams InsetGraphics::getParams() const
InsetGraphicsParams const & InsetGraphics::params() const
{
return params;
return params_;
}
@ -742,4 +752,3 @@ Inset * InsetGraphics::clone(Buffer const &, bool same_id) const
{
return new InsetGraphics(*this, same_id);
}

View File

@ -19,18 +19,14 @@
#pragma interface
#endif
#include "graphics/GraphicsTypes.h"
#include "insets/inset.h"
#include "insets/insetgraphicsParams.h"
#include "graphics/GraphicsCacheItem.h"
#include <boost/smart_ptr.hpp>
#include "LaTeXFeatures.h"
// We need a signal here to hide an active dialog when we are deleted.
#include "sigc++/signal_system.h"
class Dialogs;
class LyXImage;
class LaTeXFeatures;
///
class InsetGraphics : public Inset, public SigC::Object {
@ -92,7 +88,7 @@ public:
bool setParams(InsetGraphicsParams const & params);
/// Get the inset parameters, used by the GUIndependent dialog.
InsetGraphicsParams getParams() const;
InsetGraphicsParams const & params() const;
/** This signal is connected by our dialog and called when the inset
is deleted.
@ -100,6 +96,11 @@ public:
SigC::Signal0<void> hideDialog;
private:
/// Set the cached variables
void setCache() const;
/// Is the image ready to draw, or should we display a message instead?
bool drawImage() const;
/// Read the inset native format
void readInsetGraphics(Buffer const * buf, LyXLex & lex);
/// Read the FigInset file format
@ -113,14 +114,19 @@ private:
string const createLatexOptions() const;
/// Convert the file if needed, and return the location of the file.
string const prepareFile(Buffer const * buf) const;
/// The graphics cache handle.
mutable boost::shared_ptr<GraphicsCacheItem> cacheHandle;
/// is the pixmap initialized?
mutable bool imageLoaded;
/// the parameters
InsetGraphicsParams params;
///
InsetGraphicsParams params_;
/// holds the entity name that defines the graphics location (SGML).
string const graphic_label;
/// The cached variables
mutable grfx::ImageStatus cached_status_;
///
mutable grfx::ImagePtr cached_image_;
///
mutable bool cache_filled_;
};
#endif

View File

@ -84,10 +84,13 @@
#include "frontends/Menubar.h"
#include "frontends/Alert.h"
#include "graphics/GraphicsCache.h"
#include "support/lyxalgo.h"
#include "support/LAssert.h"
#include "support/filetools.h"
#include "support/FileInfo.h"
#include "support/forkedcontr.h"
#include "support/lstrings.h"
#include "support/path.h"
#include "support/lyxfunctional.h"
@ -968,7 +971,7 @@ string const LyXFunc::dispatch(kb_action action, string argument)
}
switch (action) {
case LFUN_ESCAPE:
{
if (!owner->view()->available()) break;
@ -1603,6 +1606,10 @@ string const LyXFunc::dispatch(kb_action action, string argument)
break;
}
bool const graphicsbg_changed =
(lyx_name == lcolor.getLyXName(LColor::graphicsbg) &&
x11_name != lcolor.getX11Name(LColor::graphicsbg));
if (!lcolor.setColor(lyx_name, x11_name)) {
static string const err1 (N_("Set-color \""));
static string const err2 (
@ -1611,7 +1618,14 @@ string const LyXFunc::dispatch(kb_action action, string argument)
setErrorMessage(_(err1) + lyx_name + _(err2));
break;
}
lyxColorHandler->updateColor(lcolor.getFromLyXName(lyx_name));
if (graphicsbg_changed) {
grfx::GCache & gc = grfx::GCache::get();
gc.changeDisplay(true);
}
owner->view()->redraw();
break;
}
@ -1628,6 +1642,21 @@ string const LyXFunc::dispatch(kb_action action, string argument)
owner->messagePop();
break;
case LFUN_FORKS_SHOW:
owner->getDialogs()->showForks();
break;
case LFUN_FORKS_KILL:
{
if (!isStrInt(argument))
break;
pid_t const pid = strToInt(argument);
ForkedcallsController & fcc = ForkedcallsController::get();
fcc.kill(pid);
break;
}
default:
// Then if it was none of the above
// Trying the BufferView::pimpl dispatch:

View File

@ -225,7 +225,7 @@ void LyXRC::setDefaults() {
make_backup = true;
backupdir_path.erase();
exit_confirmation = true;
display_graphics = "mono";
display_graphics = "color";
display_shortcuts = true;
// Spellchecker settings:
#ifdef USE_PSPELL

View File

@ -1,3 +1,14 @@
2002-02-19 Angus Leeming <a.leeming@ic.ac.uk>
* forkedcall.[Ch]:
* forkedcontr.[Ch]: new files. Asger's forked call controller is
re-born, with a working timer and a modified interface. The
startscript method is now passed a Signal rather than a pointer
to a callback function. This enables us to connect to the method of
a C++ class, if we so desire.
* Makefile.am: add forkedcall.[Ch], forkedcontr.[Ch].
2002-02-26 John Levon <moz@compsoc.man.ac.uk>
* Makefile.am:

View File

@ -5,7 +5,8 @@ noinst_LTLIBRARIES = libsupport.la
LIBS =
ETAGS_ARGS = --lang=c++
BOOST_INCLUDES = -I$(top_srcdir)/boost
INCLUDES = -I${srcdir}/../ $(BOOST_INCLUDES)
SIGC_INCLUDES = -I$(top_srcdir)
INCLUDES = -I${srcdir}/../ $(SIGC_INCLUDES) $(BOOST_INCLUDES)
EXTRA_DIST = lyxstring.C lyxstring.h regex.c lyxregex.h \
os_unix.C os_win32.C os_os2.C
@ -42,6 +43,10 @@ libsupport_la_SOURCES = \
filetools.C \
filetools.h \
fmt.C \
forkedcall.C \
forkedcall.h \
forkedcontr.C \
forkedcontr.h \
getUserName.C \
getcwd.C \
kill.C \

270
src/support/forkedcall.C Normal file
View File

@ -0,0 +1,270 @@
/**
* \file syscall.C
* Copyright 2002 the LyX Team
* Read the file COPYING
*
* \author Asger Alstrup
*
* Interface cleaned up by
* \author Angus Leeming <a.leeming@ic.ac.uk>
*
* An instance of Class Forkedcall represents a single child process.
*
* Class Forkedcall uses fork() and execvp() to lauch the child process.
*
* Once launched, control is returned immediately to the parent process
* but a Signal can be emitted upon completion of the child.
*
* The child process is not killed when the Forkedcall instance goes out of
* scope, but it can be killed by an explicit invocation of the kill() member
* function.
*/
#include <config.h>
#ifdef __GNUG__
#pragma implementation
#endif
#include "forkedcall.h"
#include "forkedcontr.h"
#include "lstrings.h"
#include "lyxlib.h"
#include "filetools.h"
#include "os.h"
#include "debug.h"
#include "frontends/Timeout.h"
#include <cerrno>
#include <sys/types.h>
#include <sys/wait.h>
#include <csignal>
#include <cstdlib>
#include <unistd.h>
using std::endl;
#ifndef CXX_GLOBAL_CSTD
using std::strerror;
#endif
Forkedcall::Forkedcall()
: pid_(0), retval_(0)
{}
int Forkedcall::startscript(Starttype wait, string const & what)
{
if (wait == Wait) {
command_ = what;
retval_ = 0;
pid_ = generateChild();
if (pid_ <= 0) { // child or fork failed.
retval_ = 1;
} else {
retval_ = waitForChild();
}
return retval_;
}
// DontWait
retval_ = startscript(what, SignalTypePtr());
return retval_;
}
int Forkedcall::startscript(string const & what, SignalTypePtr signal)
{
command_ = what;
signal_ = signal;
retval_ = 0;
pid_ = generateChild();
if (pid_ <= 0) { // child or fork failed.
retval_ = 1;
return retval_;
}
// Non-blocking execution.
// Integrate into the Controller
ForkedcallsController & contr = ForkedcallsController::get();
contr.addCall(*this);
return retval_;
}
void Forkedcall::emitSignal()
{
if (signal_.get()) {
signal_->emit(command_, pid_, retval_);
}
}
namespace {
class Murder : public SigC::Object {
public:
//
static void killItDead(int secs, pid_t pid)
{
if (secs > 0) {
new Murder(secs, pid);
} else if (pid != 0) {
lyx::kill(pid, SIGKILL);
}
}
//
void kill()
{
if (pid_ != 0) {
lyx::kill(pid_, SIGKILL);
}
lyxerr << "Killed " << pid_ << std::endl;
delete this;
}
private:
//
Murder(int secs, pid_t pid)
: timeout_(0), pid_(pid)
{
timeout_ = new Timeout(1000*secs, Timeout::ONETIME);
timeout_->timeout.connect(SigC::slot(this, &Murder::kill));
timeout_->start();
}
//
~Murder()
{
delete timeout_;
}
//
Timeout * timeout_;
//
pid_t pid_;
};
} // namespace anon
void Forkedcall::kill(int tol)
{
lyxerr << "Forkedcall::kill(" << tol << ")" << std::endl;
if (pid() == 0) {
lyxerr << "Can't kill non-existent process!" << endl;
return;
}
int const tolerance = std::max(0, tol);
if (tolerance == 0) {
// Kill it dead NOW!
Murder::killItDead(0, pid());
} else {
int ret = lyx::kill(pid(), SIGHUP);
// The process is already dead if wait_for_death is false
bool const wait_for_death = (ret == 0 && errno != ESRCH);
if (wait_for_death) {
Murder::killItDead(tolerance, pid());
}
}
}
// Wait for child process to finish. Returns returncode from child.
int Forkedcall::waitForChild() {
// We'll pretend that the child returns 1 on all error conditions.
retval_ = 1;
int status;
bool wait = true;
while (wait) {
pid_t waitrpid = waitpid(pid_, &status, WUNTRACED);
if (waitrpid == -1) {
lyxerr << "LyX: Error waiting for child:"
<< strerror(errno) << endl;
wait = false;
} else if (WIFEXITED(status)) {
// Child exited normally. Update return value.
retval_ = WEXITSTATUS(status);
wait = false;
} else if (WIFSIGNALED(status)) {
lyxerr << "LyX: Child didn't catch signal "
<< WTERMSIG(status)
<< "and died. Too bad." << endl;
wait = false;
} else if (WIFSTOPPED(status)) {
lyxerr << "LyX: Child (pid: " << pid_
<< ") stopped on signal "
<< WSTOPSIG(status)
<< ". Waiting for child to finish." << endl;
} else {
lyxerr << "LyX: Something rotten happened while "
"waiting for child " << pid_ << endl;
wait = false;
}
}
return retval_;
}
// generate child in background
pid_t Forkedcall::generateChild()
{
const int MAX_ARGV = 255;
char *syscmd = 0;
char *argv[MAX_ARGV];
string childcommand(command_); // copy
bool more = true;
string rest = split(command_, childcommand, ' ');
int index = 0;
while (more) {
childcommand = frontStrip(childcommand);
if (syscmd == 0) {
syscmd = new char[childcommand.length() + 1];
childcommand.copy(syscmd, childcommand.length());
syscmd[childcommand.length()] = '\0';
}
if (!childcommand.empty()) {
char * tmp = new char[childcommand.length() + 1];
childcommand.copy(tmp, childcommand.length());
tmp[childcommand.length()] = '\0';
argv[index++] = tmp;
}
// reinit
more = !rest.empty();
if (more)
rest = split(rest, childcommand, ' ');
}
argv[index] = 0;
#ifndef __EMX__
pid_t cpid = ::fork();
if (cpid == 0) { // child
execvp(syscmd, argv);
// If something goes wrong, we end up here:
lyxerr << "execvp failed: "
<< strerror(errno) << endl;
}
#else
pid_t cpid = spawnvp(P_SESSION|P_DEFAULT|P_MINIMIZE|P_BACKGROUND,
syscmd, argv);
#endif
if (cpid < 0) { // error
lyxerr << "Could not fork: "
<< strerror(errno) << endl;
}
return cpid;
}

138
src/support/forkedcall.h Normal file
View File

@ -0,0 +1,138 @@
// -*- C++ -*-
/**
* \file syscall.h
* Copyright 2002 the LyX Team
* Read the file COPYING
*
* \author Asger Alstrup
*
* Interface cleaned up by
* \author Angus Leeming <a.leeming@ic.ac.uk>
*
* An instance of Class Forkedcall represents a single child process.
*
* Class Forkedcall uses fork() and execvp() to lauch the child process.
*
* Once launched, control is returned immediately to the parent process
* but a Signal can be emitted upon completion of the child.
*
* The child process is not killed when the Forkedcall instance goes out of
* scope, but it can be killed by an explicit invocation of the kill() member
* function.
*/
#ifndef FORKEDCALL_H
#define FORKEDCALL_H
#ifdef __GNUG__
#pragma interface
#endif
#include "LString.h"
#include <sys/types.h>
#include <boost/smart_ptr.hpp>
#include <sigc++/signal_system.h>
class Forkedcall {
public:
///
enum Starttype {
///
Wait,
///
DontWait
};
///
Forkedcall();
/** Start the child process.
*
* The command "what" is passed to fork() for execution.
*
* There are two startscript commands available. They differ in that
* the second receives a signal that is executed on completion of
* the command. This makes sense only for a command executed
* in the background, ie DontWait.
*
* The other startscript command can be executed either blocking
* or non-blocking, but no signal will be emitted on finishing.
*/
int startscript(Starttype, string const & what);
/** A SignalType signal is can be emitted once the forked process
* has finished. It passes:
* the commandline string;
* the PID of the child and;
* the return value from the child.
*
* We use a signal rather than simply a callback function so that
* we can return easily to C++ methods, rather than just globally
* accessible functions.
*/
typedef SigC::Signal3<void, string, pid_t, int> SignalType;
/** The signal is connected in the calling routine to the desired
* slot. We pass a shared_ptr rather than a reference to the signal
* because it is eminently possible for the instance of the calling
* class (and hence the signal) to be destructed before the forked
* call is complete.
*
* It doesn't matter if the slot disappears, SigC takes care of that.
*/
typedef boost::shared_ptr<SignalType> SignalTypePtr;
///
int startscript(string const & what, SignalTypePtr);
/** Invoking the following methods makes sense only if the command
* is running asynchronously!
*/
/** gets the PID of the child process.
* Used by the timer.
*/
pid_t pid() const { return pid_; }
/** Emit the signal.
* Used by the timer.
*/
void emitSignal();
/** Set the return value of the child process.
* Used by the timer.
*/
void setRetValue(int r) { retval_ = r; }
/** Kill child prematurely.
* First, a SIGHUP is sent to the child.
* If that does not end the child process within "tolerance"
* seconds, the SIGKILL signal is sent to the child.
* When the child is dead, the callback is called.
*/
void kill(int tolerance = 5);
///
string const & command() const { return command_; }
private:
/// Callback function
SignalTypePtr signal_;
/// Commmand line
string command_;
/// Process ID of child
pid_t pid_;
/// Return value from child
int retval_;
///
pid_t generateChild();
/// Wait for child process to finish. Updates returncode from child.
int waitForChild();
};
#endif // FORKEDCALL_H

207
src/support/forkedcontr.C Normal file
View File

@ -0,0 +1,207 @@
/**
* \file forkedcontr.C
* Copyright 2001 The LyX Team
* Read COPYING
*
* \author Asger Alstrup Nielsen
* \author Angus Leeming
*
* A class for the control of child processes launched using
* fork() and execvp().
*/
#include <config.h>
#ifdef __GNUG__
#pragma implementation
#endif
#include "forkedcontr.h"
#include "forkedcall.h"
#include "lyxfunctional.h"
#include "frontends/Timeout.h"
#include "debug.h"
#include <cerrno>
#include <cstdlib>
#include <unistd.h>
#include <sys/wait.h>
using std::vector;
using std::endl;
using std::find_if;
#ifndef CXX_GLOBAL_CSTD
using std::strerror;
#endif
// Ensure, that only one controller exists inside process
ForkedcallsController & ForkedcallsController::get()
{
static ForkedcallsController singleton;
return singleton;
}
ForkedcallsController::ForkedcallsController()
{
timeout_ = new Timeout(100, Timeout::CONTINUOUS);
timeout_->timeout
.connect(SigC::slot(this, &ForkedcallsController::timer));
}
// open question: should we stop childs here?
// Asger says no: I like to have my xdvi open after closing LyX. Maybe
// I want to print or something.
ForkedcallsController::~ForkedcallsController()
{
for (ListType::iterator it = forkedCalls.begin();
it != forkedCalls.end(); ++it) {
delete *it;
}
delete timeout_;
}
// Add child process information to the list of controlled processes
void ForkedcallsController::addCall(Forkedcall const &newcall)
{
if (!timeout_->running())
timeout_->start();
Forkedcall * call = new Forkedcall(newcall);
forkedCalls.push_back(call);
childrenChanged.emit();
}
// Timer-call
// Check the list and, if there is a stopped child, emit the signal.
void ForkedcallsController::timer()
{
ListType::size_type start_size = forkedCalls.size();
for (ListType::iterator it = forkedCalls.begin();
it != forkedCalls.end(); ++it) {
Forkedcall * actCall = *it;
pid_t pid = actCall->pid();
int stat_loc;
pid_t const waitrpid = waitpid(pid, &stat_loc, WNOHANG);
bool remove_it = false;
if (waitrpid == -1) {
lyxerr << "LyX: Error waiting for child: "
<< strerror(errno) << endl;
// Child died, so pretend it returned 1
actCall->setRetValue(1);
remove_it = true;
} else if (waitrpid == 0) {
// Still running. Move on to the next child.
continue;
} else if (WIFEXITED(stat_loc)) {
// Ok, the return value goes into retval.
actCall->setRetValue(WEXITSTATUS(stat_loc));
remove_it = true;
} else if (WIFSIGNALED(stat_loc)) {
// Child died, so pretend it returned 1
actCall->setRetValue(1);
remove_it = true;
} else if (WIFSTOPPED(stat_loc)) {
lyxerr << "LyX: Child (pid: " << pid
<< ") stopped on signal "
<< WSTOPSIG(stat_loc)
<< ". Waiting for child to finish." << endl;
} else {
lyxerr << "LyX: Something rotten happened while "
"waiting for child " << pid << endl;
// Child died, so pretend it returned 1
actCall->setRetValue(1);
remove_it = true;
}
if (remove_it) {
// Emit signal and remove the item from the list
actCall->emitSignal();
delete actCall;
// erase returns the next iterator, so decrement it
// to continue the loop.
ListType::iterator prev = it;
--prev;
forkedCalls.erase(it);
it = prev;
}
}
if (forkedCalls.empty()) {
timeout_->stop();
}
if (start_size != forkedCalls.size())
childrenChanged.emit();
}
// Return a vector of the pids of all the controlled processes.
vector<pid_t> const ForkedcallsController::getPIDs() const
{
vector<pid_t> pids;
if (forkedCalls.empty())
return pids;
pids.resize(forkedCalls.size());
vector<pid_t>::iterator vit = pids.begin();
for (ListType::const_iterator lit = forkedCalls.begin();
lit != forkedCalls.end(); ++lit, ++vit) {
*vit = (*lit)->pid();
}
std::sort(pids.begin(), pids.end());
return pids;
}
// Get the command string of the process.
string const ForkedcallsController::getCommand(pid_t pid) const
{
ListType::const_iterator it =
find_if(forkedCalls.begin(), forkedCalls.end(),
lyx::compare_memfun(&Forkedcall::pid, pid));
if (it == forkedCalls.end())
return string();
return (*it)->command();
}
// Kill the process prematurely and remove it from the list
// within tolerance secs
void ForkedcallsController::kill(pid_t pid, int tolerance)
{
ListType::iterator it =
find_if(forkedCalls.begin(), forkedCalls.end(),
lyx::compare_memfun(&Forkedcall::pid, pid));
if (it == forkedCalls.end())
return;
(*it)->kill(tolerance);
forkedCalls.erase(it);
if (forkedCalls.empty()) {
timeout_->stop();
}
}

78
src/support/forkedcontr.h Normal file
View File

@ -0,0 +1,78 @@
// -*- C++ -*-
/**
* \file forkedcontr.h
* Copyright 2001 The LyX Team
* Read COPYING
*
* \author Asger Alstrup Nielsen
* \author Angus Leeming
*
* A class for the control of child processes launched using
* fork() and execvp().
*/
#ifndef FORKEDCONTR_H
#define FORKEDCONTR_H
#include <list>
#include <vector>
#include "LString.h"
#include <sigc++/signal_system.h>
#ifdef __GNUG__
#pragma interface
#endif
class Forkedcall;
class Timeout;
class ForkedcallsController : public SigC::Object {
public:
/// Get hold of the only controller that can exist inside the process.
static ForkedcallsController & get();
/// Add a new child process to the list of controlled processes.
void addCall(Forkedcall const & newcall);
/** This method is connected to the timer. Every XX ms it is called
* so that we can check on the status of the children. Those that
* are found to have finished are removed from the list and their
* callback function is passed the final return state.
*/
void timer();
/// Return a vector of the pids of all the controlled processes.
std::vector<pid_t> const getPIDs() const;
/// Get the command string of the process.
string const getCommand(pid_t) const;
/** Kill this process prematurely and remove it from the list.
* The process is killed within tolerance secs.
* See forkedcall.[Ch] for details.
*/
void kill(pid_t, int tolerance = 5);
/// Signal emitted when the list of current child processes changes.
SigC::Signal0<void> childrenChanged;
private:
/// Can't create multiple instances of ForkedcallsController.
ForkedcallsController();
///
ForkedcallsController(ForkedcallsController const &);
///
~ForkedcallsController();
/// The child processes
typedef std::list<Forkedcall *> ListType;
///
ListType forkedCalls;
/** The timer. Enables us to check the status of the children
* every XX ms and to invoke a callback on completion.
*/
Timeout * timeout_;
};
#endif // FORKEDCONTR_H