1
0
mirror of https://github.com/gryf/pentadactyl-pm.git synced 2025-12-23 07:58:00 +01:00

[muttator] allow external editor with ctrl-i when composing messages. Also a framework for more compose window related mappings (like y to send message)

This commit is contained in:
Martin Stubenschrott
2008-05-14 12:21:05 +00:00
parent f6474bb6ea
commit 64b0e3b8e1
14 changed files with 338 additions and 88 deletions

View File

@@ -2,6 +2,7 @@
<b>Note:</b> If you don't wish to appear on this list when making a donation, please tell me.
2008:
* Daniel Schaffrath
* Sam Griffin
* Ivan Pantuyev
* Spike Spiegal

View File

@@ -1,6 +1,6 @@
#### configuration
VERSION = 0.6pre
VERSION = 1.0pre
NAME = vimperator
include Makefile.common

View File

@@ -265,7 +265,7 @@ liberator.Editor = function () //{{{
["<S-Insert>"], "Insert clipboard/selection",
function () { liberator.editor.pasteClipboard(); });
liberator.mappings.add([liberator.modes.INSERT, liberator.modes.TEXTAREA],
liberator.mappings.add([liberator.modes.INSERT, liberator.modes.TEXTAREA, liberator.modes.COMPOSE],
["<C-i>"], "Edit text field with an external editor",
function () { liberator.editor.editWithExternalEditor(); });
@@ -719,15 +719,27 @@ liberator.Editor = function () //{{{
return -1;
},
// TODO: clean up with 2 functions for textboxes and currentEditor?
editWithExternalEditor: function ()
{
var textBox = document.commandDispatcher.focusedElement;
var textBox = null;
if (!(liberator.config.isComposeWindow))
textBox = document.commandDispatcher.focusedElement;
var text = "";
if (textBox)
text = textBox.value;
else if (typeof GetCurrentEditor == "function") // Thunderbird composer
text = GetCurrentEditor().outputToString("text/plain", 2);
else
return false;
var editor = liberator.options["editor"];
var args = editor.split(" ");
if (args.length < 1)
{
liberator.echoerr("no editor specified");
return;
return false;
}
try
@@ -737,28 +749,34 @@ liberator.Editor = function () //{{{
catch (e)
{
liberator.echoerr("Could not create temporary file: " + e.message);
return;
return false;
}
try
{
liberator.io.writeFile(tmpfile, textBox.value);
liberator.io.writeFile(tmpfile, text);
}
catch (e)
{
liberator.echoerr("Could not write to temporary file " + tmpfile.path + ": " + e.message);
return;
return false;
}
var prog = args.shift();
args.push(tmpfile.path)
if (textBox)
{
textBox.setAttribute("readonly", "true");
var oldBg = textBox.style.backgroundColor;
var tmpBg = "yellow";
textBox.style.backgroundColor = "#bbbbbb";
}
var newThread = Components.classes["@mozilla.org/thread-manager;1"].getService().newThread(0);
// TODO: save return value in v:shell_error
liberator.callFunctionInThread(newThread, liberator.io.run, [prog, args, true]);
if (textBox)
textBox.removeAttribute("readonly");
@@ -772,7 +790,22 @@ liberator.Editor = function () //{{{
try
{
var val = liberator.io.readFile(tmpfile);
if (textBox)
textBox.value = val;
else
{
//document.getElementById("content-frame").contentDocument.designMode = "on";
var editor = GetCurrentEditor();
var wholeDocRange = editor.document.createRange();
var rootNode = editor.rootElement.QueryInterface(Components.interfaces.nsIDOMNode);
wholeDocRange.selectNodeContents(rootNode);
editor.selection.addRange(wholeDocRange);
editor.selection.deleteFromDocument();
editor.insertText(val);
//setTimeout(function() {
// document.getElementById("content-frame").contentDocument.designMode = "off";
//}, 100);
}
}
catch (e)
{
@@ -782,6 +815,8 @@ liberator.Editor = function () //{{{
// }
// blink the textbox after returning - TODO: could use setInterval
if (textBox)
{
var timeout = 100;
textBox.style.backgroundColor = tmpBg;
setTimeout(function () {
@@ -793,8 +828,10 @@ liberator.Editor = function () //{{{
}, timeout);
}, timeout);
}, timeout);
}
tmpfile.remove(false);
return true;
},
// Abbreviations {{{

