The Winamp Remote Control suite
a remote control client and plugin for Winamp 2.x, 5.x
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
MainF.cpp
Go to the documentation of this file.
1 /*
2 winamp remote control suite ŠPatrick Michael Martin 2000, 2001, 2002
3 
4 Copyright (C) 2000,2001,2002 Patrick M. Martin
5 
6 This program is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License
8 as published by the Free Software Foundation; either version 2
9 of the License, or (at your option) any later version.
10 
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15 
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 
20 Patrick M. Martin may be reached by email at patrickmmartin@gmail.com.
21 */
22 
23 #include <vcl.h>
24 #pragma hdrstop
25 
26 #include <shellapi.h>
27 
28 // forms...
29 
30 #include "MainF.h"
31 #include "messageF.h"
32 #include "AboutF.h"
33 
34 #include "PlaylistF.h"
35 #include "SettingsF.h"
36 #include "CommandF.h"
37 #include "ServersF.h"
38 
39 // RPC functions
40 #include "RPCException.h"
41 #include "RPCBind.h"
42 #include "DetailsF.h"
43 #include "remotestrs.h"
44 
45 #include <math.h>
46 #include "WinampClientBase.h"
47 #include "CursorGuard.h"
48 #include "remoteDM.h"
49 
50 #include "ClientConfig.h"
51 
52 const int POLL_ERROR_FACTOR = 10; // seconds
53 
54 
55 #pragma resource "*.dfm"
57 
58 
59 __fastcall TfrmMain::TfrmMain(TComponent* Owner)
60  : TForm(Owner)
61 {
62 }
63 
64 void __fastcall TfrmMain::TrayNotify(TMessage& Msg)
65 {
66  POINT MousePos;
68 
69  switch(Msg.LParam)
70  {
71  case WM_RBUTTONDOWN:
72  // get round clicks to spurious systray windows when show
73  DoubleClickedR = false;
74  break;
75 
76  case WM_RBUTTONUP:
77  if (DoubleClickedR)
78  dmRemote->ShowMainForm->Execute();
79  else
80  if (GetCursorPos(&MousePos))
81  {
82  pmnuMain->PopupComponent = frmMain;
83  SetForegroundWindow(Handle);
84  pmnuMain->Popup(MousePos.x, MousePos.y);
85  }
86  break;
87 
88  case WM_RBUTTONDBLCLK:
89  DoubleClickedR = true;
90  break;
91 
92 
93  case WM_LBUTTONDOWN:
94  DoubleClickedL = false;
95  break;
96 
97  case WM_LBUTTONUP:
98  if (!DoubleClickedL)
99  dmRemote->Pause->Execute();
100  break;
101 
102  case WM_LBUTTONDBLCLK:
103  // as pause will have updated WAstatus for us
104  if (Status == WA_NOT_PLAYING)
105  {
106  dmRemote->Play->Execute();
107  }
108  if (Status == WA_PAUSED)
109  {
110  dmRemote->Stop->Execute();
111  }
112  DoubleClickedL = true;
113  break;
114 
115  default:
116  OutputDebugString(AnsiString(Msg.LParam).c_str());
117  break;
118  }
119 
120  TForm::Dispatch(&Msg);
121 
122 }
123 
124 bool __fastcall TfrmMain::TrayMessage(DWORD dwMessage)
125 {
126  NOTIFYICONDATA tnd;
127  PSTR pszTip;
128 
129  pszTip = TipText();
130 
131  tnd.cbSize = sizeof(NOTIFYICONDATA);
132  tnd.hWnd = Handle;
133  tnd.uID = 0;
134  tnd.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;
135  tnd.uCallbackMessage = TRAY_NOTIFY;
136 
137  if (dwMessage == NIM_MODIFY)
138  {
139  tnd.hIcon = IconHandle();
140  if (pszTip)
141  lstrcpyn(tnd.szTip, pszTip, sizeof(tnd.szTip));
142  else
143  tnd.szTip[0] = '\0';
144  }
145  else
146  {
147  tnd.hIcon = NULL;
148  tnd.szTip[0] = '\0';
149  }
150 
151  return (Shell_NotifyIcon(dwMessage, &tnd));
152 }
153 
154 HANDLE __fastcall TfrmMain::IconHandle(void)
155 {
156 
158 
159  switch (Status)
160  {
161  case WA_NOT_PLAYING:
162 
163  mnuPause->Caption = sPause;
164  Pause1->Caption = sPause;
165  fIconIndex = 3;
166  break;
167  case WA_PLAYING:
168  dmRemote->Pause->Caption = sPause;
169  dmRemote->Pause->Checked = false;
170  fIconIndex = 1;
171  break;
172  case WA_PAUSED:
173  dmRemote->Pause->Caption = sUnPause;
174  dmRemote->Pause->Checked = true;
175  fIconIndex = 2;
176  break;
177  default:
178  fIconIndex = 5;
179  break;
180  }
181 
182  imlTrayIcons->GetIcon( fIconIndex, icoTrayIcon->Picture->Icon);
183 
184  return icoTrayIcon->Picture->Icon->Handle;
185 
186 }
187 
188 PSTR __fastcall TfrmMain::TipText(void)
189 {
190  AnsiString str = SongTitle;
191  AnsiString state;
193 
194  switch (Status){
195  case WA_PLAYING:
196  state = sPlaying;
197  break;
198  case WA_NOT_PLAYING:
199  state = sStopped;
200  break;
201  case WA_PAUSED:
202  state = sPaused;
203  break;
204  }
205 
206  if ((AnsiString(str) + state).Length() > 64)
207  str = str.SubString(1, 60 - state.Length()) + "..." + state;
208  else
209  str = str + state;
210  strncpy(TipChars, str.c_str(), sizeof(TipChars));
211 
212  return TipChars;
213 
214 }
215 
216 
217 
218 void __fastcall TfrmMain::HideMain(TObject *)
219 {
220 
221  TrayMessage(NIM_ADD);
222  TrayMessage(NIM_MODIFY);
223 
224  // show hints on visible forms
225  frmPlaylist->ShowHint = true;
226  frmSettings->ShowHint = true;
227  frmCommands->ShowHint = true;
228 
229  ShowWindow(Application->Handle, SW_HIDE);
230  // style trickery to lose the application window
231  DWORD dwExStyle = GetWindowLong(Application->Handle, GWL_EXSTYLE);
232  dwExStyle |= WS_EX_TOOLWINDOW;
233  SetWindowLong(Application->Handle, GWL_EXSTYLE, dwExStyle);
234 
235  Hide();
236 
237  TRect FromRect, ToRect;
238  ToRect = TRect(Screen->Width - 16, Screen->Height, Screen->Width, Screen->Height - 16);
239 
240  FromRect = BoundsRect;
241 
242 
243 #ifndef IDANI_CLOSE
244  #define IDANI_CLOSE 2
245 #endif
246 #ifndef IDANI_CAPTION
247  #define IDANI_CAPTION 3
248 #endif
249 
250  DrawAnimatedRects(Handle, IDANI_CAPTION, &FromRect, &ToRect);
251 
252 }
253 
254 
255 void __fastcall TfrmMain::FormShow(TObject *)
256 {
257 
258  // show hints on visible forms
259  frmPlaylist->ShowHint = false;
260  frmSettings->ShowHint = false;
261  frmCommands->ShowHint = false;
262 
263 
264 
265  mnuShow->Visible = false;
266  TrayMessage(NIM_DELETE);
267  timerMain->Enabled = true;
268  UpdateIcon();
269  Refresh();
270  pgSettings->ActivePage = tbsMain;
271 
272 
273 }
274 
275 
276 
277 void __fastcall TfrmMain::btnCloseClick(TObject *)
278 {
279  Close();
280 }
281 
282 
283 void MessageForm(AnsiString MessageStr)
284 {
285  if (!frmMessage)
286  frmMessage = new TfrmMessage(Application);
287 
288  frmMessage->memMessage->Lines->Text = MessageStr;
289  frmMessage->Show();
290  frmMessage->Update();
291 }
292 
293 
294 void __fastcall TfrmMain::DisplayHint(TObject *)
295 {
296  sbMain->Panels->Items[1]->Text = GetShortHint(Application->Hint);
297 }
298 
299 void __fastcall TfrmMain::FormCreate(TObject *)
300 {
301 
302  Application->OnException = AppException;
303 
304  Application->OnHint = DisplayHint;
305 
306 
307  pgSettings->ActivePage = tbsMain;
308 
309  dmRemote->registerForm(this);
310 
311  dmRemote->DoBind(ebAddress->Text, ebEndPoint->Text);
312 
313 }
314 
315 
316 
317 
318 void __fastcall TfrmMain::AppException(TObject *, Exception *E)
319 {
320  AnsiString RPCHint = sRPCFailed;
321  if (connected)
322  MessageForm((AnsiString(sUnhandledException) + E->ClassName()+ ":\n" + E->Message));
323 }
324 
325 
326 
327 
328 void TfrmMain::UpdateIcon(void)
329 {
330  int index, length = 0;
331  AnsiString saveStr;
332 
333  Cardinal UpdateTime = 1000 * lstTimer->Items->Strings[lstTimer->ItemIndex].ToIntDef(1);
334 
335  bool previousConnected = connected;
336  try
337  {
338 
339  IconHandle();
340  sbMain->Refresh();
341 
342  connected = true;
343  TrayMessage(NIM_MODIFY);
344 
345  // TODO re-enable the client actions as this is the criterion for the re-connection
346  if ( connected && !previousConnected )
347  {
348  timerMain->Interval = UpdateTime;
349  lblVersion->Caption = dmRemote->WinampVersionString().c_str();
350  }
351 
352  dmRemote->GetPlaylistState(length, index);
353 
354  if (length > 0)
355  {
356 
357  SongTitle = dmRemote->CurrentSong().c_str();
358  lblMessage->Caption = SongTitle;
359 
360  if (lblMessage->Canvas->TextWidth(lblMessage->Caption) > lblMessage->Width)
361  {
362  lblMessage->Hint = lblMessage->Caption;
363  lblMessage->ParentShowHint = false;
364  lblMessage->ShowHint = true;
365 
366  while (lblMessage->Canvas->TextWidth(lblMessage->Caption + "... ") > lblMessage->Width)
367  lblMessage->Caption = lblMessage->Caption.SubString(1, lblMessage->Caption.Length() - 1);
368 
369  lblMessage->Caption = lblMessage->Caption + "...";
370 
371  }
372  else
373  {
374  lblMessage->Hint = sCurrentTrack;
375  lblMessage->ParentShowHint = true;
376  lblMessage->ShowHint = false;
377  }
378 
379  //update the playlist form
380  //always check this first, to get length straight
381 
382  if (frmPlaylist)
383  {
384  if ((rbPlaylistChange->Checked) || (index >= length))
385  {
386  if (length != LastLength)
387  {
388  dmRemote->PlaylistRefresh->Execute();
389  }
390  }
391 
392  if (rbSongChange->Checked)
393  {
394  if ((index != LastIndex) || (length != LastLength))
395  {
396  dmRemote->PlaylistRefresh->Execute();
397  dmRemote->PlaylistRefreshCurrent->Execute();
398  }
399  }
400  else
401  if (LastIndex != index)
402  {
403  CurrentIndex = index;
404  dmRemote->PlaylistRefreshCurrent->Execute();
405  }
406 
407  // set last "OK" list index
408  LastIndex = index;
409  LastLength = length;
410  if (timerMain->OnTimer != MainTimer)
411  timerMain->OnTimer = MainTimer;
412 
413  dmRemote->PlaylistRefreshStats->Execute();
414 
415  }
416 
417  }
418  } // try
419 
420  catch( ERPCException& E)
421  {
422  connected = false;
423  doHide = false;
424  if (frmPlaylist)
425  {
426  LastIndex = -1;
427  LastLength = -1;
428  }
429 
430  lblMessage->Caption = E.what();
431  lblVersion->Caption = WinampVersionString(0);
432  timerMain->Interval = 1000 * POLL_ERROR_FACTOR;
433  }
434 
435  IconHandle();
436  sbMain->Refresh();
437 
438  // in all events, update status
439  TrayMessage(NIM_MODIFY);
440 }
441 
442 
443 
444 
445 void __fastcall TfrmMain::MainTimer(TObject *)
446 {
447  UpdateIcon();
448  if (doHide)
449  {
450  doHide = false;
451  HideMain(this);
452  }
453 }
454 
455 
456 
457 void __fastcall TfrmMain::FormClose(TObject *, TCloseAction &)
458 {
459  TrayMessage(NIM_DELETE);
460 }
461 
462 
463 
464 
465 void __fastcall TfrmMain::FormCloseQuery(TObject *, bool &)
466 {
467 }
468 
469 
470 
471 
472 void __fastcall TfrmMain::lstTimerClick(TObject *)
473 {
474  timerMain->Interval = 1000 * (lstTimer->ItemIndex + 1);
475 }void __fastcall TfrmMain::AddressChange(TObject *)
476 {
477  UnBind();
478  dmRemote->DoBind(ebAddress->Text, ebEndPoint->Text);
479 
480  if (Visible)
481  {
482  // and give them 10s to decide - it will be reset
483  timerMain->Enabled = false;
484  timerMain->Interval = 1000;
485  timerMain->OnTimer = DelayTimer;
486  Delay = 11;
487  timerMain->Enabled = true;
488  timerMain->OnTimer(this);
489  sbMain->Panels->Items[1]->Text = sDelaying;
490  }
491 }
492 
493 
494 
495 void __fastcall TfrmMain::DelayTimer(TObject *)
496 {
497  if (--Delay > 0)
498  {
499  sbMain->Panels->Items[1]->Text = AnsiString().sprintf(sDelayingForFmt.c_str(), Delay);
500  }
501  else
502  {
503  sbMain->Panels->Items[1]->Text = sRestarting;
504  timerMain->Enabled = false;
505  timerMain->OnTimer = MainTimer;
506  timerMain->Enabled = true;
507  }
508 
509 }
510 
511 
512 
513 
514 void __fastcall TfrmMain::FormDockOver(TObject *, TDragDockObject *, int , int , TDragState , bool &Accept)
515 {
516  Accept = true;
517 }
518 
519 
520 
521 
522 void __fastcall TfrmMain::StartDock(TObject *Sender, TDragDockObject *&)
523 {
524  // set up dock targets manually
525  // note DragObject is not used
526  pgSettings->DockSite = false;
527 
528  if ( (Sender == frmPlaylist) || (Sender == frmSettings) )
529  pgSettings->DockSite = true;
530 
531 }
532 
533 
534 
535 
536 void __fastcall TfrmMain::pgSettingsDockDrop(TObject *, TDragDockObject *Source, int , int )
537 {
538  int ImageIndex = 0;
539 // set the imageindex for the dropped form
540  if (Source->Control == frmSettings)
541  {
542  ImageIndex = 9;
543  }
544  else if (Source->Control == frmPlaylist)
545  {
546  ImageIndex = 10;
547  }
548 
549  // set image index
550  int PageIndex = pgSettings->PageCount -1;
551  pgSettings->Pages[PageIndex]->ImageIndex = ImageIndex;
552  // and have a shortcut
553  pgSettings->Pages[PageIndex]->Caption = "&" + pgSettings->Pages[PageIndex]->Caption;
554 }
555 
556 
557 
558 void __fastcall TfrmMain::DetailsExecute(TObject *)
559 {
560  TfrmDetails *DetailsForm = new TfrmDetails(NULL);
561  try
562  {
563  DetailsForm->ShowModal();
564  }
565  __finally
566  {
567  delete DetailsForm;
568  }
569 }
570 
571 
572 
573 
574 void __fastcall TfrmMain::sbMainDrawPanel(TStatusBar *StatusBar, TStatusPanel *, const TRect &Rect)
575 {
576  imlTrayIcons->Draw(StatusBar->Canvas, Rect.Left + 3, Rect.Top + 3, fIconIndex, true);
577 }
578 
579 
580 
581 void __fastcall TfrmMain::AnimateForm(TForm * Form, bool FormVisible)
582 {
583 
584  TRect MainRect, FormRect;
585  if (Visible)
586  {
587  MainRect = pgSettings->BoundsRect;
588  OffsetRect(&MainRect, pgSettings->ClientToScreen(TPoint(0,0)).x, pgSettings->ClientToScreen(TPoint(0,0)).y);
589  }
590  else
591  MainRect = TRect(Screen->Width - 16, Screen->Height, Screen->Width, Screen->Height - 16);
592 
593  FormRect = Form->BoundsRect;
594 
595  if (FormVisible)
596  DrawAnimatedRects(Form->Handle, IDANI_CAPTION, &FormRect, &MainRect);
597  else
598  DrawAnimatedRects(Form->Handle, IDANI_CAPTION, &MainRect, &FormRect);
599 
600 }
601 
602 void __fastcall TfrmMain::FormDestroy(TObject *Sender)
603 {
604  dmRemote->unRegisterForm(this);
605 }
606 void __fastcall TfrmMain::NullAddressChange(TObject *Sender)
607 {
608  // nothing here
609 }
610 //---------------------------------------------------------------------------
611 
612