diff --git a/.gitignore b/.gitignore
index dcfe1c355d..b99c9f1145 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,5 +10,5 @@ npm-debug.log
 # test reports created by karma
 /karma-reports
 
-# ignore auto-generated component index
+/.idea
 /src/component-index.js
diff --git a/src/UnknownDeviceErrorHandler.js b/src/UnknownDeviceErrorHandler.js
index 2aa0573e22..2b1cf23380 100644
--- a/src/UnknownDeviceErrorHandler.js
+++ b/src/UnknownDeviceErrorHandler.js
@@ -22,7 +22,7 @@ let isDialogOpen = false;
 
 const onAction = function(payload) {
     if (payload.action === 'unknown_device_error' && !isDialogOpen) {
-        var UnknownDeviceDialog = sdk.getComponent("dialogs.UnknownDeviceDialog");
+        const UnknownDeviceDialog = sdk.getComponent('dialogs.UnknownDeviceDialog');
         isDialogOpen = true;
         Modal.createDialog(UnknownDeviceDialog, {
             devices: payload.err.devices,
@@ -33,17 +33,17 @@ const onAction = function(payload) {
                 // https://github.com/vector-im/riot-web/issues/3148
                 console.log('UnknownDeviceDialog closed with '+r);
             },
-        }, "mx_Dialog_unknownDevice");
+        }, 'mx_Dialog_unknownDevice');
     }
-}
+};
 
 let ref = null;
 
