aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog5
-rw-r--r--src/Screen.cc77
-rw-r--r--src/Screen.hh3
-rw-r--r--src/Slit.cc15
-rw-r--r--src/Slit.hh7
-rw-r--r--src/Toolbar.cc12
-rw-r--r--src/Toolbar.hh7
-rw-r--r--src/Xinerama.hh18
-rw-r--r--src/fluxbox.cc1
9 files changed, 122 insertions, 23 deletions
diff --git a/ChangeLog b/ChangeLog
index 687ef64..bdf4ff9 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,10 @@
1 (Format: Year/Month/Day) 1 (Format: Year/Month/Day)
2Changes for 1.0.1: 2Changes for 1.0.1:
3*08/01/27:
4 * Reload the Xinerama layout on RandR signal (Tomas)
5 Screen.cc/hh
6 * Move windows out of inactive heads upon Xinerama layout change (Tomas)
7 Screen.cc/hh
3*07/12/28: 8*07/12/28:
4 * Added new key command :Delay {<command>} [<int>], which runs the command 9 * Added new key command :Delay {<command>} [<int>], which runs the command
5 after a delay of <int> microseconds (default is 200 milliseconds) (Mark) 10 after a delay of <int> microseconds (default is 200 milliseconds) (Mark)
diff --git a/src/Screen.cc b/src/Screen.cc
index e3b08c6..36b1918 100644
--- a/src/Screen.cc
+++ b/src/Screen.cc
@@ -360,6 +360,12 @@ BScreen::BScreen(FbTk::ResourceManager &rm,
360 Display *disp = m_root_window.display(); 360 Display *disp = m_root_window.display();
361 Fluxbox *fluxbox = Fluxbox::instance(); 361 Fluxbox *fluxbox = Fluxbox::instance();
362 362
363 // TODO fluxgen: check if this is the right place (it was not -lis)
364 //
365 // Create the first one, initXinerama will expand this if needed.
366 m_head_areas.resize(1);
367 m_head_areas[0] = new HeadArea();
368
363 initXinerama(); 369 initXinerama();
364 370
365 // setup error handler to catch "screen already managed by other wm" 371 // setup error handler to catch "screen already managed by other wm"
@@ -402,9 +408,6 @@ BScreen::BScreen(FbTk::ResourceManager &rm,
402 XFree(ret_prop); 408 XFree(ret_prop);
403 } 409 }
404 410
405 // TODO fluxgen: check if this is the right place
406 m_head_areas = new HeadArea[numHeads() ? numHeads() : 1];
407
408#ifdef HAVE_RANDR 411#ifdef HAVE_RANDR
409 // setup RANDR for this screens root window 412 // setup RANDR for this screens root window
410 // we need to determine if we should use old randr select input function or not 413 // we need to determine if we should use old randr select input function or not
@@ -600,7 +603,8 @@ BScreen::~BScreen() {
600 m_slit.reset(0); 603 m_slit.reset(0);
601 604
602 // TODO fluxgen: check if this is the right place 605 // TODO fluxgen: check if this is the right place
603 delete [] m_head_areas; 606 for (int i = 0; i < m_head_areas.size(); i++)
607 delete m_head_areas[i];
604 608
605 delete m_focus_control; 609 delete m_focus_control;
606 delete m_placement_strategy; 610 delete m_placement_strategy;
@@ -722,7 +726,12 @@ unsigned int BScreen::currentWorkspaceID() const {
722} 726}
723 727
724const Strut* BScreen::availableWorkspaceArea(int head) const { 728const Strut* BScreen::availableWorkspaceArea(int head) const {
725 return m_head_areas[head ? head-1 : 0].availableWorkspaceArea(); 729 if (head > numHeads()) {
730 /* May this ever happen? */
731 static Strut whole(-1 /* should never be used */, 0, width(), 0, height());
732 return &whole;
733 }
734 return m_head_areas[head ? head-1 : 0]->availableWorkspaceArea();
726} 735}
727 736
728unsigned int BScreen::maxLeft(int head) const { 737unsigned int BScreen::maxLeft(int head) const {
@@ -1412,7 +1421,7 @@ Strut *BScreen::requestStrut(int head, int left, int right, int top, int bottom)
1412 1421
1413 Strut* next = 0; 1422 Strut* next = 0;
1414 for (int i = begin; i != end; i++) { 1423 for (int i = begin; i != end; i++) {
1415 next = m_head_areas[i].requestStrut(i+1, left, right, top, bottom, next); 1424 next = m_head_areas[i]->requestStrut(i+1, left, right, top, bottom, next);
1416 } 1425 }
1417 1426
1418 return next; 1427 return next;
@@ -1422,7 +1431,9 @@ void BScreen::clearStrut(Strut *str) {
1422 if (str->next()) 1431 if (str->next())
1423 clearStrut(str->next()); 1432 clearStrut(str->next());
1424 int head = str->head() ? str->head() - 1 : 0; 1433 int head = str->head() ? str->head() - 1 : 0;
1425 m_head_areas[head].clearStrut(str); 1434 /* The number of heads may have changed, be careful. */
1435 if (head < numHeads())
1436 m_head_areas[head]->clearStrut(str);
1426 // str is invalid now 1437 // str is invalid now
1427} 1438}
1428 1439
@@ -1431,7 +1442,7 @@ void BScreen::updateAvailableWorkspaceArea() {
1431 bool updated = false; 1442 bool updated = false;
1432 1443
1433 for (size_t i = 0; i < n; i++) { 1444 for (size_t i = 0; i < n; i++) {
1434 updated = m_head_areas[i].updateAvailableWorkspaceArea() || updated; 1445 updated = m_head_areas[i]->updateAvailableWorkspaceArea() || updated;
1435 } 1446 }
1436 1447
1437 if (updated) 1448 if (updated)
@@ -1886,6 +1897,9 @@ void BScreen::renderPosWindow() {
1886} 1897}
1887 1898
1888void BScreen::updateSize() { 1899void BScreen::updateSize() {
1900 // update xinerama layout
1901 initXinerama();
1902
1889 // force update geometry 1903 // force update geometry
1890 rootWindow().updateGeometry(); 1904 rootWindow().updateGeometry();
1891 1905
@@ -1895,6 +1909,9 @@ void BScreen::updateSize() {
1895 // send resize notify 1909 // send resize notify
1896 m_resize_sig.notify(); 1910 m_resize_sig.notify();
1897 m_workspace_area_sig.notify(); 1911 m_workspace_area_sig.notify();
1912
1913 // move windows out of inactive heads
1914 clearHeads();
1898} 1915}
1899 1916
1900 1917
@@ -1941,10 +1958,13 @@ void BScreen::initXinerama() {
1941 Display *display = FbTk::App::instance()->display(); 1958 Display *display = FbTk::App::instance()->display();
1942 1959
1943 if (!XineramaIsActive(display)) { 1960 if (!XineramaIsActive(display)) {
1961notactive:
1944#ifdef DEBUG 1962#ifdef DEBUG
1945 cerr<<"BScreen::initXinerama(): dont have Xinerama"<<endl; 1963 cerr<<"BScreen::initXinerama(): dont have Xinerama"<<endl;
1946#endif // DEBUG 1964#endif // DEBUG
1947 m_xinerama_avail = false; 1965 m_xinerama_avail = false;
1966 if (m_xinerama_headinfo)
1967 delete [] m_xinerama_headinfo;
1948 m_xinerama_headinfo = 0; 1968 m_xinerama_headinfo = 0;
1949 m_xinerama_num_heads = 0; 1969 m_xinerama_num_heads = 0;
1950 return; 1970 return;
@@ -1957,6 +1977,19 @@ void BScreen::initXinerama() {
1957 XineramaScreenInfo *screen_info; 1977 XineramaScreenInfo *screen_info;
1958 int number; 1978 int number;
1959 screen_info = XineramaQueryScreens(display, &number); 1979 screen_info = XineramaQueryScreens(display, &number);
1980
1981 /* The call may have actually failed. If this is the first time we init
1982 * Xinerama, fall back to turning it off. If not, pretend nothing
1983 * happened -- another event will tell us and it will work then. */
1984 if (!screen_info) {
1985 if (m_xinerama_headinfo)
1986 return;
1987 else
1988 goto notactive;
1989 }
1990
1991 if (m_xinerama_headinfo)
1992 delete [] m_xinerama_headinfo;
1960 m_xinerama_headinfo = new XineramaHeadInfo[number]; 1993 m_xinerama_headinfo = new XineramaHeadInfo[number];
1961 m_xinerama_num_heads = number; 1994 m_xinerama_num_heads = number;
1962 for (int i=0; i < number; i++) { 1995 for (int i=0; i < number; i++) {
@@ -1970,6 +2003,18 @@ void BScreen::initXinerama() {
1970 cerr<<"BScreen::initXinerama(): number of heads ="<<number<<endl; 2003 cerr<<"BScreen::initXinerama(): number of heads ="<<number<<endl;
1971#endif // DEBUG 2004#endif // DEBUG
1972 2005
2006 /* Reallocate to the new number of heads. */
2007 int ha_num = numHeads() ? numHeads() : 1, ha_oldnum = m_head_areas.size();
2008 if (ha_num > ha_oldnum) {
2009 m_head_areas.resize(ha_num);
2010 for (int i = ha_oldnum; i < ha_num; i++)
2011 m_head_areas[i] = new HeadArea();
2012 } else if (ha_num < ha_oldnum) {
2013 for (int i = ha_num; i < ha_oldnum; i++)
2014 delete m_head_areas[i];
2015 m_head_areas.resize(ha_num);
2016 }
2017
1973#else // XINERAMA 2018#else // XINERAMA
1974 // no xinerama 2019 // no xinerama
1975 m_xinerama_avail = false; 2020 m_xinerama_avail = false;
@@ -1978,6 +2023,22 @@ void BScreen::initXinerama() {
1978 2023
1979} 2024}
1980 2025
2026/* Move windows out of inactive heads */
2027void BScreen::clearHeads() {
2028 if (!hasXinerama()) return;
2029
2030 for (Workspaces::iterator i = m_workspaces_list.begin();
2031 i != m_workspaces_list.end(); i++) {
2032 for (Workspace::Windows::iterator win = (*i)->windowList().begin();
2033 win != (*i)->windowList().end(); win++) {
2034 if (getHead((*win)->fbWindow()) == 0) {
2035 // first head is a safe bet here
2036 (*win)->placeWindow(1);
2037 }
2038 }
2039 }
2040}
2041
1981int BScreen::getHead(int x, int y) const { 2042int BScreen::getHead(int x, int y) const {
1982 if (!hasXinerama()) return 0; 2043 if (!hasXinerama()) return 0;
1983#ifdef XINERAMA 2044#ifdef XINERAMA
diff --git a/src/Screen.hh b/src/Screen.hh
index a503285..f0dbcc6 100644
--- a/src/Screen.hh
+++ b/src/Screen.hh
@@ -406,6 +406,7 @@ public:
406 int numHeads() const { return m_xinerama_num_heads; } 406 int numHeads() const { return m_xinerama_num_heads; }
407 407
408 void initXinerama(); 408 void initXinerama();
409 void clearHeads();
409 /** 410 /**
410 * Determines head number for a position 411 * Determines head number for a position
411 * @param x position in pixels on the screen 412 * @param x position in pixels on the screen
@@ -590,7 +591,7 @@ private:
590 int m_xinerama_num_heads; 591 int m_xinerama_num_heads;
591 int m_xinerama_center_x, m_xinerama_center_y; 592 int m_xinerama_center_x, m_xinerama_center_y;
592 593
593 HeadArea *m_head_areas; 594 std::vector<HeadArea *> m_head_areas;
594 595
595 struct XineramaHeadInfo { 596 struct XineramaHeadInfo {
596 int x, y, width, height; 597 int x, y, width, height;
diff --git a/src/Slit.cc b/src/Slit.cc
index 3f371c1..ecd8280 100644
--- a/src/Slit.cc
+++ b/src/Slit.cc
@@ -48,10 +48,6 @@
48#include "RootTheme.hh" 48#include "RootTheme.hh"
49#include "FbMenu.hh" 49#include "FbMenu.hh"
50 50
51#ifdef XINERAMA
52#include "Xinerama.hh"
53#endif // XINERAMA
54
55#include "SlitTheme.hh" 51#include "SlitTheme.hh"
56#include "SlitClient.hh" 52#include "SlitClient.hh"
57#include "Xutil.hh" 53#include "Xutil.hh"
@@ -270,6 +266,9 @@ Slit::Slit(BScreen &scr, FbTk::XLayer &layer, const char *filename)
270 m_slitmenu(scr.menuTheme(), 266 m_slitmenu(scr.menuTheme(),
271 scr.imageControl(), 267 scr.imageControl(),
272 *scr.layerManager().getLayer(Layer::MENU)), 268 *scr.layerManager().getLayer(Layer::MENU)),
269#ifdef XINERAMA
270 m_xineramaheadmenu(0),
271#endif // XINERAMA
273 frame(scr.rootWindow()), 272 frame(scr.rootWindow()),
274 //For KDE dock applets 273 //For KDE dock applets
275 m_kwm1_dockwindow(XInternAtom(FbTk::App::instance()->display(), 274 m_kwm1_dockwindow(XInternAtom(FbTk::App::instance()->display(),
@@ -482,8 +481,7 @@ void Slit::addClient(Window w) {
482 if (wmhints != 0) { 481 if (wmhints != 0) {
483 if ((wmhints->flags & IconWindowHint) && 482 if ((wmhints->flags & IconWindowHint) &&
484 (wmhints->icon_window != None)) { 483 (wmhints->icon_window != None)) {
485 XMoveWindow(disp, client->clientWindow(), screen().width() + 10, 484 XMoveWindow(disp, client->clientWindow(), -100, -100);
486 screen().height() + 10);
487 XMapWindow(disp, client->clientWindow()); 485 XMapWindow(disp, client->clientWindow());
488 client->setIconWindow(wmhints->icon_window); 486 client->setIconWindow(wmhints->icon_window);
489 client->setWindow(client->iconWindow()); 487 client->setWindow(client->iconWindow());
@@ -1113,6 +1111,10 @@ void Slit::exposeEvent(XExposeEvent &ev) {
1113 1111
1114void Slit::update(FbTk::Subject *subj) { 1112void Slit::update(FbTk::Subject *subj) {
1115 reconfigure(); 1113 reconfigure();
1114#ifdef XINERAMA
1115 if (subj == &m_screen.resizeSig() && m_xineramaheadmenu)
1116 m_xineramaheadmenu->reloadHeads();
1117#endif // XINERAMA
1116} 1118}
1117 1119
1118void Slit::clearWindow() { 1120void Slit::clearWindow() {
@@ -1254,6 +1256,7 @@ void Slit::setupMenu() {
1254#ifdef XINERAMA 1256#ifdef XINERAMA
1255 if (screen().hasXinerama()) { 1257 if (screen().hasXinerama()) {
1256 m_slitmenu.insert(_FB_XTEXT(Menu, OnHead, "On Head...", "Title of On Head menu"), 1258 m_slitmenu.insert(_FB_XTEXT(Menu, OnHead, "On Head...", "Title of On Head menu"),
1259 m_xineramaheadmenu =
1257 new XineramaHeadMenu<Slit>( 1260 new XineramaHeadMenu<Slit>(
1258 screen().menuTheme(), 1261 screen().menuTheme(),
1259 screen(), 1262 screen(),
diff --git a/src/Slit.hh b/src/Slit.hh
index 64ae6b4..a7f8e40 100644
--- a/src/Slit.hh
+++ b/src/Slit.hh
@@ -29,6 +29,10 @@
29#include "LayerMenu.hh" 29#include "LayerMenu.hh"
30#include "Layer.hh" 30#include "Layer.hh"
31 31
32#ifdef XINERAMA
33#include "Xinerama.hh"
34#endif // XINERAMA
35
32#include "FbTk/Menu.hh" 36#include "FbTk/Menu.hh"
33#include "FbTk/FbWindow.hh" 37#include "FbTk/FbWindow.hh"
34#include "FbTk/Timer.hh" 38#include "FbTk/Timer.hh"
@@ -144,6 +148,9 @@ private:
144 SlitClients m_client_list; 148 SlitClients m_client_list;
145 std::auto_ptr<LayerMenu> m_layermenu; 149 std::auto_ptr<LayerMenu> m_layermenu;
146 FbMenu m_clientlist_menu, m_slitmenu; 150 FbMenu m_clientlist_menu, m_slitmenu;
151#ifdef XINERAMA
152 XineramaHeadMenu<Slit> *m_xineramaheadmenu;
153#endif // XINERAMA
147 std::string m_filename; 154 std::string m_filename;
148 155
149 struct frame { 156 struct frame {
diff --git a/src/Toolbar.cc b/src/Toolbar.cc
index 2c7bde2..00cdfee 100644
--- a/src/Toolbar.cc
+++ b/src/Toolbar.cc
@@ -35,10 +35,6 @@
35#include "Screen.hh" 35#include "Screen.hh"
36#include "WindowCmd.hh" 36#include "WindowCmd.hh"
37 37
38#ifdef XINERAMA
39#include "Xinerama.hh"
40#endif // XINERAMA
41
42#include "Strut.hh" 38#include "Strut.hh"
43#include "FbTk/CommandParser.hh" 39#include "FbTk/CommandParser.hh"
44#include "Layer.hh" 40#include "Layer.hh"
@@ -216,6 +212,9 @@ Toolbar::Toolbar(BScreen &scrn, FbTk::XLayer &layer, size_t width):
216 m_toolbarmenu(scrn.menuTheme(), 212 m_toolbarmenu(scrn.menuTheme(),
217 scrn.imageControl(), 213 scrn.imageControl(),
218 *scrn.layerManager().getLayer(Layer::MENU)), 214 *scrn.layerManager().getLayer(Layer::MENU)),
215#ifdef XINERAMA
216 m_xineramaheadmenu(0),
217#endif // XINERAMA
219 m_theme(scrn.screenNumber()), 218 m_theme(scrn.screenNumber()),
220 m_tool_factory(scrn), 219 m_tool_factory(scrn),
221 m_strut(0), 220 m_strut(0),
@@ -629,6 +628,10 @@ void Toolbar::update(FbTk::Subject *subj) {
629 else 628 else
630 reconfigure(); 629 reconfigure();
631 630
631#ifdef XINERAMA
632 if (subj == &m_screen.resizeSig() && m_xineramaheadmenu)
633 m_xineramaheadmenu->reloadHeads();
634#endif // XINERAMA
632} 635}
633 636
634void Toolbar::setPlacement(Toolbar::Placement where) { 637void Toolbar::setPlacement(Toolbar::Placement where) {
@@ -872,6 +875,7 @@ void Toolbar::setupMenus(bool skip_new_placement) {
872#ifdef XINERAMA 875#ifdef XINERAMA
873 if (screen().hasXinerama()) { 876 if (screen().hasXinerama()) {
874 menu().insert(_FB_XTEXT(Menu, OnHead, "On Head...", "Title of On Head menu"), 877 menu().insert(_FB_XTEXT(Menu, OnHead, "On Head...", "Title of On Head menu"),
878 m_xineramaheadmenu =
875 new XineramaHeadMenu<Toolbar>(screen().menuTheme(), 879 new XineramaHeadMenu<Toolbar>(screen().menuTheme(),
876 screen(), 880 screen(),
877 screen().imageControl(), 881 screen().imageControl(),
diff --git a/src/Toolbar.hh b/src/Toolbar.hh
index c528102..c295e24 100644
--- a/src/Toolbar.hh
+++ b/src/Toolbar.hh
@@ -31,6 +31,10 @@
31#include "ToolTheme.hh" 31#include "ToolTheme.hh"
32#include "Layer.hh" 32#include "Layer.hh"
33 33
34#ifdef XINERAMA
35#include "Xinerama.hh"
36#endif // XINERAMA
37
34#include "FbTk/Timer.hh" 38#include "FbTk/Timer.hh"
35#include "FbTk/Resource.hh" 39#include "FbTk/Resource.hh"
36#include "FbTk/Observer.hh" 40#include "FbTk/Observer.hh"
@@ -159,6 +163,9 @@ private:
159 FbTk::XLayerItem m_layeritem; ///< layer item, must be declared before layermenu 163 FbTk::XLayerItem m_layeritem; ///< layer item, must be declared before layermenu
160 LayerMenu m_layermenu; 164 LayerMenu m_layermenu;
161 FbMenu m_placementmenu, m_toolbarmenu; 165 FbMenu m_placementmenu, m_toolbarmenu;
166#ifdef XINERAMA
167 XineramaHeadMenu<Toolbar> *m_xineramaheadmenu;
168#endif // XINERAMA
162 169
163 170
164 // themes 171 // themes
diff --git a/src/Xinerama.hh b/src/Xinerama.hh
index f58b4f0..eab2e37 100644
--- a/src/Xinerama.hh
+++ b/src/Xinerama.hh
@@ -25,6 +25,7 @@
25 25
26#include "FbMenu.hh" 26#include "FbMenu.hh"
27#include "fluxbox.hh" 27#include "fluxbox.hh"
28#include "Screen.hh"
28 29
29#include "FbTk/RefCount.hh" 30#include "FbTk/RefCount.hh"
30#include "FbTk/SimpleCommand.hh" 31#include "FbTk/SimpleCommand.hh"
@@ -59,14 +60,16 @@ private:
59 60
60/// Create a xinerama menu 61/// Create a xinerama menu
61template <typename ItemType> 62template <typename ItemType>
62class XineramaHeadMenu : public FbMenu { 63class XineramaHeadMenu : public ToggleMenu {
63public: 64public:
64 XineramaHeadMenu(FbTk::ThemeProxy<FbTk::MenuTheme> &tm, BScreen &screen, 65 XineramaHeadMenu(FbTk::ThemeProxy<FbTk::MenuTheme> &tm, BScreen &screen,
65 FbTk::ImageControl &imgctrl, FbTk::XLayer &layer, 66 FbTk::ImageControl &imgctrl, FbTk::XLayer &layer,
66 ItemType &item, const FbTk::FbString & title = ""); 67 ItemType &item, const FbTk::FbString & title = "");
68 void reloadHeads();
67 69
68private: 70private:
69 ItemType &m_object; 71 ItemType &m_object;
72 BScreen &m_screen;
70}; 73};
71 74
72 75
@@ -75,15 +78,22 @@ XineramaHeadMenu<ItemType>::XineramaHeadMenu(
75 FbTk::ThemeProxy<FbTk::MenuTheme> &tm, BScreen &screen, 78 FbTk::ThemeProxy<FbTk::MenuTheme> &tm, BScreen &screen,
76 FbTk::ImageControl &imgctrl, FbTk::XLayer &layer, ItemType &item, 79 FbTk::ImageControl &imgctrl, FbTk::XLayer &layer, ItemType &item,
77 const FbTk::FbString & title): 80 const FbTk::FbString & title):
78 FbMenu(tm, imgctrl, layer), 81 ToggleMenu(tm, imgctrl, layer),
79 m_object(item) 82 m_object(item), m_screen(screen)
80{ 83{
81 setLabel(title); 84 setLabel(title);
85 reloadHeads();
86}
87
88template <typename ItemType>
89void XineramaHeadMenu<ItemType>::reloadHeads()
90{
91 removeAll();
82 FbTk::RefCount<FbTk::Command<void> > saverc_cmd(new FbTk::SimpleCommand<Fluxbox>( 92 FbTk::RefCount<FbTk::Command<void> > saverc_cmd(new FbTk::SimpleCommand<Fluxbox>(
83 *Fluxbox::instance(), 93 *Fluxbox::instance(),
84 &Fluxbox::save_rc)); 94 &Fluxbox::save_rc));
85 char tname[128]; 95 char tname[128];
86 for (int i=1; i <= screen.numHeads(); ++i) { 96 for (int i=1; i <= m_screen.numHeads(); ++i) {
87 // TODO: nls 97 // TODO: nls
88/* 98/*
89 sprintf(tname, I18n::instance()-> 99 sprintf(tname, I18n::instance()->
diff --git a/src/fluxbox.cc b/src/fluxbox.cc
index b20a4d9..9775b9d 100644
--- a/src/fluxbox.cc
+++ b/src/fluxbox.cc
@@ -844,6 +844,7 @@ void Fluxbox::handleEvent(XEvent * const e) {
844 844
845#ifdef HAVE_RANDR 845#ifdef HAVE_RANDR
846 if (e->type == m_randr_event_type) { 846 if (e->type == m_randr_event_type) {
847 XRRUpdateConfiguration(e);
847 // update root window size in screen 848 // update root window size in screen
848 BScreen *scr = searchScreen(e->xany.window); 849 BScreen *scr = searchScreen(e->xany.window);
849 if (scr != 0) 850 if (scr != 0)