1/* 2 * Copyright 2009-2010, Philippe Houdoin, phoudoin@haiku-os.org. All rights reserved. 3 * Copyright 2013-2018, Rene Gollent, rene@gollent.com. 4 * Distributed under the terms of the MIT License. 5 */ 6 7#include "TeamsListView.h" 8 9#include <algorithm> 10#include <new> 11 12#include <stdio.h> 13#include <string.h> 14 15#include <AppMisc.h> 16#include <Bitmap.h> 17#include <ColumnTypes.h> 18#include <ControlLook.h> 19#include <FindDirectory.h> 20#include <MimeType.h> 21#include <MessageRunner.h> 22#include <NodeInfo.h> 23#include <Path.h> 24#include <Roster.h> 25#include <String.h> 26 27#include <AutoLocker.h> 28 29#include "TargetHostInterface.h" 30 31 32enum { 33 MSG_SELECTED_INTERFACE_CHANGED = 'seic', 34 MSG_TEAM_ADDED = 'tead', 35 MSG_TEAM_REMOVED = 'tere', 36 MSG_TEAM_RENAMED = 'tern' 37}; 38 39 40// #pragma mark - BitmapStringField 41 42 43BBitmapStringField::BBitmapStringField(BBitmap* bitmap, const char* string) 44 : 45 Inherited(string), 46 fBitmap(bitmap) 47{ 48} 49 50 51BBitmapStringField::~BBitmapStringField() 52{ 53 delete fBitmap; 54} 55 56 57void 58BBitmapStringField::SetBitmap(BBitmap* bitmap) 59{ 60 delete fBitmap; 61 fBitmap = bitmap; 62 // TODO: cause a redraw? 63} 64 65 66// #pragma mark - TeamsColumn 67 68 69float TeamsColumn::sTextMargin = 0.0; 70 71 72TeamsColumn::TeamsColumn(const char* title, float width, float minWidth, 73 float maxWidth, uint32 truncateMode, alignment align) 74 : 75 Inherited(title, width, minWidth, maxWidth, align), 76 fTruncateMode(truncateMode) 77{ 78 SetWantsEvents(true); 79} 80 81 82void 83TeamsColumn::DrawField(BField* field, BRect rect, BView* parent) 84{ 85 BBitmapStringField* bitmapField 86 = dynamic_cast<BBitmapStringField*>(field); 87 BStringField* stringField = dynamic_cast<BStringField*>(field); 88 89 if (bitmapField) { 90 const BBitmap* bitmap = bitmapField->Bitmap(); 91 92 // figure out the placement 93 float x = 0.0; 94 BRect r = bitmap ? bitmap->Bounds() : BRect(0, 0, 15, 15); 95 float y = rect.top + ((rect.Height() - r.Height()) / 2); 96 float width = 0.0; 97 98 switch (Alignment()) { 99 default: 100 case B_ALIGN_LEFT: 101 case B_ALIGN_CENTER: 102 x = rect.left + sTextMargin; 103 width = rect.right - (x + r.Width()) - (2 * sTextMargin); 104 r.Set(x + r.Width(), rect.top, rect.right - width, rect.bottom); 105 break; 106 107 case B_ALIGN_RIGHT: 108 x = rect.right - sTextMargin - r.Width(); 109 width = (x - rect.left - (2 * sTextMargin)); 110 r.Set(rect.left, rect.top, rect.left + width, rect.bottom); 111 break; 112 } 113 114 if (width != bitmapField->Width()) { 115 BString truncatedString(bitmapField->String()); 116 parent->TruncateString(&truncatedString, fTruncateMode, width + 2); 117 bitmapField->SetClippedString(truncatedString.String()); 118 bitmapField->SetWidth(width); 119 } 120 121 // draw the bitmap 122 if (bitmap) { 123 parent->SetDrawingMode(B_OP_ALPHA); 124 parent->DrawBitmap(bitmap, BPoint(x, y)); 125 parent->SetDrawingMode(B_OP_OVER); 126 } 127 128 // draw the string 129 DrawString(bitmapField->ClippedString(), parent, r); 130 131 } else if (stringField) { 132 133 float width = rect.Width() - (2 * sTextMargin); 134 135 if (width != stringField->Width()) { 136 BString truncatedString(stringField->String()); 137 138 parent->TruncateString(&truncatedString, fTruncateMode, width + 2); 139 stringField->SetClippedString(truncatedString.String()); 140 stringField->SetWidth(width); 141 } 142 143 DrawString(stringField->ClippedString(), parent, rect); 144 } 145} 146 147 148float 149TeamsColumn::GetPreferredWidth(BField *_field, BView* parent) const 150{ 151 BBitmapStringField* bitmapField 152 = dynamic_cast<BBitmapStringField*>(_field); 153 BStringField* stringField = dynamic_cast<BStringField*>(_field); 154 155 float parentWidth = Inherited::GetPreferredWidth(_field, parent); 156 float width = 0.0; 157 158 if (bitmapField) { 159 const BBitmap* bitmap = bitmapField->Bitmap(); 160 BFont font; 161 parent->GetFont(&font); 162 width = font.StringWidth(bitmapField->String()) + 3 * sTextMargin; 163 if (bitmap) 164 width += bitmap->Bounds().Width(); 165 else 166 width += 16; 167 } else if (stringField) { 168 BFont font; 169 parent->GetFont(&font); 170 width = font.StringWidth(stringField->String()) + 2 * sTextMargin; 171 } 172 return max_c(width, parentWidth); 173} 174 175 176bool 177TeamsColumn::AcceptsField(const BField* field) const 178{ 179 return dynamic_cast<const BStringField*>(field) != NULL; 180} 181 182 183void 184TeamsColumn::InitTextMargin(BView* parent) 185{ 186 BFont font; 187 parent->GetFont(&font); 188 sTextMargin = ceilf(font.Size() * 0.8); 189} 190 191 192// #pragma mark - TeamRow 193 194 195enum { 196 kNameColumn, 197 kIDColumn 198}; 199 200 201TeamRow::TeamRow(TeamInfo* info) 202 : BRow(ceilf(be_control_look->DefaultLabelSpacing() * 3.3f)) 203{ 204 _SetTo(info); 205} 206 207 208bool 209TeamRow::NeedsUpdate(TeamInfo* info) 210{ 211 // Check if we need to rebuilt the row's fields because the team critical 212 // info (basically, app image running under that team ID) has changed 213 214 if (info->Arguments() != fTeamInfo.Arguments()) { 215 _SetTo(info); 216 return true; 217 } 218 219 return false; 220} 221 222 223status_t 224TeamRow::_SetTo(TeamInfo* info) 225{ 226 fTeamInfo = *info; 227 228 app_info appInfo; 229 status_t status = be_roster->GetRunningAppInfo(fTeamInfo.TeamID(), 230 &appInfo); 231 if (status != B_OK) { 232 // Not an application known to be_roster 233 234 if (fTeamInfo.TeamID() == B_SYSTEM_TEAM) { 235 // Get icon and name from kernel image 236 system_info systemInfo; 237 get_system_info(&systemInfo); 238 239 BPath kernelPath; 240 find_directory(B_BEOS_SYSTEM_DIRECTORY, &kernelPath); 241 kernelPath.Append(systemInfo.kernel_name); 242 243 get_ref_for_path(kernelPath.Path(), &appInfo.ref); 244 245 } else 246 BPrivate::get_app_ref(fTeamInfo.TeamID(), &appInfo.ref); 247 } 248 249 BBitmap* icon = new BBitmap(BRect(BPoint(0, 0), 250 be_control_look->ComposeIconSize(B_MINI_ICON)), B_RGBA32); 251 252 icon_size iconSize = (icon_size)(icon->Bounds().Width() + 1); 253 status = BNodeInfo::GetTrackerIcon(&appInfo.ref, icon, iconSize); 254 if (status != B_OK) { 255 BMimeType genericAppType(B_APP_MIME_TYPE); 256 status = genericAppType.GetIcon(icon, iconSize); 257 } 258 259 if (status != B_OK) { 260 delete icon; 261 icon = NULL; 262 } 263 264 BString tmp; 265 tmp << fTeamInfo.TeamID(); 266 267 SetField(new BBitmapStringField(icon, fTeamInfo.Arguments()), kNameColumn); 268 SetField(new BStringField(tmp), kIDColumn); 269 270 return status; 271} 272 273 274// #pragma mark - TeamsListView 275 276 277TeamsListView::TeamsListView(const char* name) 278 : 279 Inherited(name, B_NAVIGABLE, B_PLAIN_BORDER), 280 TargetHost::Listener(), 281 TeamsWindow::Listener(), 282 fInterface(NULL), 283 fHost(NULL) 284{ 285 AddColumn(new TeamsColumn("Name", 400, 100, 600, 286 B_TRUNCATE_BEGINNING), kNameColumn); 287 AddColumn(new TeamsColumn("ID", 80, 40, 100, 288 B_TRUNCATE_MIDDLE, B_ALIGN_RIGHT), kIDColumn); 289 SetSortingEnabled(false); 290} 291 292 293TeamsListView::~TeamsListView() 294{ 295 if (fHost != NULL) 296 fHost->ReleaseReference(); 297} 298 299 300void 301TeamsListView::AttachedToWindow() 302{ 303 Inherited::AttachedToWindow(); 304 TeamsColumn::InitTextMargin(ScrollView()); 305} 306 307 308void 309TeamsListView::DetachedFromWindow() 310{ 311 Inherited::DetachedFromWindow(); 312 _SetInterface(NULL); 313} 314 315 316void 317TeamsListView::MessageReceived(BMessage* message) 318{ 319 switch (message->what) { 320 case MSG_SELECTED_INTERFACE_CHANGED: 321 { 322 TargetHostInterface* interface; 323 if (message->FindPointer("interface", reinterpret_cast<void**>( 324 &interface)) == B_OK) { 325 _SetInterface(interface); 326 } 327 break; 328 } 329 330 case MSG_TEAM_ADDED: 331 { 332 TeamInfo* info; 333 team_id team; 334 if (message->FindInt32("team", &team) != B_OK) 335 break; 336 337 TargetHost* host = fInterface->GetTargetHost(); 338 AutoLocker<TargetHost> hostLocker(host); 339 info = host->TeamInfoByID(team); 340 if (info == NULL) 341 break; 342 343 TeamRow* row = new TeamRow(info); 344 AddRow(row); 345 break; 346 } 347 348 case MSG_TEAM_REMOVED: 349 { 350 team_id team; 351 if (message->FindInt32("team", &team) != B_OK) 352 break; 353 354 TeamRow* row = FindTeamRow(team); 355 if (row != NULL) { 356 RemoveRow(row); 357 delete row; 358 } 359 break; 360 } 361 362 case MSG_TEAM_RENAMED: 363 { 364 TeamInfo* info; 365 team_id team; 366 if (message->FindInt32("team", &team) != B_OK) 367 break; 368 369 TargetHost* host = fInterface->GetTargetHost(); 370 AutoLocker<TargetHost> hostLocker(host); 371 info = host->TeamInfoByID(team); 372 if (info == NULL) 373 break; 374 375 TeamRow* row = FindTeamRow(info->TeamID()); 376 if (row != NULL && row->NeedsUpdate(info)) 377 UpdateRow(row); 378 379 break; 380 } 381 382 default: 383 Inherited::MessageReceived(message); 384 } 385} 386 387 388TeamRow* 389TeamsListView::FindTeamRow(team_id teamId) 390{ 391 for (int32 i = CountRows(); i-- > 0;) { 392 TeamRow* row = dynamic_cast<TeamRow*>(RowAt(i)); 393 if (row == NULL) 394 continue; 395 396 if (row->TeamID() == teamId) 397 return row; 398 } 399 400 return NULL; 401} 402 403 404void 405TeamsListView::TeamAdded(TeamInfo* info) 406{ 407 BMessage message(MSG_TEAM_ADDED); 408 message.AddInt32("team", info->TeamID()); 409 BMessenger(this).SendMessage(&message); 410} 411 412 413void 414TeamsListView::TeamRemoved(team_id team) 415{ 416 BMessage message(MSG_TEAM_REMOVED); 417 message.AddInt32("team", team); 418 BMessenger(this).SendMessage(&message); 419} 420 421 422void 423TeamsListView::TeamRenamed(TeamInfo* info) 424{ 425 BMessage message(MSG_TEAM_RENAMED); 426 message.AddInt32("team", info->TeamID()); 427 BMessenger(this).SendMessage(&message); 428} 429 430 431void 432TeamsListView::SelectedInterfaceChanged(TargetHostInterface* interface) 433{ 434 BMessage message(MSG_SELECTED_INTERFACE_CHANGED); 435 message.AddPointer("interface", interface); 436 BMessenger(this).SendMessage(&message); 437} 438 439 440void 441TeamsListView::_InitList() 442{ 443 AutoLocker<TargetHost> hostLocker(fHost); 444 for (int32 i = 0; TeamInfo* info = fHost->TeamInfoAt(i); i++) { 445 BRow* row = new TeamRow(info); 446 AddRow(row); 447 } 448} 449 450 451void 452TeamsListView::_SetInterface(TargetHostInterface* interface) 453{ 454 if (interface == fInterface) 455 return; 456 457 if (fInterface != NULL) { 458 Clear(); 459 fHost->RemoveListener(this); 460 fHost->ReleaseReference(); 461 fHost = NULL; 462 } 463 464 fInterface = interface; 465 if (fInterface == NULL) 466 return; 467 468 fHost = fInterface->GetTargetHost(); 469 fHost->AcquireReference(); 470 fHost->AddListener(this); 471 _InitList(); 472} 473