mirror of
https://git.lyx.org/repos/lyx.git
synced 2025-01-02 08:10:39 +00:00
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:
parent
af8dccce0f
commit
607ad8d3a7
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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());
|
||||
}
|
||||
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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>
|
||||
|
||||
|
96
src/frontends/controllers/ControlForks.C
Normal file
96
src/frontends/controllers/ControlForks.C
Normal 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();
|
||||
}
|
||||
|
49
src/frontends/controllers/ControlForks.h
Normal file
49
src/frontends/controllers/ControlForks.h
Normal 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
|
@ -58,7 +58,7 @@ InsetGraphicsParams const ControlGraphics::getParams(string const &)
|
||||
InsetGraphicsParams const
|
||||
ControlGraphics::getParams(InsetGraphics const & inset)
|
||||
{
|
||||
return inset.getParams();
|
||||
return inset.params();
|
||||
}
|
||||
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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 \
|
||||
|
@ -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.
|
||||
|
@ -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));
|
||||
|
419
src/frontends/xforms/FormForks.C
Normal file
419
src/frontends/xforms/FormForks.C
Normal 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;
|
||||
}
|
||||
|
||||
|
51
src/frontends/xforms/FormForks.h
Normal file
51
src/frontends/xforms/FormForks.h
Normal 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
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -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 \
|
||||
|
80
src/frontends/xforms/form_forks.C
Normal file
80
src/frontends/xforms/form_forks.C
Normal 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;
|
||||
}
|
||||
/*---------------------------------------*/
|
||||
|
29
src/frontends/xforms/form_forks.h
Normal file
29
src/frontends/xforms/form_forks.h
Normal 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_ */
|
178
src/frontends/xforms/forms/form_forks.fd
Normal file
178
src/frontends/xforms/forms/form_forks.fd
Normal 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
|
@ -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 \
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
293
src/graphics/GraphicsConverter.C
Normal file
293
src/graphics/GraphicsConverter.C
Normal 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
|
121
src/graphics/GraphicsConverter.h
Normal file
121
src/graphics/GraphicsConverter.h
Normal 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
|
59
src/graphics/GraphicsImage.C
Normal file
59
src/graphics/GraphicsImage.C
Normal 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
|
||||
|
104
src/graphics/GraphicsImage.h
Normal file
104
src/graphics/GraphicsImage.h
Normal 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
|
660
src/graphics/GraphicsImageXPM.C
Normal file
660
src/graphics/GraphicsImageXPM.C
Normal 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
|
156
src/graphics/GraphicsImageXPM.h
Normal file
156
src/graphics/GraphicsImageXPM.h
Normal 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
|
171
src/graphics/GraphicsParams.C
Normal file
171
src/graphics/GraphicsParams.C
Normal 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
|
98
src/graphics/GraphicsParams.h
Normal file
98
src/graphics/GraphicsParams.h
Normal 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
|
57
src/graphics/GraphicsTypes.h
Normal file
57
src/graphics/GraphicsTypes.h
Normal 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
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
@ -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;
|
||||
}
|
@ -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
|
@ -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
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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
270
src/support/forkedcall.C
Normal 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
138
src/support/forkedcall.h
Normal 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
207
src/support/forkedcontr.C
Normal 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
78
src/support/forkedcontr.h
Normal 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
|
Loading…
Reference in New Issue
Block a user