View File

@@ -233,7 +233,8 @@ liberator.Events = function () //{{{
var currentMacro = "";
var lastMacro = "";
// any tab related events
try // not every extension has a getBrowser() method
{
var tabcontainer = getBrowser().mTabContainer;
if (tabcontainer) // not every VIM-like extension has a tab container
{
@@ -276,6 +277,8 @@ liberator.Events = function () //{{{
liberator.statusline.updateBufferPosition();
liberator.modes.show();
}, null);
}
catch (e) { }
// getBrowser().addEventListener("submit", function (event)
// {
@@ -430,7 +433,7 @@ liberator.Events = function () //{{{
var title = liberator.buffer.title;
//update history
if (liberator.history)
if (url && liberator.history)
liberator.history.add(url, title);
liberator.buffer.updateBufferList();
@@ -531,7 +534,7 @@ liberator.Events = function () //{{{
function () { liberator.events.onEscape(); });
// add the ":" mapping in all but insert mode mappings
liberator.mappings.add([liberator.modes.NORMAL, liberator.modes.VISUAL, liberator.modes.HINTS, liberator.modes.MESSAGE, liberator.modes.CARET, liberator.modes.TEXTAREA],
liberator.mappings.add([liberator.modes.NORMAL, liberator.modes.VISUAL, liberator.modes.HINTS, liberator.modes.MESSAGE, liberator.modes.COMPOSE, liberator.modes.CARET, liberator.modes.TEXTAREA],
[":"], "Enter command line mode",
function () { liberator.commandline.open(":", "", liberator.modes.EX); });
@@ -950,7 +953,15 @@ liberator.Events = function () //{{{
if ((win && win.document && win.document instanceof HTMLDocument)
|| elem instanceof HTMLAnchorElement)
{
if (liberator.mode != liberator.modes.MESSAGE)
if (liberator.config.isComposeWindow)
{
dump("Compose editor got focus\n");
liberator.modes.set(liberator.modes.INSERT, liberator.modes.TEXTAREA);
//setTimeout(function(){liberator.editor.editWithExternalEditor();}, 100);
win.blur();
//liberator.editor.editWithExternalEditor();
}
else if (liberator.mode != liberator.modes.MESSAGE)
liberator.mode = liberator.modes.MESSAGE;
return;
}
@@ -1092,7 +1103,7 @@ liberator.Events = function () //{{{
var stop = true; // set to false if we should NOT consume this event but let also firefox handle it
var win = document.commandDispatcher.focusedWindow;
if (win && win.document.designMode == "on")
if (win && win.document.designMode == "on" && !liberator.config.isComposeWindow)
return false;
// menus have their own command handlers
@@ -1468,7 +1479,11 @@ liberator.Events = function () //{{{
.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
.getInterface(Components.interfaces.nsIXULWindow)
.XULBrowserWindow = window.XULBrowserWindow;
try
{
getBrowser().addProgressListener(eventManager.progressListener, Components.interfaces.nsIWebProgress.NOTIFY_ALL);
}
catch(e) { }
eventManager.prefObserver.register();

View File

@@ -338,15 +338,17 @@ liberator.IO = function () //{{{
{
var file = Components.classes["@mozilla.org/file/local;1"].
createInstance(Components.interfaces.nsILocalFile);
var tmpname = liberator.config.name.toLowerCase() + ".tmp"
if (WINDOWS)
{
var dir = environmentService.get("TMP") || environmentService.get("TEMP") || "C:\\";
file.initWithPath(dir + "\\vimperator.tmp");
file.initWithPath(dir + "\\" + tmpname);
}
else
{
var dir = environmentService.get("TMP") || environmentService.get("TEMP") || "/tmp/";
file.initWithPath(dir + "/vimperator.tmp");
file.initWithPath(dir + "/" + tmpname);
}
file.createUnique(Components.interfaces.nsIFile.NORMAL_FILE_TYPE, 0600);

View File

@@ -501,16 +501,25 @@ const liberator = (function () //{{{
{
var ww = Components.classes["@mozilla.org/embedcomp/window-watcher;1"].
getService(Components.interfaces.nsIWindowWatcher);
if (window == ww.activeWindow && document.commandDispatcher.focusedElement && clearFocusedElement)
document.commandDispatcher.focusedElement.blur();
// TODO: make more generic
if (liberator.has("mail") && clearFocusedElement && gDBView)
gDBView.selection.select(gDBView.selection.currentIndex);
try
{
if (liberator.has("mail") && clearFocusedElement && !liberator.config.isComposeWindow)
{
var i = gDBView.selection.currentIndex;
if (i == -1 && gDBView.rowCount >= 0)
i = 0;
var elem = liberator.config.mainWidget || content;
if (elem != document.commandDispatcher.focusedElement)
gDBView.selection.select(i);
}
}
catch (e) { }
var elem = liberator.config.mainWidget || window.content;
if (elem && (elem != document.commandDispatcher.focusedElement))
elem.focus();
},
@@ -797,7 +806,7 @@ const liberator = (function () //{{{
// optional modules
if (liberator.has("bookmarks")) { log("bookmarks"); liberator.bookmarks = liberator.Bookmarks(); }
if (liberator.has("history")) { log("history"); liberator.history = liberator.History(); }
if (liberator.has("mail")) { log("mail"); liberator.mail = liberator.Mail(); }
if (liberator.has("mail") && liberator.Mail) { log("mail"); liberator.mail = liberator.Mail(); }
if (liberator.has("tabs")) { log("tabs"); liberator.tabs = liberator.Tabs(); }
if (liberator.has("marks")) { log("marks"); liberator.marks = liberator.Marks(); }
if (liberator.has("quickmarks")) { log("quickmarks"); liberator.quickmarks = liberator.QuickMarks(); }
@@ -888,16 +897,17 @@ const liberator = (function () //{{{
}, 0);
liberator.statusline.update();
liberator.log("Vimperator fully initialized", 1);
liberator.log(liberator.config.name + " fully initialized", 1);
},
shutdown: function ()
{
// save our preferences
liberator.commandline.destroy();
liberator.quickmarks.destroy();
liberator.options.destroy();
liberator.events.destroy();
if (liberator.has("quickmarks"))
liberator.quickmarks.destroy();
window.dump("All liberator modules destroyed\n");
},

View File

@@ -193,6 +193,11 @@ liberator.Mail = function ()
"Inspect (focus) message",
function () { content.focus(); });
liberator.mappings.add(modes, ["<Space>"],
"Scroll message or select next unread one",
function () { return true; },
{ flags: liberator.Mappings.flags.ALLOW_EVENT_ROUTING });
liberator.mappings.add(modes, ["x"],
"Select thread",
function () { gDBView.ExpandAndSelectThreadByIndex(GetThreadTree().currentIndex, false) });

View File

@@ -79,6 +79,8 @@ liberator.modes = (function () //{{{
return "-- TEXTAREA" + ext;
case liberator.modes.MESSAGE:
return "-- MESSAGE" + ext;
case liberator.modes.COMPOSE:
return "-- COMPOSE" + ext;
case liberator.modes.CUSTOM:
return "-- " + liberator.plugins.mode + ext;
default: // NORMAL mode
@@ -162,7 +164,8 @@ liberator.modes = (function () //{{{
CARET: 1 << 5, // text cursor is visible
TEXTAREA: 1 << 6, // text cursor is in a HTMLTextAreaElement
MESSAGE: 1 << 7, // for now only used in Muttator when the message has focus
CUSTOM: 1 << 8,
COMPOSE: 1 << 8,
CUSTOM: 1 << 9,
// extended modes, can include multiple modes, and even main modes
EX: 1 << 10,
INPUT_MULTILINE: 1 << 11,
@@ -189,7 +192,7 @@ liberator.modes = (function () //{{{
get all() { return [this.NONE, this.NORMAL, this.INSERT, this.VISUAL,
this.HINTS, this.COMMAND_LINE, this.CARET,
this.TEXTAREA, this.MESSAGE, this.CUSTOM]; },
this.TEXTAREA, this.MESSAGE, this.COMPOSE, this.CUSTOM]; },
// show the current mode string in the command line
show: function ()
@@ -245,6 +248,10 @@ liberator.modes = (function () //{{{
// keeps recording state
reset: function (silent)
{
//if (window.wintype == "msgcompose")
if (liberator.config.isComposeWindow)
this.set(liberator.modes.COMPOSE, liberator.modes.NONE, silent);
else
this.set(liberator.modes.NORMAL, liberator.modes.NONE, silent);
},

View File

@@ -36,8 +36,13 @@ liberator.config = {
guioptions: { m: ["mail-toolbar-menubar2"], T: ["mail-bar2"], f: ["folderPaneBox", "folderpane_splitter"], F: ["folderPaneHeader"] },
get browserModes() { return [liberator.modes.MESSAGE]; },
get mainWidget() { return GetThreadTree(); }, // focusContent() focuses this widget
mainWindowID: "messengerWindow", // used for :set titlestring
get mainWidget() { // focusContent() focuses this widget
return this.isComposeWindow ?
document.getElementById("content-frame") :
GetThreadTree();
},
get mainWindowID() { return this.isComposeWindow ? "msgcomposeWindow" : "messengerWindow"; },
isComposeWindow: false,
dialogs: [
/*["about", "About Firefox",
@@ -88,8 +93,33 @@ liberator.config = {
["o"], "Open a message",
function () { liberator.commandline.open(":", "open ", liberator.modes.EX); });
liberator.mappings.add([liberator.modes.COMPOSE],
["e"], "Edit message",
function () { liberator.editor.editWithExternalEditor(); });
// don't wait too long when selecting new messages
GetThreadTree()._selectDelay = 300; // TODO: make configurable
// GetThreadTree()._selectDelay = 300; // TODO: make configurable
this.isComposeWindow = window.wintype == "msgcompose";
if (this.isComposeWindow)
{
/*setTimeout(function() {
document.getElementById("content-frame").contentDocument.designMode = "off";
document.getElementById("content-frame").focus();
}, 500);*/
//document.getElementById("content-frame").contentWindow.addEventListener("load", function() {
/*window.addEventListener("load", function() {
alert("load");
if (! liberator.editor.editWithExternalEditor())
liberator.echoerr("Editing with external editor failed. Be sure to :set editor correctly");
}, true);
document.getElementById("content-frame").contentDocument.addEventListener("load", function() {
alert("load1");
}, true);*/
/*document.getElementById("content-frame").addEventListener("DOMContentLoaded", function() {
alert("load2");
}, true);*/
}
}
}

View File

@@ -63,11 +63,11 @@ the terms of any one of the MPL, the GPL or the LGPL.
<commandset id="onVimperatorFocus"
commandupdater="true"
events="focus"
oncommandupdate="liberator.events.onFocusChange(event);"/>
oncommandupdate="if (typeof liberator.events != 'undefined') liberator.events.onFocusChange(event);"/>
<commandset id="onVimperatorSelect"
commandupdater="true"
events="select"
oncommandupdate="liberator.events.onSelectionChange(event);"/>
oncommandupdate="if (typeof liberator.events != 'undefined') liberator.events.onSelectionChange(event);"/>
<keyset id="mainKeyset">
<key id="key_open_vimbar" key=":" oncommand="liberator.commandline.open(':', '', liberator.modes.EX);" modifiers=""/>

140
content/muttatorcompose.xul Normal file
View File

@@ -0,0 +1,140 @@
<?xml version="1.0"?>
<!-- ***** BEGIN LICENSE BLOCK ***** {{{
Version: MPL 1.1/GPL 2.0/LGPL 2.1
The contents of this file are subject to the Mozilla Public License Version
1.1 (the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.mozilla.org/MPL/
Software distributed under the License is distributed on an "AS IS" basis,
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
for the specific language governing rights and limitations under the
License.
(c) 2006-2008: Martin Stubenschrott <stubenschrott@gmx.net>
Alternatively, the contents of this file may be used under the terms of
either the GNU General Public License Version 2 or later (the "GPL"), or
the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
in which case the provisions of the GPL or the LGPL are applicable instead
of those above. If you wish to allow use of your version of this file only
under the terms of either the GPL or the LGPL, and not to allow others to
use your version of this file under the terms of the MPL, indicate your
decision by deleting the provisions above and replace them with the notice
and other provisions required by the GPL or the LGPL. If you do not delete
the provisions above, a recipient may use your version of this file under
the terms of any one of the MPL, the GPL or the LGPL.
}}} ***** END LICENSE BLOCK ***** -->
<!--?xml-stylesheet href="chrome://browser/skin/" type="text/css"?-->
<?xml-stylesheet href="chrome://muttator/skin/vimperator.css" type="text/css"?>
<overlay id="muttator"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:nc="http://home.netscape.com/NC-rdf#"
xmlns:html="http://www.w3.org/1999/xhtml"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script type="application/x-javascript;version=1.8" src="liberator.js"/>
<script type="application/x-javascript;version=1.8" src="muttator.js"/>
<script type="application/x-javascript;version=1.8" src="commands.js"/>
<script type="application/x-javascript;version=1.8" src="mappings.js"/>
<script type="application/x-javascript;version=1.8" src="options.js"/>
<script type="application/x-javascript;version=1.8" src="io.js"/>
<script type="application/x-javascript;version=1.8" src="editor.js"/>
<!--script type="application/x-javascript;version=1.8" src="mail.js"/-->
<script type="application/x-javascript;version=1.8" src="util.js"/>
<script type="application/x-javascript;version=1.8" src="modes.js"/>
<script type="application/x-javascript;version=1.8" src="events.js"/>
<script type="application/x-javascript;version=1.8" src="completion.js"/>
<script type="application/x-javascript;version=1.8" src="buffer.js"/>
<script type="application/x-javascript;version=1.8" src="find.js"/>
<script type="application/x-javascript;version=1.8" src="help.js"/>
<script type="application/x-javascript;version=1.8" src="hints.js"/>
<script type="application/x-javascript;version=1.8" src="ui.js"/>
<window id="msgcomposeWindow">
<!--this notifies us also of focus events in the XUL
from: http://developer.mozilla.org/en/docs/XUL_Tutorial:Updating_Commands !-->
<commandset id="onLiberatorFocus"
commandupdater="true"
events="focus"
oncommandupdate="if (typeof liberator.events != 'undefined') liberator.events.onFocusChange(event);"/>
<commandset id="onLiberatorSelect"
commandupdater="true"
events="select"
oncommandupdate="if (typeof liberator.events != 'undefined') liberator.events.onSelectionChange(event);"/>
<keyset id="mainKeyset">
<key id="key_open_vimbar" key=":" oncommand="liberator.commandline.open(':', '', liberator.modes.EX);" modifiers=""/>
<key id="key_send" key="y" oncommand="alert('y');" modifiers=""/>
<key id="key_stop" keycode="VK_ESCAPE" oncommand="liberator.events.onEscape();"/>
<!-- other keys are handled inside the event loop in events.js -->
</keyset>
<statusbar id="status-bar" class="hl-StatusLine">
<hbox insertafter="statusText" id="liberator-statusline" flex="1" height="10" hidden="false" align="center">
<statusbarpanel class="plain" id="liberator-statusline-field-url" readonly="false" flex="1" crop="end"/>
<statusbarpanel class="plain" id="liberator-statusline-field-inputbuffer" flex="0"/>
<statusbarpanel class="plain" id="liberator-statusline-field-progress" flex="0"/>
<statusbarpanel class="plain" id="liberator-statusline-field-tabcount" flex="0"/>
<statusbarpanel class="plain" id="liberator-statusline-field-bufferposition" flex="0"/>
</hbox>
<!-- just hide them since other elements expect them -->
<!--statusbarpanel id="statusText" hidden="true"/>
<statusbarpanel id="statusbarpanel-progress" hidden="true"/-->
</statusbar>
<vbox id="liberator-container" hidden="false">
<listbox id="liberator-bufferwindow" class="plain" rows="10" flex="1" hidden="true"
onclick= "liberator.bufferwindow.onEvent(event);"
ondblclick="liberator.bufferwindow.onEvent(event);"
onkeydown= "liberator.bufferwindow.onEvent(event);">
<listcols>
<listcol flex="1" width="50%"/>
<listcol flex="1" width="50%"/>
</listcols>
</listbox>
<listbox id="liberator-previewwindow" class="plain" rows="10" flex="1" hidden="true"
onclick= "liberator.previewwindow.onEvent(event);"
ondblclick="liberator.previewwindow.onEvent(event);"
onkeydown= "liberator.previewwindow.onEvent(event);">
<listcols>
<listcol flex="1" width="50%"/>
<listcol flex="1" width="50%"/>
</listcols>
</listbox>
<iframe id="liberator-multiline-output" src="about:blank" flex="1" height="10px" hidden="false" collapsed="true"
onclick="liberator.commandline.onMultilineOutputEvent(event)"/>
<listbox id="liberator-completion" class="plain" rows="1" flex="1" hidden="true">
<listcols>
<listcol flex="1" width="50%"/>
<listcol flex="1" width="50%"/>
</listcols>
</listbox>
<hbox insertafter="status-bar" id="liberator-commandline" hidden="false" class="hl-Normal">
<label class="plain" id="liberator-commandline-prompt" flex="0" crop="end" value="" collapsed="true"/>
<textbox class="plain" id="liberator-commandline-command" flex="1" type="timed" timeout="100"
oninput="liberator.commandline.onEvent(event);"
onfocus="liberator.commandline.onEvent(event);"
onblur="liberator.commandline.onEvent(event);"/>
</hbox>
<textbox id="liberator-multiline-input" class="plain" flex="1" rows="1" hidden="false" collapsed="true" multiline="true"
onkeypress="liberator.commandline.onMultilineInputEvent(event);"
oninput="liberator.commandline.onMultilineInputEvent(event);"
onblur="liberator.commandline.onMultilineInputEvent(event);"/>
</vbox>
</window>
</overlay>
<!-- vim: set fdm=marker sw=4 ts=4 et: -->

View File

@@ -192,10 +192,13 @@ liberator.CommandLine = function () //{{{
stylesheet.setAttribute("href", "chrome://" + liberator.config.name.toLowerCase() + "/skin/vimperator.css");
multilineOutputWidget.contentDocument.getElementsByTagName("head")[0].appendChild(stylesheet);
var availableHeight = getBrowser().mPanelContainer != undefined ?
var availableHeight = 250;
try
{
availableHeight = getBrowser().mPanelContainer != undefined ?
getBrowser().mPanelContainer.boxObject.height : getBrowser().boxObject.height;
if (!availableHeight)
availableHeight = 250;
}
catch (e) {}
var contentHeight = multilineOutputWidget.contentDocument.height;
var height = contentHeight < availableHeight ? contentHeight : availableHeight;

View File

@@ -72,11 +72,11 @@ the terms of any one of the MPL, the GPL or the LGPL.
<commandset id="onVimperatorFocus"
commandupdater="true"
events="focus"
oncommandupdate="liberator.events.onFocusChange(event);"/>
oncommandupdate="if (typeof liberator.events != 'undefined') liberator.events.onFocusChange(event);"/>
<commandset id="onVimperatorSelect"
commandupdater="true"
events="select"
oncommandupdate="liberator.events.onSelectionChange(event);"/>
oncommandupdate="if (typeof liberator.events != 'undefined') liberator.events.onSelectionChange(event);"/>
<vbox id="liberator-container" hidden="false">
<listbox id="liberator-bufferwindow" class="plain" rows="10" flex="1" hidden="true"

View File

@@ -21,7 +21,7 @@
<Description>
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
<em:minVersion>3.0b3</em:minVersion>
<em:maxVersion>3.0.0.*</em:maxVersion>
<em:maxVersion>3.0.*</em:maxVersion>
</Description>
</em:targetApplication>
</Description>