5d4f0dca0c4c20e48c7e303f50b26367cfb65825
[vlc.git] / modules / gui / wince / playlist.cpp
1 /*****************************************************************************
2  * playlist.cpp : WinCE gui plugin for VLC
3  *****************************************************************************
4  * Copyright (C) 2000-2004 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Marodon Cedric <cedric_marodon@yahoo.fr>
8  *          Gildas Bazin <gbazin@videolan.org>
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., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
31
32 #include <vlc_common.h>
33 #include <vlc_interface.h>
34
35 #include "wince.h"
36
37 #include <commctrl.h>
38 #include <commdlg.h>
39
40 #ifndef TEXTMAXBUF
41 #define TEXTMAXBUF 512 // at least 500
42 #endif
43
44 #define LONG2POINT(l, pt)  ((pt).x = (SHORT)LOWORD(l), (pt).y = (SHORT)HIWORD(l))
45
46 #define NUMIMAGES     11   // Number of buttons in the toolbar
47 #define IMAGEWIDTH    16   // Width of the buttons in the toolbar
48 #define IMAGEHEIGHT   16   // Height of the buttons in the toolbar
49 #define BUTTONWIDTH   0    // Width of the button images in the toolbar
50 #define BUTTONHEIGHT  0    // Height of the button images in the toolbar
51 #define ID_TOOLBAR    2000 // Identifier of the main tool bar
52
53 enum
54 {
55   Infos_Event = 1000,
56   Up_Event,
57   Down_Event,
58   Random_Event,
59   Loop_Event,
60   Repeat_Event,
61   PopupPlay_Event,
62   PopupDel_Event,
63   PopupEna_Event,
64   PopupInfo_Event
65 };
66
67 // Help strings
68 #define HELP_OPENPL _T("Open playlist")
69 #define HELP_SAVEPL _T("Save playlist")
70 #define HELP_ADDFILE _T("Add File")
71 #define HELP_ADDMRL _T("Add MRL")
72 #define HELP_DELETE _T("Delete selection")
73 #define HELP_INFOS _T("Item info")
74 #define HELP_UP _T("Up")
75 #define HELP_DOWN _T("Down")
76 #define HELP_RANDOM _T("Random")
77 #define HELP_LOOP _T("Repeat all")
78 #define HELP_REPEAT _T("Repeat one")
79
80 // The TBBUTTON structure contains information the toolbar buttons.
81 static TBBUTTON tbButton2[] =
82 {
83   {0, ID_MANAGE_OPENPL,        TBSTATE_ENABLED, TBSTYLE_BUTTON},
84   {1, ID_MANAGE_SAVEPL,       TBSTATE_ENABLED, TBSTYLE_BUTTON},
85   {0, 0,              TBSTATE_ENABLED, TBSTYLE_SEP},
86   {2, ID_MANAGE_ADDFILE,       TBSTATE_ENABLED, TBSTYLE_BUTTON},
87   {3, ID_MANAGE_ADDMRL,        TBSTATE_ENABLED, TBSTYLE_BUTTON},
88   {4, ID_SEL_DELETE,       TBSTATE_ENABLED, TBSTYLE_BUTTON},
89   {0, 0,              TBSTATE_ENABLED, TBSTYLE_SEP},
90   {5, Infos_Event,      TBSTATE_ENABLED, TBSTYLE_BUTTON},
91   {0, 0,              TBSTATE_ENABLED, TBSTYLE_SEP},
92   {6, Up_Event,      TBSTATE_ENABLED, TBSTYLE_BUTTON},
93   {7, Down_Event,      TBSTATE_ENABLED, TBSTYLE_BUTTON},
94   {0, 0,              TBSTATE_ENABLED, TBSTYLE_SEP},
95   {8, Random_Event,      TBSTATE_ENABLED, TBSTYLE_CHECK},
96   {9, Loop_Event,       TBSTATE_ENABLED, TBSTYLE_CHECK},
97   {10, Repeat_Event,       TBSTATE_ENABLED, TBSTYLE_CHECK}
98 };
99
100 // Toolbar ToolTips
101 TCHAR * szToolTips2[] =
102 {
103     HELP_OPENPL,
104     HELP_SAVEPL,
105     HELP_ADDFILE,
106     HELP_ADDMRL,
107     HELP_DELETE,
108     HELP_INFOS,
109     HELP_UP,
110     HELP_DOWN,
111     HELP_RANDOM,
112     HELP_LOOP,
113     HELP_REPEAT
114 };
115
116 /*****************************************************************************
117  * Event Table.
118  *****************************************************************************/
119
120 /*****************************************************************************
121  * Constructor.
122  *****************************************************************************/
123 Playlist::Playlist( intf_thread_t *p_intf, CBaseWindow *p_parent,
124                     HINSTANCE h_inst )
125   :  CBaseWindow( p_intf, p_parent, h_inst )
126 {
127     /* Initializations */
128     hListView = NULL;
129     i_title_sorted = 1;
130     i_author_sorted = 1;
131
132     b_need_update = true;
133 }
134
135 /***********************************************************************
136 FUNCTION:
137   CreateMenuBar
138
139 PURPOSE:
140   Creates a menu bar.
141 ***********************************************************************/
142 static HWND CreateMenuBar( HWND hwnd, HINSTANCE hInst )
143 {
144 #ifdef UNDER_CE
145     SHMENUBARINFO mbi;
146     memset( &mbi, 0, sizeof(SHMENUBARINFO) );
147     mbi.cbSize     = sizeof(SHMENUBARINFO);
148     mbi.hwndParent = hwnd;
149     mbi.hInstRes   = hInst;
150     mbi.nToolBarId = IDR_MENUBAR2;
151
152     if( !SHCreateMenuBar( &mbi ) )
153     {
154         MessageBox(hwnd, _T("SHCreateMenuBar Failed"), _T("Error"), MB_OK);
155         return 0;
156     }
157
158     TBBUTTONINFO tbbi;
159     tbbi.cbSize = sizeof(tbbi);
160     tbbi.dwMask = TBIF_LPARAM;
161
162     SendMessage( mbi.hwndMB, TB_GETBUTTONINFO, IDM_MANAGE, (LPARAM)&tbbi );
163     HMENU hmenu_file = (HMENU)tbbi.lParam;
164     RemoveMenu( hmenu_file, 0, MF_BYPOSITION );
165     SendMessage( mbi.hwndMB, TB_GETBUTTONINFO, IDM_SORT, (LPARAM)&tbbi );
166     HMENU hmenu_sort = (HMENU)tbbi.lParam;
167     RemoveMenu( hmenu_sort, 0, MF_BYPOSITION );
168     SendMessage( mbi.hwndMB, TB_GETBUTTONINFO, IDM_SEL, (LPARAM)&tbbi );
169     HMENU hmenu_sel = (HMENU)tbbi.lParam;
170     RemoveMenu( hmenu_sel, 0, MF_BYPOSITION );
171
172 #else
173     HMENU hmenu_file = CreatePopupMenu();
174     HMENU hmenu_sort = CreatePopupMenu();
175     HMENU hmenu_sel = CreatePopupMenu();
176 #endif
177
178     AppendMenu( hmenu_file, MF_STRING, ID_MANAGE_ADDFILE,
179                 _T("&Add File...") );
180     AppendMenu( hmenu_file, MF_STRING, ID_MANAGE_ADDDIRECTORY,
181                 _T("Add Directory...") );
182     AppendMenu( hmenu_file, MF_STRING, ID_MANAGE_ADDMRL,
183                 _T("Add MRL...") );
184     AppendMenu( hmenu_file, MF_SEPARATOR, 0, 0 );
185     AppendMenu( hmenu_file, MF_STRING, ID_MANAGE_OPENPL,
186                 _T("Open &Playlist") );
187     AppendMenu( hmenu_file, MF_STRING, ID_MANAGE_SAVEPL,
188                 _T("Save Playlist") );
189
190     AppendMenu( hmenu_sort, MF_STRING, ID_SORT_TITLE,
191                 _T("Sort by &title") );
192     AppendMenu( hmenu_sort, MF_STRING, ID_SORT_RTITLE,
193                 _T("&Reverse sort by title") );
194     AppendMenu( hmenu_sort, MF_SEPARATOR, 0, 0 );
195     AppendMenu( hmenu_sort, MF_STRING, ID_SORT_AUTHOR,
196                 _T("Sort by &author") );
197     AppendMenu( hmenu_sort, MF_STRING, ID_SORT_RAUTHOR,
198                 _T("Reverse sort by &author") );
199     AppendMenu( hmenu_sort, MF_SEPARATOR, 0, 0 );
200     AppendMenu( hmenu_sort, MF_STRING, ID_SORT_SHUFFLE,
201                 _T("&Shuffle Playlist") );
202
203     AppendMenu( hmenu_sel, MF_STRING, ID_SEL_ENABLE,
204                 _T("&Enable") );
205     AppendMenu( hmenu_sel, MF_STRING, ID_SEL_DISABLE,
206                 _T("&Disable") );
207     AppendMenu( hmenu_sel, MF_SEPARATOR, 0, 0 );
208     AppendMenu( hmenu_sel, MF_STRING, ID_SEL_INVERT,
209                 _T("&Invert") );
210     AppendMenu( hmenu_sel, MF_STRING, ID_SEL_DELETE,
211                 _T("D&elete") );
212     AppendMenu( hmenu_sel, MF_SEPARATOR, 0, 0 );
213     AppendMenu( hmenu_sel, MF_STRING, ID_SEL_SELECTALL,
214                 _T("&Select All") );
215
216
217 #ifdef UNDER_CE
218     return mbi.hwndMB;
219
220 #else
221     HMENU hmenu = CreateMenu();
222
223     AppendMenu( hmenu, MF_POPUP|MF_STRING, (UINT)hmenu_file, _T("Manage") );
224     AppendMenu( hmenu, MF_POPUP|MF_STRING, (UINT)hmenu_sort, _T("Sort") );
225     AppendMenu( hmenu, MF_POPUP|MF_STRING, (UINT)hmenu_sel, _T("Selection") );
226
227     SetMenu( hwnd, hmenu );
228     return hwnd;
229
230 #endif
231 }
232
233 /***********************************************************************
234 FUNCTION:
235   WndProc
236
237 PURPOSE:
238   Processes messages sent to the main window.
239 ***********************************************************************/
240 LRESULT Playlist::WndProc( HWND hwnd, UINT msg, WPARAM wp, LPARAM lp )
241 {
242     SHINITDLGINFO shidi;
243     SHMENUBARINFO mbi;
244     INITCOMMONCONTROLSEX iccex;
245     RECT rect, rectTB;
246     DWORD dwStyle;
247
248     int bState;
249     playlist_t *p_playlist;
250
251     switch( msg )
252     {
253     case WM_INITDIALOG:
254         shidi.dwMask = SHIDIM_FLAGS;
255         shidi.dwFlags = SHIDIF_DONEBUTTON | SHIDIF_SIPDOWN |
256             SHIDIF_FULLSCREENNOMENUBAR;//SHIDIF_SIZEDLGFULLSCREEN;
257         shidi.hDlg = hwnd;
258         SHInitDialog( &shidi );
259
260         hwndCB = CreateMenuBar( hwnd, hInst );
261
262         iccex.dwSize = sizeof (INITCOMMONCONTROLSEX);
263         iccex.dwICC = ICC_BAR_CLASSES;
264
265         // Registers TOOLBAR control classes from the common control dll
266         InitCommonControlsEx (&iccex);
267
268         //  Create the toolbar control.
269         dwStyle = WS_VISIBLE | WS_CHILD | TBSTYLE_TOOLTIPS |
270             WS_EX_OVERLAPPEDWINDOW | CCS_NOPARENTALIGN;
271
272         hwndTB = CreateToolbarEx( hwnd, dwStyle, 0, NUMIMAGES,
273                                   hInst, IDB_BITMAP3, tbButton2,
274                                   sizeof (tbButton2) / sizeof (TBBUTTON),
275                                   BUTTONWIDTH, BUTTONHEIGHT,
276                                   IMAGEWIDTH, IMAGEHEIGHT, sizeof(TBBUTTON) );
277         if( !hwndTB ) break;
278  
279         // Add ToolTips to the toolbar.
280         SendMessage( hwndTB, TB_SETTOOLTIPS, (WPARAM) NUMIMAGES,
281                      (LPARAM)szToolTips2 );
282
283         // Reposition the toolbar.
284         GetClientRect( hwnd, &rect );
285         GetWindowRect( hwndTB, &rectTB );
286         MoveWindow( hwndTB, rect.left, rect.top - 2, rect.right - rect.left,
287                     MENU_HEIGHT /*rectTB.bottom - rectTB.top */, TRUE);
288
289         // random, loop, repeat buttons states
290         vlc_value_t val;
291         p_playlist = pl_Yield( p_intf );
292         if( !p_playlist ) break;
293
294         var_Get( p_playlist , "random", &val );
295         bState = val.b_bool ? TBSTATE_CHECKED : 0;
296         SendMessage( hwndTB, TB_SETSTATE, Random_Event,
297                      MAKELONG(bState | TBSTATE_ENABLED, 0) );
298         var_Get( p_playlist , "loop", &val );
299         bState = val.b_bool ? TBSTATE_CHECKED : 0;
300         SendMessage( hwndTB, TB_SETSTATE, Loop_Event,
301                      MAKELONG(bState | TBSTATE_ENABLED, 0) );
302         var_Get( p_playlist , "repeat", &val );
303         bState = val.b_bool ? TBSTATE_CHECKED : 0;
304         SendMessage( hwndTB, TB_SETSTATE, Repeat_Event,
305                      MAKELONG(bState | TBSTATE_ENABLED, 0) );
306         pl_Release( p_intf );
307
308         GetClientRect( hwnd, &rect );
309         hListView = CreateWindow( WC_LISTVIEW, NULL, WS_VISIBLE | WS_CHILD |
310             LVS_REPORT | LVS_SHOWSELALWAYS | WS_VSCROLL | WS_HSCROLL,
311             rect.left, rect.top + 2*(MENU_HEIGHT+1), rect.right - rect.left,
312             rect.bottom - ( rect.top + 2*MENU_HEIGHT) - MENU_HEIGHT,
313             hwnd, NULL, hInst, NULL );
314         ListView_SetExtendedListViewStyle( hListView, LVS_EX_FULLROWSELECT );
315
316         LVCOLUMN lv;
317         lv.mask = LVCF_WIDTH | LVCF_FMT | LVCF_TEXT;
318         lv.fmt = LVCFMT_LEFT ;
319         GetClientRect( hwnd, &rect );
320         lv.cx = 120;
321         lv.pszText = _T("Name");
322         lv.cchTextMax = 9;
323         ListView_InsertColumn( hListView, 0, &lv);
324         lv.cx = 55;
325         lv.pszText = _T("Author");
326         lv.cchTextMax = 9;
327         ListView_InsertColumn( hListView, 1, &lv);
328         lv.cx = rect.right - rect.left - 180;
329         lv.pszText = _T("Duration");
330         lv.cchTextMax = 9;
331         ListView_InsertColumn( hListView, 2, &lv);
332
333         SetTimer( hwnd, 1, 500 /*milliseconds*/, NULL );
334         break;
335
336     case WM_TIMER:
337         UpdatePlaylist();
338         break;
339
340     case WM_CLOSE:
341         EndDialog( hwnd, LOWORD( wp ) );
342         break;
343
344     case WM_SETFOCUS:
345         SHSipPreference( hwnd, SIP_DOWN );
346         SHFullScreen( hwnd, SHFS_HIDESIPBUTTON );
347         break;
348
349     case WM_COMMAND:
350         switch( LOWORD(wp) )
351         {
352         case IDOK:
353             EndDialog( hwnd, LOWORD( wp ) );
354             break;
355
356         case ID_MANAGE_OPENPL:
357             OnOpen();
358             b_need_update = true;
359             break;
360
361         case ID_MANAGE_SAVEPL:
362             OnSave();
363             break;
364
365         case ID_MANAGE_ADDFILE:
366             p_intf->p_sys->pf_show_dialog( p_intf, INTF_DIALOG_FILE_SIMPLE,
367                                            0, 0 );
368             b_need_update = true;
369             break;
370
371         case ID_MANAGE_ADDDIRECTORY:
372             p_intf->p_sys->pf_show_dialog( p_intf, INTF_DIALOG_DIRECTORY,
373                                            0, 0 );
374             b_need_update = true;
375             break;
376
377         case ID_MANAGE_ADDMRL:
378             p_intf->p_sys->pf_show_dialog( p_intf, INTF_DIALOG_FILE, 0, 0 );
379             b_need_update = true;
380             break;
381
382         case ID_SEL_DELETE:
383             OnDeleteSelection();
384             b_need_update = true;
385             break;
386
387         case Infos_Event:
388             OnPopupInfo( hwnd );
389             b_need_update = true;
390             break;
391
392         case Up_Event:
393             OnUp();
394             b_need_update = true;
395             break;
396
397         case Down_Event:
398             OnDown();
399             b_need_update = true;
400             break;
401
402         case Random_Event:
403             OnRandom();
404             break;
405
406         case Loop_Event:
407             OnLoop();
408             break;
409
410         case Repeat_Event:
411             OnRepeat();
412             break;
413
414         case ID_SORT_TITLE:
415             OnSort( ID_SORT_TITLE );
416             break;
417
418         case ID_SORT_RTITLE:
419             OnSort( ID_SORT_RTITLE );
420             break;
421
422         case ID_SORT_AUTHOR:
423             OnSort( ID_SORT_AUTHOR );
424             break;
425
426         case ID_SORT_RAUTHOR:
427             OnSort( ID_SORT_RAUTHOR );
428             break;
429
430         case ID_SORT_SHUFFLE:
431             OnSort( ID_SORT_SHUFFLE );
432             break;
433
434         case ID_SEL_ENABLE:
435             OnEnableSelection();
436             break;
437
438         case ID_SEL_DISABLE:
439             OnDisableSelection();
440             break;
441
442         case ID_SEL_INVERT:
443             OnInvertSelection();
444             break;
445
446         case ID_SEL_SELECTALL:
447             OnSelectAll();
448             break;
449
450         case PopupPlay_Event:
451             OnPopupPlay();
452             b_need_update = true;
453             break;
454
455         case PopupDel_Event:
456             OnPopupDel();
457             b_need_update = true;
458             break;
459
460         case PopupEna_Event:
461             OnPopupEna();
462             b_need_update = true;
463             break;
464
465         case PopupInfo_Event:
466             OnPopupInfo( hwnd );
467             b_need_update = true;
468             break;
469
470         default:
471             break;
472         }
473         break;
474
475     case WM_NOTIFY:
476         if( ( ((LPNMHDR)lp)->hwndFrom == hListView ) &&
477             ( ((LPNMHDR)lp)->code == NM_CUSTOMDRAW ) )
478         {
479             SetWindowLong( hwnd, DWL_MSGRESULT,
480                            (LONG)ProcessCustomDraw(lp) );
481         }
482         else if( ( ((LPNMHDR)lp)->hwndFrom == hListView ) &&
483                  ( ((LPNMHDR)lp)->code == GN_CONTEXTMENU  ) )
484         {
485             HandlePopupMenu( hwnd, ((PNMRGINFO)lp)->ptAction );
486         }
487         else if( ( ((LPNMHDR)lp)->hwndFrom == hListView ) &&
488                  ( ((LPNMHDR)lp)->code == LVN_COLUMNCLICK  ) )
489         {
490             OnColSelect( ((LPNMLISTVIEW)lp)->iSubItem );
491         }
492         else if( ( ((LPNMHDR)lp)->hwndFrom == hListView ) &&
493                  ( ((LPNMHDR)lp)->code == LVN_ITEMACTIVATE  ) )
494         {
495             OnActivateItem( ((LPNMLISTVIEW)lp)->iSubItem );
496         }
497         break;
498
499     default:
500          // the message was not processed
501          // indicate if the base class handled it
502         break;
503     }
504
505     return FALSE;
506 }
507
508 LRESULT Playlist::ProcessCustomDraw( LPARAM lParam )
509 {
510     LPNMLVCUSTOMDRAW lplvcd = (LPNMLVCUSTOMDRAW)lParam;
511
512     switch( lplvcd->nmcd.dwDrawStage )
513     {
514     case CDDS_PREPAINT : //Before the paint cycle begins
515         //request notifications for individual listview items
516         return CDRF_NOTIFYITEMDRAW;
517
518     case CDDS_ITEMPREPAINT: //Before an item is drawn
519         playlist_t *p_playlist = pl_Yield( p_intf );
520         if( p_playlist == NULL ) return CDRF_DODEFAULT;
521         if( (int)lplvcd->nmcd.dwItemSpec == p_playlist->i_index )
522         {
523             lplvcd->clrText = RGB(255,0,0);
524             pl_Release( p_intf );
525             return CDRF_NEWFONT;
526         }
527  
528         playlist_item_t *p_item = playlist_ItemGetByPos( p_playlist,
529                                         (int)lplvcd->nmcd.dwItemSpec );
530         if( !p_item )
531         {
532             pl_Release( p_intf );
533             return CDRF_DODEFAULT;
534         }
535         if( p_item->b_enabled == false )
536         {
537             lplvcd->clrText = RGB(192,192,192);
538             pl_Release( p_intf );
539             return CDRF_NEWFONT;
540         }
541         pl_Release( p_intf );
542     }
543
544     return CDRF_DODEFAULT;
545 }
546
547 /**********************************************************************
548  * Handles the display of the "floating" popup
549  **********************************************************************/
550 void Playlist::HandlePopupMenu( HWND hwnd, POINT point )
551 {
552     HMENU hMenuTrackPopup;
553
554     // Create the popup menu.
555     hMenuTrackPopup = CreatePopupMenu();
556
557     // Append some items.
558     AppendMenu( hMenuTrackPopup, MF_STRING, PopupPlay_Event, _T("Play") );
559     AppendMenu( hMenuTrackPopup, MF_STRING, PopupDel_Event, _T("Delete") );
560     AppendMenu( hMenuTrackPopup, MF_STRING, PopupEna_Event,
561                 _T("Toggle enabled") );
562     AppendMenu( hMenuTrackPopup, MF_STRING, PopupInfo_Event, _T("Info") );
563
564     /* Draw and track the "floating" popup */
565     TrackPopupMenu( hMenuTrackPopup, 0, point.x, point.y, 0, hwnd, NULL );
566
567     /* Destroy the menu since were are done with it. */
568     DestroyMenu( hMenuTrackPopup );
569 }
570
571 /**********************************************************************
572  * Show the playlist
573  **********************************************************************/
574 void Playlist::ShowPlaylist( bool b_show )
575 {
576     if( b_show ) Rebuild();
577     Show( b_show );
578 }
579
580 /**********************************************************************
581  * Update the playlist
582  **********************************************************************/
583 void Playlist::UpdatePlaylist()
584 {
585     if( b_need_update )
586     {
587         Rebuild();
588         b_need_update = false;
589     }
590  
591     playlist_t *p_playlist = pl_Yield( p_intf );
592     if( p_playlist == NULL ) return;
593  
594     /* Update the colour of items */
595
596     vlc_object_lock( p_playlist );
597     if( p_intf->p_sys->i_playing != p_playlist->i_index )
598     {
599         // p_playlist->i_index in RED
600         Rebuild();
601
602         // if exists, p_intf->p_sys->i_playing in BLACK
603         p_intf->p_sys->i_playing = p_playlist->i_index;
604     }
605     vlc_object_unlock( p_playlist );
606
607     pl_Release( p_intf );
608 }
609
610 /**********************************************************************
611  * Rebuild the playlist
612  **********************************************************************/
613 void Playlist::Rebuild()
614 {
615     playlist_t *p_playlist = pl_Yield( p_intf );
616     if( p_playlist == NULL ) return;
617
618     int i_focused =
619         ListView_GetNextItem( hListView, -1, LVIS_SELECTED | LVNI_ALL );
620
621     /* Clear the list... */
622     ListView_DeleteAllItems( hListView );
623
624     /* ...and rebuild it */
625     vlc_object_lock( p_playlist );
626     for( int i = 0; i < p_playlist->i_size; i++ )
627     {
628         LVITEM lv;
629         lv.mask = LVIF_TEXT;
630         lv.pszText = _T("");
631         lv.cchTextMax = 1;
632         lv.iSubItem = 0;
633         lv.iItem = i;
634         ListView_InsertItem( hListView, &lv );
635         ListView_SetItemText( hListView, lv.iItem, 0,
636             _FROMMB(p_playlist->pp_items[i]->input.psz_name) );
637         UpdateItem( i );
638     }
639     vlc_object_unlock( p_playlist );
640
641     if ( i_focused )
642         ListView_SetItemState( hListView, i_focused, LVIS_FOCUSED |
643                                LVIS_SELECTED, LVIS_STATEIMAGEMASK )
644     else
645         ListView_SetItemState( hListView, i_focused, LVIS_FOCUSED,
646                                LVIS_STATEIMAGEMASK );
647
648     pl_Release( p_intf );
649 }
650
651 /**********************************************************************
652  * Update one playlist item
653  **********************************************************************/
654 void Playlist::UpdateItem( int i )
655 {
656     playlist_t *p_playlist = pl_Yield( p_intf );
657
658     if( p_playlist == NULL ) return;
659
660     playlist_item_t *p_item = playlist_ItemGetByPos( p_playlist, i );
661
662     if( !p_item )
663     {
664         pl_Release( p_intf );
665         return;
666     }
667
668     ListView_SetItemText( hListView, i, 0, _FROMMB(p_item->input.psz_name) );
669     ListView_SetItemText( hListView, i, 1,
670                           _FROMMB( input_item_GetInfo( &p_item->input,
671                                    _("General") , _("Author") ) ) );
672
673     char psz_duration[MSTRTIME_MAX_SIZE];
674     mtime_t dur = input_item_GetDuration( p_item );
675     if( dur != -1 ) secstotimestr( psz_duration, dur/1000000 );
676     else memcpy( psz_duration , "-:--:--", sizeof("-:--:--") );
677
678     ListView_SetItemText( hListView, i, 3, _FROMMB(psz_duration) );
679
680     pl_Release( p_intf );
681 }
682
683 /**********************************************************************
684  * Private functions
685  **********************************************************************/
686 void Playlist::DeleteItem( int item )
687 {
688     playlist_t *p_playlist = pl_Yield( p_intf );
689     if( p_playlist == NULL ) return;
690
691     playlist_Delete( p_playlist, item );
692     ListView_DeleteItem( hListView, item );
693
694     pl_Release( p_intf );
695 }
696
697 /**********************************************************************
698  * I/O functions
699  **********************************************************************/
700 static void OnOpenCB( intf_dialog_args_t *p_arg )
701 {
702     intf_thread_t *p_intf = (intf_thread_t *)p_arg->p_arg;
703
704     if( p_arg->i_results && p_arg->psz_results[0] )
705     {
706         playlist_t * p_playlist = pl_Yield( p_intf );
707
708         if( p_playlist )
709         {
710             playlist_Import( p_playlist, p_arg->psz_results[0] );
711             pl_Release( p_intf );
712         }
713     }
714 }
715
716 void Playlist::OnOpen()
717 {
718     char *psz_filters ="All playlists|*.pls;*.m3u;*.asx;*.b4s|M3U files|*.m3u";
719
720     intf_dialog_args_t *p_arg =
721         (intf_dialog_args_t *)malloc( sizeof(intf_dialog_args_t) );
722     memset( p_arg, 0, sizeof(intf_dialog_args_t) );
723
724     p_arg->psz_title = strdup( "Open playlist" );
725     p_arg->psz_extensions = strdup( psz_filters );
726     p_arg->p_arg = p_intf;
727     p_arg->pf_callback = OnOpenCB;
728
729     p_intf->p_sys->pf_show_dialog( p_intf, INTF_DIALOG_FILE_GENERIC, 0, p_arg);
730 }
731
732 static void OnSaveCB( intf_dialog_args_t *p_arg )
733 {
734     intf_thread_t *p_intf = (intf_thread_t *)p_arg->p_arg;
735
736     if( p_arg->i_results && p_arg->psz_results[0] )
737     {
738         playlist_t * p_playlist = pl_Yield( p_intf );
739
740         if( p_playlist )
741         {
742             char *psz_export;
743             char *psz_ext = strrchr( p_arg->psz_results[0], '.' );
744
745             if( psz_ext && !strcmp( psz_ext, ".pls") )
746                 psz_export = "export-pls";
747             else psz_export = "export-m3u";
748
749             playlist_Export( p_playlist, p_arg->psz_results[0], psz_export );
750             pl_Release( p_intf );
751         }
752     }
753 }
754
755 void Playlist::OnSave()
756 {
757     char *psz_filters ="M3U file|*.m3u|PLS file|*.pls";
758
759     intf_dialog_args_t *p_arg =
760         (intf_dialog_args_t *)malloc( sizeof(intf_dialog_args_t) );
761     memset( p_arg, 0, sizeof(intf_dialog_args_t) );
762
763     p_arg->psz_title = strdup( "Save playlist" );
764     p_arg->psz_extensions = strdup( psz_filters );
765     p_arg->b_save = true;
766     p_arg->p_arg = p_intf;
767     p_arg->pf_callback = OnSaveCB;
768
769     p_intf->p_sys->pf_show_dialog( p_intf, INTF_DIALOG_FILE_GENERIC,
770                                    0, p_arg );
771 }
772
773 /**********************************************************************
774  * Selection functions
775  **********************************************************************/
776 void Playlist::OnDeleteSelection()
777 {
778     /* Delete from the end to the beginning, to avoid a shift of indices */
779     for( long item = ((int) ListView_GetItemCount( hListView ) - 1);
780          item >= 0; item-- )
781     {
782         if( ListView_GetItemState( hListView, item, LVIS_SELECTED ) )
783         {
784             DeleteItem( item );
785         }
786     }
787 }
788
789 void Playlist::OnInvertSelection()
790 {
791     UINT iState;
792
793     for( long item = 0; item < ListView_GetItemCount( hListView ); item++ )
794     {
795         iState = ListView_GetItemState( hListView, item, LVIS_STATEIMAGEMASK );
796         ListView_SetItemState( hListView, item, iState ^ LVIS_SELECTED,
797                                LVIS_STATEIMAGEMASK );
798     }
799 }
800
801 void Playlist::OnEnableSelection()
802 {
803     playlist_t *p_playlist = pl_Yield( p_intf );
804     if( p_playlist == NULL ) return;
805
806     for( long item = ListView_GetItemCount( hListView ) - 1;
807          item >= 0; item-- )
808     {
809         if( ListView_GetItemState( hListView, item, LVIS_SELECTED ) )
810         {
811             playlist_item_t *p_item =
812                 playlist_ItemGetByPos( p_playlist, item );
813             playlist_Enable( p_playlist, p_item );
814             UpdateItem( item );
815         }
816     }
817     pl_Release( p_intf );
818 }
819
820 void Playlist::OnDisableSelection()
821 {
822     playlist_t *p_playlist = pl_Yield( p_intf );
823     if( p_playlist == NULL ) return;
824
825     for( long item = ListView_GetItemCount( hListView ) - 1;
826          item >= 0; item-- )
827     {
828         if( ListView_GetItemState( hListView, item, LVIS_SELECTED ) )
829         {
830             /*XXX*/
831             playlist_item_t *p_item =
832                 playlist_ItemGetByPos( p_playlist, item );
833             playlist_Disable( p_playlist, p_item );
834             UpdateItem( item );
835         }
836     }
837     pl_Release( p_intf );
838 }
839
840 void Playlist::OnSelectAll()
841 {
842     for( long item = 0; item < ListView_GetItemCount( hListView ); item++ )
843     {
844         ListView_SetItemState( hListView, item, LVIS_FOCUSED | LVIS_SELECTED,
845                                LVIS_STATEIMAGEMASK );
846     }
847 }
848
849 void Playlist::OnActivateItem( int i_item )
850 {
851     playlist_t *p_playlist = pl_Yield( p_intf );
852     if( p_playlist == NULL ) return;
853
854     playlist_Goto( p_playlist, i_item );
855
856     pl_Release( p_intf );
857 }
858
859 void Playlist::ShowInfos( HWND hwnd, int i_item )
860 {
861     playlist_t *p_playlist = pl_Yield( p_intf );
862     if( p_playlist == NULL ) return;
863
864     vlc_object_lock( p_playlist);
865     playlist_item_t *p_item = playlist_ItemGetByPos( p_playlist, i_item );
866     vlc_object_unlock( p_playlist );
867
868     if( p_item )
869     {
870         ItemInfoDialog *iteminfo_dialog =
871             new ItemInfoDialog( p_intf, this, hInst, p_item );
872         CreateDialogBox( hwnd, iteminfo_dialog );
873         UpdateItem( i_item );
874         delete iteminfo_dialog;
875     }
876
877     pl_Release( p_intf );
878 }
879
880 /********************************************************************
881  * Move functions
882  ********************************************************************/
883 void Playlist::OnUp()
884 {
885     playlist_t *p_playlist = pl_Yield( p_intf );
886     if( p_playlist == NULL ) return;
887
888     /* We use the first selected item, so find it */
889     long i_item =
890         ListView_GetNextItem( hListView, -1, LVIS_SELECTED | LVNI_ALL );
891
892     if( i_item > 0 && i_item < p_playlist->i_size )
893     {
894         playlist_Move( p_playlist , i_item, i_item - 1);
895         if( i_item > 1 )
896         {
897             ListView_SetItemState( hListView, i_item - 1, LVIS_FOCUSED,
898                                    LVIS_STATEIMAGEMASK );
899         }
900         else
901         {
902             ListView_SetItemState( hListView, 0, LVIS_FOCUSED,
903                                    LVIS_STATEIMAGEMASK );
904         }
905     }
906     pl_Release( p_intf );
907
908     return;
909 }
910
911 void Playlist::OnDown()
912 {
913     playlist_t *p_playlist = pl_Yield( p_intf );
914     if( p_playlist == NULL ) return;
915
916     /* We use the first selected item, so find it */
917     long i_item =
918         ListView_GetNextItem( hListView, -1, LVIS_SELECTED | LVNI_ALL );
919
920     if( i_item >= 0 && i_item < p_playlist->i_size - 1 )
921     {
922         playlist_Move( p_playlist , i_item, i_item + 2 );
923         ListView_SetItemState( hListView, i_item + 1, LVIS_FOCUSED,
924                                LVIS_STATEIMAGEMASK );
925     }
926     pl_Release( p_intf );
927
928     return;
929 }
930
931 /**********************************************************************
932  * Playlist mode functions
933  **********************************************************************/
934 void Playlist::OnRandom()
935 {
936     vlc_value_t val;
937     int bState = SendMessage( hwndTB, TB_GETSTATE, Random_Event, 0 );
938     val.b_bool = (bState & TBSTATE_CHECKED) ? true : false;
939
940     playlist_t *p_playlist = pl_Yield( p_intf );
941     if( p_playlist == NULL ) return;
942
943     var_Set( p_playlist , "random", val );
944     pl_Release( p_intf );
945 }
946
947 void Playlist::OnLoop ()
948 {
949     vlc_value_t val;
950     int bState = SendMessage( hwndTB, TB_GETSTATE, Loop_Event, 0 );
951     val.b_bool = (bState & TBSTATE_CHECKED) ? true : false;
952
953     playlist_t *p_playlist = pl_Yield( p_intf );
954     if( p_playlist == NULL ) return;
955
956     var_Set( p_playlist , "loop", val );
957     pl_Release( p_intf );
958 }
959
960 void Playlist::OnRepeat ()
961 {
962     vlc_value_t val;
963     int bState = SendMessage( hwndTB, TB_GETSTATE, Repeat_Event, 0 );
964     val.b_bool = (bState & TBSTATE_CHECKED) ? true : false;
965
966     playlist_t *p_playlist = pl_Yield( p_intf );
967     if( p_playlist == NULL ) return;
968
969     var_Set( p_playlist , "repeat", val );
970     pl_Release( p_intf );
971 }
972
973 /********************************************************************
974  * Sorting functions
975  ********************************************************************/
976 void Playlist::OnSort( UINT event )
977 {
978     playlist_t *p_playlist = pl_Yield( p_intf );
979     if( p_playlist == NULL ) return;
980
981     switch( event )
982     {
983     case ID_SORT_TITLE:
984         playlist_SortTitle( p_playlist , ORDER_NORMAL );
985         break;
986     case ID_SORT_RTITLE:
987         playlist_SortTitle( p_playlist , ORDER_REVERSE );
988         break;
989     case ID_SORT_AUTHOR:
990         playlist_SortAuthor(p_playlist , ORDER_NORMAL );
991         break;
992     case ID_SORT_RAUTHOR:
993         playlist_SortAuthor( p_playlist , ORDER_REVERSE );
994         break;
995     case ID_SORT_SHUFFLE:
996         playlist_Sort( p_playlist , SORT_RANDOM, ORDER_NORMAL );
997         break;
998     }
999
1000     pl_Release( p_intf );
1001
1002     b_need_update = true;
1003
1004     return;
1005 }
1006
1007 void Playlist::OnColSelect( int iSubItem )
1008 {
1009     playlist_t *p_playlist = pl_Yield( p_intf );
1010     if( p_playlist == NULL ) return;
1011
1012     switch( iSubItem )
1013     {
1014     case 0:
1015         if( i_title_sorted != 1 )
1016         {
1017             playlist_SortTitle( p_playlist, ORDER_NORMAL );
1018             i_title_sorted = 1;
1019         }
1020         else
1021         {
1022             playlist_SortTitle( p_playlist, ORDER_REVERSE );
1023             i_title_sorted = -1;
1024         }
1025         break;
1026     case 1:
1027         if( i_author_sorted != 1 )
1028         {
1029             playlist_SortAuthor( p_playlist, ORDER_NORMAL );
1030             i_author_sorted = 1;
1031         }
1032         else
1033         {
1034             playlist_SortAuthor( p_playlist, ORDER_REVERSE );
1035             i_author_sorted = -1;
1036         }
1037         break;
1038     default:
1039         break;
1040     }
1041
1042     pl_Release( p_intf );
1043
1044     b_need_update = true;
1045
1046     return;
1047 }
1048
1049 /*****************************************************************************
1050  * Popup management functions
1051  *****************************************************************************/
1052 void Playlist::OnPopupPlay()
1053 {
1054     int i_popup_item =
1055         ListView_GetNextItem( hListView, -1, LVIS_SELECTED | LVNI_ALL );
1056
1057     playlist_t *p_playlist = pl_Yield( p_intf );
1058     if( p_playlist == NULL ) return;
1059
1060     if( i_popup_item != -1 )
1061     {
1062         playlist_Goto( p_playlist, i_popup_item );
1063     }
1064
1065     pl_Release( p_intf );
1066 }
1067
1068 void Playlist::OnPopupDel()
1069 {
1070     int i_popup_item =
1071         ListView_GetNextItem( hListView, -1, LVIS_SELECTED | LVNI_ALL );
1072
1073     DeleteItem( i_popup_item );
1074 }
1075
1076 void Playlist::OnPopupEna()
1077 {
1078     int i_popup_item =
1079         ListView_GetNextItem( hListView, -1, LVIS_SELECTED | LVNI_ALL );
1080
1081     playlist_t *p_playlist = pl_Yield( p_intf );
1082     if( p_playlist == NULL ) return;
1083
1084     playlist_item_t *p_item =
1085         playlist_ItemGetByPos( p_playlist, i_popup_item );
1086
1087     if( p_playlist->pp_items[i_popup_item]->b_enabled )
1088         //playlist_IsEnabled( p_playlist, i_popup_item ) )
1089     {
1090         playlist_Disable( p_playlist, p_item );
1091     }
1092     else
1093     {
1094         playlist_Enable( p_playlist, p_item );
1095     }
1096
1097     pl_Release( p_intf );
1098     UpdateItem( i_popup_item );
1099 }
1100
1101 void Playlist::OnPopupInfo( HWND hwnd )
1102 {
1103     int i_popup_item =
1104         ListView_GetNextItem( hListView, -1, LVIS_SELECTED | LVNI_ALL );
1105
1106     ShowInfos( hwnd, i_popup_item );
1107 }