3 // Baulk - Copyright (C) 2008 - Jacob Alexander
5 // Baulk is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 2 of the License, or
8 // any later version, including version 3 of the License.
10 // Baulk is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program. If not, see <http://www.gnu.org/licenses/>.
20 // Constructors ***********************************************************************************
21 BaulkControl::BaulkControl( QWidget
*parent
) : BaulkWidget( parent
) {
22 // Initialize Baulk Interface Dialog
23 interfaceDialog
= new BaulkInterfaceDialog( this );
25 // Initialize BaulkXML for Config Loading/Saving
26 xmlConfig
= new BaulkXML( "BaulkControl", this );
28 // Start Client if listen name is set
29 connect( this, SIGNAL( serverListenNameSet( QString
) ), this, SLOT( startInformationClient() ) );
32 topVLayout
= new QVBoxLayout
;
33 topHLayout
= new QHBoxLayout
;
34 setLayout( topVLayout
);
35 topVLayout
->addLayout( topHLayout
);
38 tabLayer
= new QStackedWidget
;
39 topHLayout
->addWidget( tabLayer
);
41 // Dynamic Layout Setup
42 dynTopLayout
= new QSplitter( Qt::Horizontal
);
43 dynBotLayout
= new QSplitter( Qt::Vertical
);
44 tabLayer
->addWidget( dynTopLayout
);
45 dynTopLayout
->addWidget( dynBotLayout
);
48 // Layout Property Setup
49 topVLayout
->setContentsMargins( 0,0,0,0 );
50 topHLayout
->setContentsMargins( 0,0,0,0 );
51 dynTopLayout
->setHandleWidth( 1 );
52 dynBotLayout
->setHandleWidth( 1 );
57 // Set Index Fallback default
58 lastKnownGoodIndex
= -1;
61 QString loadName
= xmlConfig
->option("initialWidget").toString();
63 if ( loadName
== "" ) {
64 loadName
= "BaulkStatus";
65 xmlConfig
->setOption( "initialWidget", QVariant( loadName
) );
67 int index
= libraryList().name
.lastIndexOf( QRegExp(".*" + loadName
+ ".*") );
69 loadMainWidget( libraryList().library
[index
] );
71 // Save BaulkXML Config
72 xmlConfig
->saveConfig();
75 // QAction ****************************************************************************************
77 void BaulkControl::setupQActions() {
80 // Calls New Widget Dialog
81 connect( addGlobalAction( tr("New Widget Dialog"), tr("Alt+Meta+P"), true ), SIGNAL( triggered() ),
82 interfaceDialog
, SLOT( newWidgetDialogLoader() ) );
84 // Calls Hotkey Dialog
85 connect( addGlobalAction( tr("Available Actions Dialog"), tr("Alt+Meta+O") ),
86 SIGNAL( triggered() ), interfaceDialog
, SLOT( actionsDialogLoader() ) );
89 // ** Tile Manipulation Hotkeys
92 connect( addGlobalAction( tr("Shift Focus Down"), tr("Alt+Meta+J") ),
93 SIGNAL( triggered() ), this, SLOT( focusDown() ) );
96 connect( addGlobalAction( tr("Shift Focus Right"), tr("Alt+Meta+L") ),
97 SIGNAL( triggered() ), this, SLOT( focusRight() ) );
100 connect( addGlobalAction( tr("Shift Focus Left"), tr("Alt+Meta+H") ),
101 SIGNAL( triggered() ), this, SLOT( focusLeft() ) );
104 connect( addGlobalAction( tr("Shift Focus Up"), tr("Alt+Meta+K") ),
105 SIGNAL( triggered() ), this, SLOT( focusUp() ) );
107 // Swaps Layout Direction on the Bottom Layout
108 connect( addGlobalAction( tr("Orientation Swap Bottom Layout"), tr("Alt+Meta+Space") ),
109 SIGNAL( triggered() ), this, SLOT( swapOrientationBot() ) );
111 // Swaps Layout Direction on the Top Layout
112 connect( addGlobalAction( tr("Orientation Swap Top Layout"), tr("Alt+Meta+Z") ),
113 SIGNAL( triggered() ), this, SLOT( swapOrientationTop() ) );
115 // Remove Focused Widget
116 connect( addGlobalAction( tr("Remove Widget"), tr("Alt+Meta+C") ),
117 SIGNAL( triggered() ), this, SLOT( removeWidget() ) );
120 connect( addGlobalAction( tr("Move Widget Down"), tr("Alt+Shift+J") ),
121 SIGNAL( triggered() ), this, SLOT( moveDown() ) );
124 connect( addGlobalAction( tr("Move Widget Left"), tr("Alt+Shift+H") ),
125 SIGNAL( triggered() ), this, SLOT( moveLeft() ) );
128 connect( addGlobalAction( tr("Move Widget Right"), tr("Alt+Shift+L") ),
129 SIGNAL( triggered() ), this, SLOT( moveRight() ) );
132 connect( addGlobalAction( tr("Move Widget Up"), tr("Alt+Shift+K") ),
133 SIGNAL( triggered() ), this, SLOT( moveUp() ) );
137 // ** Add Action to Global List
138 QAction
*BaulkControl::addGlobalAction( QString title
, QString keyShortcut
, bool globalConnect
) {
139 // Check Config for Hotkey
140 QString key
= xmlConfig
->option( "hotkey", "name", QVariant( title
), false ).toString();
143 xmlConfig
->setOption( "hotkey", QVariant( key
), "name", QVariant( title
) );
147 QAction
*action
= new QAction( title
, this );
148 action
->setShortcut( key
);
150 glbQActions
<< action
;
153 connect( action
, SIGNAL( triggered() ), this, SLOT( globalActionTriggered() ) );
158 // ** Modify Shortcut Key
159 void BaulkControl::modifyGlobalKeyShortcut( int key
, QString keyShortcut
) {
160 // Change Shortcut Key
161 glbQActions
[key
]->setShortcut( keyShortcut
);
163 // Save Setting to Config
164 xmlConfig
->setOption( "hotkey", QVariant( keyShortcut
), "name", QVariant( glbQActions
[key
]->text() ) );
165 xmlConfig
->saveConfig();
168 // Daemon Interaction *****************************************************************************
169 void BaulkControl::startInformationClient() {
170 // Connection to the Daemon
171 InformationClient
*client
= new InformationClient( serverListenName(), this );
173 disconnect( this, SIGNAL( serverListenNameSet( QString
) ), this, SLOT( startInformationClient() ) );
176 // Dynamic Libraries ******************************************************************************
177 void BaulkControl::loadLibraries() {
178 QStringList libraryList
= LibraryLoader( this ).loadableLibraries();
180 for ( int c
= 0; c
< libraryList
.count(); ++c
) {
181 if ( !libraryList
[c
].contains("BaulkControl") ) {
182 QString libName
= libraryList
[c
];
183 LibraryLoader
*library
= new LibraryLoader( libName
, this );
185 // Prevent unbalanced lists
186 if ( libList
.name
.count() != libList
.library
.count() )
187 qFatal( tr("%1\n\tLibList lengths do not match\n\tName: %2\n\tLibrary: %3")
189 .arg( libList
.name
.count() )
190 .arg( libList
.library
.count() )
193 // Prevent Duplicates
194 if ( !libList
.name
.contains( libName
) ) {
195 libList
.name
<< libName
;
196 libList
.library
<< library
;
202 void BaulkControl::loadMainWidget( LibraryLoader
*library
) {
203 // Loads the primary widget of a libray
204 BaulkWidget
*widget
= library
->loadBaulkWidget( "mainWidget", this );
205 dynBotLayout
->addWidget( widget
);
208 int index
= lastKnownGoodIndex
< dynBotLayout
->count() ? lastKnownGoodIndex
+ 1 : 0;
210 // Do not use a focus index if it does not exist
211 if ( index
>= dynBotLayout
->count() - 1 )
212 index
= dynBotLayout
->count() - 1;
214 // Check Focus Index, but maintain current Layout
215 QSplitter
*tmp
= dynBotLayout
;
216 int check
= dynBotIndex();
220 if ( check
== 0 && index
== -1 )
221 dynBotLayout
->widget( 0 )->setFocus();
222 else dynBotLayout
->widget( index
)->setFocus();
225 // Tile Manipulation Control **********************************************************************
227 void BaulkControl::swapOrientationBot() {
228 switch ( dynBotLayout
->orientation() ) {
230 dynBotLayout
->setOrientation( Qt::Vertical
);
233 dynBotLayout
->setOrientation( Qt::Horizontal
);
236 qCritical( tr("%1\n\tInvalid Orientation, Bottom Layout").arg( errorName() ).toUtf8() );
241 void BaulkControl::swapOrientationTop() {
242 switch ( dynTopLayout
->orientation() ) {
244 dynTopLayout
->setOrientation( Qt::Vertical
);
247 dynTopLayout
->setOrientation( Qt::Horizontal
);
250 qCritical( tr("%1\n\tInvalid Orientation, Top Layout").arg( errorName() ).toUtf8() );
256 void BaulkControl::focusDec() {
257 int curPos
= dynBotIndex();
264 dynBotLayout
->widget( curPos
- 1 )->setFocus();
267 void BaulkControl::focusDecBorder() {
268 int curPos
= dynBotIndex();
272 // Use Inverted Index
277 // Revert Inverted Index
284 void BaulkControl::focusDown() {
285 switch ( dynBotLayout
->orientation() ) {
287 // If Layout is Horizontal/Horizontal then do nothing
288 switch ( dynTopLayout
->orientation() ) {
297 switch ( dynTopLayout
->orientation() ) {
307 qCritical( tr("%1\n\tInvalid Orientation, Focus Down").arg( errorName() ).toUtf8() );
312 void BaulkControl::focusInc() {
313 int curPos
= dynBotIndex();
316 if( curPos
== dynBotLayout
->count() - 1 )
320 dynBotLayout
->widget( curPos
+ 1 )->setFocus();
323 void BaulkControl::focusIncBorder() {
324 int curPos
= dynBotIndex();
327 if ( curPos
== dynBotLayout
->count() - 1 ) {
328 // Use Inverted Index
333 // Revert Inverted Index
340 void BaulkControl::focusLayoutDec() {
341 int curPos
= dynBotIndex();
342 int curLayoutPos
= dynTopLayout
->indexOf( dynBotLayout
);
345 if ( curLayoutPos
== 0 )
349 dynBotLayout
= qobject_cast
<QSplitter
*>( dynTopLayout
->widget( curLayoutPos
- 1 ) );
352 if ( curPos
>= dynBotLayout
->count() )
353 curPos
= dynBotLayout
->count() - 1;
357 curPos
= dynBotLayout
->count() - 1;
359 dynBotLayout
->widget( curPos
)->setFocus();
362 void BaulkControl::focusLayoutInc() {
363 int curPos
= dynBotIndex();
364 int curLayoutPos
= dynTopLayout
->indexOf( dynBotLayout
);
367 if ( curLayoutPos
== dynTopLayout
->count() - 1 )
371 dynBotLayout
= qobject_cast
<QSplitter
*>( dynTopLayout
->widget( curLayoutPos
+ 1 ) );
374 if ( curPos
>= dynBotLayout
->count() )
375 curPos
= dynBotLayout
->count() - 1;
381 dynBotLayout
->widget( curPos
)->setFocus();
384 void BaulkControl::focusLeft() {
385 switch ( dynBotLayout
->orientation() ) {
387 switch ( dynTopLayout
->orientation() ) {
397 // If Layout is Vertical/Vertical then do nothing
398 switch ( dynTopLayout
->orientation() ) {
407 qCritical( tr("%1\n\tInvalid Orientation, Focus Left").arg( errorName() ).toUtf8() );
412 void BaulkControl::focusRight() {
413 switch ( dynBotLayout
->orientation() ) {
415 switch ( dynTopLayout
->orientation() ) {
425 // If Layout is Vertical/Vertical then do nothing
426 switch ( dynTopLayout
->orientation() ) {
435 qCritical( tr("%1\n\tInvalid Orientation, Focus Right").arg( errorName() ).toUtf8() );
440 void BaulkControl::focusUp() {
441 switch ( dynBotLayout
->orientation() ) {
443 // If Layout is Horizontal/Horizontal then do nothing
444 switch ( dynTopLayout
->orientation() ) {
453 switch( dynTopLayout
->orientation() ) {
463 qCritical( tr("%1\n\tInvalid Orientation, Focus Up").arg( errorName() ).toUtf8() );
469 void BaulkControl::removeWidget() {
470 int curPos
= dynBotIndex();
471 int curBotLayout
= dynTopIndex();
472 QWidget
*widget
= dynBotLayout
->widget( curPos
);
474 // Remove the Widget completely from the layout
476 topVLayout
->addWidget( widget
);
479 // No more Widgets in bottom layout
480 if ( dynBotLayout
->count() == 0 ) {
481 // Do not close the last dynBotLayout
482 bool lastLayout
= true;
483 if ( dynTopLayout
->count() > 1 ) {
484 QWidget
*layout
= dynTopLayout
->widget( curBotLayout
);
486 topVLayout
->addWidget( layout
);
491 // Set Focus after removing dynBotLayout
492 if ( dynTopLayout
->count() != 0 ) {
493 int index
= curBotLayout
- 1;
496 dynBotLayout
= qobject_cast
<QSplitter
*>( dynTopLayout
->widget( index
) );
497 if ( dynBotLayout
->count() > 0 )
498 dynBotLayout
->widget( 0 )->setFocus();
501 // Load Default Widget if all all closed
503 int index
= libraryList().name
.lastIndexOf( QRegExp(".*BaulkStatus.*") );
505 loadMainWidget( libraryList().library
[index
] );
509 // Set the focus to the same index if available, else use the last widget
510 if ( curPos
+ 1 <= dynBotLayout
->count() )
511 dynBotLayout
->widget( curPos
)->setFocus();
512 else dynBotLayout
->widget( dynBotLayout
->count() - 1 )->setFocus();
517 void BaulkControl::moveDec() {
518 int index
= dynBotIndex();
520 // Nothing to do if there is only one widget in the dynBotLayout
521 // or if the widget is at the bottom of the layout
522 if ( dynBotLayout
->count() == 1 || index
== 0 )
525 // Decrement Widget Position
526 dynBotLayout
->insertWidget( index
- 1, dynBotLayout
->widget( index
) );
529 void BaulkControl::moveDecBorder() {
530 int curPos
= dynBotIndex();
534 // Use Inverted Index
539 // Revert Inverted Index
546 void BaulkControl::moveDown() {
547 switch ( dynBotLayout
->orientation() ) {
549 // If Layout is Horizontal/Horizontal then do nothing
550 switch ( dynTopLayout
->orientation() ) {
559 switch ( dynTopLayout
->orientation() ) {
569 qCritical( tr("%1\n\tInvalid Orientation, Move Down").arg( errorName() ).toUtf8() );
574 void BaulkControl::moveInc() {
575 int index
= dynBotIndex();
577 // Nothing to do if there is only one widget in the dynBotLayout
578 // or if the widget is already at the top of the layout
579 if ( dynBotLayout
->count() == 1 || index
== dynBotLayout
->count() - 1 )
582 // Increment Widget Position
583 dynBotLayout
->insertWidget( index
+ 1, dynBotLayout
->widget( index
) );
586 void BaulkControl::moveIncBorder() {
587 int curPos
= dynBotIndex();
590 if ( curPos
== dynBotLayout
->count() - 1 ) {
591 // Use Inverted Index
596 // Revert Inverted Index
603 void BaulkControl::moveLayoutDec() {
604 int curPos
= dynBotIndex();
605 int curLayoutPos
= dynTopLayout
->indexOf( dynBotLayout
);
608 // Last Layout, and more than one widget
609 if ( curLayoutPos
== 0 ) {
610 // Only Widget in Layout
611 if ( dynBotLayout
->count() == 1 )
615 QSplitter
*layout
= new QSplitter( Qt::Vertical
);
616 layout
->setHandleWidth( 1 );
617 dynTopLayout
->insertWidget( 0, layout
);
619 // Current Layout will now be an index of 1 rather than 0
624 QSplitter
*prevLayout
= dynBotLayout
;
625 dynBotLayout
= qobject_cast
<QSplitter
*>( dynTopLayout
->widget( curLayoutPos
- 1 ) );
627 // Widget Placement, use same index if possible
628 if ( curPos
>= dynBotLayout
->count() - 1 ) {
629 newPos
= dynBotLayout
->count() - 1;
636 newPos
= dynBotLayout
->count();
639 dynBotLayout
->insertWidget( newPos
, prevLayout
->widget( curPos
) );
641 // First Layout, and only Widget
642 if ( prevLayout
->count() == 0 ) {
644 topVLayout
->addWidget( prevLayout
);
649 dynBotLayout
->widget( newPos
)->setFocus();
652 void BaulkControl::moveLayoutInc() {
653 int curPos
= dynBotIndex();
654 int curLayoutPos
= dynTopLayout
->indexOf( dynBotLayout
);
657 // Last Layout, and more than one widget
658 if ( curLayoutPos
== dynTopLayout
->count() - 1 ) {
659 // Only Widget in Layout
660 if ( dynBotLayout
->count() == 1 )
664 QSplitter
*layout
= new QSplitter( Qt::Vertical
);
665 layout
->setHandleWidth( 1 );
666 dynTopLayout
->addWidget( layout
);
670 QSplitter
*prevLayout
= dynBotLayout
;
671 dynBotLayout
= qobject_cast
<QSplitter
*>( dynTopLayout
->widget( curLayoutPos
+ 1 ) );
673 // Widget Placement, use same index if possible
674 if ( curPos
>= dynBotLayout
->count() - 1 ) {
675 newPos
= dynBotLayout
->count() - 1;
685 dynBotLayout
->insertWidget( newPos
, prevLayout
->widget( curPos
) );
687 // First Layout, and only Widget
688 if ( prevLayout
->count() == 0 ) {
690 topVLayout
->addWidget( prevLayout
);
695 dynBotLayout
->widget( newPos
)->setFocus();
698 void BaulkControl::moveLeft() {
699 switch ( dynBotLayout
->orientation() ) {
701 switch ( dynTopLayout
->orientation() ) {
711 // If Layout is Vertical/Vertical then do nothing
712 switch ( dynTopLayout
->orientation() ) {
721 qCritical( tr("%1\n\tInvalid Orientation, Move Left").arg( errorName() ).toUtf8() );
726 void BaulkControl::moveRight() {
727 switch ( dynBotLayout
->orientation() ) {
729 switch ( dynTopLayout
->orientation() ) {
739 // If Layout is Vertical/Vertical then do nothing
740 switch ( dynTopLayout
->orientation() ) {
749 qCritical( tr("%1\n\tInvalid Orientation, Move Right").arg( errorName() ).toUtf8() );
754 void BaulkControl::moveUp() {
755 switch ( dynBotLayout
->orientation() ) {
757 // If Layout is Horizontal/Horizontal then do nothing
758 switch ( dynTopLayout
->orientation() ) {
767 switch ( dynTopLayout
->orientation() ) {
777 qCritical( tr("%1\n\tInvalid Orientation, Move Up").arg( errorName() ).toUtf8() );
782 // Widget Finders *********************************************************************************
783 int BaulkControl::dynBotIndex() {
784 // Search through Current Bottom Layout First
785 for ( int count
= 0; count
< dynBotLayout
->count(); ++count
)
786 if ( dynBotLayout
->widget( count
)->hasFocus() )
789 // Search through all App Widgets as a bonus this will fix column focus
790 for ( int cur
= 0; cur
< dynTopLayout
->count(); ++cur
) {
791 dynBotLayout
= qobject_cast
<QSplitter
*>( dynTopLayout
->widget( cur
) );
792 for ( int count
= 0; count
< dynBotLayout
->count(); ++count
)
793 if ( dynBotLayout
->widget( count
)->hasFocus() )
800 int BaulkControl::dynTopIndex() {
801 // Search through all the dynBotLayouts for the current one
802 for ( int count
= 0; count
< dynTopLayout
->count(); ++count
)
803 if( dynBotLayout
== qobject_cast
<QSplitter
*>( dynTopLayout
->widget( count
) ) )
809 // Assistive Functions
810 void BaulkControl::globalActionTriggered() {
811 // Keeps track of the last widget focus
812 lastKnownGoodIndex
= dynBotIndex();
815 // Reimplemented Functions ************************************************************************
816 void BaulkControl::closeEvent( QCloseEvent
*event
) {