[PATCH] lynx-like jumpkeys



Hello.

Here is a patch which adds a fast lynx-like navigation to the
panels and to the listbox widgets (history, hotlist, ...). Each
displayed entry is numbered and may be selected by one or two
keystrokes.

To select for example a file or a hotlist entry numbered "23",
just press the keys "2" and "3" in quick succession.  The
selection cursor will immediately move to that entry.

This type of keyboard navigation is used in lynx, mutt, slrn,
cscope, vim, links and many other programs. Once one gets used to
it, then it can help to save a lot of keystrokes.

For lack of a better term, I call this type of navigation
"jumpkeys" and use the term in descriptions and documentation.

There are two small pictures attached to this message which show
the feature in action.

When it is enabled, then the widgets intercept numeric keys. This
works without problem in listbox widgets, but requires a little
change of the command line input. The jumpkeys are processed only
when the command line is empty. This allows you to use numbers in
commands. Commands that start with a number may be executed by
prefixing them with a space (" 4command").

An option to enable this feature was added to the Configuration
menu. By default it's disabled.

The patch is against 4.6.0.pre1 and was tested on Linux.

It includes a documentation update.

Please bear in mind that I am a newbie regarding mc code.

It definitely needs a review.

Thanks.

-- 
Tomas Styblo <tripie cpan org>
PGP: http://pgp.mit.edu:11371/pks/lookup?op=get&search=0xC97EA4B6

Attachment: mc3.png
Description: PNG image

Attachment: mc6.png
Description: PNG image

diff -purN mc-4.6.0-pre1/doc/mc.1.in mc-4.6.0-pre1.new/doc/mc.1.in
--- mc-4.6.0-pre1/doc/mc.1.in	Wed Aug 21 03:08:46 2002
+++ mc-4.6.0-pre1.new/doc/mc.1.in	Mon Dec 16 19:05:41 2002
@@ -337,6 +337,12 @@ suspended by using this trick, you won't
 programs from the Midnight Commander until you terminate the suspended
 application.
 .PP
+.B number keys,
+When the "lynx-like jumpkeys" configuration option is enabled, then you
+can use number keys to select numbered entries in the panels, the
+history and the hotlist. More information about this feature is in
+the "Configuration: Panel Options" section.
+.PP
 .SH "  Directory Panels"
 This section lists the keys which operate on the directory panels. If
 you want to know how to change the appearance of the panels take a
@@ -585,6 +591,11 @@ completion
 .\"Completion"
 for you.
 .PP
+When the configuration option "lynx-like jumpkeys" is enabled and the
+command line is empty, then the number keys are intercepted by the
+panels. To execute a command which begins with a number, the command
+must be prefixed with a space.
+.PP
 .SH ""
 .SH "Menu Bar"
 The menu bar pops up when you press F9 or click the mouse on the top
@@ -732,6 +743,9 @@ the group of the file.
 .B inode,
 the inode of the file.
 .PP
+.B jumpkey,
+number of the entry, used by the "lynx-like jumpkeys" feature.
+.PP
 Also you may use these field names for arranging the display:
 .PP
 .B space,
@@ -1569,6 +1583,14 @@ mode or owner changes, etc) the display 
 if you have the option on, you have to rescan the directory manually
 (with C-r).
 .PP
+.I Lynx-like jumpkeys.
+This option enables a lynx-like navigation using numbered entries in
+the panels and other widgets. Each displayed entry is numbered and
+may be selected by one or two keystrokes.
+To select for example a file or a hotlist entry numbered "23", 
+just press the keys "2" and "3" in quick succession.
+The selection cursor will immediately move to that entry.
+.PP
 .B Pause after run
 .PP
 After executing your commands, the Midnight Commander can pause, so
diff -purN mc-4.6.0-pre1/src/complete.c mc-4.6.0-pre1.new/src/complete.c
--- mc-4.6.0-pre1/src/complete.c	Tue Mar 26 03:09:39 2002
+++ mc-4.6.0-pre1.new/src/complete.c	Mon Dec 16 19:52:56 2002
@@ -990,6 +990,7 @@ complete_engine (WInput *in, int what_to
 				    dialog_colors, query_callback,
 				    "[Completion]", "complete", DLG_NONE);
     	    query_list = listbox_new (1, 1, w - 2, h - 2, 0, querylist_callback, NULL);
