mirror of
https://github.com/fltk/fltk.git
synced 2026-06-04 15:32:12 +08:00
Adds external editor capability to fluid for all platforms.
Solves STR#3213. [CORRECTED] git-svn-id: file:///fltk/svn/fltk/branches/branch-1.3-porting@11818 ea41ed52-d2ee-0310-a9c1-e6b18d33e121
This commit is contained in:
@@ -22,6 +22,8 @@ set(CPPFILES
|
||||
Fl_Group_Type.cxx
|
||||
Fl_Menu_Type.cxx
|
||||
Fl_Type.cxx
|
||||
ExternalCodeEditor_UNIX.cxx
|
||||
ExternalCodeEditor_WIN32.cxx
|
||||
Fl_Widget_Type.cxx
|
||||
Fl_Window_Type.cxx
|
||||
Fluid_Image.cxx
|
||||
|
||||
@@ -0,0 +1,436 @@
|
||||
//
|
||||
// "$Id: ExternalCodeEditor_UNIX.cxx 10800 2016-07-10 00:00:00Z greg.ercolano $".
|
||||
//
|
||||
// External code editor management class for Unix
|
||||
//
|
||||
#ifndef WIN32 /* This entire file unix only */
|
||||
|
||||
#include <errno.h> /* errno */
|
||||
#include <string.h> /* strerror() */
|
||||
#include <sys/types.h> /* stat().. */
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h> /* waitpid().. */
|
||||
#include <fcntl.h> /* open().. */
|
||||
#include <signal.h> /* kill().. */
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h> /* free().. */
|
||||
#include <stdio.h> /* snprintf().. */
|
||||
|
||||
#include <FL/Fl.H> /* Fl_Timeout_Handler.. */
|
||||
#include <FL/fl_ask.H> /* fl_alert() */
|
||||
|
||||
#include "ExternalCodeEditor_UNIX.h"
|
||||
|
||||
extern int G_debug; // defined in fluid.cxx
|
||||
|
||||
// Static local data
|
||||
static int L_editors_open = 0; // keep track of #editors open
|
||||
static Fl_Timeout_Handler L_update_timer_cb = 0; // app's update timer callback
|
||||
|
||||
// [Static/Local] See if file exists
|
||||
static int is_file(const char *filename) {
|
||||
struct stat buf;
|
||||
if ( stat(filename, &buf) < 0 ) return(0);
|
||||
return(S_ISREG(buf.st_mode) ? 1 : 0); // regular file?
|
||||
}
|
||||
|
||||
// [Static/Local] See if dir exists
|
||||
static int is_dir(const char *dirname) {
|
||||
struct stat buf;
|
||||
if ( stat(dirname, &buf) < 0 ) return(0);
|
||||
return(S_ISDIR(buf.st_mode) ? 1 : 0); // a dir?
|
||||
}
|
||||
|
||||
// CTOR
|
||||
ExternalCodeEditor::ExternalCodeEditor() {
|
||||
pid_ = -1;
|
||||
filename_ = 0;
|
||||
file_mtime_ = 0;
|
||||
file_size_ = 0;
|
||||
}
|
||||
|
||||
// DTOR
|
||||
ExternalCodeEditor::~ExternalCodeEditor() {
|
||||
if ( G_debug )
|
||||
printf("ExternalCodeEditor() DTOR CALLED (this=%p, pid=%ld)\n",
|
||||
(void*)this, (long)pid_);
|
||||
kill_editor(); // Kill open editor, deletes tmp file
|
||||
set_filename(0); // free()s filename
|
||||
}
|
||||
|
||||
// [Protected] Set the filename. Handles memory allocation/free
|
||||
// If set to NULL, frees memory.
|
||||
//
|
||||
void ExternalCodeEditor::set_filename(const char *val) {
|
||||
if ( filename_ ) free((void*)filename_);
|
||||
filename_ = val ? strdup(val) : 0;
|
||||
}
|
||||
|
||||
// [Public] Is editor running?
|
||||
int ExternalCodeEditor::is_editing() {
|
||||
return( (pid_ != -1) ? 1 : 0 );
|
||||
}
|
||||
|
||||
// [Protected] Kill the running editor (if any)
|
||||
// Kills the editor, reaps the process, and removes the tmp file.
|
||||
// The dtor calls this to ensure no editors remain running when fluid exits.
|
||||
//
|
||||
void ExternalCodeEditor::kill_editor() {
|
||||
if ( G_debug ) printf("kill_editor() called: pid=%ld\n", (long)pid_);
|
||||
if ( !is_editing() ) return; // editor not running? return..
|
||||
kill(pid_, SIGTERM); // kill editor
|
||||
int wcount = 0;
|
||||
while ( pid_ != -1 ) { // and wait for it to finish..
|
||||
usleep(100000); // 1/10th sec delay gives editor time to close itself
|
||||
switch (reap_editor()) {
|
||||
case -1: // error
|
||||
fl_alert("Can't seem to close editor of file: %s\n"
|
||||
"waitpid() returned: %s\n"
|
||||
"Please close editor and hit OK",
|
||||
filename(), strerror(errno));
|
||||
continue;
|
||||
case 0: // process still running
|
||||
if ( ++wcount > 3 ) { // retry 3x with 1/10th delay before showing dialog
|
||||
fl_alert("Can't seem to close editor of file: %s\n"
|
||||
"Please close editor and hit OK", filename());
|
||||
}
|
||||
continue;
|
||||
default: // process reaped
|
||||
if ( G_debug )
|
||||
printf("*** REAPED KILLED EXTERNAL EDITOR: PID %ld\n", (long)pid_);
|
||||
pid_ = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// [Public] Handle if file changed since last check, and update records if so.
|
||||
// Load new data into 'code', which caller must free().
|
||||
// If 'force' set, forces reload even if file size/time didn't change.
|
||||
//
|
||||
// Returns:
|
||||
// 0 -- file unchanged or not editing
|
||||
// 1 -- file changed, internal records updated, 'code' has new content
|
||||
// -1 -- error getting file info (strerror() has reason)
|
||||
//
|
||||
int ExternalCodeEditor::handle_changes(const char **code, int force) {
|
||||
code[0] = 0;
|
||||
if ( !is_editing() ) return 0;
|
||||
// Get current time/size info, see if file changed
|
||||
int changed = 0;
|
||||
{
|
||||
struct stat sbuf;
|
||||
if ( stat(filename(), &sbuf) < 0 ) return(-1); // TODO: show fl_alert(), do this in win32 too, adjust func call docs above
|
||||
time_t now_mtime = sbuf.st_mtime;
|
||||
size_t now_size = sbuf.st_size;
|
||||
// OK, now see if file changed; update records if so
|
||||
if ( now_mtime != file_mtime_ ) { changed = 1; file_mtime_ = now_mtime; }
|
||||
if ( now_size != file_size_ ) { changed = 1; file_size_ = now_size; }
|
||||
}
|
||||
// No changes? done
|
||||
if ( !changed && !force ) return 0;
|
||||
// Changes? Load file, and fallthru to close()
|
||||
int fd = open(filename(), O_RDONLY);
|
||||
if ( fd < 0 ) {
|
||||
fl_alert("ERROR: can't open '%s': %s", filename(), strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
int ret = 0;
|
||||
char *buf = (char*)malloc(file_size_ + 1);
|
||||
ssize_t count = read(fd, buf, file_size_);
|
||||
if ( count == -1 ) {
|
||||
fl_alert("ERROR: read() %s: %s", filename(), strerror(errno));
|
||||
free((void*)buf);
|
||||
ret = -1;
|
||||
} else if ( (long)count != (long)file_size_ ) {
|
||||
fl_alert("ERROR: read() failed for %s:\n"
|
||||
"expected %ld bytes, only got %ld",
|
||||
filename(), long(file_size_), long(count));
|
||||
ret = -1;
|
||||
} else {
|
||||
// Success -- file loaded OK
|
||||
buf[count] = '\0';
|
||||
code[0] = buf; // return pointer to allocated buffer
|
||||
ret = 1;
|
||||
}
|
||||
close(fd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// [Public] Remove the tmp file (if it exists), and zero out filename/mtime/size
|
||||
// Returns:
|
||||
// -1 -- on error (dialog is posted as to why)
|
||||
// 0 -- no file to remove
|
||||
// 1 -- file was removed
|
||||
//
|
||||
int ExternalCodeEditor::remove_tmpfile() {
|
||||
const char *tmpfile = filename();
|
||||
if ( !tmpfile ) return 0;
|
||||
// Filename set? remove (if exists) and zero filename/mtime/size
|
||||
if ( is_file(tmpfile) ) {
|
||||
if ( G_debug ) printf("Removing tmpfile '%s'\n", tmpfile);
|
||||
if ( remove(tmpfile) < 0 ) {
|
||||
fl_alert("WARNING: Can't remove() '%s': %s", tmpfile, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
set_filename(0);
|
||||
file_mtime_ = 0;
|
||||
file_size_ = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// [Static/Public] Return tmpdir name for this fluid instance.
|
||||
// Returns pointer to static memory.
|
||||
//
|
||||
const char* ExternalCodeEditor::tmpdir_name() {
|
||||
static char dirname[100];
|
||||
snprintf(dirname, sizeof(dirname), "/tmp/.fluid-%ld", (long)getpid());
|
||||
return dirname;
|
||||
}
|
||||
|
||||
// [Static/Public] Clear the external editor's tempdir
|
||||
// Static so that the main program can call it on exit to clean up.
|
||||
//
|
||||
void ExternalCodeEditor::tmpdir_clear() {
|
||||
const char *tmpdir = tmpdir_name();
|
||||
if ( is_dir(tmpdir) ) {
|
||||
if ( G_debug ) printf("Removing tmpdir '%s'\n", tmpdir);
|
||||
if ( rmdir(tmpdir) < 0 ) {
|
||||
fl_alert("WARNING: Can't rmdir() '%s': %s", tmpdir, strerror(errno));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// [Protected] Creates temp dir (if doesn't exist) and returns the dirname
|
||||
// as a static string. Returns NULL on error, dialog shows reason.
|
||||
//
|
||||
const char* ExternalCodeEditor::create_tmpdir() {
|
||||
const char *dirname = tmpdir_name();
|
||||
if ( ! is_dir(dirname) ) {
|
||||
if ( mkdir(dirname, 0777) < 0 ) {
|
||||
fl_alert("can't create directory '%s': %s",
|
||||
dirname, strerror(errno));
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
return dirname;
|
||||
}
|
||||
|
||||
// [Protected] Returns temp filename in static buffer.
|
||||
// Returns NULL if can't, posts dialog explaining why.
|
||||
//
|
||||
const char* ExternalCodeEditor::tmp_filename() {
|
||||
static char path[512];
|
||||
const char *tmpdir = create_tmpdir();
|
||||
if ( !tmpdir ) return 0;
|
||||
extern const char *code_file_name; // fluid's global
|
||||
const char *ext = code_file_name; // e.g. ".cxx"
|
||||
snprintf(path, sizeof(path), "%s/%p%s", tmpdir, (void*)this, ext);
|
||||
path[sizeof(path)-1] = 0;
|
||||
return path;
|
||||
}
|
||||
|
||||
// [Static/Local] Save string 'code' to 'filename', returning file's mtime/size
|
||||
// 'code' can be NULL -- writes an empty file if so.
|
||||
// Returns:
|
||||
// 0 on success
|
||||
// -1 on error (posts dialog with reason)
|
||||
//
|
||||
static int save_file(const char *filename, const char *code) {
|
||||
int fd = open(filename, O_WRONLY|O_CREAT, 0666);
|
||||
if ( fd == -1 ) {
|
||||
fl_alert("ERROR: open() '%s': %s", filename, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
ssize_t clen = strlen(code);
|
||||
ssize_t count = write(fd, code, clen);
|
||||
int ret = 0;
|
||||
if ( count == -1 ) {
|
||||
fl_alert("ERROR: write() '%s': %s", filename, strerror(errno));
|
||||
ret = -1; // fallthru to close()
|
||||
} else if ( count != clen ) {
|
||||
fl_alert("ERROR: write() '%s': wrote only %lu bytes, expected %lu",
|
||||
filename, (unsigned long)count, (unsigned long)clen);
|
||||
ret = -1; // fallthru to close()
|
||||
}
|
||||
close(fd);
|
||||
return(ret);
|
||||
}
|
||||
|
||||
// [Static/Local] Convert string 's' to array of argv[], useful for execve()
|
||||
// o 's' will be modified (words will be NULL separated)
|
||||
// o argv[] will end up pointing to the words of 's'
|
||||
// o Caller must free argv with: free(argv);
|
||||
//
|
||||
static int make_args(char *s, // string containing words (gets trashed!)
|
||||
int *aargc, // pointer to argc
|
||||
char ***aargv) { // pointer to argv
|
||||
char *ss, **argv;
|
||||
if ((argv=(char**)malloc(sizeof(char*) * (strlen(s)/2)))==NULL) {
|
||||
return -1;
|
||||
}
|
||||
int t;
|
||||
for(t=0; (t==0)?(ss=strtok(s," \t")):(ss=strtok(0," \t")); t++) {
|
||||
argv[t] = ss;
|
||||
}
|
||||
argv[t] = 0;
|
||||
aargv[0] = argv;
|
||||
aargc[0] = t;
|
||||
return(t);
|
||||
}
|
||||
|
||||
// [Protected] Start editor in background (fork/exec)
|
||||
// Returns:
|
||||
// > 0 on success, leaves editor child process running as 'pid_'
|
||||
// > -1 on error, posts dialog with reason (child exits)
|
||||
//
|
||||
int ExternalCodeEditor::start_editor(const char *editor_cmd,
|
||||
const char *filename) {
|
||||
if ( G_debug ) printf("start_editor() cmd='%s', filename='%s'\n",
|
||||
editor_cmd, filename);
|
||||
char cmd[1024];
|
||||
snprintf(cmd, sizeof(cmd), "%s %s", editor_cmd, filename);
|
||||
// Fork editor to background..
|
||||
switch ( pid_ = fork() ) {
|
||||
case -1: // error
|
||||
fl_alert("couldn't fork(): %s", strerror(errno));
|
||||
return -1;
|
||||
case 0: { // child
|
||||
// NOTE: OSX wants minimal code between fork/exec, see Apple TN2083
|
||||
int nargs;
|
||||
char **args = 0;
|
||||
make_args(cmd, &nargs, &args);
|
||||
execvp(args[0], args); // run command - doesn't return if succeeds
|
||||
fl_alert("couldn't exec() '%s': %s", cmd, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
default: // parent
|
||||
if ( L_editors_open++ == 0 ) // first editor? start timers
|
||||
{ start_update_timer(); }
|
||||
if ( G_debug )
|
||||
printf("--- EDITOR STARTED: pid_=%ld #open=%d\n", (long)pid_, L_editors_open);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// [Public] Try to reap external editor process
|
||||
// Returns:
|
||||
// -2 -- editor not open
|
||||
// -1 -- waitpid() failed (errno has reason)
|
||||
// 0 -- process still running
|
||||
// >0 -- process finished + reaped (value is pid)
|
||||
// Handles removing tmpfile/zeroing file_mtime/file_size
|
||||
//
|
||||
pid_t ExternalCodeEditor::reap_editor() {
|
||||
if ( !is_editing() ) return -2;
|
||||
int status = 0;
|
||||
pid_t wpid;
|
||||
switch (wpid = waitpid(pid_, &status, WNOHANG)) {
|
||||
case -1: // waitpid() failed
|
||||
return -1;
|
||||
case 0: // process didn't reap, still running
|
||||
return 0;
|
||||
default: // process reaped
|
||||
remove_tmpfile(); // also zeroes mtime/size
|
||||
pid_ = -1;
|
||||
if ( --L_editors_open <= 0 )
|
||||
{ stop_update_timer(); }
|
||||
break;
|
||||
}
|
||||
if ( G_debug )
|
||||
printf("*** EDITOR REAPED: pid=%ld #open=%d\n", long(wpid), L_editors_open);
|
||||
return wpid;
|
||||
}
|
||||
|
||||
// [Public] Open external editor using 'editor_cmd' to edit 'code'
|
||||
// 'code' contains multiline code to be edited as a temp file.
|
||||
//
|
||||
// Returns:
|
||||
// 0 if succeeds
|
||||
// -1 if can't open editor (already open, etc),
|
||||
// errors were shown to user in a dialog
|
||||
//
|
||||
int ExternalCodeEditor::open_editor(const char *editor_cmd,
|
||||
const char *code) {
|
||||
// Make sure a temp filename exists
|
||||
if ( !filename() ) {
|
||||
set_filename(tmp_filename());
|
||||
if ( !filename() ) return -1;
|
||||
}
|
||||
// See if tmpfile already exists or editor already open
|
||||
if ( is_file(filename()) ) {
|
||||
if ( is_editing() ) {
|
||||
// See if editor recently closed but not reaped; try to reap
|
||||
pid_t wpid = reap_editor();
|
||||
switch (wpid) {
|
||||
case -1: // waitpid() failed
|
||||
fl_alert("ERROR: waitpid() failed: %s\nfile='%s', pid=%ld",
|
||||
strerror(errno), filename(), (long)pid_);
|
||||
return -1;
|
||||
case 0: // process still running
|
||||
fl_alert("Editor Already Open\n file='%s'\n pid=%ld",
|
||||
filename(), (long)pid_);
|
||||
return 0;
|
||||
default: // process reaped, wpid is pid reaped
|
||||
if ( G_debug )
|
||||
printf("*** REAPED EXTERNAL EDITOR: PID %ld\n", (long)wpid);
|
||||
break; // fall thru to open new editor instance
|
||||
}
|
||||
// Reinstate tmp filename (reap_editor() clears it)
|
||||
set_filename(tmp_filename());
|
||||
}
|
||||
}
|
||||
if ( save_file(filename(), code) < 0 ) {
|
||||
return -1; // errors were shown in dialog
|
||||
}
|
||||
// Update mtime/size from closed file
|
||||
struct stat sbuf;
|
||||
if ( stat(filename(), &sbuf) < 0 ) {
|
||||
fl_alert("ERROR: can't stat('%s'): %s", filename(), strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
file_mtime_ = sbuf.st_mtime;
|
||||
file_size_ = sbuf.st_size;
|
||||
if ( start_editor(editor_cmd, filename()) < 0 ) { // open file in external editor
|
||||
if ( G_debug ) printf("Editor failed to start\n");
|
||||
return -1; // errors were shown in dialog
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// [Public/Static] Start update timer
|
||||
void ExternalCodeEditor::start_update_timer() {
|
||||
if ( !L_update_timer_cb ) return;
|
||||
if ( G_debug ) printf("--- TIMER: STARTING UPDATES\n");
|
||||
Fl::add_timeout(2.0, L_update_timer_cb);
|
||||
}
|
||||
|
||||
// [Public/Static] Stop update timer
|
||||
void ExternalCodeEditor::stop_update_timer() {
|
||||
if ( !L_update_timer_cb ) return;
|
||||
if ( G_debug ) printf("--- TIMER: STOPPING UPDATES\n");
|
||||
Fl::remove_timeout(L_update_timer_cb);
|
||||
}
|
||||
|
||||
// [Public/Static] Set app's external editor update timer callback
|
||||
// This is the app's callback callback we start while editors are open,
|
||||
// and stop when all editors are closed.
|
||||
//
|
||||
void ExternalCodeEditor::set_update_timer_callback(Fl_Timeout_Handler cb) {
|
||||
L_update_timer_cb = cb;
|
||||
}
|
||||
|
||||
// [Static/Public] See if any external editors are open.
|
||||
// App's timer cb can see if any editors need checking..
|
||||
//
|
||||
int ExternalCodeEditor::editors_open() {
|
||||
return L_editors_open;
|
||||
}
|
||||
|
||||
#endif /* !WIN32 */
|
||||
//
|
||||
// End of "$Id: ExternalCodeEditor_UNIX.cxx 10800 2016-07-10 00:00:00Z greg.ercolano $".
|
||||
//
|
||||
@@ -0,0 +1,51 @@
|
||||
//
|
||||
// "$Id: ExternalCodeEditor_UNIX.h 10800 2016-07-10 00:00:00Z greg.ercolano $".
|
||||
//
|
||||
// External code editor management class for Unix
|
||||
//
|
||||
// Handles starting and keeping track of an external text editor,
|
||||
// including process start, temp file creation/removal, bookkeeping, killing..
|
||||
//
|
||||
#ifndef _EXTCODEEDITOR_H
|
||||
#define _EXTCODEEDITOR_H
|
||||
|
||||
#include <errno.h> /* errno */
|
||||
#include <string.h> /* strerror() */
|
||||
|
||||
#include <sys/types.h> /* stat().. */
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
class ExternalCodeEditor {
|
||||
int pid_;
|
||||
time_t file_mtime_; // last modify time of the file (used to determine if file changed)
|
||||
size_t file_size_; // last file size (used to determine if changed)
|
||||
const char *filename_;
|
||||
protected:
|
||||
void kill_editor();
|
||||
const char *create_tmpdir();
|
||||
const char *tmp_filename();
|
||||
int start_editor(const char *cmd, const char *filename);
|
||||
void set_filename(const char *val);
|
||||
public:
|
||||
ExternalCodeEditor();
|
||||
~ExternalCodeEditor();
|
||||
int is_editing();
|
||||
pid_t reap_editor();
|
||||
const char *filename() { return filename_; }
|
||||
int open_editor(const char *editor_cmd, const char *code);
|
||||
int handle_changes(const char **code, int force=0);
|
||||
int remove_tmpfile();
|
||||
// Public static methods
|
||||
static void start_update_timer();
|
||||
static void stop_update_timer();
|
||||
static const char* tmpdir_name();
|
||||
static void tmpdir_clear();
|
||||
static int editors_open();
|
||||
static void set_update_timer_callback(Fl_Timeout_Handler);
|
||||
};
|
||||
|
||||
#endif /*_EXTCODEEDITOR_H */
|
||||
//
|
||||
// End of "$Id: ExternalCodeEditor_UNIX.h 10800 2016-07-10 00:00:00Z greg.ercolano $".
|
||||
//
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,63 @@
|
||||
//
|
||||
// "$Id: ExternalCodeEditor_WIN32.h 10800 2016-07-10 00:00:00Z greg.ercolano $".
|
||||
//
|
||||
// External code editor management class for Windows
|
||||
//
|
||||
// Handles starting and keeping track of an external text editor,
|
||||
// including process start, temp file creation/removal, bookkeeping, killing..
|
||||
//
|
||||
#ifndef _EXTCODEEDITOR_H
|
||||
#define _EXTCODEEDITOR_H
|
||||
|
||||
/* We require at least Windows 2000 (WINVER == 0x0500) for GetFileSizeEx(). */
|
||||
/* This must be defined before #include <windows.h> - MinGW doesn't do that. */
|
||||
#if !defined(WINVER) || (WINVER < 0x0500)
|
||||
# ifdef WINVER
|
||||
# undef WINVER
|
||||
# endif
|
||||
# define WINVER 0x0500
|
||||
#endif
|
||||
#if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0500)
|
||||
# ifdef _WIN32_WINNT
|
||||
# undef _WIN32_WINNT
|
||||
# endif
|
||||
# define _WIN32_WINNT 0x0500
|
||||
#endif
|
||||
|
||||
#include <windows.h> /* CreateFile().. */
|
||||
#include <string.h> /* sprintf().. */
|
||||
|
||||
class ExternalCodeEditor {
|
||||
PROCESS_INFORMATION pinfo_; // CreateProcess() handle to running process
|
||||
FILETIME file_mtime_; // last modify time of the file (used to determine if file changed)
|
||||
LARGE_INTEGER file_size_; // last file size (used to determine if changed)
|
||||
const char * filename_; // tmpfilename editor uses
|
||||
protected:
|
||||
void kill_editor();
|
||||
void reap_cleanup();
|
||||
const char *create_tmpdir();
|
||||
const char *tmp_filename();
|
||||
int start_editor(const char *cmd, const char *filename);
|
||||
void set_filename(const char *val);
|
||||
public:
|
||||
ExternalCodeEditor();
|
||||
~ExternalCodeEditor();
|
||||
int is_editing();
|
||||
DWORD reap_editor();
|
||||
const char *filename() { return filename_; }
|
||||
int open_editor(const char *editor_cmd, const char *code);
|
||||
int handle_changes(const char **code, int force=0);
|
||||
int remove_tmpfile();
|
||||
// Public static methods
|
||||
static void start_update_timer();
|
||||
static void stop_update_timer();
|
||||
static const char* tmpdir_name();
|
||||
static void tmpdir_clear();
|
||||
static int editors_open();
|
||||
static void set_update_timer_callback(Fl_Timeout_Handler);
|
||||
};
|
||||
|
||||
#endif /*_EXTCODEEDITOR_H */
|
||||
//
|
||||
// End of "$Id: ExternalCodeEditor_WIN32.h 10800 2016-07-10 00:00:00Z greg.ercolano $".
|
||||
//
|
||||
@@ -15,16 +15,24 @@
|
||||
//
|
||||
// http://www.fltk.org/str.php
|
||||
//
|
||||
|
||||
#include <FL/Fl.H>
|
||||
#include <FL/Fl_Window.H>
|
||||
#include <FL/Fl_Preferences.H>
|
||||
#include <FL/Fl_File_Chooser.H>
|
||||
#include "Fl_Type.h"
|
||||
#include <FL/fl_show_input.H>
|
||||
#include <FL/Fl_File_Chooser.H>
|
||||
#include "alignment_panel.h"
|
||||
#include "../src/flstring.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef WIN32
|
||||
#include "ExternalCodeEditor_WIN32.h"
|
||||
#else
|
||||
#include "ExternalCodeEditor_UNIX.h"
|
||||
#endif
|
||||
|
||||
extern int i18n_type;
|
||||
extern const char* i18n_include;
|
||||
@@ -38,6 +46,7 @@ extern int batch_mode;
|
||||
extern void redraw_browser();
|
||||
extern void goto_source_dir();
|
||||
extern void leave_source_dir();
|
||||
extern Fl_Window *main_window;
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
// quick check of any C code for legality, returns an error message
|
||||
@@ -434,6 +443,14 @@ Fl_Type *Fl_Code_Type::make() {
|
||||
}
|
||||
|
||||
void Fl_Code_Type::open() {
|
||||
// Using an external code editor? Open it..
|
||||
if ( G_use_external_editor && G_external_editor_command[0] ) {
|
||||
const char *cmd = G_external_editor_command;
|
||||
const char *code = name();
|
||||
if ( editor_.open_editor(cmd, code) == 0 )
|
||||
return; // return if editor opened ok, fallthru to built-in if not
|
||||
}
|
||||
// Use built-in code editor..
|
||||
if (!code_panel) make_code_panel();
|
||||
const char *text = name();
|
||||
code_input->buffer()->text( text ? text : "" );
|
||||
@@ -459,9 +476,23 @@ BREAK2:
|
||||
|
||||
Fl_Code_Type Fl_Code_type;
|
||||
|
||||
void Fl_Code_Type::write() {
|
||||
// External editor changes? If so, load changes into ram, update mtime/size
|
||||
if ( handle_editor_changes() == 1 ) {
|
||||
main_window->redraw(); // tell fluid to redraw; edits may affect tree's contents
|
||||
}
|
||||
Fl_Type::write();
|
||||
}
|
||||
|
||||
void Fl_Code_Type::write_code1() {
|
||||
// External editor changes? If so, load changes into ram, update mtime/size
|
||||
if ( handle_editor_changes() == 1 ) {
|
||||
main_window->redraw(); // tell fluid to redraw; edits may affect tree's contents
|
||||
}
|
||||
|
||||
const char* c = name();
|
||||
if (!c) return;
|
||||
|
||||
const char *pch;
|
||||
const char *ind = indent();
|
||||
while( (pch=strchr(c,'\n')) )
|
||||
|
||||
@@ -756,6 +756,7 @@ int Fl_Type::is_menu_item() const {return 0;}
|
||||
int Fl_Type::is_menu_button() const {return 0;}
|
||||
int Fl_Type::is_group() const {return 0;}
|
||||
int Fl_Type::is_window() const {return 0;}
|
||||
int Fl_Type::is_code() const {return 0;}
|
||||
int Fl_Type::is_code_block() const {return 0;}
|
||||
int Fl_Type::is_decl_block() const {return 0;}
|
||||
int Fl_Type::is_comment() const {return 0;}
|
||||
|
||||
+49
-1
@@ -32,6 +32,12 @@
|
||||
#include <FL/fl_draw.H>
|
||||
#include <stdarg.h>
|
||||
|
||||
#ifdef WIN32
|
||||
#include "ExternalCodeEditor_WIN32.h"
|
||||
#else
|
||||
#include "ExternalCodeEditor_UNIX.h"
|
||||
#endif
|
||||
|
||||
void set_modflag(int mf);
|
||||
|
||||
class Fl_Type {
|
||||
@@ -109,7 +115,7 @@ public:
|
||||
virtual void open(); // what happens when you double-click
|
||||
|
||||
// read and write data to a saved file:
|
||||
void write();
|
||||
virtual void write();
|
||||
virtual void write_properties();
|
||||
virtual void read_property(const char *);
|
||||
virtual int read_fdesign(const char*, const char*);
|
||||
@@ -143,6 +149,7 @@ public:
|
||||
virtual int is_menu_button() const;
|
||||
virtual int is_group() const;
|
||||
virtual int is_window() const;
|
||||
virtual int is_code() const;
|
||||
virtual int is_code_block() const;
|
||||
virtual int is_decl_block() const;
|
||||
virtual int is_comment() const;
|
||||
@@ -185,15 +192,56 @@ public:
|
||||
};
|
||||
|
||||
class Fl_Code_Type : public Fl_Type {
|
||||
ExternalCodeEditor editor_;
|
||||
public:
|
||||
Fl_Type *make();
|
||||
void write();
|
||||
void write_code1();
|
||||
void write_code2();
|
||||
void open();
|
||||
virtual const char *type_name() {return "code";}
|
||||
int is_code_block() const {return 0;}
|
||||
int is_code() const {return 1;}
|
||||
int pixmapID() { return 8; }
|
||||
virtual int is_public() const;
|
||||
// See if external editor is open
|
||||
int is_editing() {
|
||||
return editor_.is_editing();
|
||||
}
|
||||
// Reap the editor's pid
|
||||
// Returns:
|
||||
// -2 -- editor not open
|
||||
// -1 -- wait failed
|
||||
// 0 -- process still running
|
||||
// >0 -- process finished + reaped (returns pid)
|
||||
//
|
||||
int reap_editor() {
|
||||
return editor_.reap_editor();
|
||||
}
|
||||
// Handle external editor file modifications
|
||||
// If changed, record keeping is updated and file's contents is loaded into ram
|
||||
//
|
||||
// Returns:
|
||||
// 0 -- file unchanged or not editing
|
||||
// 1 -- file changed, internal records updated, 'code' has new content
|
||||
// -1 -- error getting file info (get_ms_errmsg() has reason)
|
||||
//
|
||||
// TODO: Figure out how saving a fluid file can be intercepted to grab
|
||||
// current contents of editor file..
|
||||
//
|
||||
int handle_editor_changes() {
|
||||
const char *newcode = 0;
|
||||
switch ( editor_.handle_changes(&newcode) ) {
|
||||
case 1: { // (1)=changed
|
||||
name(newcode); // update value in ram
|
||||
free((void*)newcode);
|
||||
return 1;
|
||||
}
|
||||
case -1: return -1; // (-1)=error -- couldn't read file (dialog showed reason)
|
||||
default: break; // (0)=no change
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
class Fl_CodeBlock_Type : public Fl_Type {
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
|
||||
CPPFILES = \
|
||||
CodeEditor.cxx \
|
||||
ExternalCodeEditor_UNIX.cxx \
|
||||
ExternalCodeEditor_WIN32.cxx \
|
||||
Fl_Function_Type.cxx \
|
||||
Fl_Group_Type.cxx \
|
||||
Fl_Menu_Type.cxx \
|
||||
|
||||
@@ -251,7 +251,10 @@ static const char *idata_fluid[] = {
|
||||
" ........................................................................\
|
||||
.............. "
|
||||
};
|
||||
static Fl_Pixmap image_fluid(idata_fluid);
|
||||
static Fl_Image *image_fluid() {
|
||||
static Fl_Image *image = new Fl_Pixmap(idata_fluid);
|
||||
return image;
|
||||
}
|
||||
|
||||
static void cb_View(Fl_Button*, void*) {
|
||||
show_help("license.html");
|
||||
@@ -267,7 +270,7 @@ Fl_Double_Window* make_about_panel() {
|
||||
about_panel->selection_color(FL_DARK1);
|
||||
about_panel->hotspot(about_panel);
|
||||
{ Fl_Box* o = new Fl_Box(10, 10, 115, 120);
|
||||
o->image(image_fluid);
|
||||
o->image( image_fluid() );
|
||||
} // Fl_Box* o
|
||||
{ Fl_Box* o = new Fl_Box(135, 10, 205, 75, "FLTK User\nInterface Designer\nVersion x.x.x");
|
||||
o->color((Fl_Color)12);
|
||||
|
||||
@@ -197,12 +197,29 @@ static void cb_recent_spinner(Fl_Spinner*, void*) {
|
||||
load_history();
|
||||
}
|
||||
|
||||
Fl_Check_Button *use_external_editor_button=(Fl_Check_Button *)0;
|
||||
|
||||
static void cb_use_external_editor_button(Fl_Check_Button*, void*) {
|
||||
G_use_external_editor = use_external_editor_button->value();
|
||||
fluid_prefs.set("use_external_editor", G_use_external_editor);
|
||||
redraw_browser();
|
||||
}
|
||||
|
||||
Fl_Input *editor_command_input=(Fl_Input *)0;
|
||||
|
||||
static void cb_editor_command_input(Fl_Input*, void*) {
|
||||
strncpy(G_external_editor_command, editor_command_input->value(), sizeof(G_external_editor_command)-1);
|
||||
G_external_editor_command[sizeof(G_external_editor_command)-1] = 0;
|
||||
fluid_prefs.set("external_editor_command", G_external_editor_command);
|
||||
redraw_browser();
|
||||
}
|
||||
|
||||
static void cb_Close1(Fl_Button*, void*) {
|
||||
settings_window->hide();
|
||||
}
|
||||
|
||||
Fl_Double_Window* make_settings_window() {
|
||||
{ settings_window = new Fl_Double_Window(349, 241, "GUI Settings");
|
||||
{ Fl_Double_Window* o = settings_window = new Fl_Double_Window(360, 355, "GUI Settings");
|
||||
{ scheme_choice = new Fl_Choice(140, 10, 115, 25, "Scheme: ");
|
||||
scheme_choice->down_box(FL_BORDER_BOX);
|
||||
scheme_choice->labelfont(1);
|
||||
@@ -213,7 +230,7 @@ Fl_Double_Window* make_settings_window() {
|
||||
scheme_choice->value(s);
|
||||
scheme_cb(0, 0);
|
||||
} // Fl_Choice* scheme_choice
|
||||
{ Fl_Group* o = new Fl_Group(116, 43, 220, 126);
|
||||
{ Fl_Group* o = new Fl_Group(20, 43, 330, 161);
|
||||
o->labelfont(1);
|
||||
o->align(Fl_Align(FL_ALIGN_CENTER));
|
||||
{ Fl_Box* o = new Fl_Box(140, 43, 1, 25, "Options: ");
|
||||
@@ -271,10 +288,36 @@ Fl_Double_Window* make_settings_window() {
|
||||
recent_spinner->maximum(10);
|
||||
recent_spinner->value(c);
|
||||
} // Fl_Spinner* recent_spinner
|
||||
{ Fl_Button* o = new Fl_Button(276, 205, 64, 25, "Close");
|
||||
{ Fl_Group* o = new Fl_Group(10, 210, 337, 95);
|
||||
o->box(FL_THIN_UP_BOX);
|
||||
o->color(FL_DARK1);
|
||||
{ use_external_editor_button = new Fl_Check_Button(25, 218, 209, 22, "Use external editor?");
|
||||
use_external_editor_button->down_box(FL_DOWN_BOX);
|
||||
use_external_editor_button->labelsize(12);
|
||||
use_external_editor_button->callback((Fl_Callback*)cb_use_external_editor_button);
|
||||
fluid_prefs.get("use_external_editor", G_use_external_editor, 0);
|
||||
use_external_editor_button->value(G_use_external_editor);
|
||||
} // Fl_Check_Button* use_external_editor_button
|
||||
{ editor_command_input = new Fl_Input(25, 264, 305, 21, "Editor Command");
|
||||
editor_command_input->tooltip("The editor command to open your external text editor.\nInclude any necessary \
|
||||
flags to ensure your editor does not background itself.\nExamples:\n gvim -\
|
||||
f\n gedit\n emacs");
|
||||
editor_command_input->labelsize(12);
|
||||
editor_command_input->textsize(12);
|
||||
editor_command_input->callback((Fl_Callback*)cb_editor_command_input);
|
||||
editor_command_input->align(Fl_Align(FL_ALIGN_TOP_LEFT));
|
||||
editor_command_input->when(FL_WHEN_CHANGED);
|
||||
fluid_prefs.get("external_editor_command", G_external_editor_command, "", sizeof(G_external_editor_command)-1);
|
||||
editor_command_input->value(G_external_editor_command);
|
||||
} // Fl_Input* editor_command_input
|
||||
o->end();
|
||||
Fl_Group::current()->resizable(o);
|
||||
} // Fl_Group* o
|
||||
{ Fl_Button* o = new Fl_Button(285, 320, 64, 25, "Close");
|
||||
o->tooltip("Close this dialog.");
|
||||
o->callback((Fl_Callback*)cb_Close1);
|
||||
} // Fl_Button* o
|
||||
o->size_range(o->w(), o->h());
|
||||
settings_window->set_non_modal();
|
||||
settings_window->end();
|
||||
} // Fl_Double_Window* settings_window
|
||||
|
||||
@@ -46,6 +46,12 @@ decl {extern void redraw_browser();} {public local
|
||||
decl {extern int show_comments;} {public local
|
||||
}
|
||||
|
||||
decl {extern int G_use_external_editor;} {public local
|
||||
}
|
||||
|
||||
decl {extern char G_external_editor_command[512];} {public local
|
||||
}
|
||||
|
||||
decl {extern int show_coredevmenus;} {public local
|
||||
}
|
||||
|
||||
@@ -161,7 +167,8 @@ decl {void scheme_cb(Fl_Choice *, void *);} {public local
|
||||
Function {make_settings_window()} {} {
|
||||
Fl_Window settings_window {
|
||||
label {GUI Settings} open
|
||||
xywh {393 191 349 241} type Double hide non_modal
|
||||
xywh {355 85 360 355} type Double resizable
|
||||
code0 {o->size_range(o->w(), o->h());} non_modal visible
|
||||
} {
|
||||
Fl_Choice scheme_choice {
|
||||
label {Scheme: }
|
||||
@@ -194,7 +201,7 @@ Function {make_settings_window()} {} {
|
||||
}
|
||||
}
|
||||
Fl_Group {} {open
|
||||
xywh {116 43 220 126} labelfont 1 align 0
|
||||
xywh {20 43 330 161} labelfont 1 align 0
|
||||
} {
|
||||
Fl_Box {} {
|
||||
label {Options: }
|
||||
@@ -254,10 +261,38 @@ load_history();}
|
||||
code2 {recent_spinner->maximum(10);}
|
||||
code3 {recent_spinner->value(c);}
|
||||
}
|
||||
Fl_Group {} {open
|
||||
xywh {10 210 337 95} box THIN_UP_BOX color 47 resizable
|
||||
} {
|
||||
Fl_Check_Button use_external_editor_button {
|
||||
label {Use external editor?}
|
||||
callback {G_use_external_editor = use_external_editor_button->value();
|
||||
fluid_prefs.set("use_external_editor", G_use_external_editor);
|
||||
redraw_browser();}
|
||||
xywh {25 218 209 22} down_box DOWN_BOX labelsize 12
|
||||
code1 {fluid_prefs.get("use_external_editor", G_use_external_editor, 0);}
|
||||
code2 {use_external_editor_button->value(G_use_external_editor);}
|
||||
}
|
||||
Fl_Input editor_command_input {
|
||||
label {Editor Command}
|
||||
callback {strncpy(G_external_editor_command, editor_command_input->value(), sizeof(G_external_editor_command)-1);
|
||||
G_external_editor_command[sizeof(G_external_editor_command)-1] = 0;
|
||||
fluid_prefs.set("external_editor_command", G_external_editor_command);
|
||||
redraw_browser();} selected
|
||||
tooltip {The editor command to open your external text editor.
|
||||
Include any necessary flags to ensure your editor does not background itself.
|
||||
Examples:
|
||||
gvim -f
|
||||
gedit
|
||||
emacs} xywh {25 264 305 21} labelsize 12 align 5 when 1 textsize 12
|
||||
code1 {fluid_prefs.get("external_editor_command", G_external_editor_command, "", sizeof(G_external_editor_command)-1);}
|
||||
code2 {editor_command_input->value(G_external_editor_command);}
|
||||
}
|
||||
}
|
||||
Fl_Button {} {
|
||||
label Close
|
||||
callback {settings_window->hide();}
|
||||
tooltip {Close this dialog.} xywh {276 205 64 25}
|
||||
tooltip {Close this dialog.} xywh {285 320 64 25}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,8 @@
|
||||
extern void load_history();
|
||||
extern void redraw_browser();
|
||||
extern int show_comments;
|
||||
extern int G_use_external_editor;
|
||||
extern char G_external_editor_command[512];
|
||||
extern int show_coredevmenus;
|
||||
extern struct Fl_Menu_Item *dbmanager_item;
|
||||
extern Fl_Preferences fluid_prefs;
|
||||
@@ -73,6 +75,8 @@ extern Fl_Check_Button *prevpos_button;
|
||||
extern Fl_Check_Button *show_comments_button;
|
||||
#include <FL/Fl_Spinner.H>
|
||||
extern Fl_Spinner *recent_spinner;
|
||||
extern Fl_Check_Button *use_external_editor_button;
|
||||
extern Fl_Input *editor_command_input;
|
||||
Fl_Double_Window* make_settings_window();
|
||||
extern Fl_Menu_Item menu_scheme_choice[];
|
||||
extern Fl_Double_Window *shell_window;
|
||||
|
||||
+54
-1
@@ -84,6 +84,9 @@ extern "C"
|
||||
#endif // HAVE_LIBPNG && HAVE_LIBZ
|
||||
}
|
||||
|
||||
//
|
||||
// Globals..
|
||||
//
|
||||
static Fl_Help_Dialog *help_dialog = 0;
|
||||
|
||||
Fl_Preferences fluid_prefs(Fl_Preferences::USER, "fltk.org", "fluid");
|
||||
@@ -92,6 +95,9 @@ int gridy = 5;
|
||||
int snap = 1;
|
||||
int show_guides = 1;
|
||||
int show_comments = 1;
|
||||
int G_use_external_editor = 0;
|
||||
int G_debug = 0;
|
||||
char G_external_editor_command[512];
|
||||
int show_coredevmenus = 1;
|
||||
|
||||
// File history info...
|
||||
@@ -186,6 +192,41 @@ static char* cutfname(int which = 0) {
|
||||
return name[which];
|
||||
}
|
||||
|
||||
// Timer to watch for external editor modifications
|
||||
// If one or more external editors open, check if their files were modified.
|
||||
// If so: reload to ram, update size/mtime records, and change fluid's 'modified' state.
|
||||
//
|
||||
static void external_editor_timer(void*) {
|
||||
int editors_open = ExternalCodeEditor::editors_open();
|
||||
if ( G_debug ) printf("--- TIMER --- External editors open=%d\n", editors_open);
|
||||
if ( editors_open > 0 ) {
|
||||
// Walk tree looking for files modified by external editors.
|
||||
int modified = 0;
|
||||
for (Fl_Type *p = Fl_Type::first; p; p = p->next) {
|
||||
if ( p->is_code() ) {
|
||||
Fl_Code_Type *code = (Fl_Code_Type*)p;
|
||||
// Code changed by external editor?
|
||||
if ( code->handle_editor_changes() ) { // updates ram, file size/mtime
|
||||
modified++;
|
||||
}
|
||||
if ( code->is_editing() ) { // editor open?
|
||||
code->reap_editor(); // Try to reap; maybe it recently closed
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( modified ) set_modflag(1);
|
||||
}
|
||||
// Repeat timeout if editors still open
|
||||
// The ExternalCodeEditor class handles start/stopping timer, we just
|
||||
// repeat_timeout() if it's already on. NOTE: above code may have reaped
|
||||
// only open editor, which would disable further timeouts. So *recheck*
|
||||
// if editors still open, to ensure we don't accidentally re-enable them.
|
||||
//
|
||||
if ( ExternalCodeEditor::editors_open() ) {
|
||||
Fl::repeat_timeout(2.0, external_editor_timer);
|
||||
}
|
||||
}
|
||||
|
||||
void save_cb(Fl_Widget *, void *v) {
|
||||
Fl_Native_File_Chooser fnfc;
|
||||
const char *c = filename;
|
||||
@@ -368,6 +409,10 @@ void revert_cb(Fl_Widget *,void *) {
|
||||
}
|
||||
|
||||
void exit_cb(Fl_Widget *,void *) {
|
||||
|
||||
// Stop any external editor update timers
|
||||
ExternalCodeEditor::stop_update_timer();
|
||||
|
||||
if (modflag)
|
||||
switch (fl_choice("Do you want to save changes to this user\n"
|
||||
"interface before exiting?", "Cancel",
|
||||
@@ -401,6 +446,12 @@ void exit_cb(Fl_Widget *,void *) {
|
||||
|
||||
undo_clear();
|
||||
|
||||
// Destroy tree
|
||||
// Doing so causes dtors to automatically close all external editors
|
||||
// and cleans up editor tmp files. Then remove fluid tmpdir /last/.
|
||||
delete_all();
|
||||
ExternalCodeEditor::tmpdir_clear();
|
||||
|
||||
exit(0);
|
||||
}
|
||||
|
||||
@@ -1697,7 +1748,6 @@ static void sigint(SIGARG) {
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
int main(int argc,char **argv) {
|
||||
int i = 1;
|
||||
|
||||
@@ -1786,6 +1836,9 @@ int main(int argc,char **argv) {
|
||||
signal(SIGINT,sigint);
|
||||
#endif
|
||||
|
||||
// Set (but do not start) timer callback for external editor updates
|
||||
ExternalCodeEditor::set_update_timer_callback(external_editor_timer);
|
||||
|
||||
grid_cb(horizontal_input, 0); // Makes sure that windows get snap params...
|
||||
|
||||
#ifdef WIN32
|
||||
|
||||
+12
-6
@@ -158,7 +158,10 @@ static const char *idata_print_color[] = {
|
||||
" %%%%%%%%\'\'******** ",
|
||||
" %%%%%% ****** "
|
||||
};
|
||||
static Fl_Pixmap image_print_color(idata_print_color);
|
||||
static Fl_Image *image_print_color() {
|
||||
static Fl_Image *image = new Fl_Pixmap(idata_print_color);
|
||||
return image;
|
||||
}
|
||||
|
||||
static const char *idata_print_gray[] = {
|
||||
"24 24 17 1",
|
||||
@@ -204,7 +207,10 @@ static const char *idata_print_gray[] = {
|
||||
" %%%%%%%%\'\'******** ",
|
||||
" %%%%%% ****** "
|
||||
};
|
||||
static Fl_Pixmap image_print_gray(idata_print_gray);
|
||||
static Fl_Image *image_print_gray() {
|
||||
static Fl_Image *image = new Fl_Pixmap(idata_print_gray);
|
||||
return image;
|
||||
}
|
||||
|
||||
Fl_Button *print_output_mode[4]={(Fl_Button *)0};
|
||||
|
||||
@@ -457,7 +463,7 @@ Fl_Double_Window* make_print_panel() {
|
||||
print_output_mode[0]->value(1);
|
||||
print_output_mode[0]->color(FL_BACKGROUND2_COLOR);
|
||||
print_output_mode[0]->selection_color(FL_FOREGROUND_COLOR);
|
||||
print_output_mode[0]->image(image_print_color);
|
||||
print_output_mode[0]->image( image_print_color() );
|
||||
} // Fl_Button* print_output_mode[0]
|
||||
{ print_output_mode[1] = new Fl_Button(150, 50, 40, 30);
|
||||
print_output_mode[1]->type(102);
|
||||
@@ -465,7 +471,7 @@ Fl_Double_Window* make_print_panel() {
|
||||
print_output_mode[1]->down_box(FL_BORDER_BOX);
|
||||
print_output_mode[1]->color(FL_BACKGROUND2_COLOR);
|
||||
print_output_mode[1]->selection_color(FL_FOREGROUND_COLOR);
|
||||
print_output_mode[1]->image(image_print_color);
|
||||
print_output_mode[1]->image( image_print_color() );
|
||||
} // Fl_Button* print_output_mode[1]
|
||||
{ print_output_mode[2] = new Fl_Button(200, 45, 30, 40);
|
||||
print_output_mode[2]->type(102);
|
||||
@@ -473,7 +479,7 @@ Fl_Double_Window* make_print_panel() {
|
||||
print_output_mode[2]->down_box(FL_BORDER_BOX);
|
||||
print_output_mode[2]->color(FL_BACKGROUND2_COLOR);
|
||||
print_output_mode[2]->selection_color(FL_FOREGROUND_COLOR);
|
||||
print_output_mode[2]->image(image_print_gray);
|
||||
print_output_mode[2]->image( image_print_gray() );
|
||||
} // Fl_Button* print_output_mode[2]
|
||||
{ print_output_mode[3] = new Fl_Button(240, 50, 40, 30);
|
||||
print_output_mode[3]->type(102);
|
||||
@@ -481,7 +487,7 @@ Fl_Double_Window* make_print_panel() {
|
||||
print_output_mode[3]->down_box(FL_BORDER_BOX);
|
||||
print_output_mode[3]->color(FL_BACKGROUND2_COLOR);
|
||||
print_output_mode[3]->selection_color(FL_FOREGROUND_COLOR);
|
||||
print_output_mode[3]->image(image_print_gray);
|
||||
print_output_mode[3]->image( image_print_gray() );
|
||||
} // Fl_Button* print_output_mode[3]
|
||||
o->end();
|
||||
} // Fl_Group* o
|
||||
|
||||
Reference in New Issue
Block a user