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