]> git.sesse.net Git - vlc/blob - modules/gui/skins2/controls/ctrl_list.cpp
* controls/*, src/generic_window.cpp, src/generic_layout.cpp: a visibiliy
[vlc] / modules / gui / skins2 / controls / ctrl_list.cpp
1 /*****************************************************************************
2  * ctrl_list.cpp
3  *****************************************************************************
4  * Copyright (C) 2003 VideoLAN
5  * $Id: ctrl_list.cpp,v 1.3 2004/02/29 16:49:55 asmax Exp $
6  *
7  * Authors: Cyril Deguet     <asmax@via.ecp.fr>
8  *          Olivier Teulière <ipkiss@via.ecp.fr>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
23  *****************************************************************************/
24
25 #include <math.h>
26 #include "ctrl_list.hpp"
27 #include "../src/os_factory.hpp"
28 #include "../src/os_graphics.hpp"
29 #include "../src/generic_bitmap.hpp"
30 #include "../src/generic_font.hpp"
31 #include "../utils/position.hpp"
32 #include "../utils/ustring.hpp"
33 #include "../events/evt_key.hpp"
34 #include "../events/evt_mouse.hpp"
35 #include "../events/evt_scroll.hpp"
36 #include "vlc_keys.h"
37
38 #define SCROLL_STEP 0.05
39
40
41 CtrlList::CtrlList( intf_thread_t *pIntf, VarList &rList, GenericFont &rFont,
42                     uint32_t fgColor, uint32_t playColor, uint32_t bgColor1,
43                     uint32_t bgColor2, uint32_t selColor,
44                     const UString &rHelp, VarBool *pVisible ):
45     CtrlGeneric( pIntf, rHelp, pVisible ), m_rList( rList ), m_rFont( rFont ),
46     m_fgColor( fgColor ), m_playColor( playColor ), m_bgColor1( bgColor1 ),
47     m_bgColor2( bgColor2 ), m_selColor( selColor ), m_pLastSelected( NULL ),
48     m_pImage( NULL ), m_lastPos( 0 )
49 {
50     // Observe the list and position variables
51     m_rList.addObserver( this );
52     m_rList.getPositionVar().addObserver( this );
53
54     makeImage();
55 }
56
57
58 CtrlList::~CtrlList()
59 {
60     m_rList.getPositionVar().delObserver( this );
61     m_rList.delObserver( this );
62     if( m_pImage )
63     {
64         delete m_pImage;
65     }
66 }
67
68
69 void CtrlList::onUpdate( Subject<VarList> &rList )
70 {
71     makeImage();
72     notifyLayout();
73     m_pLastSelected = NULL;
74 }
75
76
77 void CtrlList::onUpdate( Subject<VarPercent> &rPercent )
78 {
79     // Get the size of the control
80     const Position *pPos = getPosition();
81     if( !pPos )
82     {
83         return;
84     }
85     int height = pPos->getHeight();
86
87     // How many lines can be displayed ?
88     int itemHeight = m_rFont.getSize();
89     int maxItems = height / itemHeight;
90
91     // Determine what is the first item to display
92     VarPercent &rVarPos = m_rList.getPositionVar();
93     int firstItem = 0;
94     int excessItems = m_rList.size() - maxItems;
95     if( excessItems > 0 )
96     {
97         // a simple (int)(...) causes rounding errors !
98 #ifdef _MSC_VER
99 #   define lrint (int)
100 #endif
101         firstItem = lrint( (1.0 - rVarPos.get()) * (double)excessItems );
102     }
103     if( m_lastPos != firstItem )
104     {
105         // Redraw the control if the position has changed
106         m_lastPos = firstItem;
107         makeImage();
108         notifyLayout();
109     }
110 }
111
112
113 void CtrlList::onResize()
114 {
115     // Get the size of the control
116     const Position *pPos = getPosition();
117     if( !pPos )
118     {
119         return;
120     }
121     int height = pPos->getHeight();
122
123     // How many lines can be displayed ?
124     int itemHeight = m_rFont.getSize();
125     int maxItems = height / itemHeight;
126
127     // Update the position variable
128     VarPercent &rVarPos = m_rList.getPositionVar();
129     int excessItems = m_rList.size() - maxItems;
130     if( excessItems > 0 )
131     {
132         double newVal = 1.0 - (double)m_lastPos / excessItems;
133         if( newVal >= 0 )
134         {
135             // Change the position to keep the same first displayed item
136             rVarPos.set( 1.0 - (double)m_lastPos / excessItems );
137         }
138         else
139         {
140             // We cannot keep the current first item
141             m_lastPos = excessItems;
142         }
143     }
144
145     makeImage();
146     notifyLayout();
147 }
148
149
150 void CtrlList::onPositionChange()
151 {
152     makeImage();
153     notifyLayout();
154 }
155
156
157 void CtrlList::handleEvent( EvtGeneric &rEvent )
158 {
159     if( rEvent.getAsString().find( "key:down" ) != string::npos )
160     {
161         char key = ((EvtKey&)rEvent).getKey();
162     }
163
164     else if( rEvent.getAsString().find( "mouse:left" ) != string::npos )
165     {
166         EvtMouse &rEvtMouse = (EvtMouse&)rEvent;
167         const Position *pos = getPosition();
168         int yPos = m_lastPos +
169                 ( rEvtMouse.getYPos() - pos->getTop() ) / m_rFont.getSize();
170         VarList::Iterator it;
171         int index = 0;
172
173         if( rEvent.getAsString().find( "mouse:left:down:ctrl,shift" ) !=
174                  string::npos )
175         {
176             // Flag to know if the current item must be selected
177             bool select = false;
178             for( it = m_rList.begin(); it != m_rList.end(); it++ )
179             {
180                 bool nextSelect = select;
181                 if( index == yPos || &*it == m_pLastSelected )
182                 {
183                     if( select )
184                     {
185                         nextSelect = false;
186                     }
187                     else
188                     {
189                         select = true;
190                         nextSelect = true;
191                     }
192                 }
193                 (*it).m_selected = (*it).m_selected || select;
194                 select = nextSelect;
195                 index++;
196             }
197         }
198
199         else if( rEvent.getAsString().find( "mouse:left:down:ctrl" ) !=
200                  string::npos )
201         {
202             for( it = m_rList.begin(); it != m_rList.end(); it++ )
203             {
204                 if( index == yPos )
205                 {
206                     (*it).m_selected = ! (*it).m_selected;
207                     m_pLastSelected = &*it;
208                     break;
209                 }
210                 index++;
211             }
212         }
213
214         else if( rEvent.getAsString().find( "mouse:left:down:shift" ) !=
215                  string::npos )
216         {
217             // Flag to know if the current item must be selected
218             bool select = false;
219             for( it = m_rList.begin(); it != m_rList.end(); it++ )
220             {
221                 bool nextSelect = select;
222                 if( index == yPos ||  &*it == m_pLastSelected )
223                 {
224                     if( select )
225                     {
226                         nextSelect = false;
227                     }
228                     else
229                     {
230                         select = true;
231                         nextSelect = true;
232                     }
233                 }
234                 (*it).m_selected = select;
235                 select = nextSelect;
236                 index++;
237             }
238         }
239
240         else if( rEvent.getAsString().find( "mouse:left:down" ) !=
241                  string::npos )
242         {
243             for( it = m_rList.begin(); it != m_rList.end(); it++ )
244             {
245                 if( index == yPos )
246                 {
247                     (*it).m_selected = true;
248                     m_pLastSelected = &*it;
249                 }
250                 else
251                 {
252                     (*it).m_selected = false;
253                 }
254                 index++;
255             }
256         }
257
258         else if( rEvent.getAsString().find( "mouse:left:dblclick" ) !=
259                  string::npos )
260         {
261             for( it = m_rList.begin(); it != m_rList.end(); it++ )
262             {
263                 if( index == yPos )
264                 {
265                     (*it).m_selected = true;
266                     m_pLastSelected = &*it;
267                     // Execute the action associated to this item
268                     m_rList.action( &*it );
269                 }
270                 else
271                 {
272                     (*it).m_selected = false;
273                 }
274                 index++;
275             }
276         }
277
278         // Redraw the control
279         makeImage();
280         notifyLayout();
281     }
282
283     else if( rEvent.getAsString().find( "scroll" ) != string::npos )
284     {
285         int direction = ((EvtScroll&)rEvent).getDirection();
286
287         double percentage = m_rList.getPositionVar().get();
288         if( direction == EvtScroll::kUp )
289         {
290             percentage += SCROLL_STEP;
291         }
292         else
293         {
294             percentage -= SCROLL_STEP;
295         }
296         m_rList.getPositionVar().set( percentage );
297     }
298 }
299
300
301 bool CtrlList::mouseOver( int x, int y ) const
302 {
303     const Position *pPos = getPosition();
304     if( pPos )
305     {
306         int width = pPos->getWidth();
307         int height = pPos->getHeight();
308         return ( x >= 0 && x <= width && y >= 0 && y <= height );
309     }
310     return false;
311 }
312
313
314 void CtrlList::draw( OSGraphics &rImage, int xDest, int yDest )
315 {
316     if( m_pImage )
317     {
318         rImage.drawGraphics( *m_pImage, 0, 0, xDest, yDest );
319     }
320 }
321
322
323 void CtrlList::makeImage()
324 {
325     if( m_pImage )
326     {
327         delete m_pImage;
328     }
329
330     // Get the size of the control
331     const Position *pPos = getPosition();
332     if( !pPos )
333     {
334         return;
335     }
336     int width = pPos->getWidth();
337     int height = pPos->getHeight();
338     int itemHeight = m_rFont.getSize();
339
340     // Create an image
341     OSFactory *pOsFactory = OSFactory::instance( getIntf() );
342     m_pImage = pOsFactory->createOSGraphics( width, height );
343
344     // Current background color
345     uint32_t bgColor = m_bgColor1;
346
347     // Draw the background
348     VarList::ConstIterator it = m_rList[m_lastPos];
349     for( int yPos = 0; yPos < height; yPos += itemHeight )
350     {
351         int rectHeight = __MIN( itemHeight, height - yPos );
352         if( it != m_rList.end() )
353         {
354             uint32_t color = ( (*it).m_selected ? m_selColor : bgColor );
355             m_pImage->fillRect( 0, yPos, width, rectHeight, color );
356             it++;
357         }
358         else
359         {
360             m_pImage->fillRect( 0, yPos, width, rectHeight, bgColor );
361         }
362         // Flip the background color
363         bgColor = ( bgColor == m_bgColor1 ? m_bgColor2 : m_bgColor1 );
364     }
365
366     // Draw the items
367     int yPos = 0;
368     for( it = m_rList[m_lastPos]; it != m_rList.end() && yPos < height; it++ )
369     {
370         UString *pStr = (UString*)((*it).m_cString.get());
371         uint32_t color = ( (*it).m_playing ? m_playColor : m_fgColor );
372
373         // Draw the text
374         GenericBitmap *pText = m_rFont.drawString( *pStr, color, width );
375         yPos += itemHeight - pText->getHeight();
376         int ySrc = 0;
377         if( yPos < 0 )
378         {
379             ySrc = - yPos;
380             yPos = 0;
381         }
382         int lineHeight = __MIN( pText->getHeight() - ySrc, height - yPos );
383         m_pImage->drawBitmap( *pText, 0, ySrc, 0, yPos, pText->getWidth(),
384                               lineHeight );
385         yPos += (pText->getHeight() - ySrc );
386         delete pText;
387
388     }
389 }
390