+            query_list->use_jumpkeys = 0; /* never use jumpkeys in completion menu */
     	    add_widget (query_dlg, query_list);
     	    for (p = in->completions + 1; *p; p++)
     	    	listbox_add_item (query_list, 0, 0, *p, NULL);
diff -purN mc-4.6.0-pre1/src/dir.h mc-4.6.0-pre1.new/src/dir.h
--- mc-4.6.0-pre1/src/dir.h	Fri Dec 28 02:11:52 2001
+++ mc-4.6.0-pre1.new/src/dir.h	Mon Dec 16 19:52:56 2002
@@ -10,6 +10,7 @@ typedef struct {
     /* File attributes */
 
     int  fnamelen;
+    int  jumpkey_num;
     char *fname;
     struct stat  buf;
 
diff -purN mc-4.6.0-pre1/src/main.c mc-4.6.0-pre1.new/src/main.c
--- mc-4.6.0-pre1/src/main.c	Wed Aug 21 03:08:49 2002
+++ mc-4.6.0-pre1.new/src/main.c	Mon Dec 16 19:52:56 2002
@@ -286,6 +286,10 @@ static int edit_one_file_start_line = 1;
    shut down */
 int midnight_shutdown = 0;
 
+/* Grab number keys in panels and listbox widgets and use them
+ * as lynx-like jumpkeys */
+int use_jumpkeys = 0;
+
 /* to show nice prompts */
 static int last_paused = 0;
 
@@ -1176,6 +1180,20 @@ toggle_show_hidden (void)
     update_panels (UP_RELOAD, UP_KEEPSEL);
 }
 