-export function startListening () {
+export function startListening() {
     ref = dis.register(onAction);
 }
 
-export function stopListening () {
+export function stopListening() {
     if (ref) {
         dis.unregister(ref);
         ref = null;
diff --git a/src/components/structures/LoggedInView.js b/src/components/structures/LoggedInView.js
index 25ca025a23..5f1aa0d32a 100644
--- a/src/components/structures/LoggedInView.js
+++ b/src/components/structures/LoggedInView.js
@@ -183,7 +183,7 @@ export default React.createClass({
                         ConferenceHandler={this.props.ConferenceHandler}
                         scrollStateMap={this._scrollStateMap}
                     />;
-                if (!this.props.collapse_rhs) right_panel = <RightPanel roomId={this.props.currentRoomId} opacity={this.props.sideOpacity} />;
+                if (!this.props.collapse_rhs) right_panel = <RightPanel roomId={this.props.currentRoomId} opacity={this.props.rightOpacity} />;
                 break;
 
             case PageTypes.UserSettings:
@@ -195,7 +195,7 @@ export default React.createClass({
                     referralBaseUrl={this.props.config.referralBaseUrl}
                     teamToken={this.props.teamToken}
                 />;
-                if (!this.props.collapse_rhs) right_panel = <RightPanel opacity={this.props.sideOpacity}/>;
+                if (!this.props.collapse_rhs) right_panel = <RightPanel opacity={this.props.rightOpacity}/>;
                 break;
 
             case PageTypes.CreateRoom:
@@ -203,7 +203,7 @@ export default React.createClass({
                     onRoomCreated={this.props.onRoomCreated}
                     collapsedRhs={this.props.collapse_rhs}
                 />;
-                if (!this.props.collapse_rhs) right_panel = <RightPanel opacity={this.props.sideOpacity}/>;
+                if (!this.props.collapse_rhs) right_panel = <RightPanel opacity={this.props.rightOpacity}/>;
                 break;
 
             case PageTypes.RoomDirectory:
@@ -219,12 +219,12 @@ export default React.createClass({
                     teamServerUrl={this.props.config.teamServerConfig.teamServerURL}
                     teamToken={this.props.teamToken}
                 />
-                if (!this.props.collapse_rhs) right_panel = <RightPanel opacity={this.props.sideOpacity}/>
+                if (!this.props.collapse_rhs) right_panel = <RightPanel opacity={this.props.rightOpacity}/>
                 break;
 
             case PageTypes.UserView:
                 page_element = null; // deliberately null for now
-                right_panel = <RightPanel userId={this.props.viewUserId} opacity={this.props.sideOpacity} />;
+                right_panel = <RightPanel userId={this.props.viewUserId} opacity={this.props.rightOpacity} />;
                 break;
         }
 
@@ -253,7 +253,7 @@ export default React.createClass({
                     <LeftPanel
                         selectedRoom={this.props.currentRoomId}
                         collapsed={this.props.collapse_lhs || false}
-                        opacity={this.props.sideOpacity}
+                        opacity={this.props.leftOpacity}
                         teamToken={this.props.teamToken}
                     />
                     <main className='mx_MatrixChat_middlePanel'>
diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js
index 625ff26604..7cbc4d3ea7 100644
--- a/src/components/structures/MatrixChat.js
+++ b/src/components/structures/MatrixChat.js
@@ -118,8 +118,9 @@ module.exports = React.createClass({
             collapse_rhs: false,
             ready: false,
             width: 10000,
-            sideOpacity: 1.0,
+            leftOpacity: 1.0,
             middleOpacity: 1.0,
+            rightOpacity: 1.0,
 
             version: null,
             newVersion: null,
@@ -248,7 +249,6 @@ module.exports = React.createClass({
         UDEHandler.startListening();
 
         this.focusComposer = false;
-        window.addEventListener("focus", this.onFocus);
 
         // this can technically be done anywhere but doing this here keeps all
         // the routing url path logic together.
@@ -492,12 +492,14 @@ module.exports = React.createClass({
                     collapse_rhs: false,
                 });
                 break;
-            case 'ui_opacity':
+            case 'ui_opacity': {
+                const sideDefault = payload.sideOpacity >= 0.0 ? payload.sideOpacity : 1.0;
                 this.setState({
-                    sideOpacity: payload.sideOpacity,
-                    middleOpacity: payload.middleOpacity,
+                    leftOpacity: payload.leftOpacity >= 0.0 ? payload.leftOpacity : sideDefault,
+                    middleOpacity: payload.middleOpacity || 1.0,
+                    rightOpacity: payload.rightOpacity >= 0.0 ? payload.rightOpacity : sideDefault,
                 });
-                break;
+                break; }
             case 'set_theme':
                 this._onSetTheme(payload.value);
                 break;
@@ -911,10 +913,6 @@ module.exports = React.createClass({
         });
     },
 
-    onFocus: function(ev) {
-        dis.dispatch({action: 'focus_composer'});
-    },
-
     showScreen: function(screen, params) {
         if (screen == 'register') {
             dis.dispatch({
diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js
index 4b3b09f1fa..e939510c72 100644
--- a/src/components/structures/RoomView.js
+++ b/src/components/structures/RoomView.js
@@ -125,6 +125,8 @@ module.exports = React.createClass({
             room: null,
             roomId: null,
             roomLoading: true,
+
+            forwardingEvent: null,
             editingRoomSettings: false,
             uploadingRoomSettings: false,
             numUnreadMessages: 0,
@@ -452,6 +454,11 @@ module.exports = React.createClass({
                     callState: callState
                 });
 
+                break;
+            case 'forward_event':
+                this.setState({
+                    forwardingEvent: payload.content,
+                });
                 break;
         }
     },
@@ -1195,7 +1202,10 @@ module.exports = React.createClass({
     onCancelClick: function() {
         console.log("updateTint from onCancelClick");
         this.updateTint();
-        this.setState({editingRoomSettings: false});
+        this.setState({
+            editingRoomSettings: false,
+            forwardingEvent: null,
+        });
         dis.dispatch({action: 'focus_composer'});
     },
 
@@ -1473,16 +1483,17 @@ module.exports = React.createClass({
     },
 
     render: function() {
-        var RoomHeader = sdk.getComponent('rooms.RoomHeader');
-        var MessageComposer = sdk.getComponent('rooms.MessageComposer');
-        var RoomSettings = sdk.getComponent("rooms.RoomSettings");
-        var AuxPanel = sdk.getComponent("rooms.AuxPanel");
-        var SearchBar = sdk.getComponent("rooms.SearchBar");
-        var ScrollPanel = sdk.getComponent("structures.ScrollPanel");
-        var TintableSvg = sdk.getComponent("elements.TintableSvg");
-        var RoomPreviewBar = sdk.getComponent("rooms.RoomPreviewBar");
-        var Loader = sdk.getComponent("elements.Spinner");
-        var TimelinePanel = sdk.getComponent("structures.TimelinePanel");
+        const RoomHeader = sdk.getComponent('rooms.RoomHeader');
+        const MessageComposer = sdk.getComponent('rooms.MessageComposer');
+        const ForwardMessage = sdk.getComponent("rooms.ForwardMessage");
+        const RoomSettings = sdk.getComponent("rooms.RoomSettings");
+        const AuxPanel = sdk.getComponent("rooms.AuxPanel");
+        const SearchBar = sdk.getComponent("rooms.SearchBar");
+        const ScrollPanel = sdk.getComponent("structures.ScrollPanel");
+        const TintableSvg = sdk.getComponent("elements.TintableSvg");
+        const RoomPreviewBar = sdk.getComponent("rooms.RoomPreviewBar");
+        const Loader = sdk.getComponent("elements.Spinner");
+        const TimelinePanel = sdk.getComponent("structures.TimelinePanel");
 
         if (!this.state.room) {
                 if (this.state.roomLoading) {
@@ -1610,17 +1621,16 @@ module.exports = React.createClass({
             />;
         }
 
-        var aux = null;
-        if (this.state.editingRoomSettings) {
+        let aux = null;
+        if (this.state.forwardingEvent !== null) {
+            aux = <ForwardMessage onCancelClick={this.onCancelClick} currentRoomId={this.state.room.roomId} mxEvent={this.state.forwardingEvent} />;
+        } else if (this.state.editingRoomSettings) {
             aux = <RoomSettings ref="room_settings" onSaveClick={this.onSettingsSaveClick} onCancelClick={this.onCancelClick} room={this.state.room} />;
-        }
-        else if (this.state.uploadingRoomSettings) {
+        } else if (this.state.uploadingRoomSettings) {
             aux = <Loader/>;
-        }
-        else if (this.state.searching) {
+        } else if (this.state.searching) {
             aux = <SearchBar ref="search_bar" searchInProgress={this.state.searchInProgress } onCancelClick={this.onCancelSearchClick} onSearch={this.onSearch}/>;
-        }
-        else if (!myMember || myMember.membership !== "join") {
+        } else if (!myMember || myMember.membership !== "join") {
             // We do have a room object for this room, but we're not currently in it.
             // We may have a 3rd party invite to it.
             var inviterName = undefined;
@@ -1733,14 +1743,13 @@ module.exports = React.createClass({
         }
 
         // console.log("ShowUrlPreview for %s is %s", this.state.room.roomId, this.state.showUrlPreview);
-
         var messagePanel = (
             <TimelinePanel ref={this._gatherTimelinePanelRef}
                 timelineSet={this.state.room.getUnfilteredTimelineSet()}
                 manageReadReceipts={!UserSettingsStore.getSyncedSetting('hideReadReceipts', false)}
                 manageReadMarkers={true}
                 hidden={hideMessagePanel}
-                highlightedEventId={this.props.highlightedEventId}
+                highlightedEventId={this.state.forwardingEvent ? this.state.forwardingEvent.getId() : this.props.highlightedEventId}
                 eventId={this.props.eventId}
                 eventPixelOffset={this.props.eventPixelOffset}
                 onScroll={ this.onMessageListScroll }
@@ -1778,7 +1787,7 @@ module.exports = React.createClass({
                     onSearchClick={this.onSearchClick}
                     onSettingsClick={this.onSettingsClick}
                     onSaveClick={this.onSettingsSaveClick}
-                    onCancelClick={this.onCancelClick}
+                    onCancelClick={aux ? this.onCancelClick : null}
                     onForgetClick={
                         (myMember && myMember.membership === "leave") ? this.onForgetClick : null
                     }
diff --git a/src/components/structures/UserSettings.js b/src/components/structures/UserSettings.js
index 993586df84..f4bf8b18cb 100644
--- a/src/components/structures/UserSettings.js
+++ b/src/components/structures/UserSettings.js
@@ -545,12 +545,14 @@ module.exports = React.createClass({
         );
     },
 
-    onLanguageChange: function(l) {
-        UserSettingsStore.setLocalSetting('language', l);
-        this.setState({
-            language: l,
-        });
-        PlatformPeg.get().reload();
+    onLanguageChange: function(newLang) {
+        if(this.state.language !== newLang) {
+            UserSettingsStore.setLocalSetting('language', newLang);
+            this.setState({
+                language: newLang,
+            });
+            PlatformPeg.get().reload();
+        }
     },
 
     _renderLanguageSetting: function () {
diff --git a/src/components/views/rooms/ForwardMessage.js b/src/components/views/rooms/ForwardMessage.js
new file mode 100644
index 0000000000..0ebcdf1e4a
--- /dev/null
+++ b/src/components/views/rooms/ForwardMessage.js
@@ -0,0 +1,95 @@
+/*
+ Copyright 2017 Vector Creations Ltd
+ Copyright 2017 Michael Telatynski
+
+ Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+
+import React from 'react';
+import MatrixClientPeg from '../../../MatrixClientPeg';
+import dis from '../../../dispatcher';
+import KeyCode from '../../../KeyCode';
+
+
+module.exports = React.createClass({
+    displayName: 'ForwardMessage',
+
+    propTypes: {
+        currentRoomId: React.PropTypes.string.isRequired,
+
+        /* the MatrixEvent to be forwarded */
+        mxEvent: React.PropTypes.object.isRequired,
+
+        onCancelClick: React.PropTypes.func.isRequired,
+    },
+
+    componentWillMount: function() {
+        dis.dispatch({
+            action: 'ui_opacity',
+            leftOpacity: 1.0,
+            rightOpacity: 0.3,
+            middleOpacity: 0.5,
+        });
+    },
+
+    componentDidMount: function() {
+        this.dispatcherRef = dis.register(this.onAction);
+        document.addEventListener('keydown', this._onKeyDown);
+    },
+
+    componentWillUnmount: function() {
+        dis.dispatch({
+            action: 'ui_opacity',
+            sideOpacity: 1.0,
+            middleOpacity: 1.0,
+        });
+        dis.unregister(this.dispatcherRef);
+        document.removeEventListener('keydown', this._onKeyDown);
+    },
+
+    onAction: function(payload) {
+        if (payload.action === 'view_room') {
+            const event = this.props.mxEvent;
+            const Client = MatrixClientPeg.get();
+            Client.sendEvent(payload.room_id, event.getType(), event.getContent()).done(() => {
+                dis.dispatch({action: 'message_sent'});
+            }, (err) => {
+                if (err.name === "UnknownDeviceError") {
+                    dis.dispatch({
+                        action: 'unknown_device_error',
+                        err: err,
+                        room: Client.getRoom(payload.room_id),
+                    });
+                }
+                dis.dispatch({action: 'message_send_failed'});
+            });
+            if (this.props.currentRoomId === payload.room_id) this.props.onCancelClick();
+        }
+    },
+
+    _onKeyDown: function(ev) {
+        switch (ev.keyCode) {
+            case KeyCode.ESCAPE:
+                this.props.onCancelClick();
+                break;
+        }
+    },
+
+    render: function() {
+        return (
+            <div className="mx_ForwardMessage">
+                <h1>Please select the destination room for this message</h1>
+            </div>
+        );
+    },
+});
diff --git a/src/components/views/rooms/RoomHeader.js b/src/components/views/rooms/RoomHeader.js
index 3463ef948f..0ef69daba3 100644
--- a/src/components/views/rooms/RoomHeader.js
+++ b/src/components/views/rooms/RoomHeader.js
@@ -189,6 +189,9 @@ module.exports = React.createClass({
             );
 
             save_button = <AccessibleButton className="mx_RoomHeader_textButton" onClick={this.props.onSaveClick}>Save</AccessibleButton>;
+        }
+
+        if (this.props.onCancelClick) {
             cancel_button = <CancelButton onClick={this.props.onCancelClick}/>;
         }
 
diff --git a/src/dispatcher.js b/src/dispatcher.js
index 9864cb3807..f3ebed8357 100644
--- a/src/dispatcher.js
+++ b/src/dispatcher.js
@@ -16,13 +16,13 @@ limitations under the License.
 
 'use strict';
 
-var flux = require("flux");
+const flux = require("flux");
 
 class MatrixDispatcher extends flux.Dispatcher {
     /**
      * @param {Object} payload Required. The payload to dispatch.
      *        Must contain at least an 'action' key.
-     * @param {boolean} sync Optional. Pass true to dispatch
+     * @param {boolean=} sync Optional. Pass true to dispatch
      *        synchronously. This is useful for anything triggering
      *        an operation that the browser requires user interaction
      *        for.