diff --git a/bigbluebutton-apps/src/main/java/org/bigbluebutton/conference/service/layout/ILayoutRoomListener.java b/bigbluebutton-apps/src/main/java/org/bigbluebutton/conference/service/layout/ILayoutRoomListener.java new file mode 100755 index 0000000000000000000000000000000000000000..526c9eb8aad7d4151a2c43d15f220d8c800095e6 --- /dev/null +++ b/bigbluebutton-apps/src/main/java/org/bigbluebutton/conference/service/layout/ILayoutRoomListener.java @@ -0,0 +1,29 @@ +/** + * BigBlueButton open source conferencing system - http://www.bigbluebutton.org/ + * + * Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below). + * + * This program is free software; you can redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License as published by the Free Software + * Foundation; either version 2.1 of the License, or (at your option) any later + * version. + * + * BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along + * with BigBlueButton; if not, see <http://www.gnu.org/licenses/>. + * + * Author: Felipe Cecagno <felipe@mconf.org> + */ +package org.bigbluebutton.conference.service.layout; + +import java.util.List; + +public interface ILayoutRoomListener { + public String getName(); + public void updateLayout(List<Object> args); +// public void lockLayout(); +// public void unlockLayout(); +} diff --git a/bigbluebutton-apps/src/main/java/org/bigbluebutton/conference/service/layout/LayoutApplication.java b/bigbluebutton-apps/src/main/java/org/bigbluebutton/conference/service/layout/LayoutApplication.java new file mode 100755 index 0000000000000000000000000000000000000000..13fbcbfdfae11ca2134eabc011b55e9cd97e9d2b --- /dev/null +++ b/bigbluebutton-apps/src/main/java/org/bigbluebutton/conference/service/layout/LayoutApplication.java @@ -0,0 +1,75 @@ +/** + * BigBlueButton open source conferencing system - http://www.bigbluebutton.org/ + * + * Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below). + * + * This program is free software; you can redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License as published by the Free Software + * Foundation; either version 2.1 of the License, or (at your option) any later + * version. + * + * BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along + * with BigBlueButton; if not, see <http://www.gnu.org/licenses/>. + * + * Author: Felipe Cecagno <felipe@mconf.org> + */ +package org.bigbluebutton.conference.service.layout; + +import java.util.List; + +import org.red5.logging.Red5LoggerFactory; +import org.slf4j.Logger; + +public class LayoutApplication { + + private static Logger log = Red5LoggerFactory.getLogger( LayoutApplication.class, "bigbluebutton" ); + + private LayoutRoomsManager roomsManager; + public LayoutHandler handler; + + public boolean createRoom(String name) { + roomsManager.addRoom(new LayoutRoom(name)); + return true; + } + + public boolean destroyRoom(String name) { + if (roomsManager.hasRoom(name)) { + roomsManager.removeRoom(name); + } + return true; + } + + public boolean hasRoom(String name) { + return roomsManager.hasRoom(name); + } + + public boolean addRoomListener(String room, ILayoutRoomListener listener) { + if (roomsManager.hasRoom(room)){ + roomsManager.addRoomListener(room, listener); + return true; + } + log.warn("Adding listener to a non-existant room " + room); + return false; + } + + public void setRoomsManager(LayoutRoomsManager r) { + log.debug("Setting room manager"); + roomsManager = r; + } + + public void lockLayout(String room, int userId, String layout) { + roomsManager.lockLayout(room, userId, layout); + } + + public void unlockLayout(String room) { + roomsManager.unlockLayout(room); + } + + public List<Object> currentLayout(String roomName) { + return roomsManager.currentLayout(roomName); + } +} diff --git a/bigbluebutton-apps/src/main/java/org/bigbluebutton/conference/service/layout/LayoutHandler.java b/bigbluebutton-apps/src/main/java/org/bigbluebutton/conference/service/layout/LayoutHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..14ea32ee214cff33d790b3e6a6858d9aa5cbbb8d --- /dev/null +++ b/bigbluebutton-apps/src/main/java/org/bigbluebutton/conference/service/layout/LayoutHandler.java @@ -0,0 +1,130 @@ +/** + * BigBlueButton open source conferencing system - http://www.bigbluebutton.org/ + * + * Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below). + * + * This program is free software; you can redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License as published by the Free Software + * Foundation; either version 2.1 of the License, or (at your option) any later + * version. + * + * BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along + * with BigBlueButton; if not, see <http://www.gnu.org/licenses/>. + * + * Author: Felipe Cecagno <felipe@mconf.org> + */ +package org.bigbluebutton.conference.service.layout; + +import org.red5.logging.Red5LoggerFactory; +import org.red5.server.adapter.ApplicationAdapter; +import org.red5.server.adapter.IApplication; +import org.red5.server.api.IClient; +import org.red5.server.api.IConnection; +import org.red5.server.api.IScope; +import org.red5.server.api.so.ISharedObject; +import org.slf4j.Logger; + +public class LayoutHandler extends ApplicationAdapter implements IApplication { + private static Logger log = Red5LoggerFactory.getLogger( LayoutHandler.class, "bigbluebutton" ); + + private static final String APP = "LAYOUT"; + private static final String LAYOUT_SO = "layoutSO"; + + private LayoutApplication layoutApplication; + + @Override + public boolean appConnect(IConnection conn, Object[] params) { + log.debug(APP + ":appConnect"); + return true; + } + + @Override + public void appDisconnect(IConnection conn) { + log.debug( APP + ":appDisconnect"); + } + + @Override + public boolean appJoin(IClient client, IScope scope) { + log.debug( APP + ":appJoin " + scope.getName()); + return true; + } + + @Override + public void appLeave(IClient client, IScope scope) { + log.debug(APP + ":appLeave " + scope.getName()); + } + + @Override + public boolean appStart(IScope scope) { + log.debug(APP + ":appStart " + scope.getName()); + return true; + } + + @Override + public void appStop(IScope scope) { + log.debug(APP + ":appStop " + scope.getName()); + } + + @Override + public boolean roomConnect(IConnection connection, Object[] params) { + log.debug(APP + ":roomConnect"); + ISharedObject so = getSharedObject(connection.getScope(), LAYOUT_SO); + log.debug("Setting up Listener"); + LayoutSender sender = new LayoutSender(so); + String room = connection.getScope().getName(); + log.debug("Adding event listener to " + room); + log.debug("Adding room listener"); + layoutApplication.addRoomListener(room, sender); + log.debug("Done setting up listener"); + return true; + } + + @Override + public void roomDisconnect(IConnection connection) { + log.debug(APP + ":roomDisconnect"); + } + + @Override + public boolean roomJoin(IClient client, IScope scope) { + log.debug(APP + ":roomJoin " + scope.getName() + " - " + scope.getParent().getName()); + return true; + } + + @Override + public void roomLeave(IClient client, IScope scope) { + log.debug(APP + ":roomLeave " + scope.getName()); + } + + @Override + public boolean roomStart(IScope scope) { + log.debug(APP + ":roomStart " + scope.getName()); + layoutApplication.createRoom(scope.getName()); + if (!hasSharedObject(scope, LAYOUT_SO)) { + if (createSharedObject(scope, LAYOUT_SO, false)) { + return true; + } + } + log.error("Failed to start room " + scope.getName()); + return false; + } + + @Override + public void roomStop(IScope scope) { + log.debug(APP + ":roomStop " + scope.getName()); + layoutApplication.destroyRoom(scope.getName()); + if (!hasSharedObject(scope, LAYOUT_SO)) { + clearSharedObjects(scope, LAYOUT_SO); + } + } + + public void setLayoutApplication(LayoutApplication a) { + log.debug("Setting layout application"); + layoutApplication = a; + layoutApplication.handler = this; + } + +} diff --git a/bigbluebutton-apps/src/main/java/org/bigbluebutton/conference/service/layout/LayoutRoom.java b/bigbluebutton-apps/src/main/java/org/bigbluebutton/conference/service/layout/LayoutRoom.java new file mode 100755 index 0000000000000000000000000000000000000000..fe4b7ee1990f097da271496804907ede189a99ab --- /dev/null +++ b/bigbluebutton-apps/src/main/java/org/bigbluebutton/conference/service/layout/LayoutRoom.java @@ -0,0 +1,96 @@ +/** + * BigBlueButton open source conferencing system - http://www.bigbluebutton.org/ + * + * Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below). + * + * This program is free software; you can redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License as published by the Free Software + * Foundation; either version 2.1 of the License, or (at your option) any later + * version. + * + * BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along + * with BigBlueButton; if not, see <http://www.gnu.org/licenses/>. + * + * Author: Felipe Cecagno <felipe@mconf.org> + */ +package org.bigbluebutton.conference.service.layout; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import net.jcip.annotations.ThreadSafe; + +import org.red5.logging.Red5LoggerFactory; +import org.slf4j.Logger; +/** + * Contains information about a LayoutRoom. + */ +@ThreadSafe +public class LayoutRoom { + private static Logger log = Red5LoggerFactory.getLogger( LayoutRoom.class, "bigbluebutton" ); + + private final String name; + private final Map<String, ILayoutRoomListener> listeners; + private boolean locked = false; + private int modifierId = -1; + private String currentLayout = ""; + + public LayoutRoom(String name) { + this.name = name; + this.listeners = new ConcurrentHashMap<String, ILayoutRoomListener>(); + } + + public String getName() { + return name; + } + + public void addRoomListener(ILayoutRoomListener listener) { + if (! listeners.containsKey(listener.getName())) { + log.debug("adding room listener"); + listeners.put(listener.getName(), listener); + } + } + + public void removeRoomListener(ILayoutRoomListener listener) { + log.debug("removing room listener"); + listeners.remove(listener); + } + + private void updateLayout() { + for (Iterator<ILayoutRoomListener> iter = listeners.values().iterator(); iter.hasNext();) { + log.debug("calling on listener"); + ILayoutRoomListener listener = (ILayoutRoomListener) iter.next(); + log.debug("calling updateLayout on listener " + listener.getName()); + listener.updateLayout(currentLayout()); + } + } + + public void lockLayout(int userId, String layout) { + locked = true; + modifierId = userId; + currentLayout = layout; + updateLayout(); + } + + public void unlockLayout() { + locked = false; + modifierId = -1; + currentLayout = ""; + updateLayout(); + } + + public List<Object> currentLayout() { + List<Object> args = new ArrayList<Object>(); + args.add(locked); + args.add(modifierId); + args.add(currentLayout); + return args; + } +} diff --git a/bigbluebutton-apps/src/main/java/org/bigbluebutton/conference/service/layout/LayoutRoomsManager.java b/bigbluebutton-apps/src/main/java/org/bigbluebutton/conference/service/layout/LayoutRoomsManager.java new file mode 100755 index 0000000000000000000000000000000000000000..89715eb2fc4976a4cb30a77d919b8914926621d9 --- /dev/null +++ b/bigbluebutton-apps/src/main/java/org/bigbluebutton/conference/service/layout/LayoutRoomsManager.java @@ -0,0 +1,113 @@ +/** + * BigBlueButton open source conferencing system - http://www.bigbluebutton.org/ + * + * Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below). + * + * This program is free software; you can redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License as published by the Free Software + * Foundation; either version 2.1 of the License, or (at your option) any later + * version. + * + * BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along + * with BigBlueButton; if not, see <http://www.gnu.org/licenses/>. + * + * Author: Felipe Cecagno <felipe@mconf.org> + */ +package org.bigbluebutton.conference.service.layout; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import net.jcip.annotations.ThreadSafe; + +import org.red5.logging.Red5LoggerFactory; +import org.slf4j.Logger; +/** + * This encapsulates access to LayoutRoom and messages. This class must be threadsafe. + */ +@ThreadSafe +public class LayoutRoomsManager { + private static Logger log = Red5LoggerFactory.getLogger( LayoutRoomsManager.class, "bigbluebutton" ); + + private final Map <String, LayoutRoom> rooms; + + public LayoutRoomsManager() { + log.debug("In LayoutRoomsManager constructor"); + rooms = new ConcurrentHashMap<String, LayoutRoom>(); + } + + public void addRoom(LayoutRoom room) { + log.debug("In LayoutRoomsManager adding room " + room.getName()); + rooms.put(room.getName(), room); + } + + public void removeRoom(String name) { + log.debug("In LayoutRoomsManager remove room " + name); + rooms.remove(name); + } + + public boolean hasRoom(String name) { + log.debug("In LayoutRoomsManager has Room " + name); + return rooms.containsKey(name); + } + + + /** + * Keeping getRoom private so that all access to ChatRoom goes through here. + */ + private LayoutRoom getRoom(String name) { + log.debug("In LayoutRoomsManager get room " + name); + return rooms.get(name); + } + + public void addRoomListener(String roomName, ILayoutRoomListener listener) { + LayoutRoom r = getRoom(roomName); + if (r != null) { + r.addRoomListener(listener); + return; + } + log.warn("Adding listener to a non-existing room " + roomName); + } + + public void removeRoomListener(String roomName, ILayoutRoomListener listener) { + LayoutRoom r = getRoom(roomName); + if (r != null) { + r.removeRoomListener(listener); + return; + } + log.warn("Removing listener to a non-existing room " + roomName); + } + + public void lockLayout(String room, int userId, String layout) { + LayoutRoom r = getRoom(room); + if (r != null) { + r.lockLayout(userId, layout); + } else { + log.warn("lockLayout: sending message to a non-existing room " + room); + } + } + + public void unlockLayout(String room) { + LayoutRoom r = getRoom(room); + if (r != null) { + r.unlockLayout(); + } else { + log.warn("unlockLayout: sending message to a non-existing room " + room); + } + } + + public List<Object> currentLayout(String room) { + LayoutRoom r = getRoom(room); + if (r != null) { + return r.currentLayout(); + } else { + log.warn("initLayout: sending message to a non-existing room " + room); + return null; + } + } +} diff --git a/bigbluebutton-apps/src/main/java/org/bigbluebutton/conference/service/layout/LayoutSender.java b/bigbluebutton-apps/src/main/java/org/bigbluebutton/conference/service/layout/LayoutSender.java new file mode 100755 index 0000000000000000000000000000000000000000..1f0e7ff50983d1676b9d69f4faf189f210d8cc92 --- /dev/null +++ b/bigbluebutton-apps/src/main/java/org/bigbluebutton/conference/service/layout/LayoutSender.java @@ -0,0 +1,59 @@ +/** + * BigBlueButton open source conferencing system - http://www.bigbluebutton.org/ + * + * Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below). + * + * This program is free software; you can redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License as published by the Free Software + * Foundation; either version 2.1 of the License, or (at your option) any later + * version. + * + * BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along + * with BigBlueButton; if not, see <http://www.gnu.org/licenses/>. + * + * Author: Felipe Cecagno <felipe@mconf.org> + */ +package org.bigbluebutton.conference.service.layout; + +import java.util.List; + +import org.red5.logging.Red5LoggerFactory; +import org.red5.server.api.so.ISharedObject; +import org.red5.server.api.statistics.ISharedObjectStatistics; +import org.slf4j.Logger; + +public class LayoutSender implements ILayoutRoomListener { + +private static Logger log = Red5LoggerFactory.getLogger( LayoutSender.class, "bigbluebutton" ); + + private ISharedObject so; + private String name = "LAYOUT"; + + public LayoutSender(ISharedObject so) { + this.so = so; + } + + @Override + public String getName() { + return name; + } + + @Override + public void updateLayout(List<Object> args) { + log.debug("Sending update layout"); + + if (so.isLocked()) log.info("Layout SO is locked"); + if (so.isAcquired()) log.info("Layout SO is acquired"); + ISharedObjectStatistics stats = so.getStatistics(); + log.debug("Before: Layout SO stats [total-sends=" + stats.getTotalSends() + "]"); + so.sendMessage("remoteUpdateLayout", args); + log.debug("After: Layout SO stats [total-sends=" + stats.getTotalSends() + "]"); + if (so.isLocked()) log.info("Layout SO is locked"); + if (so.isAcquired()) log.info("Layout SO is acquired"); + } + +} diff --git a/bigbluebutton-apps/src/main/java/org/bigbluebutton/conference/service/layout/LayoutService.java b/bigbluebutton-apps/src/main/java/org/bigbluebutton/conference/service/layout/LayoutService.java new file mode 100755 index 0000000000000000000000000000000000000000..be0348ef68c8116a9e51432a4a5c02695871bff5 --- /dev/null +++ b/bigbluebutton-apps/src/main/java/org/bigbluebutton/conference/service/layout/LayoutService.java @@ -0,0 +1,55 @@ +/** + * BigBlueButton open source conferencing system - http://www.bigbluebutton.org/ + * + * Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below). + * + * This program is free software; you can redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License as published by the Free Software + * Foundation; either version 2.1 of the License, or (at your option) any later + * version. + * + * BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along + * with BigBlueButton; if not, see <http://www.gnu.org/licenses/>. + * + * Author: Felipe Cecagno <felipe@mconf.org> + */ +package org.bigbluebutton.conference.service.layout; + import java.util.List; + +import org.red5.logging.Red5LoggerFactory; +import org.red5.server.api.Red5; +import org.slf4j.Logger; + +public class LayoutService { + + private static Logger log = Red5LoggerFactory.getLogger( LayoutService.class, "bigbluebutton" ); + + private LayoutApplication application; + + public List<Object> init() { + log.debug("Initializing layout"); + String roomName = Red5.getConnectionLocal().getScope().getName(); + return application.currentLayout(roomName); + } + + public void lock(int userId, String layout) { + log.debug("Layout locked"); + String roomName = Red5.getConnectionLocal().getScope().getName(); + application.lockLayout(roomName, userId, layout); + } + + public void unlock() { + log.debug("Layout unlocked"); + String roomName = Red5.getConnectionLocal().getScope().getName(); + application.unlockLayout(roomName); + } + + public void setLayoutApplication(LayoutApplication a) { + log.debug("Setting layout application"); + application = a; + } +} diff --git a/bigbluebutton-apps/src/main/webapp/WEB-INF/bbb-apps.xml b/bigbluebutton-apps/src/main/webapp/WEB-INF/bbb-apps.xml index 0b1a7183b8ea70816ab15c5ff4e14e3f7d7c1dd7..e0acad00de434bcf3ecc417f2ed63f04acdb940d 100755 --- a/bigbluebutton-apps/src/main/webapp/WEB-INF/bbb-apps.xml +++ b/bigbluebutton-apps/src/main/webapp/WEB-INF/bbb-apps.xml @@ -27,6 +27,23 @@ <property name="participantsApplication"> <ref local="participantsApplication"/></property> </bean> + + <!-- BEGIN LAYOUT --> + <bean id="layoutRoomsManager" class="org.bigbluebutton.conference.service.layout.LayoutRoomsManager"/> + + <bean id="layoutHandler" class="org.bigbluebutton.conference.service.layout.LayoutHandler"> + <property name="layoutApplication"><ref local="layoutApplication"/></property> + </bean> + + <bean id="layoutApplication" class="org.bigbluebutton.conference.service.layout.LayoutApplication"> + <property name="roomsManager"><ref local="layoutRoomsManager"/></property> + </bean> + + <bean id="layout.service" class="org.bigbluebutton.conference.service.layout.LayoutService"> + <property name="layoutApplication"><ref local="layoutApplication"/></property> + </bean> + <!-- END LAYOUT --> + <!-- BEGIN PRESENTATION --> <bean id="presentationRoomsManager" class="org.bigbluebutton.conference.service.presentation.PresentationRoomsManager"/> diff --git a/bigbluebutton-apps/src/main/webapp/WEB-INF/red5-web.xml b/bigbluebutton-apps/src/main/webapp/WEB-INF/red5-web.xml index 6b55275cf40813b22b9823383189eb6848a7a1d5..fa36b5981ea19f980f26252c8d816cbfc6c1eff1 100755 --- a/bigbluebutton-apps/src/main/webapp/WEB-INF/red5-web.xml +++ b/bigbluebutton-apps/src/main/webapp/WEB-INF/red5-web.xml @@ -32,6 +32,7 @@ <set> <ref bean="participantsHandler" /> <ref bean="chatHandler" /> + <ref bean="layoutHandler" /> <ref bean="presentationHandler" /> <ref bean="voiceHandler" /> <ref bean="whiteboardApplication" /> diff --git a/bigbluebutton-client/build.xml b/bigbluebutton-client/build.xml index efc9e47b2364e8cb06bbeb052a55b42a1c06e722..d27038ccb04abdc18a7706d3ff72766ca4dd0b50 100755 --- a/bigbluebutton-client/build.xml +++ b/bigbluebutton-client/build.xml @@ -31,6 +31,7 @@ <property name="CLASSROOM_AUDIO" value="ClassroomAudioModule" /> <property name="SETTINGS" value="SettingsModule" /> <property name="VIDEO_DOCK" value="VideodockModule" /> + <property name="LAYOUT" value="LayoutModule" /> <xmlproperty file="${SRC_DIR}/conf/locales.xml" collapseAttributes="true"/> @@ -231,9 +232,13 @@ <build-module src="${SRC_DIR}" target="${DYN_INFO}" /> </target> + <target name="build-layout" description="Compile Layout Module"> + <build-module src="${SRC_DIR}" target="${LAYOUT}" /> + </target> + <!-- just a grouping of modules to compile --> <target name="build-main-chat-viewers-listeners-present" - depends="build-bbb-main, build-bbb-main-test, build-chat, build-viewers, build-listeners, build-present, build-breakout" + depends="build-bbb-main, build-bbb-main-test, build-chat, build-viewers, build-listeners, build-present, build-breakout, build-layout" description="Compile main, chat, viewers, listeners, present, breakout modules"> </target> @@ -378,18 +383,20 @@ <copy file="${PROD_RESOURCES_DIR}/expressInstall.swf" todir="${OUTPUT_DIR}" overwrite="true"/> <copy file="${PROD_RESOURCES_DIR}/example-info-data.xml" todir="${OUTPUT_DIR}/conf" overwrite="true"/> <if> - <equals arg1="${BUILD_ENV}" arg2="DEV"/> - <then> - <echo message="Copying config.xml for development environment"/> + <equals arg1="${BUILD_ENV}" arg2="DEV"/> + <then> + <echo message="Copying config.xml for development environment"/> <copy file="${BASE_DIR}/src/conf/config.xml" todir="${OUTPUT_DIR}/conf" /> - <echo message="Copying join-mock.xml for development environment"/> + <echo message="Copying join-mock.xml for development environment"/> <copy file="${BASE_DIR}/src/conf/join-mock.xml" todir="${OUTPUT_DIR}/conf" /> - </then> - <else> - <echo message="Need to copy config.xml.template for production environment"/> - <copy file="${RESOURCES_DIR}/config.xml.template" todir="${OUTPUT_DIR}/conf" overwrite="true"/> - </else> - </if> + <echo message="Copying layout.xml for development environment"/> + <copy file="${BASE_DIR}/src/conf/layout.xml" todir="${OUTPUT_DIR}/conf" /> + </then> + <else> + <echo message="Need to copy config.xml.template for production environment"/> + <copy file="${RESOURCES_DIR}/config.xml.template" todir="${OUTPUT_DIR}/conf" overwrite="true"/> + </else> + </if> </target> diff --git a/bigbluebutton-client/locale/en_US/bbbResources.properties b/bigbluebutton-client/locale/en_US/bbbResources.properties index c345b3038a8bbee8d3f55dad4248ba5374386f28..f0f38cc855b96f49758307c80c86194b1800f3fb 100755 --- a/bigbluebutton-client/locale/en_US/bbbResources.properties +++ b/bigbluebutton-client/locale/en_US/bbbResources.properties @@ -132,6 +132,18 @@ bbb.desktopView.actualSize = Display actual size bbb.toolbar.phone.toolTip = Share My Microphone bbb.toolbar.deskshare.toolTip = Share My Desktop bbb.toolbar.video.toolTip = Share My Camera +bbb.layout.addButton.toolTip = Add the custom layout to the list +bbb.layout.combo.toolTip = Change the current layout +bbb.layout.loadButton.toolTip = Load layouts from a file +bbb.layout.saveButton.toolTip = Save layouts to a file +bbb.layout.lockButton.toolTip = Lock layout +bbb.layout.combo.prompt = Apply a layout +bbb.layout.combo.custom = * Custom layout +bbb.layout.combo.customName = Custom layout +bbb.layout.combo.remote = Remote +bbb.layout.save.complete = Layouts were successfully saved +bbb.layout.load.complete = Layouts were successfully loaded +bbb.layout.load.failed = Failed to load the layouts bbb.highlighter.toolbar.pencil = Highlighter bbb.highlighter.toolbar.ellipse = Circle bbb.highlighter.toolbar.rectangle = Rectangle diff --git a/bigbluebutton-client/resources/config.xml.template b/bigbluebutton-client/resources/config.xml.template index 5fcc26e00324cea8e6ccaf5dafa2924f57b353cf..495b73762a7ea9fc06cd1865ce3cdf26c3cef85a 100755 --- a/bigbluebutton-client/resources/config.xml.template +++ b/bigbluebutton-client/resources/config.xml.template @@ -95,6 +95,12 @@ oneAlwaysBigger="false" /> + <module name="LayoutModule" url="LayoutModule.swf?v=VERSION" + uri="rtmp://HOST/bigbluebutton" + layoutConfig="conf/layout.xml" + enableEdit="true" + /> + <!-- new module in development: <module name="DynamicInfoModule" url="DynamicInfoModule.swf?v=VERSION" uri="rtmp://HOST/bigbluebutton" diff --git a/bigbluebutton-client/src/LayoutModule.mxml b/bigbluebutton-client/src/LayoutModule.mxml new file mode 100755 index 0000000000000000000000000000000000000000..45d939f1afbcbc3f88da86d67312840d3be09c2a --- /dev/null +++ b/bigbluebutton-client/src/LayoutModule.mxml @@ -0,0 +1,61 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + BigBlueButton open source conferencing system - http://www.bigbluebutton.org/ + + Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below). + + This program is free software; you can redistribute it and/or modify it under the + terms of the GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1 of the License, or (at your option) any later + version. + + BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along + with BigBlueButton; if not, see <http://www.gnu.org/licenses/>. + + Author: Felipe Cecagno <felipe@mconf.org> + + $Id: $ +--> +<mx:Module xmlns:mx="http://www.adobe.com/2006/mxml" + xmlns:maps="org.bigbluebutton.modules.layout.maps.*" + implements="org.bigbluebutton.common.IBigBlueButtonModule"> + + <maps:LayoutEventMap id="layoutEventMap" /> + + <mx:Script> + <![CDATA[ + import com.asfusion.mate.events.Dispatcher; + + import org.bigbluebutton.common.LogUtil; + import org.bigbluebutton.modules.layout.events.StartLayoutModuleEvent; + import org.bigbluebutton.modules.layout.events.LayoutEvent; + + private var _moduleName:String = "Layout Module"; + private var _globalDispatcher:Dispatcher = new Dispatcher(); + + private function onCreationComplete():void { + LogUtil.debug(_moduleName + " initialized"); + } + + public function get moduleName():String { + return _moduleName; + } + + public function start(attributes:Object):void { + var event:StartLayoutModuleEvent = new StartLayoutModuleEvent(); + event.attributes = attributes; + _globalDispatcher.dispatchEvent(event); + } + + public function stop():void { + var event:LayoutEvent = new LayoutEvent(LayoutEvent.STOP_LAYOUT_MODULE_EVENT); + _globalDispatcher.dispatchEvent(event); + } + + ]]> + </mx:Script> +</mx:Module> \ No newline at end of file diff --git a/bigbluebutton-client/src/conf/layout.xml b/bigbluebutton-client/src/conf/layout.xml new file mode 100644 index 0000000000000000000000000000000000000000..47ec87b4b10230f505a4f8af0ed2e25189c8cf7e --- /dev/null +++ b/bigbluebutton-client/src/conf/layout.xml @@ -0,0 +1,52 @@ +<?xml version="1.0"?> +<layouts> + <layout name="Default" default="true"> + <window name="ViewersWindow" width="0.1772793053545586" height="0.33643617021276595" x="0" y="0" /> + <window name="ListenersWindow" width="0.1772793053545586" height="0.33643617021276595" x="0" y="0.34308510638297873" /> + <window name="PresentationWindow" width="0.5137481910274964" height="0.9946808510638298" x="0.18017366136034732" y="0" /> + <window name="VideoDock" width="0.1772793053545586" height="0.30851063829787234" x="0" y="0.6875" /> + <window name="ChatWindow" width="0.3031837916063676" height="0.9960106382978723" x="0.6968162083936325" y="0" /> + </layout> + <layout name="Meeting"> + <window name="VideoDock" width="0.6570188133140377" height="0.9960106382978723" x="0" y="0" /> + <window name="ChatWindow" width="0.3393632416787265" height="0.5305851063829787" x="0.658465991316932" y="0" /> + <window name="ListenersWindow" hidden="true" /> + <window name="ViewersWindow" hidden="true" /> + <window name="PresentationWindow" width="0.34008683068017365" height="0.4601063829787234" x="0.658465991316932" y="0.535904255319149" /> + </layout> + <layout name="Webinar"> + <window name="ViewersWindow" minimized="true" /> + <window name="VideoDock" width="0.2923611111111111" height="0.4640957446808511" x="0.7048611111111112" y="0.535904255319149" /> + <window name="ListenersWindow" minimized="true" /> + <window name="PresentationWindow" width="0.7027777777777777" height="0.9986702127659575" x="0" y="0" /> + <window name="ChatWindow" width="0.2923611111111111" height="0.5305851063829787" x="0.7048611111111112" y="0" /> + </layout> + <layout name="Lecture assistant"> + <window name="ChatWindow" width="0.4597222222222222" height="0.9958677685950413" x="0.2263888888888889" y="0" /> + <window name="ListenersWindow" width="0.2222222222222222" height="0.4765840220385675" x="0" y="0.5179063360881543" /> + <window name="ViewersWindow" width="0.22152777777777777" height="0.5055096418732782" x="0" y="0" /> + <window name="PresentationWindow" width="0.3104166666666667" height="0.5537190082644629" x="0.6895833333333333" y="0" /> + <window name="VideoDock" width="0.30972222222222223" height="0.4256198347107438" x="0.6902777777777778" y="0.568870523415978" /> + </layout> + <layout name="Lecture"> + <window name="ViewersWindow" hidden="true" /> + <window name="VideoDock" width="0.2923611111111111" height="0.4640957446808511" x="0.7048611111111112" y="0.535904255319149" /> + <window name="ListenersWindow" hidden="true" /> + <window name="PresentationWindow" width="0.7027777777777777" height="0.9986702127659575" x="0" y="0" /> + <window name="ChatWindow" width="0.2923611111111111" height="0.5305851063829787" x="0.7048611111111112" y="0" /> + </layout> + <layout name="Lecture" role="presenter"> + <window name="ChatWindow" hidden="true" /> + <window name="ListenersWindow" hidden="true" /> + <window name="ViewersWindow" hidden="true" /> + <window name="PresentationWindow" maximized="true" /> + <window name="VideoDock" hidden="true" /> + </layout> + <layout name="Lecture" role="moderator"> + <window name="ChatWindow" width="0.4597222222222222" height="0.9958677685950413" x="0.2263888888888889" y="0" /> + <window name="ListenersWindow" width="0.2222222222222222" height="0.4765840220385675" x="0" y="0.5179063360881543" /> + <window name="ViewersWindow" width="0.22152777777777777" height="0.5055096418732782" x="0" y="0" /> + <window name="PresentationWindow" width="0.3104166666666667" height="0.5537190082644629" x="0.6895833333333333" y="0" /> + <window name="VideoDock" width="0.30972222222222223" height="0.4256198347107438" x="0.6902777777777778" y="0.568870523415978" /> + </layout> +</layouts> diff --git a/bigbluebutton-client/src/org/bigbluebutton/common/Images.as b/bigbluebutton-client/src/org/bigbluebutton/common/Images.as index 425075e6e81d552b21b5de334a7e0e9fedb4d76c..79c38b53e48ca797d6869f8b9a474d4dbd9a6099 100755 --- a/bigbluebutton-client/src/org/bigbluebutton/common/Images.as +++ b/bigbluebutton-client/src/org/bigbluebutton/common/Images.as @@ -173,5 +173,11 @@ package org.bigbluebutton.common [Embed(source="assets/images/shape_handles.png")] public var shape_handles:Class; + + [Embed(source="assets/images/disk.png")] + public var disk:Class; + + [Embed(source="assets/images/folder.png")] + public var folder:Class; } } \ No newline at end of file diff --git a/bigbluebutton-client/src/org/bigbluebutton/common/assets/images/disk.png b/bigbluebutton-client/src/org/bigbluebutton/common/assets/images/disk.png new file mode 100644 index 0000000000000000000000000000000000000000..99d532e8b1750115952f97302a92d713c0486f97 Binary files /dev/null and b/bigbluebutton-client/src/org/bigbluebutton/common/assets/images/disk.png differ diff --git a/bigbluebutton-client/src/org/bigbluebutton/common/assets/images/folder.png b/bigbluebutton-client/src/org/bigbluebutton/common/assets/images/folder.png new file mode 100644 index 0000000000000000000000000000000000000000..784e8fa48234f4f64b6922a6758f254ee0ca08ec Binary files /dev/null and b/bigbluebutton-client/src/org/bigbluebutton/common/assets/images/folder.png differ diff --git a/bigbluebutton-client/src/org/bigbluebutton/common/events/ToolbarButtonEvent.as b/bigbluebutton-client/src/org/bigbluebutton/common/events/ToolbarButtonEvent.as index f6a2ba324379dcfddd63e9a6e96b54f938d53c75..e31b44334e2098a135b08fc1d81ab98ab840edf9 100755 --- a/bigbluebutton-client/src/org/bigbluebutton/common/events/ToolbarButtonEvent.as +++ b/bigbluebutton-client/src/org/bigbluebutton/common/events/ToolbarButtonEvent.as @@ -31,11 +31,13 @@ package org.bigbluebutton.common.events { public static const ADD:String = "Add Toolbar Button Event"; public static const REMOVE:String = "Remove Toolbar Button Event"; - + public static const TOP_TOOLBAR:String = "Top Toolbar"; + public static const BOTTOM_TOOLBAR:String = "Bottom Toolbar"; /** * The ui component to add to the toolbar. */ public var button:IBbbToolbarComponent; + public var location:String = TOP_TOOLBAR; public function ToolbarButtonEvent(type:String, bubbles:Boolean=true, cancelable:Boolean=false) { diff --git a/bigbluebutton-client/src/org/bigbluebutton/main/model/modules/ModuleManager.as b/bigbluebutton-client/src/org/bigbluebutton/main/model/modules/ModuleManager.as index 1b08994d342b22b187344975827a9a36f6276973..9fbdee58cfab5ae7fa7bf513d6fd93dc218cc656 100755 --- a/bigbluebutton-client/src/org/bigbluebutton/main/model/modules/ModuleManager.as +++ b/bigbluebutton-client/src/org/bigbluebutton/main/model/modules/ModuleManager.as @@ -103,7 +103,7 @@ package org.bigbluebutton.main.model.modules if (m != null) { LogUtil.debug('Stopping ' + name); var bbb:IBigBlueButtonModule = m.module as IBigBlueButtonModule; - if(bbb == null) { //Still has null object refrence on logout sometimes. + if(bbb == null) { //Still has null object reference on logout sometimes. LogUtil.debug('Module ' + name + ' was null skipping'); return; } diff --git a/bigbluebutton-client/src/org/bigbluebutton/main/views/LanguageSelector.mxml b/bigbluebutton-client/src/org/bigbluebutton/main/views/LanguageSelector.mxml index 348676283b82febfce6f5b42a92c1e7ed8216413..4a6244560f049db9fb9848ad34fe4b7b7b9af82e 100755 --- a/bigbluebutton-client/src/org/bigbluebutton/main/views/LanguageSelector.mxml +++ b/bigbluebutton-client/src/org/bigbluebutton/main/views/LanguageSelector.mxml @@ -22,7 +22,7 @@ <mx:ComboBox xmlns:mx="http://www.adobe.com/2006/mxml" dataProvider="{ResourceUtil.getInstance().localeNames}" selectedIndex="{ResourceUtil.getInstance().localeIndex}" - change="changeLanguage()" rowCount="15" width="120"> + change="changeLanguage()" rowCount="15" width="120" height="22"> <mx:Script> <![CDATA[ import org.bigbluebutton.util.i18n.ResourceUtil; diff --git a/bigbluebutton-client/src/org/bigbluebutton/main/views/MainApplicationShell.mxml b/bigbluebutton-client/src/org/bigbluebutton/main/views/MainApplicationShell.mxml index 482c427ecf60b9322952d211fa463b98d7affb1e..2d7d8ed8e087f9113a7396bb7b6ad56672e71386 100755 --- a/bigbluebutton-client/src/org/bigbluebutton/main/views/MainApplicationShell.mxml +++ b/bigbluebutton-client/src/org/bigbluebutton/main/views/MainApplicationShell.mxml @@ -41,6 +41,8 @@ <mate:Listener type="{ConnectionFailedEvent.CONNECTION_REJECTED}" method="attemptReconnect" /> <mate:Listener type="configLoadedEvent" method="initOptions" /> <mate:Listener type="SHOW_MIC_SETTINGS" method="showMicSettings" /> + <mate:Listener type="{ToolbarButtonEvent.ADD}" method="handleAddToolbarComponent" /> + <mate:Listener type="{ToolbarButtonEvent.REMOVE}" method="handleRemoveToolbarComponent"/> <mx:Script> <![CDATA[ @@ -55,6 +57,7 @@ import mx.containers.TitleWindow; import mx.controls.Alert; import mx.core.FlexGlobals; + import mx.core.UIComponent; import mx.events.CloseEvent; import mx.managers.PopUpManager; @@ -64,6 +67,7 @@ import org.bigbluebutton.common.events.AddUIComponentToMainCanvas; import org.bigbluebutton.common.events.CloseWindowEvent; import org.bigbluebutton.common.events.OpenWindowEvent; + import org.bigbluebutton.common.events.ToolbarButtonEvent; import org.bigbluebutton.core.BBB; import org.bigbluebutton.main.events.AppVersionEvent; import org.bigbluebutton.main.events.BBBEvent; @@ -133,10 +137,10 @@ receivedConfigLocaleVer = true; appVersion = event.appVersion; localeVersion = event.localeVersion; - LogUtil.debug("Received locale version fron config.xml"); + LogUtil.debug("Received locale version from config.xml"); } else { receivedResourceLocaleVer = true; - LogUtil.debug("Received locale version fron locale file."); + LogUtil.debug("Received locale version from locale file."); } if (receivedConfigLocaleVer && receivedResourceLocaleVer) { @@ -298,20 +302,33 @@ Alert.show(evt.status, evt.module); } + private function handleAddToolbarComponent(event:ToolbarButtonEvent):void { + if (event.location == ToolbarButtonEvent.BOTTOM_TOOLBAR) { + addedBtns.addChild(event.button as UIComponent); +// realignButtons(); + } + } + + private function handleRemoveToolbarComponent(event:ToolbarButtonEvent):void { + if (addedBtns.contains(event.button as UIComponent)) + addedBtns.removeChild(event.button as UIComponent); + } + ]]> </mx:Script> - <views:MainToolbar id="toolbar" dock="true" width="100%" height="30" visible="{showToolbarOpt}" verticalAlign="middle" toolbarOptions="{layoutOptions}"/> + <views:MainToolbar id="toolbar" dock="true" width="100%" height="30" visible="{showToolbarOpt}" verticalAlign="middle" toolbarOptions="{layoutOptions}" paddingTop="4"/> <views:MainCanvas id="mdiCanvas" horizontalScrollPolicy="off" verticalScrollPolicy="off" effectsLib="{flexlib.mdi.effects.effectsLib.MDIVistaEffects}" width="100%" height="100%"> <views:LoadingBar id="progressBar" x="{this.width/2 - progressBar.width/2}" y="{this.height/2 - progressBar.height/2}" width="{this.width/2}" /> <views:BrandingLogo x="{this.width - 300}" y="{this.height - 300}" /> </views:MainCanvas> - <mx:ControlBar width="100%" height="20" paddingTop="0"> + <mx:ControlBar width="100%" height="24" paddingTop="0"> <mx:Label text="{ResourceUtil.getInstance().getString('bbb.mainshell.copyrightLabel2',[appVersion])}" id="copyrightLabel2"/> <mx:Spacer width="20"/> <mx:Spacer width="100%"/> <mx:Button width="20" height="20" visible="{layoutOptions.showLogButton}" toolTip="{ResourceUtil.getInstance().getString('bbb.mainshell.logBtn.toolTip')}" id="logBtn" icon="{logs_icon}" click="openLogWindow()" /> - <mx:Button width="20" height="20" visible="{layoutOptions.showResetLayout}" toolTip="{ResourceUtil.getInstance().getString('bbb.mainshell.resetLayoutBtn.toolTip')}" id="btnResetLayout" icon="{reset_layout_icon}" click="resetLayout()" /> + <!-- mx:Button width="20" height="20" visible="{layoutOptions.showResetLayout}" toolTip="{ResourceUtil.getInstance().getString('bbb.mainshell.resetLayoutBtn.toolTip')}" id="btnResetLayout" icon="{reset_layout_icon}" click="resetLayout()" /--> + <mx:HBox id="addedBtns" /> </mx:ControlBar> </mx:VBox> diff --git a/bigbluebutton-client/src/org/bigbluebutton/main/views/MainCanvas.mxml b/bigbluebutton-client/src/org/bigbluebutton/main/views/MainCanvas.mxml index 76440141fa2ee2c79bd9345249b69812869a5995..68fd1874a73bc4c0500bbe00fcb976dc9f58a483 100755 --- a/bigbluebutton-client/src/org/bigbluebutton/main/views/MainCanvas.mxml +++ b/bigbluebutton-client/src/org/bigbluebutton/main/views/MainCanvas.mxml @@ -201,75 +201,6 @@ windowManager.absPos(win, x, y); } - private function getVideoWindows():Array - { - var windows:Array = this.windowManager.getOpenWindowList(); - var videowindows:Array = new Array(); - for (var i:Number=0; i<windows.length; i++) { - var window:IBbbModuleWindow = windows[i] as IBbbModuleWindow; - var type:String = getType(window); - if (type == "PublishWindow" || type == "VideoWindow") - videowindows.push(window); - } - return videowindows; - } - - private function getType(window:IBbbModuleWindow):String { - return String(window).substr(String(window).lastIndexOf(".") + 1).match(/[a-zA-Z]+/).join(); - } - - public function layoutSmallVideos():void - { - var windows:Array = getVideoWindows(); - if (windows.length == 0) - return; - - var width:int = 166; - var height:int = 149; - - var rows:int = Math.ceil(this.height/height); - var winPerRow:int = Math.floor(windows.length/rows); - - // try to fit better the number of windows per row - winPerRow = Math.floor(this.width / width); - rows = Math.ceil(windows.length / winPerRow); - while (Math.ceil(windows.length / (winPerRow - 1)) == rows) { - winPerRow -= 1; - } - - var x:int = 0; - var y:int = this.height; - - for (var i:Number=0; i<windows.length; i++) { - var window:IBbbModuleWindow = windows[i] as IBbbModuleWindow; - var type:String = getType(window); - - (window as MDIWindow).width = width; - - var win:MDIWindow = window as MDIWindow; - if (win.minimized || win.maximized) - win.restore(); - windowManager.bringToFront(win); - - // change the row - if (i % winPerRow == 0) { - x = Math.floor((this.width - (width * winPerRow)) / 2); - y -= height; - } - - windowManager.absPos(win, x, y); - x += win.width; - } - } - - public function layoutPresenter():void - { - } - - public function layoutMeeting():void - { - } - ]]> </mx:Script> diff --git a/bigbluebutton-client/src/org/bigbluebutton/main/views/MainToolbar.mxml b/bigbluebutton-client/src/org/bigbluebutton/main/views/MainToolbar.mxml index 33702bd91e4d7541f0db38876be20e7ea5649d0a..cb7637ad0aca0a0c9652bbe0255f7b71d5577204 100755 --- a/bigbluebutton-client/src/org/bigbluebutton/main/views/MainToolbar.mxml +++ b/bigbluebutton-client/src/org/bigbluebutton/main/views/MainToolbar.mxml @@ -111,12 +111,15 @@ } private function handleAddToolbarButtonEvent(event:ToolbarButtonEvent):void { - addedBtns.addChild(event.button as UIComponent); - realignButtons(); + if (event.location == ToolbarButtonEvent.TOP_TOOLBAR) { + addedBtns.addChild(event.button as UIComponent); + realignButtons(); + } } private function handleRemoveToolbarButtonEvent(event:ToolbarButtonEvent):void { - addedBtns.removeChild(event.button as UIComponent); + if (addedBtns.contains(event.button as UIComponent)) + addedBtns.removeChild(event.button as UIComponent); } private function realignButtons():void{ @@ -153,11 +156,10 @@ ]]> </mx:Script> - <mx:HBox id="addedBtns"> - </mx:HBox> + <mx:HBox id="addedBtns"/> <mx:Spacer width="100%"/> <views:LanguageSelector id="langSelector" visible="false" /> - <mx:LinkButton id="helpBtn" label="{ResourceUtil.getInstance().getString('bbb.mainToolbar.helpBtn')}" visible="{showHelpBtn}" click="onHelpButtonClicked()"/> + <mx:LinkButton id="helpBtn" label="{ResourceUtil.getInstance().getString('bbb.mainToolbar.helpBtn')}" visible="{showHelpBtn}" click="onHelpButtonClicked()" height="22"/> <mx:Button label="{ResourceUtil.getInstance().getString('bbb.mainToolbar.logoutBtn')}" id="btnLogout" - toolTip="{ResourceUtil.getInstance().getString('bbb.mainToolbar.logoutBtn.toolTip')}" right="10" click="doLogout()"/> + toolTip="{ResourceUtil.getInstance().getString('bbb.mainToolbar.logoutBtn.toolTip')}" right="10" click="doLogout()" height="22"/> </mx:ApplicationControlBar> diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/layout/events/ConnectionEvent.as b/bigbluebutton-client/src/org/bigbluebutton/modules/layout/events/ConnectionEvent.as new file mode 100755 index 0000000000000000000000000000000000000000..2efb56d72d438361056967793f86bf96c9b92afb --- /dev/null +++ b/bigbluebutton-client/src/org/bigbluebutton/modules/layout/events/ConnectionEvent.as @@ -0,0 +1,35 @@ +/** +* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/ +* +* Copyright (c) 2010 BigBlueButton Inc. and by respective authors (see below). +* +* This program is free software; you can redistribute it and/or modify it under the +* terms of the GNU Lesser General Public License as published by the Free Software +* Foundation; either version 2.1 of the License, or (at your option) any later +* version. +* +* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY +* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License along +* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>. +* +*/ +package org.bigbluebutton.modules.layout.events +{ + import flash.events.Event; + + public class ConnectionEvent extends Event + { + public static const CONNECT_EVENT:String = 'CONNECT_EVENT'; + public var success:Boolean = false; + public var errors:Array; + + public function ConnectionEvent(type:String=CONNECT_EVENT, bubbles:Boolean=false, cancelable:Boolean=false) + { + super(type, bubbles, cancelable); + } + + } +} \ No newline at end of file diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/layout/events/LayoutEvent.as b/bigbluebutton-client/src/org/bigbluebutton/modules/layout/events/LayoutEvent.as new file mode 100644 index 0000000000000000000000000000000000000000..d66559b3a9451da003376a67c3877cceda335ade --- /dev/null +++ b/bigbluebutton-client/src/org/bigbluebutton/modules/layout/events/LayoutEvent.as @@ -0,0 +1,47 @@ +/** + * BigBlueButton open source conferencing system - http://www.bigbluebutton.org/ + * + * Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below). + * + * This program is free software; you can redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License as published by the Free Software + * Foundation; either version 2.1 of the License, or (at your option) any later + * version. + * + * BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along + * with BigBlueButton; if not, see <http://www.gnu.org/licenses/>. + * + * Author: Felipe Cecagno <felipe@mconf.org> + */ +package org.bigbluebutton.modules.layout.events +{ + import flash.events.Event; + + public class LayoutEvent extends Event + { + public static const REMOTE_LOCK_LAYOUT_EVENT:String = 'REMOTE_LOCK_LAYOUT_EVENT'; + public static const REMOTE_UNLOCK_LAYOUT_EVENT:String = 'REMOTE_UNLOCK_LAYOUT_EVENT'; + + public static const LOCK_LAYOUT_EVENT:String = 'LOCK_LAYOUT_EVENT'; + public static const UNLOCK_LAYOUT_EVENT:String = 'UNLOCK_LAYOUT_EVENT'; + public static const STOP_LAYOUT_MODULE_EVENT:String = 'STOP_LAYOUT_MODULE_EVENT'; + public static const VIEW_INITIALIZED_EVENT:String = 'VIEW_INITIALIZED_EVENT'; + + public static const SAVE_LAYOUTS_EVENT:String = 'SAVE_LAYOUTS_EVENT'; + public static const LOAD_LAYOUTS_EVENT:String = 'LOAD_LAYOUTS_EVENT'; + public static const ADD_CURRENT_LAYOUT_EVENT:String = 'ADD_CURRENT_LAYOUT_EVENT'; + public static const FILE_LOADED_SUCCESSFULLY_EVENT:String = 'FILE_LOADED_SUCCESSFULLY_EVENT'; + public static const APPLY_DEFAULT_LAYOUT_EVENT:String = 'APPLY_DEFAULT_LAYOUT_EVENT'; + public static const INVALIDATE_LAYOUT_EVENT:String = 'INVALIDATE_LAYOUT_EVENT'; + + public function LayoutEvent(type:String, bubbles:Boolean=true, cancelable:Boolean=false) + { + super(type, bubbles, cancelable); + } + + } +} \ No newline at end of file diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/layout/events/LayoutsLoadedEvent.as b/bigbluebutton-client/src/org/bigbluebutton/modules/layout/events/LayoutsLoadedEvent.as new file mode 100755 index 0000000000000000000000000000000000000000..ba5a85db3e358d32d9951237c456a9fb5bea6e29 --- /dev/null +++ b/bigbluebutton-client/src/org/bigbluebutton/modules/layout/events/LayoutsLoadedEvent.as @@ -0,0 +1,38 @@ +/** + * BigBlueButton open source conferencing system - http://www.bigbluebutton.org/ + * + * Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below). + * + * This program is free software; you can redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License as published by the Free Software + * Foundation; either version 2.1 of the License, or (at your option) any later + * version. + * + * BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along + * with BigBlueButton; if not, see <http://www.gnu.org/licenses/>. + * + * Author: Felipe Cecagno <felipe@mconf.org> + */ +package org.bigbluebutton.modules.layout.events +{ + import flash.events.Event; + import org.bigbluebutton.modules.layout.model.LayoutDefinitionFile; + + public class LayoutsLoadedEvent extends Event + { + public static const LAYOUTS_LOADED_EVENT:String = "LAYOUTS_LOADED_EVENT"; + public var layouts:LayoutDefinitionFile = null; + public var success:Boolean = false; + public var error:TypeError = null; + + public function LayoutsLoadedEvent(type:String = LAYOUTS_LOADED_EVENT) + { + super(type, true, false); + } + + } +} diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/layout/events/RedefineLayoutEvent.as b/bigbluebutton-client/src/org/bigbluebutton/modules/layout/events/RedefineLayoutEvent.as new file mode 100755 index 0000000000000000000000000000000000000000..ed3a328fe6a633e6d22b81b327469d791015b22c --- /dev/null +++ b/bigbluebutton-client/src/org/bigbluebutton/modules/layout/events/RedefineLayoutEvent.as @@ -0,0 +1,38 @@ +/** + * BigBlueButton open source conferencing system - http://www.bigbluebutton.org/ + * + * Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below). + * + * This program is free software; you can redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License as published by the Free Software + * Foundation; either version 2.1 of the License, or (at your option) any later + * version. + * + * BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along + * with BigBlueButton; if not, see <http://www.gnu.org/licenses/>. + * + * Author: Felipe Cecagno <felipe@mconf.org> + */ +package org.bigbluebutton.modules.layout.events +{ + import flash.events.Event; + + import org.bigbluebutton.modules.layout.model.LayoutDefinition; + + public class RedefineLayoutEvent extends Event + { + public static const REDEFINE_LAYOUT_EVENT:String = "REDEFINE_LAYOUT_EVENT"; + public var layout:LayoutDefinition = null; + public var remote:Boolean = false; + + public function RedefineLayoutEvent(type:String = REDEFINE_LAYOUT_EVENT) + { + super(type, true, false); + } + + } +} diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/layout/events/StartLayoutModuleEvent.as b/bigbluebutton-client/src/org/bigbluebutton/modules/layout/events/StartLayoutModuleEvent.as new file mode 100644 index 0000000000000000000000000000000000000000..eaa21782762213071d3e37070eef3608ab9f448d --- /dev/null +++ b/bigbluebutton-client/src/org/bigbluebutton/modules/layout/events/StartLayoutModuleEvent.as @@ -0,0 +1,35 @@ +/** + * BigBlueButton open source conferencing system - http://www.bigbluebutton.org/ + * + * Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below). + * + * This program is free software; you can redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License as published by the Free Software + * Foundation; either version 2.1 of the License, or (at your option) any later + * version. + * + * BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along + * with BigBlueButton; if not, see <http://www.gnu.org/licenses/>. + * + * Author: Felipe Cecagno <felipe@mconf.org> + */ +package org.bigbluebutton.modules.layout.events +{ + import flash.events.Event; + + public class StartLayoutModuleEvent extends Event + { + public static const START_LAYOUT_MODULE_EVENT:String = 'START_LAYOUT_MODULE_EVENT'; + public var attributes:Object; + + public function StartLayoutModuleEvent(type:String=START_LAYOUT_MODULE_EVENT, bubbles:Boolean=true, cancelable:Boolean=false) + { + super(type, bubbles, cancelable); + } + + } +} \ No newline at end of file diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/layout/events/UpdateLayoutEvent.as b/bigbluebutton-client/src/org/bigbluebutton/modules/layout/events/UpdateLayoutEvent.as new file mode 100644 index 0000000000000000000000000000000000000000..c9cbd34bb074206af9074762cc8dfc19b151bff4 --- /dev/null +++ b/bigbluebutton-client/src/org/bigbluebutton/modules/layout/events/UpdateLayoutEvent.as @@ -0,0 +1,37 @@ +/** + * BigBlueButton open source conferencing system - http://www.bigbluebutton.org/ + * + * Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below). + * + * This program is free software; you can redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License as published by the Free Software + * Foundation; either version 2.1 of the License, or (at your option) any later + * version. + * + * BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along + * with BigBlueButton; if not, see <http://www.gnu.org/licenses/>. + * + * Author: Felipe Cecagno <felipe@mconf.org> + */ +package org.bigbluebutton.modules.layout.events +{ + import flash.events.Event; + + import org.bigbluebutton.modules.layout.model.LayoutDefinition; + + public class UpdateLayoutEvent extends Event + { + public static const UPDATE_LAYOUT_EVENT:String = 'UPDATE_LAYOUT_EVENT'; + public var layout:LayoutDefinition; + + public function UpdateLayoutEvent(type:String=UPDATE_LAYOUT_EVENT, bubbles:Boolean=true, cancelable:Boolean=false) + { + super(type, bubbles, cancelable); + } + + } +} \ No newline at end of file diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/layout/events/ViewInitializedEvent.as b/bigbluebutton-client/src/org/bigbluebutton/modules/layout/events/ViewInitializedEvent.as new file mode 100755 index 0000000000000000000000000000000000000000..727e0047073b8dd37d7623d814c2140895cb9abe --- /dev/null +++ b/bigbluebutton-client/src/org/bigbluebutton/modules/layout/events/ViewInitializedEvent.as @@ -0,0 +1,37 @@ +/** + * BigBlueButton open source conferencing system - http://www.bigbluebutton.org/ + * + * Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below). + * + * This program is free software; you can redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License as published by the Free Software + * Foundation; either version 2.1 of the License, or (at your option) any later + * version. + * + * BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along + * with BigBlueButton; if not, see <http://www.gnu.org/licenses/>. + * + * Author: Felipe Cecagno <felipe@mconf.org> + */ +package org.bigbluebutton.modules.layout.events +{ + import flash.events.Event; + + import flexlib.mdi.containers.MDICanvas; + + public class ViewInitializedEvent extends Event + { + public static const VIEW_INITIALIZED_EVENT:String = "VIEW_INITIALIZED_EVENT"; + public var canvas:MDICanvas = null; + + public function ViewInitializedEvent(type:String = VIEW_INITIALIZED_EVENT) + { + super(type, true, false); + } + + } +} diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/layout/managers/LayoutManager.as b/bigbluebutton-client/src/org/bigbluebutton/modules/layout/managers/LayoutManager.as new file mode 100755 index 0000000000000000000000000000000000000000..1277741033dd137d78cd2c4fdbc5b3df2193843a --- /dev/null +++ b/bigbluebutton-client/src/org/bigbluebutton/modules/layout/managers/LayoutManager.as @@ -0,0 +1,306 @@ +/** + * BigBlueButton open source conferencing system - http://www.bigbluebutton.org/ + * + * Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below). + * + * This program is free software; you can redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License as published by the Free Software + * Foundation; either version 2.1 of the License, or (at your option) any later + * version. + * + * BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along + * with BigBlueButton; if not, see <http://www.gnu.org/licenses/>. + * + * Author: Felipe Cecagno <felipe@mconf.org> + */ +package org.bigbluebutton.modules.layout.managers +{ + import com.asfusion.mate.events.Dispatcher; + + import flash.events.Event; + import flash.events.EventDispatcher; + import flash.events.TimerEvent; + import flash.net.FileReference; + import flash.net.URLLoader; + import flash.net.URLRequest; + import flash.utils.Dictionary; + import flash.utils.Timer; + + import flexlib.mdi.containers.MDICanvas; + import flexlib.mdi.containers.MDIWindow; + import flexlib.mdi.events.MDIManagerEvent; + + import mx.controls.Alert; + import mx.events.ResizeEvent; + + import org.bigbluebutton.common.LogUtil; + import org.bigbluebutton.core.EventBroadcaster; + import org.bigbluebutton.core.managers.UserManager; + import org.bigbluebutton.core.model.Config; + import org.bigbluebutton.modules.layout.events.LayoutEvent; + import org.bigbluebutton.modules.layout.events.LayoutsLoadedEvent; + import org.bigbluebutton.modules.layout.events.UpdateLayoutEvent; + import org.bigbluebutton.modules.layout.model.LayoutDefinition; + import org.bigbluebutton.modules.layout.model.LayoutDefinitionFile; + import org.bigbluebutton.modules.layout.model.LayoutLoader; + import org.bigbluebutton.modules.layout.model.WindowLayout; + import org.bigbluebutton.modules.layout.events.RedefineLayoutEvent; + import org.bigbluebutton.util.i18n.ResourceUtil; + + public class LayoutManager extends EventDispatcher { + private var _layouts:LayoutDefinitionFile = null; + private var _canvas:MDICanvas = null; + private var _globalDispatcher:Dispatcher = new Dispatcher(); + private var _locked:Boolean = false; + private var _currentLayout:LayoutDefinition = null; + private var _detectContainerChange:Boolean = true; + private var _containerDeactivated:Boolean = false; + private var _sendCurrentLayoutUpdateTimer:Timer = new Timer(500,1); + private var _applyCurrentLayoutTimer:Timer = new Timer(150,1); + private var _customLayoutsCount:int = 0; + private var _eventsToDelay:Array = new Array(MDIManagerEvent.WINDOW_RESTORE, + MDIManagerEvent.WINDOW_MINIMIZE, + MDIManagerEvent.WINDOW_MAXIMIZE); + + + public function LayoutManager() { + _applyCurrentLayoutTimer.addEventListener(TimerEvent.TIMER, function(e:TimerEvent):void { + applyLayout(_currentLayout); + }); + _sendCurrentLayoutUpdateTimer.addEventListener(TimerEvent.TIMER, function(e:TimerEvent):void { + sendLayoutUpdate(updateCurrentLayout()); + }); + } + + public function loadServerLayouts(layoutUrl:String):void { + LogUtil.debug("LayoutManager: loading server layouts from " + layoutUrl); + var loader:LayoutLoader = new LayoutLoader(); + loader.addEventListener(LayoutsLoadedEvent.LAYOUTS_LOADED_EVENT, function(e:LayoutsLoadedEvent):void { + if (e.success) { + _layouts = e.layouts; + LogUtil.debug("LayoutManager: layouts loaded successfully"); + } else { + LogUtil.error("LayoutManager: layouts not loaded (" + e.error.message + ")"); + } + }); + loader.loadFromUrl(layoutUrl); + } + + public function saveLayoutsToFile():void { + var _fileRef:FileReference = new FileReference(); + _fileRef.addEventListener(Event.COMPLETE, function(e:Event):void { + Alert.show(ResourceUtil.getInstance().getString('bbb.layout.save.complete'), "", Alert.OK, _canvas); + }); + _fileRef.save(_layouts.toXml().toXMLString(), "layouts.xml"); + } + + public function loadLayoutsFromFile():void { + var loader:LayoutLoader = new LayoutLoader(); + loader.addEventListener(LayoutsLoadedEvent.LAYOUTS_LOADED_EVENT, function(e:LayoutsLoadedEvent):void { + if (e.success) { + _layouts = e.layouts; + + /* + * \TODO why do I need to create a new Event for this? + */ + var layoutsLoaded:LayoutsLoadedEvent = new LayoutsLoadedEvent(); + layoutsLoaded.layouts = _layouts; + _globalDispatcher.dispatchEvent(layoutsLoaded); + /* + * it will update the ComboBox label, and will go back to this class + * to apply the default layout + */ + _globalDispatcher.dispatchEvent(new LayoutEvent(LayoutEvent.APPLY_DEFAULT_LAYOUT_EVENT)); + + Alert.show(ResourceUtil.getInstance().getString('bbb.layout.load.complete'), "", Alert.OK, _canvas); + } else + Alert.show(ResourceUtil.getInstance().getString('bbb.layout.load.failed'), "", Alert.OK, _canvas); + }); + loader.loadFromLocalFile(); + } + + public function addCurrentLayoutToList():void { + var remotePrefix:String = "[" + ResourceUtil.getInstance().getString('bbb.layout.combo.remote') + "] "; + // starts with + var isRemoteLayout:Boolean = (_currentLayout.name.indexOf(remotePrefix) == 0); + if (isRemoteLayout) { + // remove the remote prefix + _currentLayout.name = _currentLayout.name.substring(remotePrefix.length); + // if it's a remote custom layout, just remove the counter + if (_currentLayout.name.indexOf(ResourceUtil.getInstance().getString('bbb.layout.combo.customName')) == 0) + _currentLayout.name = ResourceUtil.getInstance().getString('bbb.layout.combo.customName'); + } + + // only add a layout to the list if it's a custom layout + if (_currentLayout.name == ResourceUtil.getInstance().getString('bbb.layout.combo.customName')) { + _currentLayout.name += " " + (++_customLayoutsCount); + _layouts.push(_currentLayout); + var layoutsLoaded:LayoutsLoadedEvent = new LayoutsLoadedEvent(); + layoutsLoaded.layouts = _layouts; + _globalDispatcher.dispatchEvent(layoutsLoaded); + + var redefineLayout:RedefineLayoutEvent = new RedefineLayoutEvent(); + redefineLayout.layout = _currentLayout; + // this is to force LayoutCombo to update the current label + redefineLayout.remote = true; + _globalDispatcher.dispatchEvent(redefineLayout); + } + } + + public function setCanvas(canvas:MDICanvas):void { + _canvas = canvas; + /* + * it should be dispatched when the layouts get loaded, but + * the view is not ready at that point to receive the layouts + * and populate the ComboBox + */ + if (_layouts != null) { + var e:LayoutsLoadedEvent = new LayoutsLoadedEvent(); + e.layouts = _layouts; + _globalDispatcher.dispatchEvent(e); + } + + // this is to detect changes on the container + _canvas.windowManager.container.addEventListener(ResizeEvent.RESIZE, onContainerResized); +// _canvas.windowManager.container.addEventListener(Event.ACTIVATE, onContainerActivated); +// _canvas.windowManager.container.addEventListener(Event.DEACTIVATE, onContainerDeactivated); + + _canvas.windowManager.addEventListener(MDIManagerEvent.WINDOW_RESIZE_END, onActionOverWindowFinished); + _canvas.windowManager.addEventListener(MDIManagerEvent.WINDOW_DRAG_END, onActionOverWindowFinished); + _canvas.windowManager.addEventListener(MDIManagerEvent.WINDOW_MINIMIZE, onActionOverWindowFinished); + _canvas.windowManager.addEventListener(MDIManagerEvent.WINDOW_MAXIMIZE, onActionOverWindowFinished); + _canvas.windowManager.addEventListener(MDIManagerEvent.WINDOW_RESTORE, onActionOverWindowFinished); + _canvas.windowManager.addEventListener(MDIManagerEvent.WINDOW_ADD, function(e:MDIManagerEvent):void { + checkPermissionsOverWindow(e.window); + applyLayout(_currentLayout); + }); + + _canvas.windowManager.addEventListener(MDIManagerEvent.WINDOW_FOCUS_START, function(e:MDIManagerEvent):void { + OrderManager.getInstance().bringToFront(e.window); + }); + for each (var window:MDIWindow in _canvas.windowManager.windowList.reverse()) { + OrderManager.getInstance().bringToFront(window); + } + } + + public function applyDefaultLayout():void { + applyLayout(_layouts.getDefault()); + sendLayoutUpdate(_currentLayout); + } + + public function lockLayout():void { + _locked = true; + LogUtil.debug("LayoutManager: layout locked by myself"); + sendLayoutUpdate(_currentLayout); + } + + private function sendLayoutUpdate(layout:LayoutDefinition):void { + if (_locked && UserManager.getInstance().getConference().amIModerator()) { + LogUtil.debug("LayoutManager: sending layout to remotes"); + var e:UpdateLayoutEvent = new UpdateLayoutEvent(); + e.layout = layout; + _globalDispatcher.dispatchEvent(e); + } + } + + private function applyLayout(layout:LayoutDefinition):void { + _detectContainerChange = false; + if (layout != null) + layout.applyToCanvas(_canvas); + updateCurrentLayout(layout); + _detectContainerChange = true; + } + + public function redefineLayout(e:RedefineLayoutEvent):void { + var layout:LayoutDefinition = e.layout; + applyLayout(layout); + if (!e.remote) + sendLayoutUpdate(layout); + } + + public function remoteLockLayout():void { + LogUtil.debug("LayoutManager: remote lock received"); + _locked = true; + checkPermissionsOverWindow(); + } + + public function remoteUnlockLayout():void { + LogUtil.debug("LayoutManager: remote unlock received"); + _locked = false; + checkPermissionsOverWindow(); + } + + private function checkPermissionsOverWindow(window:MDIWindow=null):void { + if (window != null) { + if (!UserManager.getInstance().getConference().amIModerator() + && !LayoutDefinition.ignoreWindow(window)) { + window.draggable + = window.resizable + = window.showControls + = !_locked; + } + } else { + for each (window in _canvas.windowManager.windowList) { + checkPermissionsOverWindow(window); + } + } + } + + private function onContainerResized(e:ResizeEvent):void { + /* + * the main canvas has been resized + * while the user is resizing the window, this event is dispatched + * multiple times, so we use a timer to re-apply the current layout + * only once, when the user finished his action + */ + _applyCurrentLayoutTimer.reset(); + _applyCurrentLayoutTimer.start(); + } + +// private function onContainerActivated(e:Event):void { +// printSomething("onContainerActivated"); +// } +// +// private function onContainerDeactivated(e:Event = null):void { +// printSomething("onContainerDeactivated"); +// } + + private function onActionOverWindowFinished(e:MDIManagerEvent):void { + if (LayoutDefinition.ignoreWindow(e.window)) + return; + + checkPermissionsOverWindow(e.window); + if (_detectContainerChange) { + _globalDispatcher.dispatchEvent(new LayoutEvent(LayoutEvent.INVALIDATE_LAYOUT_EVENT)); + /* + * some events related to animated actions must be delayed because if it's not the + * current layout doesn't get properly updated + */ + if (_eventsToDelay.indexOf(e.type) != -1) { + LogUtil.debug("LayoutManager: waiting the end of the animation to update the current layout"); + _sendCurrentLayoutUpdateTimer.reset(); + _sendCurrentLayoutUpdateTimer.start(); + } else { + sendLayoutUpdate(updateCurrentLayout()); + } + } + } + + private function updateCurrentLayout(layout:LayoutDefinition=null):LayoutDefinition { + _currentLayout = (layout != null? layout: LayoutDefinition.getLayout(_canvas, ResourceUtil.getInstance().getString('bbb.layout.combo.customName'))); + return _currentLayout; + } + + /* + * this is because a unique layout may have multiple definitions depending + * on the role of the participant + */ + public function presenterChanged():void { + applyLayout(_currentLayout); + } + } +} \ No newline at end of file diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/layout/managers/OrderManager.as b/bigbluebutton-client/src/org/bigbluebutton/modules/layout/managers/OrderManager.as new file mode 100644 index 0000000000000000000000000000000000000000..f6470fd687d80d0dc0d7c00de2d73f6ed7a4d70d --- /dev/null +++ b/bigbluebutton-client/src/org/bigbluebutton/modules/layout/managers/OrderManager.as @@ -0,0 +1,110 @@ +/** + * BigBlueButton open source conferencing system - http://www.bigbluebutton.org/ + * + * Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below). + * + * This program is free software; you can redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License as published by the Free Software + * Foundation; either version 2.1 of the License, or (at your option) any later + * version. + * + * BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along + * with BigBlueButton; if not, see <http://www.gnu.org/licenses/>. + * + * Author: Felipe Cecagno <felipe@mconf.org> + */ +package org.bigbluebutton.modules.layout.managers +{ + + import flash.utils.Dictionary; + + import flexlib.mdi.containers.MDIWindow; + + import org.bigbluebutton.common.LogUtil; + import org.bigbluebutton.modules.layout.model.LayoutDefinition; + import org.bigbluebutton.modules.layout.model.WindowLayout; + + public class OrderManager { + private static var _instance:OrderManager = null; + private var _windowsOrder:Dictionary = new Dictionary(); + + /** + * This class is a singleton. Please initialize it using the getInstance() method. + * + */ + public function OrderManager(enforcer:SingletonEnforcer) { + if (enforcer == null){ + throw new Error("There can only be 1 OrderManager instance"); + } + initialize(); + } + + private function initialize():void{ + } + + /** + * Return the single instance of the UserManager class + */ + public static function getInstance():OrderManager{ + if (_instance == null){ + _instance = new OrderManager(new SingletonEnforcer()); + } + return _instance; + } + + public function bringToFront(window:MDIWindow):void { + if (LayoutDefinition.ignoreWindow(window)) + return; + + var type:String = WindowLayout.getType(window); + var currentOrder:int = int.MAX_VALUE; + if (_windowsOrder.hasOwnProperty(type)) + currentOrder = _windowsOrder[type].order; + + for (var key:Object in _windowsOrder) { + var tmpOrder:int = _windowsOrder[key].order; + if (tmpOrder <= currentOrder) + _windowsOrder[key].order = tmpOrder + 1; +// LogUtil.debug("==========> " + key + " order: " + _windowsOrder[key].order); + } + _windowsOrder[type] = { order: 0 }; + +// if (_windowsOrder.length > window.windowManager.windowList.length) { +// var openWindows:Array = new Array(); +// for each (var tmp:MDIWindow in window.windowManager.windowList) { +// openWindows.push(WindowLayout.getType(tmp)); +// } +// for (key in _windowsOrder) { +// if (openWindows.indexOf(key) == -1) { +// LogUtil.debug("Removing order for " + key); +// delete _windowsOrder[key]; +// } +// } +// } +// LogUtil.debug("Manipulating " + type); +// for (key in _windowsOrder) { +// LogUtil.debug("=====> " + key + " order: " + _windowsOrder[key].order); +// } + +// window.windowManager.bringToFront(window); + } + + public function getOrderByType(type:String):int { + if (_windowsOrder.hasOwnProperty(type)) + return _windowsOrder[type].order; + else + return -1; + } + + public function getOrderByRef(window:MDIWindow):int { + return getOrderByType(WindowLayout.getType(window)); + } + + } +} + +class SingletonEnforcer{} \ No newline at end of file diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/layout/maps/LayoutEventMap.mxml b/bigbluebutton-client/src/org/bigbluebutton/modules/layout/maps/LayoutEventMap.mxml new file mode 100755 index 0000000000000000000000000000000000000000..97849f5acdb6502c4393862c9e604fec24bb91cf --- /dev/null +++ b/bigbluebutton-client/src/org/bigbluebutton/modules/layout/maps/LayoutEventMap.mxml @@ -0,0 +1,125 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + BigBlueButton open source conferencing system - http://www.bigbluebutton.org/ + + Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below). + + This program is free software; you can redistribute it and/or modify it under the + terms of the GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1 of the License, or (at your option) any later + version. + + BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along + with BigBlueButton; if not, see <http://www.gnu.org/licenses/>. + + Author: Felipe Cecagno <felipe@mconf.org> + + $Id: $ +--> +<EventMap xmlns:mx="http://www.adobe.com/2006/mxml" xmlns="http://mate.asfusion.com/"> + <mx:Script> + <![CDATA[ + import mx.events.FlexEvent; + + import org.bigbluebutton.main.events.MadePresenterEvent; + import org.bigbluebutton.modules.layout.events.ConnectionEvent; + import org.bigbluebutton.modules.layout.events.LayoutEvent; + import org.bigbluebutton.modules.layout.events.UpdateLayoutEvent; + import org.bigbluebutton.modules.layout.events.StartLayoutModuleEvent; + import org.bigbluebutton.modules.layout.events.ViewInitializedEvent; + import org.bigbluebutton.modules.layout.events.RedefineLayoutEvent; + import org.bigbluebutton.modules.layout.managers.LayoutManager; + import org.bigbluebutton.modules.layout.services.LayoutService; + ]]> + </mx:Script> + + <EventHandlers type="{FlexEvent.PREINITIALIZE}"> + <ObjectBuilder generator="{LayoutEventMapDelegate}" constructorArguments="{scope.dispatcher}" /> + <ObjectBuilder generator="{LayoutManager}" /> + </EventHandlers> + + <EventHandlers type="{StartLayoutModuleEvent.START_LAYOUT_MODULE_EVENT}"> + <ObjectBuilder generator="{LayoutService}" constructorArguments="{event.attributes}" /> + <MethodInvoker generator="{LayoutEventMapDelegate}" method="startModule" arguments="{event.attributes}" /> + <MethodInvoker generator="{LayoutManager}" method="loadServerLayouts" arguments="{event.attributes.layoutConfig}" /> + </EventHandlers> + + <EventHandlers type="{ViewInitializedEvent.VIEW_INITIALIZED_EVENT}"> + <MethodInvoker generator="{LayoutManager}" method="setCanvas" arguments="{event.canvas}" /> + <MethodInvoker generator="{LayoutService}" method="join" /> + </EventHandlers> + + <EventHandlers type="{LayoutEvent.STOP_LAYOUT_MODULE_EVENT}"> + <MethodInvoker generator="{LayoutService}" method="leave" /> + <MethodInvoker generator="{LayoutEventMapDelegate}" method="stopModule" /> + </EventHandlers> + + <EventHandlers type="{ConnectionEvent.CONNECT_EVENT}"> + <MethodInvoker generator="{LayoutService}" method="initLayout" arguments="{event.success}" /> + </EventHandlers> + + <EventHandlers type="{LayoutEvent.APPLY_DEFAULT_LAYOUT_EVENT}"> + <MethodInvoker generator="{LayoutManager}" method="applyDefaultLayout" /> + </EventHandlers> + + <EventHandlers type="{LayoutEvent.LOCK_LAYOUT_EVENT}"> + <MethodInvoker generator="{LayoutManager}" method="lockLayout" /> + </EventHandlers> + + <EventHandlers type="{LayoutEvent.UNLOCK_LAYOUT_EVENT}"> + <MethodInvoker generator="{LayoutService}" method="unlockLayout" /> + </EventHandlers> + + <EventHandlers type="{UpdateLayoutEvent.UPDATE_LAYOUT_EVENT}"> + <MethodInvoker generator="{LayoutService}" method="lockLayout" arguments="{event.layout}" /> + </EventHandlers> + + <EventHandlers type="{RedefineLayoutEvent.REDEFINE_LAYOUT_EVENT}"> + <MethodInvoker generator="{LayoutManager}" method="redefineLayout" arguments="{event}" /> + </EventHandlers> + + <EventHandlers type="{LayoutEvent.SAVE_LAYOUTS_EVENT}"> + <MethodInvoker generator="{LayoutManager}" method="saveLayoutsToFile" /> + </EventHandlers> + + <EventHandlers type="{LayoutEvent.LOAD_LAYOUTS_EVENT}"> + <MethodInvoker generator="{LayoutManager}" method="loadLayoutsFromFile" /> + </EventHandlers> + + <EventHandlers type="{LayoutEvent.ADD_CURRENT_LAYOUT_EVENT}"> + <MethodInvoker generator="{LayoutManager}" method="addCurrentLayoutToList" /> + </EventHandlers> + + <EventHandlers type="{LayoutEvent.REMOTE_LOCK_LAYOUT_EVENT}"> + <MethodInvoker generator="{LayoutManager}" method="remoteLockLayout" /> + </EventHandlers> + + <EventHandlers type="{LayoutEvent.REMOTE_UNLOCK_LAYOUT_EVENT}"> + <MethodInvoker generator="{LayoutManager}" method="remoteUnlockLayout" /> + </EventHandlers> + + <EventHandlers type="{MadePresenterEvent.SWITCH_TO_VIEWER_MODE}"> + <MethodInvoker generator="{LayoutManager}" method="presenterChanged" /> + </EventHandlers> + + <EventHandlers type="{MadePresenterEvent.SWITCH_TO_PRESENTER_MODE}"> + <MethodInvoker generator="{LayoutManager}" method="presenterChanged" /> + </EventHandlers> + + <!-- EventHandlers type="{FlexEvent.CREATION_COMPLETE}"> + <MethodInvoker generator="{LayoutManager}" method="printSomething" arguments="{FlexEvent.CREATION_COMPLETE}" /> + </EventHandlers> + + <EventHandlers type="{FlexEvent.CONTENT_CREATION_COMPLETE}"> + <MethodInvoker generator="{LayoutManager}" method="printSomething" arguments="{FlexEvent.CONTENT_CREATION_COMPLETE}" /> + </EventHandlers> + + <EventHandlers type="{FlexEvent.INIT_COMPLETE}"> + <MethodInvoker generator="{LayoutManager}" method="printSomething" arguments="{FlexEvent.INIT_COMPLETE}" /> + </EventHandlers--> + +</EventMap> \ No newline at end of file diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/layout/maps/LayoutEventMapDelegate.mxml b/bigbluebutton-client/src/org/bigbluebutton/modules/layout/maps/LayoutEventMapDelegate.mxml new file mode 100755 index 0000000000000000000000000000000000000000..5be9e3935c03f025410247ad7a50bdd57a7b0913 --- /dev/null +++ b/bigbluebutton-client/src/org/bigbluebutton/modules/layout/maps/LayoutEventMapDelegate.mxml @@ -0,0 +1,52 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + BigBlueButton open source conferencing system - http://www.bigbluebutton.org/ + + Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below). + + This program is free software; you can redistribute it and/or modify it under the + terms of the GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1 of the License, or (at your option) any later + version. + + BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along + with BigBlueButton; if not, see <http://www.gnu.org/licenses/>. + + Author: Felipe Cecagno <felipe@mconf.org> + + $Id: $ +--> +<EventMap xmlns:mx="http://www.adobe.com/2006/mxml" xmlns="http://mate.asfusion.com/"> + <mx:Script> + <![CDATA[ + import com.asfusion.mate.events.Dispatcher; + + import org.bigbluebutton.common.LogUtil; + import org.bigbluebutton.common.events.ToolbarButtonEvent; + import org.bigbluebutton.modules.layout.views.ToolbarComponent; + + private var _attributes:Object; + private var _globalDispatcher:Dispatcher = new Dispatcher(); + + public function stopModule():void { + } + + public function startModule(attributes:Object):void { + _attributes = attributes; + + var layoutComponent:ToolbarComponent = new ToolbarComponent(); + // using the negation will keep it enabled if the property is not there + layoutComponent.enableEdit = !(_attributes.enableEdit == "false"); + + var event:ToolbarButtonEvent = new ToolbarButtonEvent(ToolbarButtonEvent.ADD); + event.button = layoutComponent; + event.location = ToolbarButtonEvent.BOTTOM_TOOLBAR; + _globalDispatcher.dispatchEvent(event); + } + ]]> + </mx:Script> +</EventMap> \ No newline at end of file diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/layout/model/LayoutDefinition.as b/bigbluebutton-client/src/org/bigbluebutton/modules/layout/model/LayoutDefinition.as new file mode 100644 index 0000000000000000000000000000000000000000..7d40db860ff10e11676790dacd07a5df574bffe9 --- /dev/null +++ b/bigbluebutton-client/src/org/bigbluebutton/modules/layout/model/LayoutDefinition.as @@ -0,0 +1,220 @@ +/** + * BigBlueButton open source conferencing system - http://www.bigbluebutton.org/ + * + * Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below). + * + * This program is free software; you can redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License as published by the Free Software + * Foundation; either version 2.1 of the License, or (at your option) any later + * version. + * + * BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along + * with BigBlueButton; if not, see <http://www.gnu.org/licenses/>. + * + * Author: Felipe Cecagno <felipe@mconf.org> + */ +package org.bigbluebutton.modules.layout.model { + + public class LayoutDefinition { + + import flash.utils.Dictionary; + import flexlib.mdi.containers.MDICanvas; + import flexlib.mdi.containers.MDIWindow; + + import org.bigbluebutton.common.LogUtil; + import org.bigbluebutton.common.Role; + import org.bigbluebutton.core.managers.UserManager; + import org.bigbluebutton.modules.layout.managers.OrderManager; + + [Bindable] public var name:String; + // default is a reserved word in actionscript + [Bindable] public var defaultLayout:Boolean = false; + private var _windows:Dictionary = new Dictionary(); + + static private var _ignoredWindows:Array = new Array("PublishWindow", + "VideoWindow", "DesktopPublishWindow", "DesktopViewWindow", + "LogWindow"); + static private var _roles:Array = new Array(Role.VIEWER, Role.MODERATOR, Role.PRESENTER); + + public function LayoutDefinition() { + + } + + private function loadLayout(vxml:XML):void { + if (vxml.@name != undefined) { + name = vxml.@name.toString(); + } + if (vxml.@default != undefined) { + defaultLayout = (vxml.@default.toString().toUpperCase() == "TRUE") ? true : false; + } + var role:String = Role.VIEWER; + if (vxml.@role != undefined && _roles.indexOf(vxml.@role.toString().toUpperCase()) != -1) { + role = vxml.@role.toString().toUpperCase(); + } + if (!_windows.hasOwnProperty(role)) + _windows[role] = new Dictionary(); + for each (var n:XML in vxml.window) { + var window:WindowLayout = new WindowLayout(); + window.load(n); + _windows[role][window.name] = window; + } + } + + public function load(vxml:XML):void { + if (vxml == null) + return; + + if (vxml.name().localName == "layout") + loadLayout(vxml); + else if (vxml.name().localName == "layout-block") { + for each (var tmp:XML in vxml.layout) + loadLayout(tmp); + } + } + + private function get myLayout():Dictionary { + var hasViewerLayout:Boolean = _windows.hasOwnProperty(Role.VIEWER); + var hasModeratorLayout:Boolean = _windows.hasOwnProperty(Role.MODERATOR); + var hasPresenterLayout:Boolean = _windows.hasOwnProperty(Role.PRESENTER); + + if (UserManager.getInstance().getConference().amIPresenter() && hasPresenterLayout) + return _windows[Role.PRESENTER]; + else if (UserManager.getInstance().getConference().amIModerator() && hasModeratorLayout) + return _windows[Role.MODERATOR]; + else if (hasViewerLayout) + return _windows[Role.VIEWER]; + else if (hasModeratorLayout) + return _windows[Role.MODERATOR]; + else if (hasPresenterLayout) + return _windows[Role.PRESENTER]; + else { + LogUtil.error("There's no layout that fits the participants profile"); + return null; + } + } + + public function windowLayout(name:String):WindowLayout { + return myLayout[name]; + } + + private function windowsToXml(windows:Dictionary):XML { + var xml:XML = <layout/>; + xml.@name = name; + if (defaultLayout) + xml.@default = true; + for each (var value:WindowLayout in windows) { + xml.appendChild(value.toXml()); + } + return xml; + } + + public function toXml():XML { + var xml:XML = <layout-block/>; + var tmp:XML; + for each (var value:String in _roles) { + if (_windows.hasOwnProperty(value)) { + tmp = windowsToXml(_windows[value]); + if (value != Role.VIEWER) + tmp.@role = value; + xml.appendChild(tmp); + } + } + return xml; + } + + /* + * 0 if there's no order + * 1 if "a" should appears after "b" + * -1 if "a" should appears before "b" + */ + private function sortWindows(a:Object, b:Object):int { + // ignored windows are positioned in front + if (a.ignored && b.ignored) return 0; + if (a.ignored) return 1; + if (b.ignored) return -1; + // then comes the windows that has no layout definition + if (!a.hasLayoutDefinition && !b.hasLayoutDefinition) return 0; + if (!a.hasLayoutDefinition) return 1; + if (!b.hasLayoutDefinition) return -1; + // then the focus order is used to sort + if (a.order == b.order) return 0; + if (a.order == -1) return 1; + if (b.order == -1) return -1; + return (a.order < b.order? 1: -1); + } + + private function adjustWindowsOrder(canvas:MDICanvas):void { + var orderedList:Array = new Array(); + var type:String; + var order:int; + var ignored:Boolean; + var hasLayoutDefinition:Boolean; + +// LogUtil.debug("=> Before sort"); + for each (var window:MDIWindow in canvas.windowManager.windowList) { + type = WindowLayout.getType(window); + hasLayoutDefinition = myLayout.hasOwnProperty(type); + if (hasLayoutDefinition) + order = myLayout[type].order; + else + order = -1; + ignored = ignoreWindowByType(type); + var item:Object = { window:window, order:order, type:type, ignored:ignored, hasLayoutDefinition:hasLayoutDefinition }; + orderedList.push(item); +// LogUtil.debug("===> type: " + item.type + " ignored? " + item.ignored + " hasLayoutDefinition? " + item.hasLayoutDefinition + " order? " + item.order); + } + orderedList.sort(this.sortWindows); +// LogUtil.debug("=> After sort"); + for each (var obj:Object in orderedList) { +// LogUtil.debug("===> type: " + obj.type + " ignored? " + obj.ignored + " hasLayoutDefinition? " + obj.hasLayoutDefinition + " order? " + obj.order); + if (!obj.ignored) + OrderManager.getInstance().bringToFront(obj.window); + canvas.windowManager.bringToFront(obj.window); + } + } + + public function applyToCanvas(canvas:MDICanvas):void { + if (canvas == null) + return; + + adjustWindowsOrder(canvas); + + for each (var window:MDIWindow in canvas.windowManager.windowList) { + applyToWindow(canvas, window); + } + } + + public function applyToWindow(canvas:MDICanvas, window:MDIWindow, type:String=null):void { + if (type == null) + type = WindowLayout.getType(window); + + if (!ignoreWindowByType(type)) + WindowLayout.setLayout(canvas, window, myLayout[type]); + } + + static private function ignoreWindowByType(type:String):Boolean { + return (_ignoredWindows.indexOf(type) != -1); + } + + static public function ignoreWindow(window:MDIWindow):Boolean { + var type:String = WindowLayout.getType(window); + return ignoreWindowByType(type); + } + + static public function getLayout(canvas:MDICanvas, name:String):LayoutDefinition { + var layoutDefinition:LayoutDefinition = new LayoutDefinition(); + layoutDefinition.name = name; + layoutDefinition._windows[Role.VIEWER] = new Dictionary(); + for each (var window:MDIWindow in canvas.windowManager.windowList) { + var layout:WindowLayout = WindowLayout.getLayout(canvas, window); + if (!ignoreWindowByType(layout.name)) + layoutDefinition._windows[Role.VIEWER][layout.name] = layout; + } + return layoutDefinition; + } + } +} diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/layout/model/LayoutDefinitionFile.as b/bigbluebutton-client/src/org/bigbluebutton/modules/layout/model/LayoutDefinitionFile.as new file mode 100644 index 0000000000000000000000000000000000000000..a5ade46218d614879662743c224760bb304057e5 --- /dev/null +++ b/bigbluebutton-client/src/org/bigbluebutton/modules/layout/model/LayoutDefinitionFile.as @@ -0,0 +1,80 @@ +/** + * BigBlueButton open source conferencing system - http://www.bigbluebutton.org/ + * + * Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below). + * + * This program is free software; you can redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License as published by the Free Software + * Foundation; either version 2.1 of the License, or (at your option) any later + * version. + * + * BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along + * with BigBlueButton; if not, see <http://www.gnu.org/licenses/>. + * + * Author: Felipe Cecagno <felipe@mconf.org> + */ +package org.bigbluebutton.modules.layout.model +{ + import flash.events.EventDispatcher; + + import org.bigbluebutton.common.LogUtil; + import org.bigbluebutton.core.EventBroadcaster; + import org.bigbluebutton.core.model.Config; + import org.bigbluebutton.modules.layout.events.LayoutsLoadedEvent; + import org.bigbluebutton.modules.layout.model.LayoutDefinition; + + public class LayoutDefinitionFile extends EventDispatcher { + private var _layouts:Array = new Array(); + + public function get list():Array { + return _layouts; + } + + public function pushXml(xml:XML):void { + if (xml.@name == undefined) + return; + + var layoutDefinition:LayoutDefinition = null; + for each (var layout:LayoutDefinition in _layouts) { + if (layout.name == xml.@name) { + layoutDefinition = layout; + break; + } + } + + if (layoutDefinition == null) { + layoutDefinition = new LayoutDefinition(); + layoutDefinition.load(xml); + _layouts.push(layoutDefinition); + } else { + layoutDefinition.load(xml); + } + } + + public function push(layoutDefinition:LayoutDefinition):void { + _layouts.push(layoutDefinition); + } + + public function getDefault():LayoutDefinition { + for each (var value:LayoutDefinition in _layouts) { + if (value.defaultLayout) + return value; + } + return null; + } + + public function toXml():XML { + var xml:XML = <layouts/>; + for each (var layoutDefinition:LayoutDefinition in _layouts) { + for each (var value:XML in layoutDefinition.toXml()) { + xml.appendChild(value); + } + } + return xml; + } + } +} \ No newline at end of file diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/layout/model/LayoutLoader.as b/bigbluebutton-client/src/org/bigbluebutton/modules/layout/model/LayoutLoader.as new file mode 100644 index 0000000000000000000000000000000000000000..de96f8a05db7045e8081c5e09a50033ed4b0479e --- /dev/null +++ b/bigbluebutton-client/src/org/bigbluebutton/modules/layout/model/LayoutLoader.as @@ -0,0 +1,111 @@ +/** + * BigBlueButton open source conferencing system - http://www.bigbluebutton.org/ + * + * Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below). + * + * This program is free software; you can redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License as published by the Free Software + * Foundation; either version 2.1 of the License, or (at your option) any later + * version. + * + * BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along + * with BigBlueButton; if not, see <http://www.gnu.org/licenses/>. + * + * Author: Felipe Cecagno <felipe@mconf.org> + */ +package org.bigbluebutton.modules.layout.model +{ + import flash.events.EventDispatcher; + import flash.events.Event; + import flash.events.ProgressEvent; + import flash.events.IOErrorEvent; + import flash.events.SecurityErrorEvent; + import flash.net.FileReference; + + import flash.net.URLLoader; + import flash.net.URLRequest; + + import org.bigbluebutton.common.LogUtil; + import org.bigbluebutton.core.EventBroadcaster; + import org.bigbluebutton.core.model.Config; + import org.bigbluebutton.modules.layout.events.LayoutsLoadedEvent; + import org.bigbluebutton.modules.layout.model.LayoutDefinition; + import org.bigbluebutton.util.i18n.ResourceUtil; + + public class LayoutLoader extends EventDispatcher { + private var _fileRef:FileReference; + + public function loadFromUrl(layoutUrl:String):void { + var urlLoader:URLLoader = new URLLoader(); + urlLoader.addEventListener(Event.COMPLETE, onComplete); + urlLoader.addEventListener(IOErrorEvent.IO_ERROR, onIOError); + var date:Date = new Date(); + try { + urlLoader.load(new URLRequest(layoutUrl + "?a=" + date.time)); + } catch (e:Error) { + LogUtil.debug("LayoutsLoader: exception while loading the layout definition file (" + e + ")"); + } + } + + public function loadFromLocalFile():void { + _fileRef = new FileReference(); + _fileRef.addEventListener(Event.SELECT, onFileSelected); + _fileRef.addEventListener(Event.CANCEL, onCancel); + _fileRef.addEventListener(IOErrorEvent.IO_ERROR, onIOError); + _fileRef.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onSecurityError); + _fileRef.browse(); + } + + private function onComplete(evt:Event):void { + LogUtil.debug("LayoutsLoader: completed, parsing..."); + var layouts:LayoutDefinitionFile = new LayoutDefinitionFile(); + var event:LayoutsLoadedEvent = new LayoutsLoadedEvent(); + try { + var data:XML = new XML(evt.target.data); + for each (var n:XML in data.layout) { + layouts.pushXml(n); + } + event.layouts = layouts; + event.success = true; + dispatchEvent(event); + } catch (error:TypeError) { + event.success = false; + event.error = new TypeError("Failed to parse the XML: " + error.message); + dispatchEvent(event); + } + } + + private function onFileSelected(evt:Event):void { + LogUtil.debug("LayoutsLoader: file selected"); + _fileRef.addEventListener(ProgressEvent.PROGRESS, onProgress); + _fileRef.addEventListener(Event.COMPLETE, onComplete); + _fileRef.load(); + } + + private function onProgress(evt:ProgressEvent):void { + LogUtil.debug("LayoutsLoader: loaded " + evt.bytesLoaded + " of " + evt.bytesTotal + " bytes"); + } + + private function onCancel(evt:Event):void { + LogUtil.debug("LayoutsLoader: the browse request was canceled by the user"); + } + + private function onIOError(evt:IOErrorEvent):void { + var event:LayoutsLoadedEvent = new LayoutsLoadedEvent(); + event.success = false; + event.error = new TypeError(ResourceUtil.getInstance().getString('bbb.layout.load.ioError')); + dispatchEvent(event); + } + + private function onSecurityError(evt:Event):void { + var event:LayoutsLoadedEvent = new LayoutsLoadedEvent(); + event.success = false; + event.error = new TypeError(ResourceUtil.getInstance().getString('bbb.layout.load.securityError')); + dispatchEvent(event); + } + } +} \ No newline at end of file diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/layout/model/WindowLayout.as b/bigbluebutton-client/src/org/bigbluebutton/modules/layout/model/WindowLayout.as new file mode 100644 index 0000000000000000000000000000000000000000..2fc4c5750cd695be3476c14d4b518cf987d09012 --- /dev/null +++ b/bigbluebutton-client/src/org/bigbluebutton/modules/layout/model/WindowLayout.as @@ -0,0 +1,230 @@ +/** + * BigBlueButton open source conferencing system - http://www.bigbluebutton.org/ + * + * Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below). + * + * This program is free software; you can redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License as published by the Free Software + * Foundation; either version 2.1 of the License, or (at your option) any later + * version. + * + * BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along + * with BigBlueButton; if not, see <http://www.gnu.org/licenses/>. + * + * Author: Felipe Cecagno <felipe@mconf.org> + */ +package org.bigbluebutton.modules.layout.model { + + public class WindowLayout { + + import flexlib.mdi.containers.MDICanvas; + import flexlib.mdi.containers.MDIWindow; + + import mx.effects.Fade; + import mx.effects.Move; + import mx.effects.Parallel; + import mx.effects.Resize; + import mx.events.EffectEvent; + + import flash.display.DisplayObject; + import flash.display.DisplayObjectContainer; + import flash.utils.Dictionary; + import flash.utils.getQualifiedClassName; + import flash.utils.Timer; + import flash.events.TimerEvent; + import org.bigbluebutton.common.LogUtil; + import org.bigbluebutton.modules.layout.managers.OrderManager; + + [Bindable] public var name:String; + [Bindable] public var width:Number; + [Bindable] public var height:Number; + [Bindable] public var x:Number; + [Bindable] public var y:Number; + [Bindable] public var minimized:Boolean = false; + [Bindable] public var maximized:Boolean = false; + [Bindable] public var hidden:Boolean = false; + [Bindable] public var order:int = -1; + + + static private var EVENT_DURATION:int = 500; + + public function load(vxml:XML):void { + if (vxml != null) { + if (vxml.@name != undefined) { + name = vxml.@name.toString(); + } + if (vxml.@width != undefined) { + width = Number(vxml.@width); + } + if (vxml.@height != undefined) { + height = Number(vxml.@height); + } + if (vxml.@x != undefined) { + x = Number(vxml.@x); + } + if (vxml.@y != undefined) { + y = Number(vxml.@y); + } + if (vxml.@minimized != undefined) { + minimized = (vxml.@minimized.toString().toUpperCase() == "TRUE") ? true : false; + } + if (vxml.@maximized != undefined) { + maximized = (vxml.@maximized.toString().toUpperCase() == "TRUE") ? true : false; + } + if (vxml.@hidden != undefined) { + hidden = (vxml.@hidden.toString().toUpperCase() == "TRUE") ? true : false; + } + if (vxml.@order != undefined) { + order = int(vxml.@order); + } + } + } + + static public function getLayout(canvas:MDICanvas, window:MDIWindow):WindowLayout { + var layout:WindowLayout = new WindowLayout(); + layout.name = getType(window); + layout.width = window.width / canvas.width; + layout.height = window.height / canvas.height; + layout.x = window.x / canvas.width; + layout.y = window.y / canvas.height; + layout.minimized = window.minimized; + layout.maximized = window.maximized; + layout.hidden = !window.visible; + layout.order = OrderManager.getInstance().getOrderByRef(window); + return layout; + } + + static public function setLayout(canvas:MDICanvas, window:MDIWindow, layout:WindowLayout):void { + if (layout == null) return; + layout.applyToWindow(canvas, window); + } + + private var _delayedEffects:Array = new Array(); + private function delayEffect(canvas:MDICanvas, window:MDIWindow):void { + var obj:Object = {canvas:canvas, window:window}; + _delayedEffects.push(obj); + var timer:Timer = new Timer(150,1); + timer.addEventListener(TimerEvent.TIMER, onTimerComplete); + timer.start(); + } + + private function onTimerComplete(event:TimerEvent = null):void { + var obj:Object = _delayedEffects.pop(); + applyToWindow(obj.canvas, obj.window); + } + + public function applyToWindow(canvas:MDICanvas, window:MDIWindow):void { + var effect:Parallel = new Parallel(); + effect.duration = EVENT_DURATION; + effect.target = window; + + if (this.minimized) { + if (!window.minimized) window.minimize(); + } else if (this.maximized) { + if (!window.maximized) window.maximize(); + } else if (window.minimized && !this.minimized && !this.hidden) { + window.unMinimize(); + delayEffect(canvas, window); + return; + } else if (window.maximized && !this.maximized && !this.hidden) { + window.maximizeRestore(); + delayEffect(canvas, window); + return; + } else { + if (!this.hidden) { + var newWidth:int = int(this.width * canvas.width); + var newHeight:int = int(this.height * canvas.height); + var newX:int = int(this.x * canvas.width); + var newY:int = int(this.y * canvas.height); + + if (newX != window.x || newY != window.y) { + var mover:Move = new Move(); + mover.xTo = newX; + mover.yTo = newY; + effect.addChild(mover); + } + + if (newWidth != window.width || newHeight != window.height) { + var resizer:Resize = new Resize(); + resizer.widthTo = newWidth; + resizer.heightTo = newHeight; + effect.addChild(resizer) + } + } + } + + var layoutHidden:Boolean = this.hidden; +// var windowVisible:Boolean = (window.alpha == 1); + var windowVisible:Boolean = window.visible; + if (windowVisible == layoutHidden) { + var fader:Fade = new Fade(); + fader.alphaFrom = (layoutHidden? 1: 0); + fader.alphaTo = (layoutHidden? 0: 1); + fader.addEventListener(EffectEvent.EFFECT_START, function(e:EffectEvent):void { + if (!windowVisible) + window.enabled = window.visible = true; + }); + fader.addEventListener(EffectEvent.EFFECT_END, function(e:EffectEvent):void { + if (windowVisible) + window.enabled = window.visible = false; + }); + effect.addChild(fader); + } + + if (effect.children.length > 0) + effect.play(); + } + + static public function getType(obj:Object):String { + var qualifiedClass:String = String(getQualifiedClassName(obj)); + var pattern:RegExp = /(\w+)::(\w+)/g; + if (qualifiedClass.match(pattern)) { + return qualifiedClass.split("::")[1]; + } else { + return String(Object).substr(String(Object).lastIndexOf(".") + 1).match(/[a-zA-Z]+/).join(); + } + } + + public function toAbsoluteXml(canvas:MDICanvas):XML { + var xml:XML = <window/>; + xml.@name = name; + if (minimized) + xml.@minimized = true; + else if (maximized) + xml.@maximized = true; + else if (hidden) + xml.@hidden = true; + else { + xml.@width = int(width * canvas.width); + xml.@height = int(height * canvas.height); + xml.@x = int(x * canvas.width); + xml.@y = int(y * canvas.height); + } + xml.@order = order; + return xml; + } + + public function toXml():XML { + var xml:XML = <window/>; + xml.@name = name; + if (minimized) + xml.@minimized = true; + else if (maximized) + xml.@maximized = true; + else if (hidden) + xml.@hidden = true; + else { + xml.@width = width; + xml.@height = height; + xml.@x = x; + xml.@y = y; + } + xml.@order = order; + return xml; + } + } +} diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/layout/services/LayoutService.as b/bigbluebutton-client/src/org/bigbluebutton/modules/layout/services/LayoutService.as new file mode 100755 index 0000000000000000000000000000000000000000..7ffbac0ff1524b7112fd9d4692e3fce3c5a799e6 --- /dev/null +++ b/bigbluebutton-client/src/org/bigbluebutton/modules/layout/services/LayoutService.as @@ -0,0 +1,57 @@ +/** + * BigBlueButton open source conferencing system - http://www.bigbluebutton.org/ + * + * Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below). + * + * This program is free software; you can redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License as published by the Free Software + * Foundation; either version 2.1 of the License, or (at your option) any later + * version. + * + * BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along + * with BigBlueButton; if not, see <http://www.gnu.org/licenses/>. + * + * Author: Felipe Cecagno <felipe@mconf.org> + */ +package org.bigbluebutton.modules.layout.services +{ + import flash.events.IEventDispatcher; + + import org.bigbluebutton.modules.layout.model.LayoutDefinition; + + public class LayoutService + { + private var _attributes:Object; + private var _layoutSOService:LayoutSharedObjectService; + + public function LayoutService(attributes:Object) { + _attributes = attributes; + } + + public function join():void { + _layoutSOService = new LayoutSharedObjectService(_attributes.connection); + _layoutSOService.join(_attributes.uri + "/" + _attributes.room); + } + + public function leave():void { + _layoutSOService.leave(); + } + + public function initLayout(success:Boolean):void { + if (success) + _layoutSOService.initLayout(); + } + + public function lockLayout(layout:LayoutDefinition):void { + _layoutSOService.lockLayout(layout); + } + + public function unlockLayout():void { + _layoutSOService.unlockLayout(); + } + } +} \ No newline at end of file diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/layout/services/LayoutSharedObjectService.as b/bigbluebutton-client/src/org/bigbluebutton/modules/layout/services/LayoutSharedObjectService.as new file mode 100755 index 0000000000000000000000000000000000000000..7e267ce26dee2337a5b57398afb97d11a0abdbd5 --- /dev/null +++ b/bigbluebutton-client/src/org/bigbluebutton/modules/layout/services/LayoutSharedObjectService.as @@ -0,0 +1,203 @@ +/** + * BigBlueButton open source conferencing system - http://www.bigbluebutton.org/ + * + * Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below). + * + * This program is free software; you can redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License as published by the Free Software + * Foundation; either version 2.1 of the License, or (at your option) any later + * version. + * + * BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along + * with BigBlueButton; if not, see <http://www.gnu.org/licenses/>. + * + * Author: Felipe Cecagno <felipe@mconf.org> + */ +package org.bigbluebutton.modules.layout.services +{ + import com.asfusion.mate.events.Dispatcher; + + import flash.events.AsyncErrorEvent; + import flash.events.IEventDispatcher; + import flash.events.NetStatusEvent; + import flash.events.SyncEvent; + import flash.events.TimerEvent; + import flash.net.NetConnection; + import flash.net.Responder; + import flash.net.SharedObject; + import flash.utils.Timer; + + import mx.controls.Alert; + + import org.bigbluebutton.common.LogUtil; + import org.bigbluebutton.core.managers.UserManager; + import org.bigbluebutton.modules.layout.events.ConnectionEvent; + import org.bigbluebutton.modules.layout.events.LayoutEvent; + import org.bigbluebutton.modules.layout.events.RedefineLayoutEvent; + import org.bigbluebutton.modules.layout.model.LayoutDefinition; + import org.bigbluebutton.util.i18n.ResourceUtil; + + public class LayoutSharedObjectService + { + public static const NAME:String = "LayoutSharedObjectService"; + + private var _layoutSO:SharedObject; + private var _connection:NetConnection; + private var _dispatcher:Dispatcher; + private var _locked:Boolean = false; + /* + * the application of the first layout should be delayed to avoid + * strange movements of the windows before set the correct position + */ + private var _applyFirstLayoutTimer:Timer = new Timer(750,1); + + public function LayoutSharedObjectService(connection:NetConnection) + { + _connection = connection; + _dispatcher = new Dispatcher(); + } + + public function join(uri:String):void + { + _layoutSO = SharedObject.getRemote("layoutSO", uri, false); + _layoutSO.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler); + _layoutSO.addEventListener(AsyncErrorEvent.ASYNC_ERROR, asyncErrorHandler); + _layoutSO.addEventListener(SyncEvent.SYNC, sharedObjectSyncHandler); + _layoutSO.client = this; + _layoutSO.connect(_connection); + } + + public function leave():void + { + if (_layoutSO != null) { + _layoutSO.close(); + } + } + + private function netStatusHandler(event:NetStatusEvent):void + { + var statusCode:String = event.info.code; + var connEvent:ConnectionEvent = new ConnectionEvent(ConnectionEvent.CONNECT_EVENT); + + switch ( statusCode ) + { + case "NetConnection.Connect.Success": + connEvent.success = true; + break; + default: + connEvent.success = false; + break; + } + _dispatcher.dispatchEvent(connEvent); + } + + public function initLayout():void { + var nc:NetConnection = _connection; + nc.call( + "layout.init", + new Responder( + function(result:Object):void { + _applyFirstLayoutTimer.addEventListener(TimerEvent.TIMER, function(e:TimerEvent):void { + onReceivedFirstLayout(result); + }); + _applyFirstLayoutTimer.start(); + }, + function(status:Object):void { + LogUtil.error("LayoutSharedObjectService:initLayout - An error occurred"); + for (var x:Object in status) { + LogUtil.error(x + " : " + status[x]); + } + } + ) + ); + } + + private function onReceivedFirstLayout(result:Object):void { + LogUtil.debug("LayoutService: handling the first layout"); + var locked:Boolean = result[0]; + var userId:int = result[1]; + var layout:String = result[2]; + if (locked) + remoteUpdateLayout(locked, userId, layout); + else + _dispatcher.dispatchEvent(new LayoutEvent(LayoutEvent.APPLY_DEFAULT_LAYOUT_EVENT)); + } + + public function lockLayout(layout:LayoutDefinition):void { + var nc:NetConnection = _connection; + nc.call( + "layout.lock", + new Responder( + function(result:Object):void { + }, + function(status:Object):void { + LogUtil.error("LayoutSharedObjectService:lockLayout - An error occurred"); + for (var x:Object in status) { + LogUtil.error(x + " : " + status[x]); + } + } + ), + UserManager.getInstance().getConference().getMyUserId(), + layout.toXml().toXMLString() + ); + } + + public function unlockLayout():void { + var nc:NetConnection = _connection; + nc.call( + "layout.unlock", + new Responder( + function(result:Object):void { + }, + function(status:Object):void { + LogUtil.error("LayoutSharedObjectService:unlockLayout - An error occurred"); + for (var x:Object in status) { + LogUtil.error(x + " : " + status[x]); + } + } + ) + ); + } + + public function remoteUpdateLayout(locked:Boolean, userId:int, layout:String):void { + var dispatchedByMe:Boolean = UserManager.getInstance().getConference().amIThisUser(userId); + + LogUtil.debug("LayoutService: received a remote update" + (locked? " from " + (dispatchedByMe? "myself": "a remote user"): "")); + LogUtil.debug("Locked? " + (locked? "yes": "no")); + + if (!_locked && locked) { + _dispatcher.dispatchEvent(new LayoutEvent(LayoutEvent.REMOTE_LOCK_LAYOUT_EVENT)); + } else if (_locked && !locked) { + _dispatcher.dispatchEvent(new LayoutEvent(LayoutEvent.REMOTE_UNLOCK_LAYOUT_EVENT)); + } + + if (locked && !dispatchedByMe) { + LogUtil.debug("LayoutService: handling remote layout"); + LogUtil.debug(layout); + var layoutDefinition:LayoutDefinition = new LayoutDefinition(); + layoutDefinition.load(new XML(layout)); + layoutDefinition.name = "[" + ResourceUtil.getInstance().getString('bbb.layout.combo.remote') + "] " + layoutDefinition.name; + var redefineLayout:RedefineLayoutEvent = new RedefineLayoutEvent(); + redefineLayout.layout = layoutDefinition; + redefineLayout.remote = true; + _dispatcher.dispatchEvent(redefineLayout); + } + _locked = locked; + } + + private function asyncErrorHandler(event:AsyncErrorEvent):void { + LogUtil.debug("LayoutService: layoutSO asynchronous error (" + event + ")"); + } + + private function sharedObjectSyncHandler(event:SyncEvent):void { + LogUtil.debug("LayoutService: layoutSO connection established"); + var connEvent:ConnectionEvent = new ConnectionEvent(ConnectionEvent.CONNECT_EVENT); + connEvent.success = true; + _dispatcher.dispatchEvent(connEvent); + } + } +} \ No newline at end of file diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/layout/views/AddButton.mxml b/bigbluebutton-client/src/org/bigbluebutton/modules/layout/views/AddButton.mxml new file mode 100644 index 0000000000000000000000000000000000000000..6f00a98358747b204504c5560c220fbb2ca7b376 --- /dev/null +++ b/bigbluebutton-client/src/org/bigbluebutton/modules/layout/views/AddButton.mxml @@ -0,0 +1,57 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + BigBlueButton open source conferencing system - http://www.bigbluebutton.org/ + + Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below). + + This program is free software; you can redistribute it and/or modify it under the + terms of the GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1 of the License, or (at your option) any later + version. + + BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along + with BigBlueButton; if not, see <http://www.gnu.org/licenses/>. + + Author: Felipe Cecagno <felipe@mconf.org> + + $Id: $ +--> +<views:LayoutButton xmlns:mx="http://www.adobe.com/2006/mxml" + creationComplete="init()" + xmlns:mate="http://mate.asfusion.com/" + xmlns:views="org.bigbluebutton.modules.layout.views.*" + toolTip="{ResourceUtil.getInstance().getString('bbb.layout.addButton.toolTip')}" + icon="{icon_add}" + click="onClick(event)" + enabled="{UserManager.getInstance().getConference().amIModerator()}"> + + <mx:Script> + <![CDATA[ + import com.asfusion.mate.events.Dispatcher; + + import flash.events.Event; + + import org.bigbluebutton.common.Images; + import org.bigbluebutton.common.LogUtil; + import org.bigbluebutton.core.managers.UserManager; + import org.bigbluebutton.util.i18n.ResourceUtil; + import org.bigbluebutton.modules.layout.events.LayoutEvent; + + private var _dispatcher:Dispatcher = new Dispatcher(); + private var _images:Images = new Images(); + [Bindable] private var icon_add:Class = _images.add; + + private function init():void { + } + + private function onClick(e:Event):void { + _dispatcher.dispatchEvent(new LayoutEvent(LayoutEvent.ADD_CURRENT_LAYOUT_EVENT)); + } + + ]]> + </mx:Script> +</views:LayoutButton> diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/layout/views/LayoutButton.mxml b/bigbluebutton-client/src/org/bigbluebutton/modules/layout/views/LayoutButton.mxml new file mode 100644 index 0000000000000000000000000000000000000000..ce2398d3285eb2703fa073895e6fff83aa5c04fd --- /dev/null +++ b/bigbluebutton-client/src/org/bigbluebutton/modules/layout/views/LayoutButton.mxml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + BigBlueButton open source conferencing system - http://www.bigbluebutton.org/ + + Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below). + + This program is free software; you can redistribute it and/or modify it under the + terms of the GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1 of the License, or (at your option) any later + version. + + BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along + with BigBlueButton; if not, see <http://www.gnu.org/licenses/>. + + Author: Felipe Cecagno <felipe@mconf.org> + + $Id: $ +--> +<mx:Button xmlns:mx="http://www.adobe.com/2006/mxml" + xmlns:mate="http://mate.asfusion.com/" + width="{BUTTON_SIZE}" + height="{BUTTON_SIZE}"> + + <mx:Script> + <![CDATA[ + import mx.controls.Button; + + static public const BUTTON_SIZE:int = 20; + ]]> + </mx:Script> +</mx:Button> diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/layout/views/LayoutsCombo.mxml b/bigbluebutton-client/src/org/bigbluebutton/modules/layout/views/LayoutsCombo.mxml new file mode 100644 index 0000000000000000000000000000000000000000..33c44e17d2ed847837da5bca8515453ecc3d4720 --- /dev/null +++ b/bigbluebutton-client/src/org/bigbluebutton/modules/layout/views/LayoutsCombo.mxml @@ -0,0 +1,133 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + BigBlueButton open source conferencing system - http://www.bigbluebutton.org/ + + Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below). + + This program is free software; you can redistribute it and/or modify it under the + terms of the GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1 of the License, or (at your option) any later + version. + + BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along + with BigBlueButton; if not, see <http://www.gnu.org/licenses/>. + + Author: Felipe Cecagno <felipe@mconf.org> + + $Id: $ +--> +<mx:ComboBox xmlns:mx="http://www.adobe.com/2006/mxml" + creationComplete="init()" + xmlns:mate="http://mate.asfusion.com/" + toolTip="{ResourceUtil.getInstance().getString('bbb.layout.combo.toolTip')}" + prompt="{ResourceUtil.getInstance().getString('bbb.layout.combo.prompt')}" + height="{LayoutButton.BUTTON_SIZE}" + change="onSelectedItemChanged(event)" + disabledColor="{getStyle('color')}"> + + <mate:Listener type="{LayoutEvent.APPLY_DEFAULT_LAYOUT_EVENT}" method="onApplyDefaultLayout" /> + <mate:Listener type="{LayoutEvent.REMOTE_LOCK_LAYOUT_EVENT}" receive="{enabled=false || UserManager.getInstance().getConference().amIModerator()}" /> + <mate:Listener type="{LayoutEvent.REMOTE_UNLOCK_LAYOUT_EVENT}" receive="{enabled=true}" /> + <mate:Listener type="{LayoutEvent.INVALIDATE_LAYOUT_EVENT}" method="invalidadeLayout" /> + <mate:Listener type="{LayoutsLoadedEvent.LAYOUTS_LOADED_EVENT}" method="populateLayoutsList" /> + <mate:Listener type="{RedefineLayoutEvent.REDEFINE_LAYOUT_EVENT}" method="onRedefineLayout" /> + + <mx:Script> + <![CDATA[ + import com.asfusion.mate.events.Dispatcher; + import flash.events.Event; + import flash.events.ProgressEvent; + import flash.events.IOErrorEvent; + + import mx.controls.Alert; + import mx.events.ResizeEvent; + + import flash.net.FileReference; + + import flexlib.mdi.managers.MDIManager; + import flexlib.mdi.containers.MDICanvas; + import flexlib.mdi.containers.MDIWindow; + import flexlib.mdi.events.MDIManagerEvent; + import flexlib.mdi.events.MDIWindowEvent; + + import org.bigbluebutton.common.IBbbModuleWindow; + import org.bigbluebutton.common.Images; + import org.bigbluebutton.common.LogUtil; + import org.bigbluebutton.common.events.OpenWindowEvent; + import org.bigbluebutton.core.managers.UserManager; + import org.bigbluebutton.main.views.MainToolbar; + import org.bigbluebutton.util.i18n.ResourceUtil; + import org.bigbluebutton.modules.layout.events.LayoutEvent; + import org.bigbluebutton.modules.layout.events.LayoutsLoadedEvent; + import org.bigbluebutton.modules.layout.events.RedefineLayoutEvent; + import org.bigbluebutton.modules.layout.events.ViewInitializedEvent; + import org.bigbluebutton.modules.layout.model.LayoutDefinitionFile; + import org.bigbluebutton.modules.layout.model.LayoutDefinition; + import org.bigbluebutton.modules.layout.model.WindowLayout; + import org.bigbluebutton.modules.layout.views.LayoutButton; + + private var _dispatcher:Dispatcher = new Dispatcher(); + + private var _defaultLayout:Object = null; + + private function init():void { + LogUtil.debug("LayoutsCombo: view initialized"); + } + + private function populateLayoutsList(e:LayoutsLoadedEvent):void { + LogUtil.debug("LayoutsCombo: populating list"); + _defaultLayout = null; + dataProvider.removeAll(); + var idx:int = 0; + for each (var value:LayoutDefinition in e.layouts.list) { + var item:Object = {index:idx, label:value.name, layout:value}; + if (value.defaultLayout) + _defaultLayout = item; + dataProvider.addItem(item); + idx++; + } + invalidateDisplayList(); + } + + private function onApplyDefaultLayout(e:Event):void { + if (_defaultLayout != null) + selectedItem = _defaultLayout; + } + + private function onRedefineLayout(e:RedefineLayoutEvent):void { + /* + * remote means that the this event wasn't dispatched by this class + * it will come tipically from LayoutService or LayoutManager + */ + if (e.remote) { + var idx:int = -1; + prompt = e.layout.name; + + for each (var obj:Object in dataProvider) { + if (obj.label == e.layout.name) + idx = obj.index; + } + selectedIndex = idx; + } + } + + private function onSelectedItemChanged(e:Event):void { + LogUtil.debug("LayoutsCombo: different layout selected"); + var redefineLayout:RedefineLayoutEvent = new RedefineLayoutEvent(); + redefineLayout.layout = e.currentTarget.selectedItem.layout; + redefineLayout.remote = false; + _dispatcher.dispatchEvent(redefineLayout); + } + + private function invalidadeLayout(e:Event):void { + selectedIndex = -1; + prompt = ResourceUtil.getInstance().getString('bbb.layout.combo.custom'); + } + + ]]> + </mx:Script> +</mx:ComboBox> diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/layout/views/LoadButton.mxml b/bigbluebutton-client/src/org/bigbluebutton/modules/layout/views/LoadButton.mxml new file mode 100644 index 0000000000000000000000000000000000000000..4a83654875774880fb2190257bfa17a774ca0817 --- /dev/null +++ b/bigbluebutton-client/src/org/bigbluebutton/modules/layout/views/LoadButton.mxml @@ -0,0 +1,57 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + BigBlueButton open source conferencing system - http://www.bigbluebutton.org/ + + Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below). + + This program is free software; you can redistribute it and/or modify it under the + terms of the GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1 of the License, or (at your option) any later + version. + + BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along + with BigBlueButton; if not, see <http://www.gnu.org/licenses/>. + + Author: Felipe Cecagno <felipe@mconf.org> + + $Id: $ +--> +<views:LayoutButton xmlns:mx="http://www.adobe.com/2006/mxml" + creationComplete="init()" + xmlns:mate="http://mate.asfusion.com/" + xmlns:views="org.bigbluebutton.modules.layout.views.*" + toolTip="{ResourceUtil.getInstance().getString('bbb.layout.loadButton.toolTip')}" + icon="{icon_load}" + click="onClick(event)" + enabled="{UserManager.getInstance().getConference().amIModerator()}"> + + <mx:Script> + <![CDATA[ + import com.asfusion.mate.events.Dispatcher; + + import flash.events.Event; + + import org.bigbluebutton.common.Images; + import org.bigbluebutton.common.LogUtil; + import org.bigbluebutton.core.managers.UserManager; + import org.bigbluebutton.util.i18n.ResourceUtil; + import org.bigbluebutton.modules.layout.events.LayoutEvent; + + private var _dispatcher:Dispatcher = new Dispatcher(); + private var _images:Images = new Images(); + [Bindable] private var icon_load:Class = _images.folder; + + private function init():void { + } + + private function onClick(e:Event):void { + _dispatcher.dispatchEvent(new LayoutEvent(LayoutEvent.LOAD_LAYOUTS_EVENT)); + } + + ]]> + </mx:Script> +</views:LayoutButton> diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/layout/views/LockButton.mxml b/bigbluebutton-client/src/org/bigbluebutton/modules/layout/views/LockButton.mxml new file mode 100644 index 0000000000000000000000000000000000000000..f04cf00a0f96f90b8d912e841aa2687e9c0df788 --- /dev/null +++ b/bigbluebutton-client/src/org/bigbluebutton/modules/layout/views/LockButton.mxml @@ -0,0 +1,75 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + BigBlueButton open source conferencing system - http://www.bigbluebutton.org/ + + Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below). + + This program is free software; you can redistribute it and/or modify it under the + terms of the GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1 of the License, or (at your option) any later + version. + + BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along + with BigBlueButton; if not, see <http://www.gnu.org/licenses/>. + + Author: Felipe Cecagno <felipe@mconf.org> + + $Id: $ +--> +<views:LayoutButton xmlns:mx="http://www.adobe.com/2006/mxml" + creationComplete="init()" + xmlns:mate="http://mate.asfusion.com/" + xmlns:views="org.bigbluebutton.modules.layout.views.*" + toolTip="{ResourceUtil.getInstance().getString('bbb.layout.lockButton.toolTip')}" + icon="{icon_unlocked}" + click="onClick(event)" + enabled="{UserManager.getInstance().getConference().amIModerator()}"> + + <mate:Listener type="{LayoutEvent.REMOTE_LOCK_LAYOUT_EVENT}" method="onLockLayoutEvent" /> + <mate:Listener type="{LayoutEvent.REMOTE_UNLOCK_LAYOUT_EVENT}" method="onUnlockLayoutEvent" /> + + <mx:Script> + <![CDATA[ + import com.asfusion.mate.events.Dispatcher; + + import flash.events.Event; + + import org.bigbluebutton.common.Images; + import org.bigbluebutton.common.LogUtil; + import org.bigbluebutton.core.managers.UserManager; + import org.bigbluebutton.util.i18n.ResourceUtil; + import org.bigbluebutton.modules.layout.events.LayoutEvent; + + private var _dispatcher:Dispatcher = new Dispatcher(); + private var _images:Images = new Images(); + [Bindable] private var icon_locked:Class = _images.locked; + [Bindable] private var icon_unlocked:Class = _images.unlocked; + + private function init():void { + } + + private function onClick(e:Event):void { + if (!this.selected) { + _dispatcher.dispatchEvent(new LayoutEvent(LayoutEvent.LOCK_LAYOUT_EVENT)); + } else { + _dispatcher.dispatchEvent(new LayoutEvent(LayoutEvent.UNLOCK_LAYOUT_EVENT)); + } + } + + private function onLockLayoutEvent(e:Event):void { + this.selected = true; + this.setStyle("icon", icon_locked); + } + + private function onUnlockLayoutEvent(e:Event):void { + this.selected = false; + this.setStyle("icon", icon_unlocked); + } + + ]]> + </mx:Script> +</views:LayoutButton> diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/layout/views/SaveButton.mxml b/bigbluebutton-client/src/org/bigbluebutton/modules/layout/views/SaveButton.mxml new file mode 100644 index 0000000000000000000000000000000000000000..b22ad8230892a83f11b99cff4273ce191521b8d5 --- /dev/null +++ b/bigbluebutton-client/src/org/bigbluebutton/modules/layout/views/SaveButton.mxml @@ -0,0 +1,57 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + BigBlueButton open source conferencing system - http://www.bigbluebutton.org/ + + Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below). + + This program is free software; you can redistribute it and/or modify it under the + terms of the GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1 of the License, or (at your option) any later + version. + + BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along + with BigBlueButton; if not, see <http://www.gnu.org/licenses/>. + + Author: Felipe Cecagno <felipe@mconf.org> + + $Id: $ +--> +<views:LayoutButton xmlns:mx="http://www.adobe.com/2006/mxml" + creationComplete="init()" + xmlns:mate="http://mate.asfusion.com/" + xmlns:views="org.bigbluebutton.modules.layout.views.*" + toolTip="{ResourceUtil.getInstance().getString('bbb.layout.saveButton.toolTip')}" + icon="{icon_save}" + click="onClick(event)" + enabled="{UserManager.getInstance().getConference().amIModerator()}"> + + <mx:Script> + <![CDATA[ + import com.asfusion.mate.events.Dispatcher; + + import flash.events.Event; + + import org.bigbluebutton.common.Images; + import org.bigbluebutton.common.LogUtil; + import org.bigbluebutton.core.managers.UserManager; + import org.bigbluebutton.util.i18n.ResourceUtil; + import org.bigbluebutton.modules.layout.events.LayoutEvent; + + private var _dispatcher:Dispatcher = new Dispatcher(); + private var _images:Images = new Images(); + [Bindable] private var icon_save:Class = _images.disk; + + private function init():void { + } + + private function onClick(e:Event):void { + _dispatcher.dispatchEvent(new LayoutEvent(LayoutEvent.SAVE_LAYOUTS_EVENT)); + } + + ]]> + </mx:Script> +</views:LayoutButton> diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/layout/views/ToolbarComponent.mxml b/bigbluebutton-client/src/org/bigbluebutton/modules/layout/views/ToolbarComponent.mxml new file mode 100644 index 0000000000000000000000000000000000000000..4c83dd783517b9f1088a8a6f71a77982ea5be624 --- /dev/null +++ b/bigbluebutton-client/src/org/bigbluebutton/modules/layout/views/ToolbarComponent.mxml @@ -0,0 +1,84 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + BigBlueButton open source conferencing system - http://www.bigbluebutton.org/ + + Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below). + + This program is free software; you can redistribute it and/or modify it under the + terms of the GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1 of the License, or (at your option) any later + version. + + BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along + with BigBlueButton; if not, see <http://www.gnu.org/licenses/>. + + Author: Felipe Cecagno <felipe@mconf.org> + + $Id: $ +--> +<mx:HBox xmlns:mx="http://www.adobe.com/2006/mxml" + creationComplete="init()" + xmlns:mate="http://mate.asfusion.com/" + xmlns:views="org.bigbluebutton.modules.layout.views.*" + implements="org.bigbluebutton.common.IBbbToolbarComponent"> + + <mx:Script> + <![CDATA[ + import com.asfusion.mate.events.Dispatcher; + + import flexlib.mdi.containers.MDICanvas; + + import org.bigbluebutton.core.managers.UserManager; + import org.bigbluebutton.main.views.MainToolbar; + import org.bigbluebutton.modules.layout.events.ViewInitializedEvent; + import org.bigbluebutton.modules.layout.model.WindowLayout; + + private var _dispatcher:Dispatcher = new Dispatcher(); + [Bindable] private var _enableEdit:Boolean = false; + + private function init():void { + var evt:ViewInitializedEvent = new ViewInitializedEvent(); + evt.canvas = getMdiCanvas(parent) as MDICanvas; + _dispatcher.dispatchEvent(evt); + } + + public function set enableEdit(arg:Boolean):void { + _enableEdit = arg && UserManager.getInstance().getConference().amIModerator(); + } + + private function getMdiCanvas(p:DisplayObjectContainer):DisplayObject { + if (p == null) + return null; + + for (var i:int = 0; i < p.numChildren; ++i) { + //if (String(getQualifiedClassName(p.getChildAt(i))).match("MainCanvas")) + if (WindowLayout.getType(p.getChildAt(i)) == "MainCanvas") + return p.getChildAt(i); + + var obj:DisplayObject = getMdiCanvas(p.parent); + if (obj != null) + return obj; + } + return null; + } + + public function getAlignment():String{ + return MainToolbar.ALIGN_RIGHT; + } + + ]]> + </mx:Script> + + <views:LayoutsCombo id="comboBox" /> + <views:AddButton id="addButton" + includeInLayout="{_enableEdit}" visible="{_enableEdit}" /> + <views:SaveButton id="saveButton" + includeInLayout="{_enableEdit}" visible="{_enableEdit}" /> + <views:LoadButton id="loadButton" + includeInLayout="{_enableEdit}" visible="{_enableEdit}" /> + <views:LockButton id="lockButton" /> +</mx:HBox>