+void
+toggle_use_jumpkeys (void)
+{
+    int i;
+    WPanel *p;
+    
+    use_jumpkeys = !use_jumpkeys;
+    for (i = 0; i <= 1; i++) {
+        p = (i == 0 ? left_panel : right_panel);
+        if (p) set_panel_formats (p);
+    }
+    update_panels (UP_RELOAD, UP_KEEPSEL);
+}
+
 /*
  * Just a hack for allowing url-like pathnames to be accepted from the
  * command line.
diff -purN mc-4.6.0-pre1/src/main.h mc-4.6.0-pre1.new/src/main.h
--- mc-4.6.0-pre1/src/main.h	Mon Aug 19 06:16:47 2002
+++ mc-4.6.0-pre1.new/src/main.h	Mon Dec 16 19:52:56 2002
@@ -5,6 +5,7 @@ void toggle_fast_reload (void);
 void toggle_mix_all_files (void);
 void toggle_show_backup (void);
 void toggle_show_hidden (void);
+void toggle_use_jumpkeys (void);
 
 enum {
     RP_NOCLEAR,
@@ -84,6 +85,7 @@ extern int only_leading_plus_minus;
 extern int ftpfs_directory_timeout;
 extern int output_starts_shell;
 extern int midnight_shutdown;
+extern int use_jumpkeys;
 extern char search_buffer [256];
 extern char cmd_buf [512];
 extern char *cmdline_geometry;
diff -purN mc-4.6.0-pre1/src/option.c mc-4.6.0-pre1.new/src/option.c
--- mc-4.6.0-pre1/src/option.c	Tue Aug 20 04:57:25 2002
+++ mc-4.6.0-pre1.new/src/option.c	Mon Dec 16 19:52:56 2002
@@ -82,6 +82,7 @@ static struct {
    {N_("shell &Patterns"),    &easy_patterns,     TOGGLE_VARIABLE,       0, "shell-patt" },
    {N_("Compute &Totals"),    &file_op_compute_totals,           TOGGLE_VARIABLE,       0, "compute-totals" },
    {N_("&Verbose operation"), &verbose,           TOGGLE_VARIABLE,       0, "verbose" },
+   {N_("&lynx-like jumpkeys"),   &use_jumpkeys,   toggle_use_jumpkeys,    0, "jumpkeys" },
    {N_("&Fast dir reload"),   &fast_reload,       toggle_fast_reload,    0, "fast-reload" },
    {N_("mi&X all files"),     &mix_all_files,     toggle_mix_all_files,  0, "mix-files" },
    {N_("&Drop down menus"),   &drop_menus,        TOGGLE_VARIABLE,       0, "drop-menus" },
@@ -105,7 +106,7 @@ static int configure_callback (struct Dl
 	attrset (COLOR_NORMAL);
 	dlg_erase (h);
 	draw_box (h, 1, 2, h->lines - 2, h->cols - 4);
-	draw_box (h, PY, PX, 8, first_width);
+	draw_box (h, PY, PX, 9, first_width);
 	draw_box (h, RY, RX, 5, first_width);
 	draw_box (h, OY, OX, 15, second_width);
 
@@ -221,8 +222,8 @@ static void init_configure (void)
     pause_radio = radio_new (RY+1, RX+2, 3, pause_options, 1, "pause-radio");
     pause_radio->sel = pause_after_run;
     add_widget (conf_dlg, pause_radio);
-    for (i = 0; i < 6; i++){
-	check_options [i+13].widget = check_new (PY + (6-i), PX+2,
+    for (i = 0; i < 7; i++){
+	check_options [i+13].widget = check_new (PY + (7-i), PX+2,
 						  XTRACT(i+13));
 	add_widget (conf_dlg, check_options [i+13].widget);
     }
diff -purN mc-4.6.0-pre1/src/screen.c mc-4.6.0-pre1.new/src/screen.c
--- mc-4.6.0-pre1/src/screen.c	Mon Aug 19 00:07:09 2002
+++ mc-4.6.0-pre1.new/src/screen.c	Mon Dec 16 19:52:56 2002
@@ -44,6 +44,7 @@
 #include "user.h"
 #include "profile.h"
 #include "widget.h"
+#include "command.h"
 #include "../vfs/vfs.h"
 
 #ifdef _OS_NT
@@ -371,6 +372,14 @@ string_dot (file_entry *fe, int len)
     return ".";
 }
 
+static const char *
+string_jumpkey (file_entry *fe, int len)
+{
+    static char jumpkey[4];
+    snprintf(jumpkey, sizeof(jumpkey), "%3d", fe->jumpkey_num);
+    return (jumpkey);
+}
+
 #define GT 1
 
 static struct {
@@ -402,6 +411,7 @@ static struct {
 { "|",     1,  0, J_RIGHT,	" ",		0, NULL,		   NULL },
 { "space", 1,  0, J_RIGHT,	" ",		0, string_space,	   NULL },
 { "dot",   1,  0, J_RIGHT,	" ",		0, string_dot,		   NULL },
+{ "jumpkey",   3,  0, J_RIGHT,	" ",		0, string_jumpkey,		   NULL },
 };
 
 static char *
@@ -679,6 +689,7 @@ paint_dir (WPanel *panel)
 	    color = 2 * (panel->dir.list [i+panel->top_file].f.marked);
 	    color += (panel->selected==i+panel->top_file && panel->active);
 	}
+    panel->dir.list[i+panel->top_file].jumpkey_num = i + 1;
 	repaint_file (panel, i+panel->top_file, 1, color, 0);
     }
     standend ();
@@ -1399,17 +1410,26 @@ panel_format (WPanel *panel)
     switch (panel->list_type){
 
     case list_long:
-	return "full perm,space,nlink,space,owner,space,group,space,size,space,mtime,space,name";
+    if (use_jumpkeys)
+	    return "full jumpkey,|,perm,space,nlink,space,owner,space,group,space,size,space,mtime,space,name";
+    else 
+    	return "full perm,space,nlink,space,owner,space,group,space,size,space,mtime,space,name";
 
     case list_brief:
-	return "half 2,type,name";
+    if (use_jumpkeys)
+        return "half 2,jumpkey,|,type,name";
+    else
+    	return "half 2,type,name";
 
     case list_user:
 	return panel->user_format;
 
     default:
     case list_full:
-	return "half type,name,|,size,|,mtime";
+    if (use_jumpkeys)
+        return "half jumpkey,|,type,name,|,size,|,mtime";
+    else
+	    return "half type,name,|,size,|,mtime";
     }
 }
 
@@ -2137,7 +2157,10 @@ static inline int
 panel_key (WPanel *panel, int key)
 {
     int i;
-
+    static int jumpkey_num = 0; /* number of entry the user wants to select */
+    static struct timeval jumpkey_time; /* time of last jumpkey keystroke */
+    int items = llines (panel) * (panel->split ? 2 : 1);
+    
     for (i = 0; panel_keymap [i].key_code; i++){
 	if (key == panel_keymap [i].key_code){
 	    if (panel_keymap [i].fn != start_search)
@@ -2165,6 +2188,40 @@ panel_key (WPanel *panel, int key)
 	return 1;
     }
 
+    switch (key) {
+        case '0': case '1': case '2': case '3': case '4': 
+        case '5': case '6': case '7': case '8': case '9':
+            if (! use_jumpkeys)
+                break;
+            
+            /* process jumpkeys only if command line is empty */
+            if (input_w(cmdline)->point > 0)
+                break;
+            
+            if (jumpkey_num > 0 && 
+                gettimeofday_diff_ms(&jumpkey_time, NULL) <= JUMPKEY_MAXDELAY) {
+                if ((jumpkey_num * 10) + char_to_int(key) > items) {
+                jumpkey_num = char_to_int(key);
+                }
+                else
+                jumpkey_num = (jumpkey_num * 10) + char_to_int(key);
+            }
+            else {
+                jumpkey_num = char_to_int(key);
+            }
+            gettimeofday(&jumpkey_time, NULL);
+
+            if (jumpkey_num == 0 || jumpkey_num > items)
+                return 0;
+
+            unselect_item(panel);
+            panel->selected = (panel->top_file + jumpkey_num) - 1; 
+            select_item(panel);
+            paint_panel(panel);
+            return 1;
+            break;
+    }
+    
     /* Do not eat characters not meant for the panel below ' ' (e.g. C-l). */
     if ((key >= ' '&& key <= 255) || key == 8 || key == KEY_BACKSPACE) {
 	if (panel->searching){
diff -purN mc-4.6.0-pre1/src/setup.c mc-4.6.0-pre1.new/src/setup.c
--- mc-4.6.0-pre1/src/setup.c	Mon Aug 19 06:16:47 2002
+++ mc-4.6.0-pre1.new/src/setup.c	Mon Dec 16 19:52:56 2002
@@ -193,6 +193,7 @@ static const struct {
     { "xtree_mode", &xtree_mode },
     { "num_history_items_recorded", &num_history_items_recorded },
     { "file_op_compute_totals", &file_op_compute_totals },
+    { "use_jumpkeys", &use_jumpkeys },
 #ifdef SAVE_CHANGES_OUTSIDE_OPTIONS_MENU
     { "dive_into_subdirs", &dive_into_subdirs },
     { "preserve_uidgid", &preserve_uidgid },
diff -purN mc-4.6.0-pre1/src/util.h mc-4.6.0-pre1.new/src/util.h
--- mc-4.6.0-pre1/src/util.h	Tue Aug 20 04:57:25 2002
+++ mc-4.6.0-pre1.new/src/util.h	Mon Dec 16 19:52:56 2002
@@ -31,6 +31,7 @@ char *reverse_string (char *string);
 char *diff_two_paths (char *first, char *second);
 
 char *x_basename (char *s);
+#define char_to_int(CH) (CH - '0')
 
 /* Profile managing functions */
 int set_int (char *, char *, int);
@@ -90,6 +91,7 @@ char *canonicalize_pathname (char *);
 char *get_current_wd (char *buffer, int size);
 int my_mkdir (char *s, mode_t mode);
 int my_rmdir (char *s);
+long gettimeofday_diff_ms (struct timeval *from, struct timeval *to);
 
 /* Rotating dash routines */
 void use_dash (int flag); /* Disable/Enable rotate_dash routines */
diff -purN mc-4.6.0-pre1/src/utilunix.c mc-4.6.0-pre1.new/src/utilunix.c
--- mc-4.6.0-pre1/src/utilunix.c	Tue Aug 20 04:57:25 2002
+++ mc-4.6.0-pre1.new/src/utilunix.c	Mon Dec 16 19:52:56 2002
@@ -640,6 +640,34 @@ int gettimeofday (struct timeval *tp, vo
 }
 #endif /* HAVE_GET_PROCESS_STATS */
 
+/* Return the difference between two timeval timestamps in
+ * miliseconds.
+ * The seconds timestamp must be larger or equal to the first.
+ * The first timestamp is mandatory.
+ * If the second timestamp is NULL then the first timestamp is
+ * compared to the current time.
+ * The return value is "long", therefore it may overflow if the
+ * difference is too big. */
+long gettimeofday_diff_ms (struct timeval *from, struct timeval *to)
+{
+    struct timeval now;
+    long diff;
+   
+    if (from == NULL)
+	return (-1);
+    
+    if (to == NULL) {
+	gettimeofday(&now, NULL);
+	to = &now;
+    }
+
+    diff = ((to->tv_sec == from->tv_sec ? 
+		to->tv_usec - from->tv_usec :
+		to->tv_usec + (1e6 - from->tv_usec) + 
+		(((to->tv_sec - from->tv_sec) - 1) * 1e6)) / 1000);
+    return (diff);
+}
+
 #ifndef HAVE_PUTENV
 
 /* The following piece of code was copied from the GNU C Library */
diff -purN mc-4.6.0-pre1/src/widget.c mc-4.6.0-pre1.new/src/widget.c
--- mc-4.6.0-pre1/src/widget.c	Sat Jul 20 04:54:52 2002
+++ mc-4.6.0-pre1.new/src/widget.c	Mon Dec 16 19:52:56 2002
@@ -42,6 +42,7 @@
 #include "complete.h"
 #include "key.h"		/* XCTRL and ALT macros  */
 #include "profile.h"	/* for history loading and saving */
+#include "main.h"
 
 static int button_event (Gpm_Event *event, WButton *b);
 
@@ -1728,7 +1729,10 @@ listbox_draw (WListbox *l, Dlg_head *h, 
 	    text = e->text;
 	    e = e->next;
 	}
-	printw (" %-*s ", l->width-2, name_trunc (text, l->width-2));
+	if (l->use_jumpkeys)
+	    printw (" %3d | %-*s ", i + 1, l->width-7, name_trunc (text, l->width-7));
+	else
+	    printw (" %-*s ", l->width-2, name_trunc (text, l->width-2));
     }
     l->cursor_y = sel_line;
     if (!l->scrollbar)
@@ -1919,7 +1923,9 @@ listbox_key (WListbox *l, int key)
 {
     int i;
     int j = 0;
-
+    static int jumpkey_num = 0; /* number of entry the user wants to select */
+    static struct timeval jumpkey_time; /* time of last jumpkey keystroke */
+	WLEntry *e = NULL;
     if (!l->list)
 	return 0;
     
@@ -1959,6 +1965,39 @@ listbox_key (WListbox *l, int key)
 	for (i = 0; i < l->height-1; i++)
 	    j |= listbox_back (l);
 	return j > 0;
+
+    case '0': case '1': case '2': case '3': case '4': 
+    case '5': case '6': case '7': case '8': case '9':
+	if (! l->use_jumpkeys)
+	    break;
+
+	if (jumpkey_num > 0 && 
+		gettimeofday_diff_ms(&jumpkey_time, NULL) <= JUMPKEY_MAXDELAY) {
+	    if ((jumpkey_num * 10) + char_to_int(key) > 
+		    (l->height > l->count ? l->count : l->height)) {
+		jumpkey_num = char_to_int(key);
+	    }
+	    else
+		jumpkey_num = (jumpkey_num * 10) + char_to_int(key);
+	}
+	else {
+	    jumpkey_num = char_to_int(key);
+	}
+	gettimeofday(&jumpkey_time, NULL);
+
+	if (jumpkey_num == 0 || jumpkey_num > 
+		(l->height > l->count ? l->count : l->height))
+	    return 0;
+	if (! (e = l->top))
+	    return 0;
+	for (i = 1; i < jumpkey_num; i++) {
+	    if (e->next)
+		e = e->next;	
+	}
+	if (e != l->current) {
+	    listbox_select_entry(l, e);
+	    return 1;
+	}
     }
     return 0;
 }
@@ -2108,6 +2147,7 @@ listbox_new (int y, int x, int width, in
     l->cback  = callback;
     l->action = action;
     l->allow_duplicates = 1;
+    l->use_jumpkeys = use_jumpkeys;
     l->scrollbar = slow_terminal ? 0 : 1;
     widget_want_hotkey (l->widget, 1);
     
diff -purN mc-4.6.0-pre1/src/widget.h mc-4.6.0-pre1.new/src/widget.h
--- mc-4.6.0-pre1/src/widget.h	Mon Oct  1 15:30:24 2001
+++ mc-4.6.0-pre1.new/src/widget.h	Mon Dec 16 19:52:56 2002
@@ -96,6 +96,7 @@ typedef struct {
     int	   transparent;		/* Paint in the default color fg/bg */
 } WLabel;
 
+#define JUMPKEY_MAXDELAY 1500
 typedef struct WLEntry {
     char *text;			/* Text to display */
     int  hotkey;
@@ -128,6 +129,7 @@ typedef struct {
     int height;			/* Size of the widget */
     int action;			/* Action type */
     int allow_duplicates;	/* Do we allow duplicates on the list? */
+    int use_jumpkeys;
     int scrollbar;		/* Draw a scrollbar? */
     lcback cback;		/* The callback function */
     int cursor_x, cursor_y;	/* Cache the values */


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]