Commit 172416dd authored by Albert Gräf's avatar Albert Gräf
Browse files

Add a Recent Files menu (fixes #124 and #221).

parent eefbef29
......@@ -99,6 +99,10 @@
"new_tt": "Erzeuge einen leeren Pd-Patch",
"open": "Öffnen",
"open_tt": "Öffne eine oder mehrere Pd-Dateien",
"recent_files": "Zuletzt geöffnete Dateien",
"recent_files_tt": "Öffne eine der zuletzt geöffneten Pd-Dateien",
"clear_recent_files": "Liste leeren",
"clear_recent_files_tt": "Leere die Liste der zuletzt geöffneten Pd-Dateien",
"k12_demos": "K12-Demos",
"k12_demos_tt": "Demo-Patches zur Verwendung im K12-Modus",
"save": "Speichern",
......
......@@ -99,6 +99,10 @@
"new_tt": "Create an empty Pd patch",
"open": "Open",
"open_tt": "Open one or more Pd files",
"recent_files": "Recent Files",
"recent_files_tt": "Open a recently opened Pd file",
"clear_recent_files": "Clear List",
"clear_recent_files_tt": "Clear the recent files list",
"k12_demos": "K12 Demos",
"k12_demos_tt": "Demo patches for use with K12 Mode",
"save": "Save",
......
......@@ -3,6 +3,7 @@
var pdgui = require("./pdgui.js");
var l = pdgui.get_local_string; // For menu names
var osx_menu = null; // OSX App menu -- a single one per running instance
var recent_files_submenu = null;
function create_menu(gui, type) {
// On OSX we create a menu only once, and then enable/disable menuitems
......@@ -26,6 +27,16 @@ function create_menu(gui, type) {
media_menu,
help_menu;
// We only maintain a single instance of the recent files submenu which
// gets updated in pdgui.js via a callback from the engine.
if (!recent_files_submenu) {
recent_files_submenu = new gui.Menu();
// NOTE: Since we can't be sure whether the GUI or the engine runs
// first, make sure that we populate the submenu on the first run in
// either case.
pdgui.populate_recent_files(recent_files_submenu);
}
// OSX just spawns a single canvas menu and then enables/disables
// the various menu items as needed.
canvas_menu = osx || (type !== "console");
......@@ -63,6 +74,11 @@ function create_menu(gui, type) {
modifiers: cmd_or_ctrl,
tooltip: l("menu.open_tt")
}));
file_menu.append(m.file.recent_files = new gui.MenuItem({
label: l("menu.recent_files"),
submenu: recent_files_submenu,
tooltip: l("menu.recent_files_tt")
}));
if (pdgui.k12_mode == 1) {
file_menu.append(m.file.k12 = new gui.MenuItem({
label: l("menu.k12_demos"),
......
......@@ -741,6 +741,9 @@ function saveas_callback(cid, file, close_flag) {
}
pdsend(cid, "savetofile", enquote(basename), enquote(directory),
close_flag);
// update the recent files list
var norm_path = path.normalize(directory);
pdsend("pd add-recent-file", enquote(path.join(norm_path, basename)));
}
exports.saveas_callback = saveas_callback;
......@@ -976,6 +979,9 @@ function open_file(file) {
(enquote(directory)));
set_pd_opendir(directory);
//::pd_guiprefs::update_recentfiles "$filename" 1
// update the recent files list
var norm_path = path.normalize(directory);
pdsend("pd add-recent-file", enquote(path.join(norm_path, basename)));
}
}
......@@ -4446,6 +4452,58 @@ function open_search() {
exports.open_search= open_search;
// This is the same for all windows (initialization is in pd_menus.js).
var recent_files_submenu = null;
var recent_files = null;
// We need to jump through some hoops here since JS closures capture variables
// by reference, which causes trouble when closures are created within a
// loop.
function recent_files_callback(i) {
return function() {
var fname = recent_files[i];
//post("clicked recent file: "+fname);
open_file(fname);
}
}
function populate_recent_files(submenu) {
if (submenu) recent_files_submenu = submenu;
if (recent_files && recent_files_submenu) {
//post("recent files: " + recent_files.join(" "));
while (recent_files_submenu.items.length > 0)
recent_files_submenu.removeAt(0);
for (var i = 0; i < recent_files.length; i++) {
var item = new nw.MenuItem({
label: path.basename(recent_files[i]),
tooltip: recent_files[i]
});
item.click = recent_files_callback(i);
recent_files_submenu.append(item);
}
if (recent_files_submenu.items.length > 0) {
recent_files_submenu.append(new nw.MenuItem({
type: "separator"
}));
var item = new nw.MenuItem({
label: lang.get_local_string("menu.clear_recent_files"),
tooltip: lang.get_local_string("menu.clear_recent_files_tt")
});
item.click = function() {
pdsend("pd clear-recent-files");
};
recent_files_submenu.append(item);
}
}
}
exports.populate_recent_files = populate_recent_files;
function gui_recent_files(dummy, recent_files_array) {
recent_files = recent_files_array;
populate_recent_files(recent_files_submenu);
}
function gui_audio_properties(gfxstub, sys_indevs, sys_outdevs,
pd_indevs, pd_inchans, pd_outdevs, pd_outchans, audio_attrs) {
var attrs = audio_attrs.concat([
......
......@@ -37,6 +37,9 @@ void glob_ping(t_pd *dummy);
void glob_watchdog(t_pd *dummy);
void glob_savepreferences(t_pd *dummy);
void glob_forward_files_from_secondary_instance(void);
void glob_recent_files(t_pd *dummy);
void glob_add_recent_file(t_pd *dummy, t_symbol *s);
void glob_clear_recent_files(t_pd *dummy);
void alsa_resync( void);
......@@ -168,6 +171,12 @@ void glob_init(void)
gensym("gui-preset"), A_SYMBOL, 0);
class_addmethod(glob_pdobject, (t_method)glob_gui_properties,
gensym("gui-properties"), 0);
class_addmethod(glob_pdobject, (t_method)glob_recent_files,
gensym("recent-files"), 0);
class_addmethod(glob_pdobject, (t_method)glob_add_recent_file,
gensym("add-recent-file"), A_SYMBOL, 0);
class_addmethod(glob_pdobject, (t_method)glob_clear_recent_files,
gensym("clear-recent-files"), 0);
#ifdef UNIX
class_addmethod(glob_pdobject, (t_method)glob_watchdog,
gensym("watchdog"), 0);
......
......@@ -149,7 +149,7 @@ static void sys_initsavepreferences( void)
// user config dir doesn't exist yet, try to create it
if (mkdir(filenamebuf, 0755)) {
pd_error(0, "%s: %s",filenamebuf, strerror(errno));
return;
return;
}
}
snprintf(filenamebuf, FILENAME_MAX, "%s/.pd-l2ork/user.settings", homedir);
......@@ -636,3 +636,165 @@ void glob_savepreferences(t_pd *dummy)
sys_donesavepreferences();
}
/* AG: Recent files table */
int sys_n_recent_files = 0;
char *sys_recent_files[MAX_RECENT_FILES];
static int fexists(const char *s)
{
struct stat statbuf;
return stat(s, &statbuf) == 0;
}
void sys_add_recent_file(const char *s)
{
int i;
// only add the file if it actually exists
if (!fexists(s)) return;
for (i = 0; i < sys_n_recent_files && strcmp(sys_recent_files[i], s); i++) ;
if (i < sys_n_recent_files) {
// already got an existing entry, move it to the front
char *t = sys_recent_files[i];
memmove(sys_recent_files+1, sys_recent_files, i*sizeof(char*));
sys_recent_files[0] = t;
} else {
char *t = strdup(s);
if (!t) return;
if (sys_n_recent_files == MAX_RECENT_FILES) {
// kick out the oldest entry to make room for a new one
free(sys_recent_files[--sys_n_recent_files]);
}
// add a new entry at the beginning of the table
memmove(sys_recent_files+1, sys_recent_files,
sys_n_recent_files*sizeof(char*));
sys_recent_files[0] = t;
sys_n_recent_files++;
}
}
void sys_save_recent_files(void)
{
int i;
#ifdef UNIX
// UNIX/Linux: save in recent_files file
FILE *fp;
char filenamebuf[FILENAME_MAX], *homedir = getenv("HOME");
struct stat statbuf;
if (!homedir) return;
snprintf(filenamebuf, FILENAME_MAX, "%s/.pd-l2ork", homedir);
filenamebuf[FILENAME_MAX-1] = 0;
if (stat(filenamebuf, &statbuf) || !S_ISDIR(statbuf.st_mode)) {
// user config dir doesn't exist yet, try to create it
if (mkdir(filenamebuf, 0755)) {
pd_error(0, "%s: %s",filenamebuf, strerror(errno));
return;
}
}
snprintf(filenamebuf, FILENAME_MAX, "%s/.pd-l2ork/recent_files", homedir);
filenamebuf[FILENAME_MAX-1] = 0;
if ((fp = fopen(filenamebuf, "w")) == NULL) {
pd_error(0, "%s: %s",filenamebuf, strerror(errno));
return;
}
for (i = 0; i < sys_n_recent_files; i++) {
fprintf(fp, "%s\n", sys_recent_files[i]);
}
fclose(fp);
#else
// Mac/Windows (use the defaults/registry)
char buf[MAXPDSTRING];
sys_initsavepreferences();
for (i = 0; i < sys_n_recent_files; i++) {
sprintf(buf, "recent%d", i+1);
sys_putpreference(buf, sys_recent_files[i]);
}
sprintf(buf, "%d", i);
sys_putpreference("nrecent", buf);
sys_donesavepreferences();
#endif
}
void sys_load_recent_files(void)
{
#ifdef UNIX
// UNIX/Linux: load from recent_files file
FILE *fp;
char filenamebuf[FILENAME_MAX], *homedir = getenv("HOME");
if (!homedir) return;
snprintf(filenamebuf, FILENAME_MAX, "%s/.pd-l2ork/recent_files", homedir);
filenamebuf[FILENAME_MAX-1] = 0;
if ((fp = fopen(filenamebuf, "r")) == NULL) return;
for (sys_n_recent_files = 0; sys_n_recent_files < MAX_RECENT_FILES &&
fgets(filenamebuf, FILENAME_MAX, fp); ) {
char *s;
int l = strlen(filenamebuf);
if (l > 0 && filenamebuf[l-1] == '\n') filenamebuf[--l] = 0;
// only add files which actually exist
if (l == 0 || !fexists(filenamebuf)) continue;
s = strdup(filenamebuf);
if (s) sys_recent_files[sys_n_recent_files++] = s;
}
fclose(fp);
#else
// Mac/Windows (use the defaults/registry)
char prefbuf[MAXPDSTRING], keybuf[80];
int i, maxi = MAX_RECENT_FILES;
sys_initloadpreferences();
if (sys_getpreference("nrecent", prefbuf, MAXPDSTRING))
sscanf(prefbuf, "%d", &maxi);
for (i = 0; i < maxi; i++) {
int l;
char *s;
sprintf(keybuf, "recent%d", i+1);
if (!sys_getpreference(keybuf, prefbuf, MAXPDSTRING))
break;
l = strlen(prefbuf);
if (l == 0 || !fexists(prefbuf)) continue;
s = strdup(prefbuf);
if (s) sys_recent_files[sys_n_recent_files++] = s;
}
sys_doneloadpreferences();
#endif
}
void sys_clear_recent_files(void)
{
int i;
for (i = 0; i < sys_n_recent_files; i++) {
free(sys_recent_files[i]);
}
sys_n_recent_files = 0;
}
// send the recent files list back to the gui so that the Recent Files menu
// can be updated accordingly
void glob_recent_files(t_pd *dummy)
{
int i;
gui_start_vmess("gui_recent_files", "x", dummy);
gui_start_array();
for (i = 0; i < sys_n_recent_files; i++)
{
gui_s(sys_recent_files[i]);
}
gui_end_array();
gui_end_vmess();
}
// add an entry to the recent files list, save the list and update the gui
void glob_add_recent_file(t_pd *dummy, t_symbol *s)
{
sys_add_recent_file(s->s_name);
sys_save_recent_files();
glob_recent_files(dummy);
}
// clear the recent files list, save the list and update the gui
void glob_clear_recent_files(t_pd *dummy)
{
sys_clear_recent_files();
sys_save_recent_files();
glob_recent_files(dummy);
}
......@@ -301,6 +301,7 @@ int sys_main(int argc, char **argv)
noprefs = 1;
if (!noprefs)
sys_loadpreferences(); /* load default settings */
sys_load_recent_files(); /* load recent files table */
#ifndef MSW
if (!noprefs)
sys_rcfile(); /* parse the startup file */
......@@ -320,6 +321,8 @@ int sys_main(int argc, char **argv)
gui_vmess("gui_set_lib_dir", "s", sys_libdir->s_name);
/* send the name of the gui preset */
gui_vmess("gui_set_gui_preset", "s", sys_gui_preset->s_name);
/* send the recent files list */
glob_recent_files(0);
if (sys_externalschedlib)
return (sys_run_scheduler(sys_externalschedlibname,
......
......@@ -42,6 +42,14 @@ extern int sys_defeatrt;
extern t_symbol *sys_gui_preset;
extern t_symbol *sys_flags;
#define MAX_RECENT_FILES 8
void sys_load_recent_files(void);
void sys_save_recent_files(void);
void sys_add_recent_file(const char *s);
void sys_clear_recent_files(void);
extern int sys_n_recent_files;
extern char *sys_recent_files[];
/* s_main.c */
extern int sys_debuglevel;
extern int sys_verbose;
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment