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:
Greg Ercolano
2016-07-18 21:12:25 +00:00
parent bcb75b518f
commit 8850c5c822
15 changed files with 1343 additions and 17 deletions
+2
View File
@@ -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
+436
View File
@@ -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 $".
//
+51
View File
@@ -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
+63
View File
@@ -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 $".
//
+32 -1
View File
@@ -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')) )
+1
View File
@@ -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
View File
@@ -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 {
+2
View File
@@ -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 \
+5 -2
View File
@@ -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);
+46 -3
View File
@@ -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
+38 -3
View File
@@ -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}
}
}
}
+4
View File
@@ -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
View File
@@ -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
View File
@@ -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