1/* 2 * Copyright 2009-2010, Philippe Houdoin, phoudoin@haiku-os.org. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7#include <new> 8 9#include <stdio.h> 10#include <string.h> 11 12#include <AppMisc.h> 13#include <Bitmap.h> 14#include <ColumnTypes.h> 15#include <FindDirectory.h> 16#include <MimeType.h> 17#include <MessageRunner.h> 18#include <NodeInfo.h> 19#include <Path.h> 20#include <Roster.h> 21#include <String.h> 22 23#include "TeamsListView.h" 24 25 26// #pragma mark - BitmapStringField 27 28 29BBitmapStringField::BBitmapStringField(BBitmap* bitmap, const char* string) 30 : 31 Inherited(string), 32 fBitmap(bitmap) 33{ 34} 35 36 37BBitmapStringField::~BBitmapStringField() 38{ 39 delete fBitmap; 40} 41 42 43void 44BBitmapStringField::SetBitmap(BBitmap* bitmap) 45{ 46 delete fBitmap; 47 fBitmap = bitmap; 48 // TODO: cause a redraw? 49} 50 51 52// #pragma mark - TeamsColumn 53 54 55float TeamsColumn::sTextMargin = 0.0; 56 57 58TeamsColumn::TeamsColumn(const char* title, float width, float minWidth, 59 float maxWidth, uint32 truncateMode, alignment align) 60 : 61 Inherited(title, width, minWidth, maxWidth, align), 62 fTruncateMode(truncateMode) 63{ 64 SetWantsEvents(true); 65} 66 67 68void 69TeamsColumn::DrawField(BField* field, BRect rect, BView* parent) 70{ 71 BBitmapStringField* bitmapField 72 = dynamic_cast<BBitmapStringField*>(field); 73 BStringField* stringField = dynamic_cast<BStringField*>(field); 74 75 if (bitmapField) { 76 const BBitmap* bitmap = bitmapField->Bitmap(); 77 78 // figure out the placement 79 float x = 0.0; 80 BRect r = bitmap ? bitmap->Bounds() : BRect(0, 0, 15, 15); 81 float y = rect.top + ((rect.Height() - r.Height()) / 2); 82 float width = 0.0; 83 84 switch (Alignment()) { 85 default: 86 case B_ALIGN_LEFT: 87 case B_ALIGN_CENTER: 88 x = rect.left + sTextMargin; 89 width = rect.right - (x + r.Width()) - (2 * sTextMargin); 90 r.Set(x + r.Width(), rect.top, rect.right - width, rect.bottom); 91 break; 92 93 case B_ALIGN_RIGHT: 94 x = rect.right - sTextMargin - r.Width(); 95 width = (x - rect.left - (2 * sTextMargin)); 96 r.Set(rect.left, rect.top, rect.left + width, rect.bottom); 97 break; 98 } 99 100 if (width != bitmapField->Width()) { 101 BString truncatedString(bitmapField->String()); 102 parent->TruncateString(&truncatedString, fTruncateMode, width + 2); 103 bitmapField->SetClippedString(truncatedString.String()); 104 bitmapField->SetWidth(width); 105 } 106 107 // draw the bitmap 108 if (bitmap) { 109 parent->SetDrawingMode(B_OP_ALPHA); 110 parent->DrawBitmap(bitmap, BPoint(x, y)); 111 parent->SetDrawingMode(B_OP_OVER); 112 } 113 114 // draw the string 115 DrawString(bitmapField->ClippedString(), parent, r); 116 117 } else if (stringField) { 118 119 float width = rect.Width() - (2 * sTextMargin); 120 121 if (width != stringField->Width()) { 122 BString truncatedString(stringField->String()); 123 124 parent->TruncateString(&truncatedString, fTruncateMode, width + 2); 125 stringField->SetClippedString(truncatedString.String()); 126 stringField->SetWidth(width); 127 } 128 129 DrawString(stringField->ClippedString(), parent, rect); 130 } 131} 132 133 134float 135TeamsColumn::GetPreferredWidth(BField *_field, BView* parent) const 136{ 137 BBitmapStringField* bitmapField 138 = dynamic_cast<BBitmapStringField*>(_field); 139 BStringField* stringField = dynamic_cast<BStringField*>(_field); 140 141 float parentWidth = Inherited::GetPreferredWidth(_field, parent); 142 float width = 0.0; 143 144 if (bitmapField) { 145 const BBitmap* bitmap = bitmapField->Bitmap(); 146 BFont font; 147 parent->GetFont(&font); 148 width = font.StringWidth(bitmapField->String()) + 3 * sTextMargin; 149 if (bitmap) 150 width += bitmap->Bounds().Width(); 151 else 152 width += 16; 153 } else if (stringField) { 154 BFont font; 155 parent->GetFont(&font); 156 width = font.StringWidth(stringField->String()) + 2 * sTextMargin; 157 } 158 return max_c(width, parentWidth); 159} 160 161 162bool 163TeamsColumn::AcceptsField(const BField* field) const 164{ 165 return dynamic_cast<const BStringField*>(field) != NULL; 166} 167 168 169void 170TeamsColumn::InitTextMargin(BView* parent) 171{ 172 BFont font; 173 parent->GetFont(&font); 174 sTextMargin = ceilf(font.Size() * 0.8); 175} 176 177 178// #pragma mark - TeamRow 179 180 181enum { 182 kNameColumn, 183 kIDColumn, 184 kThreadCountColumn, 185}; 186 187 188TeamRow::TeamRow(team_info& info) 189 : BRow(20.0) 190{ 191 _SetTo(info); 192} 193 194 195TeamRow::TeamRow(team_id team) 196 : BRow(20.0) 197{ 198 team_info info; 199 get_team_info(team, &info); 200 _SetTo(info); 201} 202 203 204bool 205TeamRow::NeedsUpdate(team_info& info) 206{ 207 // Check if we need to rebuilt the row's fields because the team critical 208 // info (basically, app image running under that team ID) has changed 209 210 if (info.argc != fTeamInfo.argc 211 || strncmp(info.args, fTeamInfo.args, sizeof(fTeamInfo.args)) != 0) { 212 _SetTo(info); 213 return true; 214 } 215 216 return false; 217} 218 219 220status_t 221TeamRow::_SetTo(team_info& info) 222{ 223 team_info teamInfo = fTeamInfo = info; 224 225 // strip any trailing space(s)... 226 for (int len = strlen(teamInfo.args) - 1; 227 len >= 0 && teamInfo.args[len] == ' '; len--) { 228 teamInfo.args[len] = 0; 229 } 230 231 app_info appInfo; 232 status_t status = be_roster->GetRunningAppInfo(teamInfo.team, &appInfo); 233 if (status != B_OK) { 234 // Not an application known to be_roster 235 236 if (teamInfo.team == B_SYSTEM_TEAM) { 237 // Get icon and name from kernel image 238 system_info systemInfo; 239 get_system_info(&systemInfo); 240 241 BPath kernelPath; 242 find_directory(B_BEOS_SYSTEM_DIRECTORY, &kernelPath); 243 kernelPath.Append(systemInfo.kernel_name); 244 245 get_ref_for_path(kernelPath.Path(), &appInfo.ref); 246 247 } else 248 BPrivate::get_app_ref(teamInfo.team, &appInfo.ref); 249 } 250 251 BBitmap* icon = new BBitmap(BRect(0, 0, B_MINI_ICON - 1, B_MINI_ICON - 1), B_RGBA32); 252 253 status = BNodeInfo::GetTrackerIcon(&appInfo.ref, icon, B_MINI_ICON); 254 if (status != B_OK) { 255 BMimeType genericAppType(B_APP_MIME_TYPE); 256 status = genericAppType.GetIcon(icon, B_MINI_ICON); 257 } 258 259 if (status != B_OK) { 260 delete icon; 261 icon = NULL; 262 } 263 264 BString tmp; 265 tmp << teamInfo.team; 266 267 SetField(new BBitmapStringField(icon, teamInfo.args), kNameColumn); 268 SetField(new BStringField(tmp), kIDColumn); 269 270 tmp = ""; 271 tmp << teamInfo.thread_count; 272 273 SetField(new BStringField(tmp), kThreadCountColumn); 274 275 return status; 276} 277 278 279// #pragma mark - TeamsListView 280 281 282TeamsListView::TeamsListView(BRect frame, const char* name) 283 : 284 Inherited(frame, name, B_FOLLOW_ALL, 0, B_NO_BORDER, true), 285 fUpdateRunner(NULL) 286{ 287 AddColumn(new TeamsColumn("Name", 400, 100, 600, 288 B_TRUNCATE_BEGINNING), kNameColumn); 289 AddColumn(new TeamsColumn("ID", 80, 40, 100, 290 B_TRUNCATE_MIDDLE, B_ALIGN_RIGHT), kIDColumn); 291 292/* 293 AddColumn(new TeamsColumn("Thread count", 100, 50, 500, 294 B_TRUNCATE_MIDDLE, B_ALIGN_RIGHT), kThreadCountColumn); 295*/ 296 SetSortingEnabled(false); 297 298 team_info tmi; 299 get_team_info(B_CURRENT_TEAM, &tmi); 300 fThisTeam = tmi.team; 301/* 302#ifdef __HAIKU__ 303 SetFlags(Flags() | B_SUBPIXEL_PRECISE); 304#endif 305*/ 306} 307 308 309TeamsListView::~TeamsListView() 310{ 311 delete fUpdateRunner; 312} 313 314 315void 316TeamsListView::AttachedToWindow() 317{ 318 Inherited::AttachedToWindow(); 319 TeamsColumn::InitTextMargin(ScrollView()); 320 321 _InitList(); 322 323 be_roster->StartWatching(this, B_REQUEST_LAUNCHED | B_REQUEST_QUIT); 324 325 BMessage msg(kMsgUpdateTeamsList); 326 fUpdateRunner = new BMessageRunner(this, &msg, 100000L); // 10Hz 327} 328 329 330void 331TeamsListView::DetachedFromWindow() 332{ 333 Inherited::DetachedFromWindow(); 334 335 be_roster->StopWatching(this); 336 337 delete fUpdateRunner; 338 fUpdateRunner = NULL; 339 340 Clear(); // MakeEmpty(); 341} 342 343 344void 345TeamsListView::MessageReceived(BMessage* message) 346{ 347 switch (message->what) { 348 case kMsgUpdateTeamsList: 349 _UpdateList(); 350 break; 351 352 case B_SOME_APP_LAUNCHED: 353 { 354 team_id team; 355 if (message->FindInt32("be:team", &team) != B_OK) 356 break; 357 358 TeamRow* row = new(std::nothrow) TeamRow(team); 359 if (row != NULL) { 360 AddRow(row); 361 /*else 362 SortItems(&TeamListItem::Compare); 363 */ 364 } 365 break; 366 } 367 368 case B_SOME_APP_QUIT: 369 { 370 team_id team; 371 if (message->FindInt32("be:team", &team) != B_OK) 372 break; 373 374 TeamRow* row = FindTeamRow(team); 375 if (row != NULL) { 376 RemoveRow(row); 377 delete 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::_InitList() 406{ 407 int32 tmi_cookie = 0; 408 team_info tmi; 409 410 while (get_next_team_info(&tmi_cookie, &tmi) == B_OK) { 411 TeamRow* row = new(std::nothrow) TeamRow(tmi); 412 if (row == NULL) { 413 // Memory issue. Bail out. 414 break; 415 } 416 417 if (tmi.team == B_SYSTEM_TEAM || 418 tmi.team == fThisTeam) { 419 // We don't support debugging kernel and... ourself! 420 row->SetEnabled(false); 421 } 422 423 AddRow(row); 424 } 425} 426 427 428void 429TeamsListView::_UpdateList() 430{ 431 int32 tmi_cookie = 0; 432 team_info tmi; 433 TeamRow* row; 434 int32 index = 0; 435 436 // NOTA: assuming get_next_team_info() returns teams ordered by team ID... 437 while (get_next_team_info(&tmi_cookie, &tmi) == B_OK) { 438 439 row = dynamic_cast<TeamRow*>(RowAt(index)); 440 while (row && tmi.team > row->TeamID()) { 441 RemoveRow(row); 442 delete row; 443 row = dynamic_cast<TeamRow*>(RowAt(index)); 444 } 445 446 if (row != NULL && tmi.team == row->TeamID() 447 && row->NeedsUpdate(tmi)) { 448 // The team image app could have change due after an exec*() call, 449 UpdateRow(row); 450 } else if (row == NULL || tmi.team != row->TeamID()) { 451 // Team not found in previously known teams list: insert a new row 452 TeamRow* newRow = new(std::nothrow) TeamRow(tmi); 453 if (newRow != NULL) { 454 if (row == NULL) { 455 // No row found with bigger team id: append at list end 456 AddRow(newRow); 457 } else 458 AddRow(newRow, index); 459 } 460 } 461 index++; // Move list sync head. 462 } 463 464 // Remove tail list rows, if we don't walk list thru the end 465 while ((row = dynamic_cast<TeamRow*>(RowAt(index))) != NULL) { 466 RemoveRow(row); 467 delete row; 468 row = dynamic_cast<TeamRow*>(RowAt(++index)); 469 } 470} 471