1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 #include "precompiled_sfx2.hxx"
23 #include "sfx2/sidebar/GridLayouter.hxx"
24 
25 #include <vcl/window.hxx>
26 
27 namespace sfx2 { namespace sidebar {
28 
29 typedef std::vector<CellDescriptor> CellData;
30 typedef std::vector<CellData> ColumnData;
31 
32 class GridLayouter::Implementation
33 {
34 public:
35     Implementation (Window& rParent);
36     ~Implementation (void);
37 
38     CellDescriptor& GetCell (
39         const sal_Int32 nRow,
40         const sal_Int32 nColumn,
41         const sal_Int32 nVariant);
42 
43     void Layout (void);
44     void LayoutColumn(
45         ColumnData& rColumn,
46         const sal_Int32 nX,
47         const sal_Int32 nColumnIndex);
48 
49     void DistributeWidth (const sal_Int32 nTotalWidth);
50     sal_Int32 GetMinimumColumnWidth (
51         ColumnData& rColumn,
52         const ColumnDescriptor& rDescriptor) const;
53 
54     void Paint (void);
55 
56     Window& mrParent;
57     ::std::vector<ColumnData> maColumns;
58     ::std::vector<ColumnDescriptor> maColumnDescriptors;
59 };
60 
61 #define ForAllColumnDescriptors(I)                              \
62     for (::std::vector<ColumnDescriptor>::iterator              \
63              I(maColumnDescriptors.begin()),                    \
64              iEnd(maColumnDescriptors.end());                   \
65          I!=iEnd;                                               \
66          ++I)
67 
68 #define ForAllColumns(I,N)                                      \
69     sal_Int32 N (0);                                            \
70     for (::std::vector<ColumnData>::iterator                    \
71              I(maColumns.begin()),                              \
72              iEnd(maColumns.end());                             \
73          I!=iEnd;                                               \
74          ++I,++N)
75 
76 #define ForAllRows(ColumnData,I)                                \
77     for (std::vector<CellData>::iterator                        \
78              I((ColumnData).begin()),                           \
79              iRowEnd((ColumnData).end());                       \
80          I!=iRowEnd;                                            \
81          ++I)
82 
83 #define ForAllCells(CellData,I)                                 \
84     for (::std::vector<CellDescriptor>::iterator                \
85              I((CellData).begin()),                             \
86              iCellEnd((CellData).end());                        \
87          I!=iCellEnd;                                           \
88          ++I)
89 
90 
91 //===== GridLayouter ==========================================================
92 
GridLayouter(Window & rParent)93 GridLayouter::GridLayouter (Window& rParent)
94     : mpImplementation(new Implementation(rParent))
95 {
96 }
97 
98 
99 
100 
~GridLayouter(void)101 GridLayouter::~GridLayouter (void)
102 {
103 }
104 
105 
106 
107 
GetCell(const sal_Int32 nRow,const sal_Int32 nColumn,const sal_Int32 nVariant)108 CellDescriptor& GridLayouter::GetCell (
109     const sal_Int32 nRow,
110     const sal_Int32 nColumn,
111     const sal_Int32 nVariant)
112 {
113     return mpImplementation->GetCell(nRow, nColumn, nVariant);
114 }
115 
116 
117 
118 
GetColumn(const sal_Int32 nColumn)119 ColumnDescriptor& GridLayouter::GetColumn (
120     const sal_Int32 nColumn)
121 {
122     // Make sure that the specified column exists.
123     mpImplementation->GetCell(0, nColumn, 0);
124     return mpImplementation->maColumnDescriptors[nColumn];
125 }
126 
127 
128 
129 
Layout(void)130 void GridLayouter::Layout (void)
131 {
132     mpImplementation->Layout();
133 }
134 
135 
136 
137 
Paint(const Rectangle & rBox)138 void GridLayouter::Paint (const Rectangle& rBox)
139 {
140     (void)rBox;
141 
142     mpImplementation->Paint();
143 }
144 
145 
146 
147 
148 //===== CellDescriptor ========================================================
149 
CellDescriptor(void)150 CellDescriptor::CellDescriptor (void)
151     : mpControl(NULL),
152       mnGridWidth(1),
153       mnMinimumWidth(-1),
154       mnMaximumWidth(-1),
155       mnOffset(0)
156 {
157 }
158 
159 
160 
161 
~CellDescriptor(void)162 CellDescriptor::~CellDescriptor (void)
163 {
164 }
165 
166 
167 
168 
SetGridWidth(const sal_Int32 nColumnCount)169 CellDescriptor& CellDescriptor::SetGridWidth (const sal_Int32 nColumnCount)
170 {
171     mnGridWidth = nColumnCount;
172     return *this;
173 }
174 
175 
176 
177 
SetControl(Window & rControl)178 CellDescriptor& CellDescriptor::SetControl (Window& rControl)
179 {
180     mpControl = &rControl;
181     return *this;
182 }
183 
184 
185 
186 
SetFixedWidth(const sal_Int32 nWidth)187 CellDescriptor& CellDescriptor::SetFixedWidth (const sal_Int32 nWidth)
188 {
189     mnMinimumWidth = nWidth;
190     mnMaximumWidth = nWidth;
191     return *this;
192 }
193 
194 
195 
SetOffset(const sal_Int32 nOffset)196 CellDescriptor& CellDescriptor::SetOffset (const sal_Int32 nOffset)
197 {
198     mnOffset = nOffset;
199     return *this;
200 }
201 
202 
203 
204 
SetFixedWidth(void)205 CellDescriptor& CellDescriptor::SetFixedWidth (void)
206 {
207     sal_Int32 nMaxControlWidth (0);
208     if (mpControl != NULL)
209     {
210         const sal_Int32 nControlWidth (mpControl->GetSizePixel().Width());
211         if (nControlWidth > nMaxControlWidth)
212             nMaxControlWidth = nControlWidth;
213     }
214     mnMinimumWidth = nMaxControlWidth;
215     mnMaximumWidth = nMaxControlWidth;
216 
217     return *this;
218 }
219 
220 
221 
222 
SetMinimumWidth(const sal_Int32 nWidth)223 CellDescriptor& CellDescriptor::SetMinimumWidth (const sal_Int32 nWidth)
224 {
225     mnMinimumWidth = nWidth;
226     return *this;
227 }
228 
229 
230 
GetGridWidth(void) const231 sal_Int32 CellDescriptor::GetGridWidth (void) const
232 {
233     return mnGridWidth;
234 }
235 
236 
237 
238 
GetControl(void) const239 Window* CellDescriptor::GetControl (void) const
240 {
241     return mpControl;
242 }
243 
244 
245 
246 
GetMinimumWidth(void) const247 sal_Int32 CellDescriptor::GetMinimumWidth (void) const
248 {
249     return mnMinimumWidth + mnOffset;
250 }
251 
252 
253 
254 
GetMaximumWidth(void) const255 sal_Int32 CellDescriptor::GetMaximumWidth (void) const
256 {
257     return mnMaximumWidth;
258 }
259 
260 
261 
GetOffset(void) const262 sal_Int32 CellDescriptor::GetOffset (void) const
263 {
264     return mnOffset;
265 }
266 
267 
268 
269 
270 //===== GridLayouter::Implementation ==========================================
271 
Implementation(Window & rParent)272 GridLayouter::Implementation::Implementation (Window& rParent)
273     : mrParent(rParent),
274       maColumns(),
275       maColumnDescriptors()
276 {
277 }
278 
279 
280 
281 
~Implementation(void)282 GridLayouter::Implementation::~Implementation (void)
283 {
284 }
285 
286 
287 
288 
GetCell(const sal_Int32 nRow,const sal_Int32 nColumn,const sal_Int32 nVariant)289 CellDescriptor& GridLayouter::Implementation::GetCell (
290     const sal_Int32 nRow,
291     const sal_Int32 nColumn,
292     const sal_Int32 nVariant)
293 {
294     if (nColumn<0 || nRow<0 || nVariant<0)
295     {
296         OSL_ASSERT(nColumn>=0);
297         OSL_ASSERT(nRow>=0);
298         OSL_ASSERT(nVariant>=0);
299         return GetCell(0,0,0);
300     }
301 
302     // Provide missing columns.
303     if (maColumns.size() <= static_cast<size_t>(nColumn))
304     {
305         maColumns.resize(nColumn+1);
306         maColumnDescriptors.resize(nColumn+1);
307     }
308 
309     // Provide missing rows.
310     ColumnData& rColumn (maColumns[nColumn]);
311     if (rColumn.size() <= static_cast<size_t>(nRow))
312         rColumn.resize(nRow+1);
313 
314     // Provide missing variants.
315     CellData& rCellData (rColumn[nRow]);
316     if (rCellData.size() <= static_cast<size_t>(nVariant))
317         rCellData.resize(nVariant+1);
318 
319     return rCellData[nVariant];
320 }
321 
322 
323 
324 
Layout(void)325 void GridLayouter::Implementation::Layout (void)
326 {
327     if (maColumns.empty())
328     {
329         // There are no columns and therefore no controls => nothing
330         // to do.
331         return;
332     }
333 
334     const Size aParentSize (mrParent.GetSizePixel());
335 
336     // Determine the total column weight.
337     sal_Int32 nTotalColumnWeight (0);
338     ForAllColumnDescriptors(iDescriptor)
339         nTotalColumnWeight += iDescriptor->GetWeight();
340     if (nTotalColumnWeight <= 0)
341     {
342         OSL_ASSERT(nTotalColumnWeight>0);
343         return;
344     }
345 
346     // Distribute the width of the parent window to the columns.
347     DistributeWidth(aParentSize.Width());
348 
349     // Set the new positions and widths.
350     sal_Int32 nX (0);
351     ForAllColumns(iColumn,nColumnIndex)
352     {
353         LayoutColumn(
354             *iColumn,
355             nX,
356             nColumnIndex);
357 
358         nX += maColumnDescriptors[nColumnIndex].GetWidth();
359     }
360 }
361 
362 
363 
364 
LayoutColumn(ColumnData & rColumn,const sal_Int32 nX,const sal_Int32 nColumnIndex)365 void GridLayouter::Implementation::LayoutColumn(
366     ColumnData& rColumn,
367     const sal_Int32 nX,
368     const sal_Int32 nColumnIndex)
369 {
370     ColumnDescriptor& rDescriptor (maColumnDescriptors[nColumnIndex]);
371     const sal_Int32 nLeft (nX + rDescriptor.GetLeftPadding());
372     const sal_Int32 nWidth (rDescriptor.GetWidth() - rDescriptor.GetLeftPadding() - rDescriptor.GetRightPadding());
373 
374     sal_Int32 nRow (-1);
375     ForAllRows(rColumn, iCell)
376     {
377         ++nRow;
378 
379         ForAllCells(*iCell, iCellDescriptor)
380         {
381             Window* pControl = iCellDescriptor->GetControl();
382             if (pControl==NULL || ! pControl->IsVisible())
383                 continue;
384 
385             sal_Int32 nCellWidth (nWidth);
386             const sal_Int32 nGridWidth (iCellDescriptor->GetGridWidth());
387             if (nGridWidth < 0)
388                 continue;
389             else if (nGridWidth > 1)
390             {
391                 // Cell spans more than one column.  Sum all their
392                 // widths.
393                 for (sal_Int32 nOffset=1;
394                      nOffset<nGridWidth && static_cast<size_t>(nColumnIndex+nOffset)<maColumnDescriptors.size();
395                      ++nOffset)
396                 {
397                     nCellWidth += maColumnDescriptors[nColumnIndex+nOffset].GetWidth();
398                 }
399                 nCellWidth -= maColumnDescriptors[nColumnIndex+nGridWidth-1].GetRightPadding();
400             }
401 
402             // Check width against valid range of cell.
403             if (iCellDescriptor->GetMinimumWidth() > 0)
404                 if (nCellWidth < iCellDescriptor->GetMinimumWidth())
405                     nCellWidth = iCellDescriptor->GetMinimumWidth();
406             if (iCellDescriptor->GetMaximumWidth() > 0)
407                 if (nCellWidth > iCellDescriptor->GetMaximumWidth())
408                     nCellWidth = iCellDescriptor->GetMaximumWidth();
409 
410             pControl->SetPosSizePixel(
411                 nLeft + iCellDescriptor->GetOffset(),
412                 0,
413                 nCellWidth,
414                 0,
415                 WINDOW_POSSIZE_X | WINDOW_POSSIZE_WIDTH);
416         }
417     }
418 }
419 
420 
421 
422 
DistributeWidth(const sal_Int32 nTotalWidth)423 void GridLayouter::Implementation::DistributeWidth (const sal_Int32 nTotalWidth)
424 {
425     // Prepare width distribution:
426     // a) Setup minimum widths for all columns.
427     // b) Sum up the width of columns that have zero weight.
428     // c) Sum up the non-zero weights.
429     sal_Int32 nZeroWeightWidth (0);
430     sal_Int32 nTotalColumnWeight (0);
431     for (sal_uInt32 nColumn=0; nColumn<maColumns.size(); ++nColumn)
432     {
433         ColumnDescriptor& rDescriptor (maColumnDescriptors[nColumn]);
434         ColumnData& rColumn (maColumns[nColumn]);
435 
436         const sal_Int32 nWidth (GetMinimumColumnWidth(rColumn, rDescriptor));
437 
438         rDescriptor.SetWidth(nWidth);
439 
440         if (rDescriptor.GetWeight() <= 0)
441             nZeroWeightWidth += nWidth;
442         else
443             nTotalColumnWeight += rDescriptor.GetWeight();
444     }
445 
446     sal_Int32 nRemainingWidth (nTotalWidth - nZeroWeightWidth);
447     if (nRemainingWidth < 0)
448         nRemainingWidth = 0;
449 
450 
451     // Distribute the remaining width between columns that have
452     // non-zero width.
453     const sal_Int32 nDistributableWidth (nRemainingWidth);
454     for (sal_uInt32 nColumn=0; nColumn<maColumns.size(); ++nColumn)
455     {
456         ColumnDescriptor& rDescriptor (maColumnDescriptors[nColumn]);
457 
458         if (rDescriptor.GetWeight() > 0)
459         {
460             sal_Int32 nWidth (nDistributableWidth * rDescriptor.GetWeight() / nTotalColumnWeight);
461             // Make sure the width lies inside the valid range of
462             // column widths.
463             if (nWidth < rDescriptor.GetWidth())
464                 nWidth = rDescriptor.GetWidth();
465             if (rDescriptor.GetMaximumWidth()>0)
466                 if (nWidth > rDescriptor.GetTotalMaximumWidth())
467                     nWidth = rDescriptor.GetTotalMaximumWidth();
468 
469             rDescriptor.SetWidth(nWidth);
470             nRemainingWidth -= nWidth;
471         }
472     }
473 
474     // If there are some pixels left (due to rounding errors), then
475     // give them to the first column that has non-zero weight.
476     if (nRemainingWidth > 0)
477         for (sal_uInt32 nColumn=0; nColumn<maColumns.size(); ++nColumn)
478         {
479             ColumnDescriptor& rDescriptor (maColumnDescriptors[nColumn]);
480             if (rDescriptor.GetWeight() > 0)
481             {
482                 rDescriptor.SetWidth(rDescriptor.GetWidth() + nRemainingWidth);
483                 break;
484             }
485         }
486 }
487 
488 
489 
490 
GetMinimumColumnWidth(ColumnData & rColumn,const ColumnDescriptor & rDescriptor) const491 sal_Int32 GridLayouter::Implementation::GetMinimumColumnWidth (
492     ColumnData& rColumn,
493     const ColumnDescriptor& rDescriptor) const
494 {
495     // Start with the minimum width of the whole column.
496     sal_Int32 nMinimumWidth (rDescriptor.GetMinimumWidth());
497 
498     // Take also into account the minimum widths of all cells in the column.
499     ForAllRows(rColumn, iCell)
500         ForAllCells(*iCell, iCellDescriptor)
501         {
502             if (iCellDescriptor->GetGridWidth() != 1)
503                 continue;
504             const sal_Int32 nMinimumCellWidth (iCellDescriptor->GetMinimumWidth());
505             if (nMinimumCellWidth > nMinimumWidth)
506                 nMinimumWidth = nMinimumCellWidth;
507         }
508 
509     // Make sure that the minimum width does not become larger than
510     // the maximum width of the column.
511     if (nMinimumWidth > rDescriptor.GetMaximumWidth() && rDescriptor.GetMaximumWidth()>0)
512         nMinimumWidth = rDescriptor.GetMaximumWidth();
513 
514     // Add the horizontal padding.
515     return  nMinimumWidth
516         + rDescriptor.GetLeftPadding()
517         + rDescriptor.GetRightPadding();
518 }
519 
520 
521 
522 
Paint(void)523 void GridLayouter::Implementation::Paint (void)
524 {
525     const Size aParentSize (mrParent.GetSizePixel());
526 
527     static const Color aSeparatorColor (0x66cdaa);
528     static const Color aLeftPaddingColor (0x98fb98);
529     static const Color aRightPaddingColor (0xff69b4);
530     static const Color aControlOverlayColor (0xffff00);
531 
532     sal_Int32 nX (0);
533     mrParent.SetLineColor();
534     mrParent.SetFillColor(aLeftPaddingColor);
535     ForAllColumnDescriptors(iColumn)
536     {
537         if (iColumn->GetLeftPadding() > 0)
538         {
539             mrParent.DrawRect(Rectangle(
540                     nX,0,
541                     nX+iColumn->GetLeftPadding(),aParentSize.Height()));
542         }
543 
544         nX += iColumn->GetWidth();
545     }
546 
547     nX = 0;
548     mrParent.SetFillColor(aRightPaddingColor);
549     ForAllColumnDescriptors(iColumn)
550     {
551         if (iColumn->GetRightPadding() > 0)
552         {
553             const sal_Int32 nRight (nX + iColumn->GetWidth());
554             const sal_Int32 nLeft (nRight - iColumn->GetRightPadding());
555             mrParent.DrawRect(Rectangle(
556                     nLeft,0,
557                     nRight,aParentSize.Height()));
558         }
559 
560         nX += iColumn->GetWidth();
561     }
562 
563     nX = 0;
564     mrParent.SetFillColor();
565     mrParent.SetLineColor(aSeparatorColor);
566     ForAllColumnDescriptors(iColumn)
567     {
568         mrParent.DrawLine(Point(nX,0), Point(nX,aParentSize.Height()));
569         nX += iColumn->GetWidth();
570     }
571 
572     mrParent.SetFillColor();
573     mrParent.SetLineColor(aControlOverlayColor);
574     ForAllColumns(iColumn,nColumnIndex)
575         ForAllRows(*iColumn, iCell)
576             ForAllCells(*iCell, iCellDescriptor)
577             {
578                 Window* pControl (iCellDescriptor->GetControl());
579                 if (pControl!=NULL && pControl->IsVisible())
580                 {
581                     Rectangle aBox (
582                         pControl->GetPosPixel(),
583                         pControl->GetSizePixel());
584                     --aBox.Left();
585                     --aBox.Top();
586                     ++aBox.Right();
587                     ++aBox.Bottom();
588                     mrParent.DrawRect(aBox);
589                 }
590             }
591 }
592 
593 
594 
595 
596 //===== ColumnDescriptor ======================================================
597 
ColumnDescriptor(void)598 ColumnDescriptor::ColumnDescriptor (void)
599     : mnWeight(1),
600       mnMinimumWidth(0),
601       mnMaximumWidth(-1),
602       mnLeftPadding(0),
603       mnRightPadding(0),
604       mnWidth(0)
605 {
606 }
607 
608 
609 
610 
~ColumnDescriptor(void)611 ColumnDescriptor::~ColumnDescriptor (void)
612 {
613 }
614 
615 
616 
617 
SetWeight(const sal_Int32 nWeight)618 ColumnDescriptor& ColumnDescriptor::SetWeight (const sal_Int32 nWeight)
619 {
620     mnWeight = nWeight;
621 
622     return *this;
623 }
624 
625 
626 
627 
SetMinimumWidth(const sal_Int32 nWidth)628 ColumnDescriptor& ColumnDescriptor::SetMinimumWidth (const sal_Int32 nWidth)
629 {
630     mnMinimumWidth = nWidth;
631 
632     return *this;
633 }
634 
635 
636 
SetFixedWidth(const sal_Int32 nWidth)637 ColumnDescriptor& ColumnDescriptor::SetFixedWidth (const sal_Int32 nWidth)
638 {
639     mnMinimumWidth = nWidth;
640     mnMaximumWidth = nWidth;
641 
642     return *this;
643 }
644 
645 
646 
SetLeftPadding(const sal_Int32 nPadding)647 ColumnDescriptor& ColumnDescriptor::SetLeftPadding (const sal_Int32 nPadding)
648 {
649     mnLeftPadding = nPadding;
650 
651     return *this;
652 }
653 
654 
655 
656 
SetRightPadding(const sal_Int32 nPadding)657 ColumnDescriptor& ColumnDescriptor::SetRightPadding (const sal_Int32 nPadding)
658 {
659     mnRightPadding = nPadding;
660 
661     return *this;
662 }
663 
664 
665 
666 
GetWeight(void) const667 sal_Int32 ColumnDescriptor::GetWeight (void) const
668 {
669     return mnWeight;
670 }
671 
672 
673 
674 
GetMinimumWidth(void) const675 sal_Int32 ColumnDescriptor::GetMinimumWidth (void) const
676 {
677     return mnMinimumWidth;
678 }
679 
680 
681 
682 
GetMaximumWidth(void) const683 sal_Int32 ColumnDescriptor::GetMaximumWidth (void) const
684 {
685     return mnMaximumWidth;
686 }
687 
688 
689 
690 
GetTotalMaximumWidth(void) const691 sal_Int32 ColumnDescriptor::GetTotalMaximumWidth (void) const
692 {
693     return mnMaximumWidth + mnLeftPadding + mnRightPadding;
694 }
695 
696 
697 
698 
GetLeftPadding(void) const699 sal_Int32 ColumnDescriptor::GetLeftPadding (void) const
700 {
701     return mnLeftPadding;
702 }
703 
704 
705 
706 
GetRightPadding(void) const707 sal_Int32 ColumnDescriptor::GetRightPadding (void) const
708 {
709     return mnRightPadding;
710 }
711 
712 
713 
714 
SetWidth(const sal_Int32 nWidth)715 void ColumnDescriptor::SetWidth (const sal_Int32 nWidth)
716 {
717     mnWidth = nWidth;
718 }
719 
720 
721 
722 
GetWidth(void) const723 sal_Int32 ColumnDescriptor::GetWidth (void) const
724 {
725     return mnWidth;
726 }
727 
728 } } // end of namespace sfx2::sidebar
729