Skip to content

Commit 886821f

Browse files
author
Satvik Kumar
committed
Add list controller and responding to cursor events
1 parent a1455a5 commit 886821f

9 files changed

Lines changed: 374 additions & 4 deletions

File tree

programs/editor/widgets/toggleLists.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ define("webodf/editor/widgets/toggleLists", [
3434
var ToggleLists = function (callback) {
3535
var self = this,
3636
editorSession,
37+
listController,
3738
widget = {},
3839
numberedList,
3940
bulletedList;
@@ -73,10 +74,26 @@ define("webodf/editor/widgets/toggleLists", [
7374
return widget;
7475
};
7576

77+
function updateToggleButtons(styleSummary) {
78+
bulletedList.set("checked", styleSummary.isBulletedList, false);
79+
numberedList.set("checked", styleSummary.isNumberedList, false);
80+
81+
}
82+
7683
this.onToolDone = function () {
7784
};
7885

7986
this.setEditorSession = function (session) {
87+
if (editorSession) {
88+
listController.unsubscribe(gui.ListController.listStylingChanged, updateToggleButtons);
89+
}
90+
91+
editorSession = session;
92+
93+
if (editorSession) {
94+
listController = editorSession.sessionController.getListController();
95+
listController.subscribe(gui.ListController.listStylingChanged, updateToggleButtons);
96+
}
8097
};
8198

8299
callback(widget);

webodf/lib/gui/ListController.js

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
/**
2+
* Copyright (C) 2010-2014 KO GmbH <copyright@kogmbh.com>
3+
*
4+
* @licstart
5+
* This file is part of WebODF.
6+
*
7+
* WebODF is free software: you can redistribute it and/or modify it
8+
* under the terms of the GNU Affero General Public License (GNU AGPL)
9+
* as published by the Free Software Foundation, either version 3 of
10+
* the License, or (at your option) any later version.
11+
*
12+
* WebODF is distributed in the hope that it will be useful, but
13+
* WITHOUT ANY WARRANTY; without even the implied warranty of
14+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+
* GNU Affero General Public License for more details.
16+
*
17+
* You should have received a copy of the GNU Affero General Public License
18+
* along with WebODF. If not, see <http://www.gnu.org/licenses/>.
19+
* @licend
20+
*
21+
* @source: http://www.webodf.org/
22+
* @source: https://github.com/kogmbh/WebODF/
23+
*/
24+
25+
/*global core, ops, gui, odf, runtime*/
26+
27+
/**
28+
* @implements {core.Destroyable}
29+
* @param {!ops.Session} session
30+
* @param {!string} inputMemberId
31+
* @constructor
32+
*/
33+
gui.ListController = function ListController(session, inputMemberId) {
34+
"use strict";
35+
var odtDocument = session.getOdtDocument(),
36+
odfUtils = odf.OdfUtils,
37+
eventNotifier = new core.EventNotifier([
38+
gui.ListController.listStylingChanged
39+
]),
40+
/**@type{!gui.ListStyleSummary}*/
41+
lastSignalledSelectionInfo,
42+
/**@type{!core.LazyProperty.<!gui.ListStyleSummary>}*/
43+
cachedSelectionInfo;
44+
45+
/**
46+
* @param {!ops.OdtCursor|!string} cursorOrId
47+
* @return {undefined}
48+
*/
49+
function onCursorEvent(cursorOrId) {
50+
var cursorMemberId = (typeof cursorOrId === "string")
51+
? cursorOrId : cursorOrId.getMemberId();
52+
53+
if (cursorMemberId === inputMemberId) {
54+
cachedSelectionInfo.reset();
55+
}
56+
}
57+
58+
/**
59+
* @return {undefined}
60+
*/
61+
function onParagraphStyleModified() {
62+
// this is reset on paragraph style change due to paragraph styles possibly linking to list styles
63+
// through the use of the style:list-style-name attribute
64+
// http://docs.oasis-open.org/office/v1.2/os/OpenDocument-v1.2-os-part1.html#attribute-style_list-style-name
65+
cachedSelectionInfo.reset();
66+
}
67+
68+
/**
69+
* @param {!{paragraphElement:Element}} args
70+
* @return {undefined}
71+
*/
72+
function onParagraphChanged(args) {
73+
var cursor = odtDocument.getCursor(inputMemberId),
74+
p = args.paragraphElement;
75+
76+
if (cursor && odfUtils.getParagraphElement(cursor.getNode()) === p) {
77+
cachedSelectionInfo.reset();
78+
}
79+
}
80+
81+
/**
82+
* @return {!gui.ListStyleSummary}
83+
*/
84+
function getSelectionInfo() {
85+
var cursor = odtDocument.getCursor(inputMemberId),
86+
cursorNode = cursor && cursor.getNode();
87+
88+
return new gui.ListStyleSummary(cursorNode, odtDocument.getRootNode(), odtDocument.getFormatting());
89+
}
90+
91+
/**
92+
* @return {undefined}
93+
*/
94+
function emitSelectionChanges() {
95+
var hasChanged = true,
96+
newStyleSummary = cachedSelectionInfo.value();
97+
98+
if (lastSignalledSelectionInfo) {
99+
hasChanged = lastSignalledSelectionInfo.isNumberedList !== newStyleSummary.isNumberedList ||
100+
lastSignalledSelectionInfo.isBulletedList !== newStyleSummary.isBulletedList;
101+
}
102+
103+
if (hasChanged) {
104+
lastSignalledSelectionInfo = newStyleSummary;
105+
eventNotifier.emit(gui.ListController.listStylingChanged, lastSignalledSelectionInfo);
106+
}
107+
}
108+
109+
/**
110+
* @param {!string} eventid
111+
* @param {!Function} cb
112+
* @return {undefined}
113+
*/
114+
this.subscribe = function (eventid, cb) {
115+
eventNotifier.subscribe(eventid, cb);
116+
};
117+
118+
/**
119+
* @param {!string} eventid
120+
* @param {!Function} cb
121+
* @return {undefined}
122+
*/
123+
this.unsubscribe = function (eventid, cb) {
124+
eventNotifier.unsubscribe(eventid, cb);
125+
};
126+
127+
/**
128+
* @param {!function(!Error=)} callback
129+
* @return {undefined}
130+
*/
131+
this.destroy = function (callback) {
132+
odtDocument.unsubscribe(ops.Document.signalCursorAdded, onCursorEvent);
133+
odtDocument.unsubscribe(ops.Document.signalCursorRemoved, onCursorEvent);
134+
odtDocument.unsubscribe(ops.Document.signalCursorMoved, onCursorEvent);
135+
odtDocument.unsubscribe(ops.OdtDocument.signalParagraphChanged, onParagraphChanged);
136+
odtDocument.unsubscribe(ops.OdtDocument.signalParagraphStyleModified, onParagraphStyleModified);
137+
odtDocument.unsubscribe(ops.OdtDocument.signalProcessingBatchEnd, emitSelectionChanges);
138+
callback();
139+
};
140+
141+
/**
142+
* @return {undefined}
143+
*/
144+
function init() {
145+
odtDocument.subscribe(ops.Document.signalCursorAdded, onCursorEvent);
146+
odtDocument.subscribe(ops.Document.signalCursorRemoved, onCursorEvent);
147+
odtDocument.subscribe(ops.Document.signalCursorMoved, onCursorEvent);
148+
odtDocument.subscribe(ops.OdtDocument.signalParagraphChanged, onParagraphChanged);
149+
odtDocument.subscribe(ops.OdtDocument.signalParagraphStyleModified, onParagraphStyleModified);
150+
odtDocument.subscribe(ops.OdtDocument.signalProcessingBatchEnd, emitSelectionChanges);
151+
152+
cachedSelectionInfo = new core.LazyProperty(getSelectionInfo);
153+
}
154+
155+
init();
156+
};
157+
158+
/**@const*/
159+
gui.ListController.listStylingChanged = "listStyling/changed";

webodf/lib/gui/ListStyleSummary.js

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
/**
2+
* Copyright (C) 2010-2014 KO GmbH <copyright@kogmbh.com>
3+
*
4+
* @licstart
5+
* This file is part of WebODF.
6+
*
7+
* WebODF is free software: you can redistribute it and/or modify it
8+
* under the terms of the GNU Affero General Public License (GNU AGPL)
9+
* as published by the Free Software Foundation, either version 3 of
10+
* the License, or (at your option) any later version.
11+
*
12+
* WebODF is distributed in the hope that it will be useful, but
13+
* WITHOUT ANY WARRANTY; without even the implied warranty of
14+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+
* GNU Affero General Public License for more details.
16+
*
17+
* You should have received a copy of the GNU Affero General Public License
18+
* along with WebODF. If not, see <http://www.gnu.org/licenses/>.
19+
* @licend
20+
*
21+
* @source: http://www.webodf.org/
22+
* @source: https://github.com/kogmbh/WebODF/
23+
*/
24+
25+
/*global runtime, odf, gui*/
26+
27+
/**
28+
* This finds a text:list element containing the given node and then
29+
* determines the type of list based on its list style
30+
*
31+
* @param {?Element} node
32+
* @param {!Element} rootNode
33+
* @param {!odf.Formatting} formatting
34+
* @constructor
35+
*/
36+
gui.ListStyleSummary = function ListStyleSummary(node, rootNode, formatting) {
37+
"use strict";
38+
39+
var self = this,
40+
odfUtils = odf.OdfUtils;
41+
42+
/**
43+
* @type {!boolean}
44+
*/
45+
this.isNumberedList = false;
46+
47+
/**
48+
* @type {!boolean}
49+
*/
50+
this.isBulletedList = false;
51+
52+
/**
53+
* @param {!Element} node
54+
* @return {?Element}
55+
*/
56+
function getListStyleElementAtNode(node) {
57+
var appliedStyles,
58+
filteredStyles,
59+
listStyleName,
60+
listStyleElement = null;
61+
62+
// find the styles applied on this node and search for any list styles
63+
appliedStyles = formatting.getAppliedStyles([node]);
64+
if (appliedStyles[0]) {
65+
filteredStyles = appliedStyles[0].orderedStyles.filter(function (style) {
66+
return style.family === "list-style";
67+
});
68+
69+
listStyleName = filteredStyles[0] && filteredStyles[0].name;
70+
}
71+
72+
if(listStyleName) {
73+
listStyleElement = formatting.getStyleElement(listStyleName, "list-style");
74+
}
75+
76+
return listStyleElement;
77+
}
78+
79+
/**
80+
* @param {!Element} node
81+
* @return {!number}
82+
*/
83+
function getListLevelAtNode(node) {
84+
var listLevel = 0,
85+
currentNode = node;
86+
87+
// find the text:list element that contains the given node
88+
// and then find the highest text:list element in this DOM hierarchy
89+
while (currentNode) {
90+
if (odfUtils.isListElement(currentNode)) {
91+
listLevel += 1;
92+
}
93+
94+
if (currentNode === rootNode) {
95+
break;
96+
}
97+
currentNode = currentNode.parentNode;
98+
}
99+
100+
return listLevel;
101+
}
102+
103+
/**
104+
* @return {undefined}
105+
*/
106+
function init() {
107+
var listLevelAtNode,
108+
currentListStyleElement,
109+
textLevelAttribute;
110+
111+
if(!node) {
112+
return;
113+
}
114+
115+
// find the depth of the list at the node and then find the matching level
116+
// in the list style applied to that node to determine type of list
117+
listLevelAtNode = getListLevelAtNode(node);
118+
currentListStyleElement = getListStyleElementAtNode(node);
119+
currentListStyleElement = currentListStyleElement && currentListStyleElement.firstElementChild;
120+
121+
while (currentListStyleElement) {
122+
textLevelAttribute = currentListStyleElement.getAttributeNS(odf.Namespaces.textns, "level");
123+
124+
if (textLevelAttribute) {
125+
textLevelAttribute = parseInt(textLevelAttribute, 10);
126+
if (textLevelAttribute === listLevelAtNode) {
127+
self.isBulletedList = currentListStyleElement.localName === "list-level-style-bullet";
128+
self.isNumberedList = currentListStyleElement.localName === "list-level-style-number";
129+
}
130+
}
131+
currentListStyleElement = currentListStyleElement.nextElementSibling;
132+
}
133+
}
134+
135+
init();
136+
};

webodf/lib/gui/SessionController.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ gui.SessionControllerOptions = function () {
9090
createParagraphStyleOps = /**@type {function (!number):!Array.<!ops.Operation>}*/ (directFormattingController.createParagraphStyleOps),
9191
textController = new gui.TextController(session, sessionConstraints, sessionContext, inputMemberId, createCursorStyleOp, createParagraphStyleOps),
9292
imageController = new gui.ImageController(session, sessionConstraints, sessionContext, inputMemberId, objectNameGenerator),
93+
listController = new gui.ListController(session, inputMemberId),
9394
imageSelector = new gui.ImageSelector(odtDocument.getOdfCanvas()),
9495
shadowCursorIterator = odtDocument.createPositionIterator(odtDocument.getRootNode()),
9596
/**@type{!core.ScheduledTask}*/
@@ -1026,6 +1027,13 @@ gui.SessionControllerOptions = function () {
10261027
return directFormattingController;
10271028
};
10281029

1030+
/**
1031+
* @return {!gui.ListController}
1032+
*/
1033+
this.getListController = function () {
1034+
return listController;
1035+
};
1036+
10291037
/**
10301038
* @return {!gui.HyperlinkClickHandler}
10311039
*/
@@ -1123,6 +1131,7 @@ gui.SessionControllerOptions = function () {
11231131
metadataController.destroy,
11241132
selectionController.destroy,
11251133
textController.destroy,
1134+
listController.destroy,
11261135
destroy
11271136
];
11281137

webodf/lib/manifest.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,14 @@
153153
"core.typedefs",
154154
"gui.VisualStepScanner"
155155
],
156+
"gui.ListController": [
157+
"core.LazyProperty",
158+
"gui.ListStyleSummary",
159+
"ops.Session"
160+
],
161+
"gui.ListStyleSummary": [
162+
"odf.Formatting"
163+
],
156164
"gui.MetadataController": [
157165
"ops.Session"
158166
],
@@ -201,6 +209,7 @@
201209
"gui.ImageController",
202210
"gui.ImageSelector",
203211
"gui.InputMethodEditor",
212+
"gui.ListController",
204213
"gui.MetadataController",
205214
"gui.PasteController",
206215
"gui.SelectionController",

0 commit comments

Comments
 (0)