574 lines
19 KiB
C++
574 lines
19 KiB
C++
// IconbarTool.cc
|
|
// Copyright (c) 2003 - 2006 Henrik Kinnunen (fluxgen at fluxbox dot org)
|
|
// and Simon Bowden (rathnor at users.sourceforge.net)
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a
|
|
// copy of this software and associated documentation files (the "Software"),
|
|
// to deal in the Software without restriction, including without limitation
|
|
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
// and/or sell copies of the Software, and to permit persons to whom the
|
|
// Software is furnished to do so, subject to the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included in
|
|
// all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
// DEALINGS IN THE SOFTWARE.
|
|
|
|
#include "IconbarTool.hh"
|
|
|
|
#include "fluxbox.hh"
|
|
#include "WindowCmd.hh"
|
|
#include "Screen.hh"
|
|
#include "IconbarTheme.hh"
|
|
#include "Window.hh"
|
|
#include "IconButton.hh"
|
|
#include "Workspace.hh"
|
|
#include "FbMenu.hh"
|
|
#include "FbTk/CommandParser.hh"
|
|
#include "WinClient.hh"
|
|
#include "FocusControl.hh"
|
|
#include "FbCommands.hh"
|
|
#include "Layer.hh"
|
|
#include "Debug.hh"
|
|
|
|
#include "FbTk/STLUtil.hh"
|
|
#include "FbTk/I18n.hh"
|
|
#include "FbTk/Menu.hh"
|
|
#include "FbTk/RadioMenuItem.hh"
|
|
#include "FbTk/BoolMenuItem.hh"
|
|
#include "FbTk/RefCount.hh"
|
|
#include "FbTk/SimpleCommand.hh"
|
|
#include "FbTk/ImageControl.hh"
|
|
#include "FbTk/MacroCommand.hh"
|
|
#include "FbTk/MenuSeparator.hh"
|
|
#include "FbTk/Util.hh"
|
|
#include "FbTk/STLUtil.hh"
|
|
#include "FbTk/Select2nd.hh"
|
|
#include "FbTk/Compose.hh"
|
|
|
|
#include <typeinfo>
|
|
#include <iterator>
|
|
#ifdef HAVE_CSTRING
|
|
#include <cstring>
|
|
#else
|
|
#include <string.h>
|
|
#endif
|
|
|
|
using std::string;
|
|
using std::list;
|
|
using std::endl;
|
|
|
|
namespace FbTk {
|
|
|
|
template<>
|
|
void FbTk::Resource<FbTk::Container::Alignment>::setDefaultValue() {
|
|
m_value = FbTk::Container::RELATIVE;
|
|
}
|
|
|
|
template<>
|
|
string FbTk::Resource<FbTk::Container::Alignment>::getString() const {
|
|
if (m_value == FbTk::Container::LEFT)
|
|
return string("Left");
|
|
if (m_value == FbTk::Container::RIGHT)
|
|
return string("Right");
|
|
return string("Relative");
|
|
}
|
|
|
|
template<>
|
|
void FbTk::Resource<FbTk::Container::Alignment>::setFromString(const char *str) {
|
|
if (strcasecmp(str, "Left") == 0)
|
|
m_value = FbTk::Container::LEFT;
|
|
else if (strcasecmp(str, "Right") == 0)
|
|
m_value = FbTk::Container::RIGHT;
|
|
else if (strcasecmp(str, "Relative") == 0)
|
|
m_value = FbTk::Container::RELATIVE;
|
|
else
|
|
setDefaultValue();
|
|
}
|
|
|
|
} // end namespace FbTk
|
|
|
|
namespace {
|
|
|
|
class ToolbarModeMenuItem : public FbTk::RadioMenuItem {
|
|
public:
|
|
ToolbarModeMenuItem(const FbTk::FbString &label, IconbarTool &handler,
|
|
string mode,
|
|
FbTk::RefCount<FbTk::Command<void> > &cmd):
|
|
FbTk::RadioMenuItem(label, cmd), m_handler(handler), m_mode(mode) {
|
|
setCloseOnClick(false);
|
|
}
|
|
bool isSelected() const { return m_handler.mode() == m_mode; }
|
|
void click(int button, int time, unsigned int mods) {
|
|
m_handler.setMode(m_mode);
|
|
FbTk::RadioMenuItem::click(button, time, mods);
|
|
}
|
|
|
|
private:
|
|
IconbarTool &m_handler;
|
|
string m_mode;
|
|
};
|
|
|
|
class ToolbarAlignMenuItem: public FbTk::RadioMenuItem {
|
|
public:
|
|
ToolbarAlignMenuItem(const FbTk::FbString &label, IconbarTool &handler,
|
|
FbTk::Container::Alignment mode,
|
|
FbTk::RefCount<FbTk::Command<void> > &cmd):
|
|
FbTk::RadioMenuItem(label, cmd), m_handler(handler), m_mode(mode) {
|
|
setCloseOnClick(false);
|
|
}
|
|
bool isSelected() const { return m_handler.alignment() == m_mode; }
|
|
void click(int button, int time, unsigned int mods) {
|
|
m_handler.setAlignment(m_mode);
|
|
FbTk::RadioMenuItem::click(button, time, mods);
|
|
}
|
|
|
|
private:
|
|
IconbarTool &m_handler;
|
|
FbTk::Container::Alignment m_mode;
|
|
};
|
|
|
|
void setupModeMenu(FbTk::Menu &menu, IconbarTool &handler) {
|
|
using namespace FbTk;
|
|
_FB_USES_NLS;
|
|
|
|
menu.setLabel(_FB_XTEXT(Toolbar, IconbarMode, "Iconbar Mode", "Menu title - chooses which set of icons are shown in the iconbar"));
|
|
|
|
RefCount<Command<void> > saverc_cmd(new FbCommands::SaveResources());
|
|
|
|
|
|
menu.insert(new ToolbarModeMenuItem(_FB_XTEXT(Toolbar, IconbarModeNone,
|
|
"None", "No icons are shown in the iconbar"),
|
|
handler,
|
|
"none", saverc_cmd));
|
|
|
|
menu.insert(new ToolbarModeMenuItem(
|
|
_FB_XTEXT(Toolbar, IconbarModeIcons,
|
|
"Icons", "Iconified windows from all workspaces are shown"),
|
|
handler,
|
|
"{static groups} (minimized=yes)", saverc_cmd));
|
|
|
|
menu.insert(new ToolbarModeMenuItem(
|
|
_FB_XTEXT(Toolbar, IconbarModeNoIcons,
|
|
"NoIcons", "No iconified windows from all workspaces are shown"),
|
|
handler,
|
|
"{static groups} (minimized=no)", saverc_cmd));
|
|
|
|
menu.insert(new ToolbarModeMenuItem(
|
|
_FB_XTEXT(Toolbar, IconbarModeWorkspaceIcons,
|
|
"WorkspaceIcons", "Iconified windows from this workspace are shown"),
|
|
handler,
|
|
"{static groups} (minimized=yes) (workspace)", saverc_cmd));
|
|
|
|
menu.insert(new ToolbarModeMenuItem(
|
|
_FB_XTEXT(Toolbar, IconbarModeWorkspaceNoIcons,
|
|
"WorkspaceNoIcons", "No iconified windows from this workspace are shown"),
|
|
handler,
|
|
"{static groups} (minimized=no) (workspace)", saverc_cmd));
|
|
|
|
menu.insert(new ToolbarModeMenuItem(
|
|
_FB_XTEXT(Toolbar, IconbarModeWorkspace,
|
|
"Workspace", "Normal and iconified windows from this workspace are shown"),
|
|
handler,
|
|
"{static groups} (workspace)", saverc_cmd));
|
|
|
|
menu.insert(new ToolbarModeMenuItem(
|
|
_FB_XTEXT(Toolbar, IconbarModeAllWindows, "All Windows", "All windows are shown"),
|
|
handler,
|
|
"{static groups}", saverc_cmd));
|
|
|
|
menu.insert(new FbTk::MenuSeparator());
|
|
|
|
menu.insert(new ToolbarAlignMenuItem(
|
|
_FB_XTEXT(Align, Left, "Left", "Align to the left"),
|
|
handler,
|
|
FbTk::Container::LEFT, saverc_cmd));
|
|
|
|
menu.insert(new ToolbarAlignMenuItem(
|
|
_FB_XTEXT(Align, Relative, "Relative", "Align relative to the width"),
|
|
handler,
|
|
FbTk::Container::RELATIVE, saverc_cmd));
|
|
|
|
menu.insert(new ToolbarAlignMenuItem(
|
|
_FB_XTEXT(Align, Right, "Right", "Align to the right"),
|
|
handler,
|
|
FbTk::Container::RIGHT, saverc_cmd));
|
|
|
|
menu.insert(new FbTk::MenuSeparator());
|
|
|
|
menu.updateMenu();
|
|
}
|
|
|
|
typedef FbTk::RefCount<FbTk::Command<void> > RefCmd;
|
|
|
|
class ShowMenu: public FbTk::Command<void> {
|
|
public:
|
|
explicit ShowMenu(FluxboxWindow &win):m_win(win) { }
|
|
void execute() {
|
|
// get last button pos
|
|
const XEvent &e = Fluxbox::instance()->lastEvent();
|
|
m_win.popupMenu(e.xbutton.x_root, e.xbutton.y_root);
|
|
}
|
|
private:
|
|
FluxboxWindow &m_win;
|
|
};
|
|
|
|
class FocusCommand: public FbTk::Command<void> {
|
|
public:
|
|
explicit FocusCommand(Focusable &win): m_win(win) { }
|
|
void execute() {
|
|
// this needs to be a local variable, as this object could be destroyed
|
|
// if the workspace is changed.
|
|
FluxboxWindow *fbwin = m_win.fbwindow();
|
|
if (!fbwin)
|
|
return;
|
|
if (m_win.isFocused())
|
|
fbwin->iconify();
|
|
else {
|
|
m_win.focus();
|
|
fbwin->raise();
|
|
}
|
|
}
|
|
|
|
private:
|
|
Focusable &m_win;
|
|
};
|
|
|
|
} // end anonymous namespace
|
|
|
|
IconbarTool::IconbarTool(const FbTk::FbWindow &parent, IconbarTheme &theme,
|
|
FbTk::ThemeProxy<IconbarTheme> &focused_theme,
|
|
FbTk::ThemeProxy<IconbarTheme> &unfocused_theme,
|
|
BScreen &screen, FbTk::Menu &menu):
|
|
ToolbarItem(ToolbarItem::RELATIVE),
|
|
m_screen(screen),
|
|
m_icon_container(parent, false),
|
|
m_theme(theme),
|
|
m_focused_theme(focused_theme),
|
|
m_unfocused_theme(unfocused_theme),
|
|
m_empty_pm( screen.imageControl() ),
|
|
m_winlist(new FocusableList(screen)),
|
|
m_mode("none"),
|
|
m_rc_mode(screen.resourceManager(), "{static groups} (workspace)",
|
|
screen.name() + ".iconbar.mode", screen.altName() + ".Iconbar.Mode"),
|
|
m_rc_alignment(screen.resourceManager(), FbTk::Container::RELATIVE,
|
|
screen.name() + ".iconbar.alignment", screen.altName() + ".Iconbar.Alignment"),
|
|
m_rc_client_width(screen.resourceManager(), 128,
|
|
screen.name() + ".iconbar.iconWidth", screen.altName() + ".Iconbar.IconWidth"),
|
|
m_rc_client_padding(screen.resourceManager(), 10,
|
|
screen.name() + ".iconbar.iconTextPadding", screen.altName() + ".Iconbar.IconTextPadding"),
|
|
m_rc_use_pixmap(screen.resourceManager(), true,
|
|
screen.name() + ".iconbar.usePixmap", screen.altName() + ".Iconbar.UsePixmap"),
|
|
m_menu(screen.menuTheme(), screen.imageControl(),
|
|
*screen.layerManager().getLayer(Layer::MENU)),
|
|
m_alpha(255) {
|
|
|
|
// setup mode menu
|
|
setupModeMenu(m_menu, *this);
|
|
_FB_USES_NLS;
|
|
using namespace FbTk;
|
|
// setup use pixmap item to reconfig iconbar and save resource on click
|
|
MacroCommand *save_and_reconfig = new MacroCommand();
|
|
RefCount<Command<void> > reconfig(new SimpleCommand<IconbarTool>(*this, &IconbarTool::renderTheme));
|
|
RefCount<Command<void> > save(FbTk::CommandParser<void>::instance().parse("saverc"));
|
|
save_and_reconfig->add(reconfig);
|
|
save_and_reconfig->add(save);
|
|
RefCount<Command<void> > s_and_reconfig(save_and_reconfig);
|
|
m_menu.insert(new FbTk::BoolMenuItem(_FB_XTEXT(Toolbar, ShowIcons,
|
|
"Show Pictures", "chooses if little icons are shown next to title in the iconbar"),
|
|
m_rc_use_pixmap, s_and_reconfig));
|
|
m_menu.updateMenu();
|
|
// must be internal menu, otherwise toolbar main menu tries to delete it.
|
|
m_menu.setInternalMenu();
|
|
|
|
// add iconbar menu to toolbar menu
|
|
menu.insert(m_menu.label().logical(), &m_menu);
|
|
|
|
// setup signals
|
|
theme.reconfigSig().attach(this);
|
|
focused_theme.reconfigSig().attach(this);
|
|
unfocused_theme.reconfigSig().attach(this);
|
|
setMode(*m_rc_mode);
|
|
}
|
|
|
|
IconbarTool::~IconbarTool() {
|
|
deleteIcons();
|
|
}
|
|
|
|
void IconbarTool::move(int x, int y) {
|
|
m_icon_container.move(x, y);
|
|
}
|
|
|
|
void IconbarTool::resize(unsigned int width, unsigned int height) {
|
|
m_icon_container.resize(width, height);
|
|
m_icon_container.setMaxTotalSize(m_icon_container.orientation() == FbTk::ROT0 || m_icon_container.orientation() ? width : height);
|
|
renderTheme();
|
|
}
|
|
|
|
void IconbarTool::moveResize(int x, int y,
|
|
unsigned int width, unsigned int height) {
|
|
|
|
m_icon_container.moveResize(x, y, width, height);
|
|
m_icon_container.setMaxTotalSize(m_icon_container.orientation() == FbTk::ROT0 || m_icon_container.orientation() ? width : height);
|
|
renderTheme();
|
|
}
|
|
|
|
void IconbarTool::show() {
|
|
m_icon_container.show();
|
|
}
|
|
|
|
void IconbarTool::hide() {
|
|
m_icon_container.hide();
|
|
}
|
|
|
|
void IconbarTool::setAlignment(FbTk::Container::Alignment align) {
|
|
*m_rc_alignment = align;
|
|
update(0);
|
|
m_menu.reconfigure();
|
|
}
|
|
|
|
void IconbarTool::setMode(string mode) {
|
|
if (mode == m_mode)
|
|
return;
|
|
|
|
*m_rc_mode = m_mode = mode;
|
|
|
|
// lock graphics update
|
|
m_icon_container.setUpdateLock(true);
|
|
|
|
if (m_winlist.get()) {
|
|
m_winlist->addSig().detach(this);
|
|
m_winlist->removeSig().detach(this);
|
|
m_winlist->orderSig().detach(this);
|
|
m_winlist->resetSig().detach(this);
|
|
}
|
|
if (mode == "none")
|
|
m_winlist.reset(new FocusableList(m_screen));
|
|
else
|
|
m_winlist.reset(new FocusableList(m_screen,
|
|
mode + " (iconhidden=no)"));
|
|
if (m_winlist.get()) {
|
|
m_winlist->addSig().attach(this);
|
|
m_winlist->removeSig().attach(this);
|
|
m_winlist->orderSig().attach(this);
|
|
m_winlist->resetSig().attach(this);
|
|
}
|
|
reset();
|
|
|
|
// unlock graphics update
|
|
m_icon_container.setUpdateLock(false);
|
|
m_icon_container.update();
|
|
m_icon_container.showSubwindows();
|
|
|
|
renderTheme();
|
|
|
|
m_menu.reconfigure();
|
|
}
|
|
|
|
unsigned int IconbarTool::width() const {
|
|
return m_icon_container.width();
|
|
}
|
|
|
|
unsigned int IconbarTool::height() const {
|
|
return m_icon_container.height();
|
|
}
|
|
|
|
unsigned int IconbarTool::borderWidth() const {
|
|
return m_icon_container.borderWidth();
|
|
}
|
|
|
|
void IconbarTool::update(FbTk::Subject *subj) {
|
|
// ignore updates if we're shutting down
|
|
if (m_screen.isShuttingdown()) {
|
|
if (!m_icons.empty())
|
|
deleteIcons();
|
|
return;
|
|
}
|
|
|
|
m_icon_container.setAlignment(*m_rc_alignment);
|
|
|
|
*m_rc_client_width = FbTk::Util::clamp(*m_rc_client_width, 10, 400);
|
|
m_icon_container.setMaxSizePerClient(*m_rc_client_width);
|
|
|
|
if (subj == &m_focused_theme.reconfigSig() ||
|
|
subj == &m_unfocused_theme.reconfigSig() ||
|
|
subj == &m_theme.reconfigSig()) {
|
|
setMode(*m_rc_mode);
|
|
return;
|
|
}
|
|
|
|
// lock graphic update
|
|
m_icon_container.setUpdateLock(true);
|
|
|
|
if (subj && typeid(*subj) == typeid(FocusableList::FocusableListSubject)) {
|
|
FocusableList::FocusableListSubject *fsubj =
|
|
static_cast<FocusableList::FocusableListSubject *>(subj);
|
|
if (subj == &m_winlist->addSig())
|
|
insertWindow(*fsubj->win());
|
|
else if (subj == &m_winlist->removeSig())
|
|
removeWindow(*fsubj->win());
|
|
else if (subj == &m_winlist->resetSig())
|
|
reset();
|
|
else if (subj == &m_winlist->orderSig())
|
|
insertWindow(*fsubj->win());
|
|
}
|
|
|
|
// unlock container and update graphics
|
|
m_icon_container.setUpdateLock(false);
|
|
m_icon_container.update();
|
|
m_icon_container.showSubwindows();
|
|
|
|
// another renderTheme we hopefully shouldn't need? These renders
|
|
// should be done individually above
|
|
|
|
// nope, we still need it (or at least I'm not bothering to fix it yet)
|
|
// a new IconButton doesn't get resized properly until the
|
|
// m_icon_container.update() above; then, it never runs drawText() again,
|
|
// so text can end up behind program icons
|
|
renderTheme();
|
|
}
|
|
|
|
void IconbarTool::insertWindow(Focusable &win, int pos) {
|
|
IconButton *button = 0;
|
|
|
|
IconMap::iterator icon_it = m_icons.find(&win);
|
|
if (icon_it != m_icons.end())
|
|
button = icon_it->second;
|
|
|
|
if (button)
|
|
m_icon_container.removeItem(button);
|
|
else
|
|
button = makeButton(win);
|
|
if (!button) return;
|
|
|
|
if (pos == -2) {
|
|
pos = 0;
|
|
list<Focusable *>::iterator it = m_winlist->clientList().begin(),
|
|
it_end = m_winlist->clientList().end();
|
|
for (; it != it_end && *it != &win; ++it)
|
|
pos++;
|
|
}
|
|
|
|
m_icon_container.insertItem(button, pos);
|
|
}
|
|
|
|
void IconbarTool::reset() {
|
|
deleteIcons();
|
|
updateList();
|
|
}
|
|
|
|
void IconbarTool::updateSizing() {
|
|
m_icon_container.setBorderWidth(m_theme.border().width());
|
|
m_icon_container.setBorderColor(m_theme.border().color());
|
|
|
|
FbTk::STLUtil::forAll(m_icons,
|
|
FbTk::Compose(std::mem_fun(&IconButton::reconfigTheme),
|
|
FbTk::Select2nd<IconMap::value_type>()));
|
|
|
|
}
|
|
|
|
void IconbarTool::renderTheme(int alpha) {
|
|
|
|
m_alpha = alpha;
|
|
renderTheme();
|
|
}
|
|
|
|
void IconbarTool::renderTheme() {
|
|
|
|
// update button sizes before we get max width per client!
|
|
updateSizing();
|
|
|
|
// if we dont have any icons then we should render empty texture
|
|
if (!m_theme.emptyTexture().usePixmap()) {
|
|
m_empty_pm.reset( 0 );
|
|
m_icon_container.setBackgroundColor(m_theme.emptyTexture().color());
|
|
} else {
|
|
m_empty_pm.reset(m_screen.imageControl().
|
|
renderImage(m_icon_container.width(),
|
|
m_icon_container.height(),
|
|
m_theme.emptyTexture(), orientation()));
|
|
m_icon_container.setBackgroundPixmap(m_empty_pm);
|
|
}
|
|
|
|
m_icon_container.setAlpha(m_alpha);
|
|
|
|
// update buttons
|
|
IconMap::iterator icon_it = m_icons.begin();
|
|
const IconMap::iterator icon_it_end = m_icons.end();
|
|
for (; icon_it != icon_it_end; ++icon_it)
|
|
renderButton(*icon_it->second);
|
|
|
|
}
|
|
|
|
void IconbarTool::renderButton(IconButton &button, bool clear) {
|
|
|
|
button.setPixmap(*m_rc_use_pixmap);
|
|
button.setTextPadding(*m_rc_client_padding);
|
|
button.reconfigTheme();
|
|
if (clear)
|
|
button.clear(); // the clear also updates transparent
|
|
}
|
|
|
|
void IconbarTool::deleteIcons() {
|
|
m_icon_container.removeAll();
|
|
FbTk::STLUtil::destroyAndClearSecond(m_icons);
|
|
}
|
|
|
|
void IconbarTool::removeWindow(Focusable &win) {
|
|
// got window die signal, lets find and remove the window
|
|
IconMap::iterator it = m_icons.find(&win);
|
|
if (it == m_icons.end())
|
|
return;
|
|
|
|
fbdbg<<"IconbarTool::"<<__FUNCTION__<<"( 0x"<<&win<<" title = "<<win.title().logical()<<") found!"<<endl;
|
|
|
|
// remove from list and render theme again
|
|
IconButton *button = it->second;
|
|
m_icons.erase(it);
|
|
m_icon_container.removeItem(button);
|
|
delete button;
|
|
}
|
|
|
|
IconButton *IconbarTool::makeButton(Focusable &win) {
|
|
// we just want windows that have clients
|
|
FluxboxWindow *fbwin = win.fbwindow();
|
|
if (!fbwin || fbwin->clientList().empty())
|
|
return 0;
|
|
|
|
fbdbg<<"IconbarTool::addWindow(0x"<<&win<<" title = "<<win.title().logical()<<")"<<endl;
|
|
|
|
IconButton *button = new IconButton(m_icon_container, m_focused_theme,
|
|
m_unfocused_theme, win);
|
|
|
|
RefCmd focus_cmd(new ::FocusCommand(win));
|
|
RefCmd menu_cmd(new ::ShowMenu(*fbwin));
|
|
button->setOnClick(focus_cmd, 1);
|
|
button->setOnClick(menu_cmd, 3);
|
|
|
|
renderButton(*button, false); // update the attributes, but don't clear it
|
|
m_icons[&win] = button;
|
|
return button;
|
|
}
|
|
|
|
void IconbarTool::updateList() {
|
|
list<Focusable *>::iterator it = m_winlist->clientList().begin();
|
|
list<Focusable *>::iterator it_end = m_winlist->clientList().end();
|
|
for (; it != it_end; ++it) {
|
|
if ((*it)->fbwindow())
|
|
insertWindow(**it, -1);
|
|
}
|
|
|
|
renderTheme();
|
|
}
|
|
|
|
void IconbarTool::setOrientation(FbTk::Orientation orient) {
|
|
m_icon_container.setOrientation(orient);
|
|
ToolbarItem::setOrientation(orient);
|
|
}
|
|
|