added Lars Beikirch's <Lars.Beikirch@gmx.net> patch for QextMDI
[kdevelop:kdevelop.git] / kdevelop / widgets / qextmdi / src / qextmdichildarea.cpp
1 //----------------------------------------------------------------------------
2 //    filename             : qextmdichildarea.cpp
3 //----------------------------------------------------------------------------
4 //    Project              : Qt MDI extension
5 //
6 //    begin                : 07/1999       by Szymon Stefanek as part of kvirc
7 //                                         (an IRC application)
8 //    changes              : 09/1999       by Falk Brettschneider to create an
9 //                           - 06/2000     stand-alone Qt extension set of
10 //                                         classes and a Qt-based library
11 //
12 //    copyright            : (C) 1999-2000 by Szymon Stefanek (stefanek@tin.it)
13 //                                         and
14 //                                         Falk Brettschneider
15 //    email                :  gigafalk@yahoo.com (Falk Brettschneider)
16 //----------------------------------------------------------------------------
17 //
18 //----------------------------------------------------------------------------
19 //
20 //    This program is free software; you can redistribute it and/or modify
21 //    it under the terms of the GNU Library General Public License as
22 //    published by the Free Software Foundation; either version 2 of the
23 //    License, or (at your option) any later version.
24 //
25 //----------------------------------------------------------------------------
26
27 #include <math.h>
28 #include <qpopupmenu.h>
29
30 #include "qextmdidefines.h"
31 #include "qextmdichildarea.h"
32
33 ///////////////////////////////////////////////////////////////////////////////
34 // QextMdiChildArea
35 ///////////////////////////////////////////////////////////////////////////////
36
37 //============ QextMdiChildArea ============//
38
39 QextMdiChildArea::QextMdiChildArea(QWidget *parent)
40 :QFrame(parent, "qextmdi_childarea")
41 {
42    setFrameStyle(QFrame::Panel|QFrame::Sunken);
43    m_captionFont = QFont();//F.B.QFont("clean",16);
44    QFontMetrics fm(m_captionFont);
45    m_captionFontLineSpacing = fm.lineSpacing();
46    m_captionActiveBackColor = QColor(0,0,128);
47    m_captionActiveForeColor = QColor(255,255,255);
48    m_captionInactiveBackColor = QColor(160,160,164);
49    m_captionInactiveForeColor = QColor(255,255,255);
50    m_pZ = new QList<QextMdiChildFrm>;
51    m_pZ->setAutoDelete(TRUE);
52    setFocusPolicy(ClickFocus);
53    m_defaultChildFrmSize = QSize(400,300);
54 }
55
56 //============ ~QextMdiChildArea ============//
57
58 QextMdiChildArea::~QextMdiChildArea()
59 {
60    delete m_pZ; //This will destroy all the widgets inside.
61 }
62
63 //============ manageChild ============//
64
65 void QextMdiChildArea::manageChild(QextMdiChildFrm *lpC,bool bShow,bool bCascade)
66 {
67    QextMdiChildFrm * top=topChild();
68    if (bShow)
69       m_pZ->append(lpC); //visible -> first in the Z order
70    else
71       m_pZ->insert(0,lpC); //hidden -> last in the Z order
72
73    if(bCascade)lpC->move(getCascadePoint(m_pZ->count()-1));
74    if(bShow){
75       if(top){ //Maximize if needed
76          if(top->state() == QextMdiChildFrm::Maximized){
77             emit sysButtonConnectionsMustChange( top,lpC);
78             top->setState(QextMdiChildFrm::Normal,FALSE);
79             lpC->setState(QextMdiChildFrm::Maximized,FALSE);
80          }
81       }
82       lpC->show();
83       focusTopChild();
84    }
85 }
86
87 //============ destroyChild ============//
88
89 void QextMdiChildArea::destroyChild(QextMdiChildFrm *lpC,bool bFocusTopChild)
90 {
91    bool bWasMaximized = lpC->state() == QextMdiChildFrm::Maximized;
92
93    // destroy the old one
94    QObject::disconnect(lpC);
95    lpC->blockSignals(TRUE);
96    m_pZ->setAutoDelete(FALSE);
97    m_pZ->removeRef(lpC);
98
99    // focus the next new childframe
100    QextMdiChildFrm* newTopChild = topChild();
101    if (bWasMaximized){
102       if (newTopChild) {
103          newTopChild->setState(QextMdiChildFrm::Maximized,FALSE);
104          emit sysButtonConnectionsMustChange(lpC, newTopChild);
105       }
106       else {
107          emit noMaximizedChildFrmLeft(lpC); // last childframe removed
108       }
109    }
110    delete lpC;
111    m_pZ->setAutoDelete(TRUE);
112
113    if (bFocusTopChild)
114       focusTopChild();
115 }
116
117 //============ destroyChildButNotItsView ============//
118
119 void QextMdiChildArea::destroyChildButNotItsView(QextMdiChildFrm *lpC,bool bFocusTopChild)
120 {
121    bool bWasMaximized = lpC->state() == QextMdiChildFrm::Maximized;
122
123    // destroy the old one
124    QObject::disconnect(lpC);
125    lpC->unsetClient();
126    m_pZ->setAutoDelete(FALSE);
127    m_pZ->removeRef(lpC);
128
129    // focus the next new childframe
130    QextMdiChildFrm* newTopChild = topChild();
131    if (bWasMaximized){
132       if (newTopChild) {
133          newTopChild->setState(QextMdiChildFrm::Maximized,FALSE);
134          emit sysButtonConnectionsMustChange(lpC, newTopChild);
135       }
136       else {
137          emit noMaximizedChildFrmLeft(lpC); // last childframe removed
138       }
139    }
140    delete lpC;
141    m_pZ->setAutoDelete(TRUE);
142
143    if (bFocusTopChild)
144       focusTopChild();
145 }
146
147 //============= setTopChlid ============//
148
149 void QextMdiChildArea::setTopChild(QextMdiChildFrm *lpC,bool bSetFocus)
150 {
151    if(m_pZ->last() != lpC){
152       m_pZ->setAutoDelete(FALSE);
153       if (lpC) {
154          m_pZ->removeRef(lpC);
155       }
156       //disable the labels of all the other children
157       for(QextMdiChildFrm *pC=m_pZ->first();pC;pC=m_pZ->next()){
158          pC->m_pCaption->setActive(FALSE);
159       }
160       if (!lpC) {
161          return;
162       }
163
164       QextMdiChildFrm *pMaximizedChild = m_pZ->last();
165       if (pMaximizedChild->m_state != QextMdiChildFrm::Maximized) {
166          pMaximizedChild = 0L;
167       }
168       m_pZ->setAutoDelete(TRUE);
169       m_pZ->append(lpC);
170       int nChildAreaMinW = 0,               nChildAreaMinH = 0;
171       int nChildAreaMaxW = QWIDGETSIZE_MAX, nChildAreaMaxH = QWIDGETSIZE_MAX;
172       if ( (pMaximizedChild != 0L) && (lpC->m_pClient != 0L) ) {
173          nChildAreaMinW = lpC->m_pClient->minimumWidth();
174          nChildAreaMinH = lpC->m_pClient->minimumHeight();
175          // XXX TODO: setting the maximum size doesn't work properly - fix this later
176          // nChildAreaMaxW = lpC->m_pClient->maximumWidth();
177          // nChildAreaMaxH = lpC->m_pClient->maximumHeight();
178       }
179       setMinimumSize(nChildAreaMinW, nChildAreaMinH);
180       setMaximumSize(nChildAreaMaxW, nChildAreaMaxH);
181       if (pMaximizedChild) {
182          const bool bDontAnimate = FALSE;
183          // first maximize the new view
184          lpC->setState(QextMdiChildFrm::Maximized, bDontAnimate);
185          qApp->sendPostedEvents();
186          // then restore the old maximized view in background
187          pMaximizedChild->setState(QextMdiChildFrm::Normal, bDontAnimate);
188          qApp->processOneEvent();
189          emit sysButtonConnectionsMustChange( pMaximizedChild, lpC);
190       }
191       else {
192          lpC->raise();
193       }
194       if (bSetFocus) {
195          if(!lpC->hasFocus())lpC->setFocus();
196       }
197       lpC->m_pClient->activate();//setFocus();
198    }
199 }
200
201 //============== resizeEvent ================//
202
203 void QextMdiChildArea::resizeEvent(QResizeEvent* e)
204 {
205    //If we have a maximized children at the top , adjust its size
206    QextMdiChildFrm *lpC=m_pZ->last();
207    if(lpC){
208       if(lpC->m_state == QextMdiChildFrm::Maximized) {
209          int clientw = 0, clienth = 0;
210          if (lpC->m_pClient != 0L) {
211             clientw = lpC->m_pClient->width();
212             clienth = lpC->m_pClient->height();
213          }
214          lpC->resize( width() + QEXTMDI_MDI_CHILDFRM_DOUBLE_BORDER,
215                       height() + lpC->m_pCaption->heightHint() + QEXTMDI_MDI_CHILDFRM_SEPARATOR + QEXTMDI_MDI_CHILDFRM_DOUBLE_BORDER);
216       }
217    }
218    layoutMinimizedChildren();
219    QWidget::resizeEvent(e);
220 }
221
222 //=============== mousePressEvent =============//
223
224 void QextMdiChildArea::mousePressEvent(QMouseEvent *e)
225 {
226    //Popup the window menu
227    if(e->button() & RightButton)
228       emit popupWindowMenu( mapToGlobal( e->pos()));
229 }
230
231 //=============== getCascadePoint ============//
232
233 QPoint QextMdiChildArea::getCascadePoint(int indexOfWindow)
234 {
235    if (indexOfWindow < 0) {
236       indexOfWindow = m_pZ->count();
237    }
238    QPoint pnt(0,0);
239    if(indexOfWindow==0)return pnt;
240
241    bool bTopLevelMode = FALSE;
242    if( height() == 1)   // hacky?!
243                 bTopLevelMode = TRUE;
244
245    QextMdiChildFrm *lpC=m_pZ->first();
246    int step=(lpC ? lpC->m_pCaption->heightHint()+QEXTMDI_MDI_CHILDFRM_BORDER : 20);
247    int h=(bTopLevelMode ? QApplication::desktop()->height() : height());
248    int w=(bTopLevelMode ? QApplication::desktop()->width() : width());
249
250    int availableHeight=h-(lpC ? lpC->minimumSize().height() : m_defaultChildFrmSize.height());
251    int availableWidth=w-(lpC ? lpC->minimumSize().width() : m_defaultChildFrmSize.width());
252    int ax=0;
253    int ay=0;
254    for(int i=0;i<indexOfWindow;i++){
255       ax+=step;
256       ay+=step;
257       if(ax>availableWidth)ax=0;
258       if(ay>availableHeight)ay=0;
259    }
260    pnt.setX(ax);
261    pnt.setY(ay);
262    return pnt;
263 }
264
265 //================ childMinimized ===============//
266
267 void QextMdiChildArea::childMinimized(QextMdiChildFrm *lpC,bool bWasMaximized)
268 {
269    if(m_pZ->findRef(lpC) == -1)return;
270    if(m_pZ->count() > 1){
271       m_pZ->setAutoDelete(FALSE);
272       m_pZ->removeRef(lpC);
273       m_pZ->setAutoDelete(TRUE);
274       m_pZ->insert(0,lpC);
275       if(bWasMaximized){
276          // Need to maximize the top child
277          lpC = m_pZ->last();
278          if(!lpC)return; //??
279          if(lpC->m_state==QextMdiChildFrm::Minimized)return;
280          lpC->setState(QextMdiChildFrm::Maximized,FALSE); //do not animate the change
281       }
282       focusTopChild();
283    } else {
284       setFocus(); //Remove focus from the child
285    }
286 }
287
288 //============= focusTopChild ===============//
289
290 void QextMdiChildArea::focusTopChild()
291 {
292    QextMdiChildFrm *lpC=m_pZ->last();
293    if(!lpC) {
294       emit lastChildFrmClosed();
295       return;
296    }
297    //disable the labels of all the other children
298    for(QextMdiChildFrm *pC=m_pZ->first();pC;pC=m_pZ->next()){
299       if(pC != lpC)pC->m_pCaption->setActive(FALSE);
300    }
301    lpC->raise();
302    if(!lpC->hasFocus()) {
303       lpC->setFocus();
304    }
305    if (!lpC->m_pClient->hasFocus()) {
306       lpC->m_pClient->setFocus();
307    }
308 }
309
310 //============= cascadeWindows ===============//
311
312 void QextMdiChildArea::cascadeWindows()
313 {
314    int idx=0;
315    QList<QextMdiChildFrm> list(*m_pZ);
316    list.setAutoDelete(FALSE);
317    while(!list.isEmpty()){
318       QextMdiChildFrm *lpC=list.first();
319       if(lpC->m_state != QextMdiChildFrm::Minimized){
320          if(lpC->m_state==QextMdiChildFrm::Maximized)lpC->restorePressed();
321          lpC->move(getCascadePoint(idx));
322          idx++;
323       }
324       list.removeFirst();
325    }
326    focusTopChild();
327 }
328
329 //============= cascadeMaximized ===============//
330
331 void QextMdiChildArea::cascadeMaximized()
332 {
333    int idx=0;
334    QList<QextMdiChildFrm> list(*m_pZ);
335
336    list.setAutoDelete(FALSE);
337    while(!list.isEmpty()){
338       QextMdiChildFrm *lpC=list.first();
339       if(lpC->m_state != QextMdiChildFrm::Minimized){
340          if(lpC->m_state==QextMdiChildFrm::Maximized)lpC->restorePressed();
341          QPoint pnt(getCascadePoint(idx));
342          lpC->move(pnt);
343          QSize curSize(width()-pnt.x(),height()-pnt.y());
344          if((lpC->minimumSize().width() > curSize.width()) ||
345             (lpC->minimumSize().height() > curSize.height()))lpC->resize(lpC->minimumSize());
346          else lpC->resize(curSize);
347          idx++;
348       }
349       list.removeFirst();
350    }
351    focusTopChild();
352 }
353
354 void QextMdiChildArea::expandVertical()
355 {
356    int idx=0;
357    QList<QextMdiChildFrm> list(*m_pZ);
358    list.setAutoDelete(FALSE);
359    while(!list.isEmpty()){
360       QextMdiChildFrm *lpC=list.first();
361       if(lpC->m_state != QextMdiChildFrm::Minimized){
362          if(lpC->m_state==QextMdiChildFrm::Maximized)lpC->restorePressed();
363          lpC->setGeometry(lpC->x(),0,lpC->width(),height());
364          idx++;
365       }
366       list.removeFirst();
367    }
368    focusTopChild();
369 }
370
371 void QextMdiChildArea::expandHorizontal()
372 {
373    int idx=0;
374    QList<QextMdiChildFrm> list(*m_pZ);
375    list.setAutoDelete(FALSE);
376    while(!list.isEmpty()){
377       QextMdiChildFrm *lpC=list.first();
378       if(lpC->m_state != QextMdiChildFrm::Minimized){
379          if(lpC->m_state==QextMdiChildFrm::Maximized)lpC->restorePressed();
380          lpC->setGeometry(0,lpC->y(),width(),lpC->height());
381          idx++;
382       }
383       list.removeFirst();
384    }
385    focusTopChild();
386 }
387
388 //============= getVisibleChildCount =============//
389
390 int QextMdiChildArea::getVisibleChildCount()
391 {
392    int cnt=0;
393    for(QextMdiChildFrm *lpC=m_pZ->first();lpC;lpC=m_pZ->next()){
394       if ((lpC->m_state != QextMdiChildFrm::Minimized) &&
395           (lpC->isVisible())) cnt++;
396    }
397    return cnt;
398 }
399
400 //============ tilePragma ============//
401
402 void QextMdiChildArea::tilePragma()
403 {
404    tileAllInternal(9);
405 }
406
407 //============ tileAllInternal ============//
408
409 void QextMdiChildArea::tileAllInternal(int maxWnds)
410 {
411    //NUM WINDOWS =           1,2,3,4,5,6,7,8,9
412    static int colstable[9]={ 1,1,1,2,2,2,3,3,3 }; //num columns
413    static int rowstable[9]={ 1,2,3,2,3,3,3,3,3 }; //num rows
414    static int lastwindw[9]={ 1,1,1,1,2,1,3,2,1 }; //last window multiplier
415    static int colrecall[9]={ 0,0,0,3,3,3,6,6,6 }; //adjust self
416    static int rowrecall[9]={ 0,0,0,0,4,4,4,4,4 }; //adjust self
417
418    QextMdiChildFrm *lpTop = topChild();
419    int numVisible = getVisibleChildCount();
420    if (numVisible<1) return;
421    int numToHandle = ((numVisible > maxWnds) ? maxWnds : numVisible);
422    int xQuantum = width()/colstable[numToHandle-1];
423    if (xQuantum < ((lpTop->minimumSize().width() > m_defaultChildFrmSize.width()) ? lpTop->minimumSize().width() : m_defaultChildFrmSize.width())) {
424       if (colrecall[numToHandle-1] != 0) {
425          tileAllInternal(colrecall[numToHandle-1]);
426          return;
427       }
428    }
429    int yQuantum=height()/rowstable[numToHandle-1];
430    if (yQuantum < ((lpTop->minimumSize().height() > m_defaultChildFrmSize.height()) ? lpTop->minimumSize().height() : m_defaultChildFrmSize.height())) {
431       if (rowrecall[numToHandle-1] != 0) {
432          tileAllInternal(rowrecall[numToHandle-1]);
433          return;
434       }
435    }
436    int curX=0;
437    int curY=0;
438    int curRow=1;
439    int curCol=1;
440    int curWin=1;
441    for (QextMdiChildFrm *lpC=m_pZ->first();lpC;lpC=m_pZ->next()) {
442       if (lpC->m_state!=QextMdiChildFrm::Minimized) {
443          //restore the window
444          if (lpC->m_state==QextMdiChildFrm::Maximized)
445             lpC->restorePressed();
446          if ((curWin%numToHandle)==0)
447             lpC->setGeometry(curX,curY,xQuantum * lastwindw[numToHandle-1],yQuantum);
448          else 
449             lpC->setGeometry(curX,curY,xQuantum,yQuantum);
450          //example : 12 windows : 3 cols 3 rows
451          if (curCol<colstable[numToHandle-1]) { //curCol<3
452             curX+=xQuantum; //add a column in the same row
453             curCol++;       //increase current column
454          } 
455          else {
456             curX = 0;         //new row
457             curCol = 1;       //column 1
458             if (curRow < rowstable[numToHandle-1]) { //curRow<3
459                curY += yQuantum; //add a row
460                curRow++;       //
461             } 
462             else {
463                curY = 0;         //restart from beginning
464                curRow = 1;       //
465             }
466          }
467          curWin++;
468       }
469    }
470    if (lpTop)
471       lpTop->setFocus();
472 }
473 //============ tileAnodine ============//
474 void QextMdiChildArea::tileAnodine()
475 {
476    QextMdiChildFrm *lpTop=topChild();
477    int numVisible=getVisibleChildCount(); // count visible windows
478    if(numVisible<1)return;
479    int numCols=int(sqrt((double)numVisible)); // set columns to square root of visible count
480    // create an array to form grid layout
481    int *numRows=new int[numCols];
482    int numCurCol=0;
483    while(numCurCol<numCols){
484       numRows[numCurCol]=numCols; // create primary grid values
485       numCurCol++;
486    }
487    int numDiff=numVisible-(numCols*numCols); // count extra rows
488    int numCurDiffCol=numCols; // set column limiting for grid updates
489    while(numDiff>0){
490       numCurDiffCol--;
491       numRows[numCurDiffCol]++; // add extra rows to column grid
492       if(numCurDiffCol<1)numCurDiffCol=numCols; // rotate through the grid
493       numDiff--;
494    }
495    numCurCol=0;
496    int numCurRow=0;
497    int curX=0;
498    int curY=0;
499    // the following code will size everything based on my grid above
500    // there is no limit to the number of windows it will handle
501    // it's great when a kick-ass theory works!!!                      // Pragma :)
502    int xQuantum=width()/numCols;
503    int yQuantum=height()/numRows[numCurCol];
504    for(QextMdiChildFrm *lpC=m_pZ->first();lpC;lpC=m_pZ->next()){
505       if(lpC->m_state != QextMdiChildFrm::Minimized){
506          if(lpC->m_state==QextMdiChildFrm::Maximized)lpC->restorePressed();
507          lpC->setGeometry(curX,curY,xQuantum,yQuantum);
508          numCurRow++;
509          curY+=yQuantum;
510          if(numCurRow==numRows[numCurCol]){
511             numCurRow=0;
512             numCurCol++;
513             curY=0;
514             curX+=xQuantum;
515             if(numCurCol!=numCols)yQuantum=height()/numRows[numCurCol];
516          }
517       }
518    }
519    delete[] numRows;
520    if(lpTop)lpTop->setFocus();
521 }
522
523 //============ tileVertically===========//
524 void QextMdiChildArea::tileVertically()
525 {
526    QextMdiChildFrm *lpTop=topChild();
527    int numVisible=getVisibleChildCount(); // count visible windows
528    if(numVisible<1)return;
529    
530    int w = width() / numVisible;
531    int lastWidth = 0;
532    if( numVisible > 1)
533       lastWidth = width() - (w * (numVisible - 1));
534    else
535       lastWidth = w;
536    int h = height();
537    int posX = 0;
538    int countVisible = 0;
539    
540    for(QextMdiChildFrm *lpC=m_pZ->first();lpC;lpC=m_pZ->next()){
541       if(lpC->m_state != QextMdiChildFrm::Minimized){
542          if(lpC->m_state==QextMdiChildFrm::Maximized)lpC->restorePressed();
543          countVisible++;
544          if( countVisible < numVisible) {
545             lpC->setGeometry( posX, 0, w, h);
546             posX += w;
547          }
548          else { // last visible childframe
549             lpC->setGeometry( posX, 0, lastWidth, h);
550          }
551       }
552    }
553    if(lpTop)lpTop->setFocus();
554 }
555
556 //============ layoutMinimizedChildren ============//
557 void QextMdiChildArea::layoutMinimizedChildren()
558 {
559    int posX = 0;
560    int posY = height();
561    for(QextMdiChildFrm* child = m_pZ->first(); child ; child = m_pZ->next())
562    {
563       if( child->state() == QextMdiChildFrm::Minimized) {
564          if( ( posX > 0) && ( posX + child->width() > width()) )
565          {
566             posX = 0;
567             posY -= child->height();
568          }
569          child->move( posX, posY - child->height());
570          posX = child->geometry().right();
571       }
572    }
573 }
574
575
576 void QextMdiChildArea::setMdiCaptionFont(const QFont &fnt)
577 {
578    m_captionFont = fnt;
579    QFontMetrics fm(m_captionFont);
580    m_captionFontLineSpacing = fm.lineSpacing();
581 }
582
583 void QextMdiChildArea::setMdiCaptionActiveForeColor(const QColor &clr)
584 {
585    m_captionActiveForeColor = clr;
586 }
587
588 void QextMdiChildArea::setMdiCaptionActiveBackColor(const QColor &clr)
589 {
590    m_captionActiveBackColor = clr;
591 }
592
593 void QextMdiChildArea::setMdiCaptionInactiveForeColor(const QColor &clr)
594 {
595    m_captionInactiveForeColor = clr;
596 }
597
598 void QextMdiChildArea::setMdiCaptionInactiveBackColor(const QColor &clr)
599 {
600    m_captionInactiveBackColor = clr;
601 }
602
603 #ifndef NO_INCLUDE_MOCFILES
604 #include "qextmdichildarea.moc"
605 #endif