1 /* 2 * Copyright 2002-2017, Haiku, Inc. 3 * Distributed under the terms of the MIT license. 4 * 5 * Authors: 6 * Oliver Siebenmarck 7 * Andrew McCall, mccall@digitalparadise.co.uk 8 * Michael Wilber 9 * Maxime Simon 10 */ 11 12 13 #include "DataTranslationsWindow.h" 14 15 #include <algorithm> 16 17 #include <math.h> 18 #include <stdio.h> 19 20 #include <Alert.h> 21 #include <Alignment.h> 22 #include <Application.h> 23 #include <Bitmap.h> 24 #include <Box.h> 25 #include <Catalog.h> 26 #include <ControlLook.h> 27 #include <Entry.h> 28 #include <GroupView.h> 29 #include <IconView.h> 30 #include <LayoutBuilder.h> 31 #include <ListView.h> 32 #include <Path.h> 33 #include <Screen.h> 34 #include <ScrollView.h> 35 #include <String.h> 36 #include <StringView.h> 37 #include <SupportDefs.h> 38 #include <TextView.h> 39 #include <TranslationDefs.h> 40 #include <TranslatorRoster.h> 41 42 43 #include "DataTranslations.h" 44 #include "DataTranslationsSettings.h" 45 #include "TranslatorListView.h" 46 47 48 #undef B_TRANSLATION_CONTEXT 49 #define B_TRANSLATION_CONTEXT "DataTranslations" 50 51 52 const uint32 kMsgTranslatorInfo = 'trin'; 53 const uint32 kMsgSelectedTranslator = 'trsl'; 54 55 56 DataTranslationsWindow::DataTranslationsWindow() 57 : 58 BWindow(BRect(0.0f, 0.0f, 597.0f, 368.0f), 59 B_TRANSLATE_SYSTEM_NAME("DataTranslations"), 60 B_TITLED_WINDOW, B_ASYNCHRONOUS_CONTROLS | B_NOT_RESIZABLE 61 | B_NOT_ZOOMABLE | B_AUTO_UPDATE_SIZE_LIMITS), 62 fRelease(NULL) 63 { 64 MoveTo(DataTranslationsSettings::Instance()->WindowCorner()); 65 66 _SetupViews(); 67 68 // Make sure that the window isn't positioned off screen 69 BScreen screen; 70 BRect screenFrame = screen.Frame(); 71 if (!screenFrame.Contains(Frame())) 72 CenterOnScreen(); 73 74 BTranslatorRoster* roster = BTranslatorRoster::Default(); 75 roster->StartWatching(this); 76 77 Show(); 78 } 79 80 81 DataTranslationsWindow::~DataTranslationsWindow() 82 { 83 BTranslatorRoster* roster = BTranslatorRoster::Default(); 84 roster->StopWatching(this); 85 } 86 87 88 // Reads the installed translators and adds them to our BListView 89 status_t 90 DataTranslationsWindow::_PopulateListView() 91 { 92 BTranslatorRoster* roster = BTranslatorRoster::Default(); 93 94 // Get all Translators on the system. Gives us the number of translators 95 // installed in num_translators and a reference to the first one 96 int32 numTranslators; 97 translator_id* translators = NULL; 98 roster->GetAllTranslators(&translators, &numTranslators); 99 100 float maxWidth = 0; 101 102 for (int32 i = 0; i < numTranslators; i++) { 103 // Getting the first three Infos: Name, Info & Version 104 int32 version; 105 const char* name; 106 const char* info; 107 roster->GetTranslatorInfo(translators[i], &name, &info, &version); 108 fTranslatorListView->AddItem(new TranslatorItem(translators[i], name)); 109 maxWidth = std::max(maxWidth, fTranslatorListView->StringWidth(name)); 110 } 111 112 fTranslatorListView->SortItems(); 113 114 fTranslatorListView->SetExplicitSize(BSize(maxWidth + 20, B_SIZE_UNSET)); 115 116 delete[] translators; 117 return B_OK; 118 } 119 120 121 status_t 122 DataTranslationsWindow::_GetTranslatorInfo(int32 id, const char*& name, 123 const char*& info, int32& version, BPath& path) 124 { 125 // Returns information about the translator with the given id 126 127 if (id < 0) 128 return B_BAD_VALUE; 129 130 BTranslatorRoster* roster = BTranslatorRoster::Default(); 131 if (roster->GetTranslatorInfo(id, &name, &info, &version) != B_OK) 132 return B_ERROR; 133 134 // Get the translator's path 135 entry_ref ref; 136 if (roster->GetRefFor(id, &ref) == B_OK) { 137 BEntry entry(&ref); 138 path.SetTo(&entry); 139 } else 140 path.Unset(); 141 142 return B_OK; 143 } 144 145 146 status_t 147 DataTranslationsWindow::_ShowConfigView(int32 id) 148 { 149 // Shows the config panel for the translator with the given id 150 151 if (id < 0) 152 return B_BAD_VALUE; 153 154 BTranslatorRoster* roster = BTranslatorRoster::Default(); 155 156 if (fConfigView != NULL) { 157 fRightBox->RemoveChild(fConfigView); 158 delete fConfigView; 159 fConfigView = NULL; 160 fInfoText = NULL; 161 if (fRelease != NULL) { 162 fRelease->Release(); 163 fRelease = NULL; 164 } 165 } 166 167 BMessage emptyMessage; 168 BRect rect(0.0f, 0.0f, 200.0f, 233.0f); 169 status_t result = roster->MakeConfigurationView(id, &emptyMessage, 170 &fConfigView, &rect); 171 172 if (result != B_OK) 173 return result; 174 175 fConfigView->SetViewUIColor(B_PANEL_BACKGROUND_COLOR); 176 // force config views to all have the same color 177 fRightBox->AddChild(fConfigView); 178 179 // for default 12pt font size: 597 ≈ (0.85 * 12 * 49) 180 fConfigView->SetExplicitMinSize( 181 BSize(ceilf(be_control_look->DefaultItemSpacing() * 49) 182 - fTranslatorListView->Frame().Width(), B_SIZE_UNSET)); 183 184 // Make sure the translator's image doesn't get unloaded while we are still 185 // showing a config view whose code is in the image 186 fRelease = roster->AcquireTranslator(id); 187 188 return B_OK; 189 } 190 191 192 void 193 DataTranslationsWindow::_ShowInfoView() 194 { 195 if (fConfigView != NULL) { 196 fRightBox->RemoveChild(fConfigView); 197 delete fConfigView; 198 fConfigView = NULL; 199 fInfoText = NULL; 200 if (fRelease != NULL) { 201 fRelease->Release(); 202 fRelease = NULL; 203 } 204 } 205 206 fInfoText = new BTextView("info text"); 207 fInfoText->MakeEditable(false); 208 fInfoText->MakeSelectable(false); 209 fInfoText->SetViewUIColor(B_PANEL_BACKGROUND_COLOR); 210 fInfoText->SetText(B_TRANSLATE( 211 "Use this control panel to set default values for translators, " 212 "to be used when no other settings are specified by an application.")); 213 rgb_color textColor = ui_color(B_PANEL_TEXT_COLOR); 214 fInfoText->SetFontAndColor(be_plain_font, B_FONT_ALL, &textColor); 215 216 BGroupView* group = new BGroupView(B_VERTICAL); 217 group->AddChild(fInfoText); 218 float spacing = be_control_look->DefaultItemSpacing(); 219 group->GroupLayout()->SetInsets(spacing, spacing, spacing, spacing); 220 fRightBox->AddChild(group); 221 fConfigView = group; 222 223 fConfigView->SetExplicitMinSize( 224 BSize(ceilf(spacing * be_plain_font->Size() * 0.7) 225 - fTranslatorListView->Frame().Width(), 226 ceilf(spacing * be_plain_font->Size() * 0.4))); 227 } 228 229 230 void 231 DataTranslationsWindow::_SetupViews() 232 { 233 fInfoText = NULL; 234 fConfigView = NULL; 235 // This is NULL until a translator is 236 // selected from the listview 237 238 // Add the translators list view 239 fTranslatorListView = new TranslatorListView("TransList"); 240 fTranslatorListView->SetSelectionMessage( 241 new BMessage(kMsgSelectedTranslator)); 242 243 BScrollView* scrollView = new BScrollView("scroll_trans", 244 fTranslatorListView, B_WILL_DRAW | B_FRAME_EVENTS, false, 245 true, B_FANCY_BORDER); 246 247 // Box around the config and info panels 248 fRightBox = new BBox("Right_Side"); 249 fRightBox->SetExplicitAlignment(BAlignment(B_ALIGN_USE_FULL_WIDTH, 250 B_ALIGN_USE_FULL_HEIGHT)); 251 252 // Add the translator icon view 253 fIconView = new IconView(); 254 255 // Add the translator info button 256 fButton = new BButton("info", B_TRANSLATE("Info"), 257 new BMessage(kMsgTranslatorInfo), 258 B_WILL_DRAW | B_FRAME_EVENTS | B_NAVIGABLE); 259 fButton->SetEnabled(false); 260 261 // Populate the translators list view 262 _PopulateListView(); 263 264 // Build the layout 265 BLayoutBuilder::Group<>(this, B_HORIZONTAL) 266 .SetInsets(B_USE_WINDOW_SPACING) 267 .Add(scrollView, 3) 268 .AddGroup(B_VERTICAL) 269 .Add(fRightBox) 270 .AddGroup(B_HORIZONTAL) 271 .Add(fIconView) 272 .AddGlue() 273 .Add(fButton) 274 .End() 275 .End() 276 .End(); 277 278 fTranslatorListView->MakeFocus(); 279 _ShowInfoView(); 280 } 281 282 283 bool 284 DataTranslationsWindow::QuitRequested() 285 { 286 BPoint pt(Frame().LeftTop()); 287 DataTranslationsSettings::Instance()->SetWindowCorner(pt); 288 be_app->PostMessage(B_QUIT_REQUESTED); 289 return true; 290 } 291 292 293 void 294 DataTranslationsWindow::_ShowInfoAlert(int32 id) 295 { 296 const char* name = NULL; 297 const char* info = NULL; 298 BPath path; 299 int32 version = 0; 300 _GetTranslatorInfo(id, name, info, version, path); 301 302 const char* labels[] = { B_TRANSLATE("Name:"), B_TRANSLATE("Version:"), 303 B_TRANSLATE("Info:"), B_TRANSLATE("Path:"), NULL }; 304 int offsets[4]; 305 306 BString message; 307 BString temp; 308 309 offsets[0] = 0; 310 temp.SetToFormat("%s %s\n", labels[0], name); 311 312 message.Append(temp); 313 314 offsets[1] = message.Length(); 315 // Convert the version number into a readable format 316 temp.SetToFormat("%s %" B_PRId32 ".%" B_PRId32 ".%" B_PRId32 "\n\n", labels[1], 317 B_TRANSLATION_MAJOR_VERSION(version), 318 B_TRANSLATION_MINOR_VERSION(version), 319 B_TRANSLATION_REVISION_VERSION(version)); 320 321 message.Append(temp); 322 323 offsets[2] = message.Length(); 324 temp.SetToFormat("%s\n%s\n\n", labels[2], info); 325 326 message.Append(temp); 327 328 offsets[3] = message.Length(); 329 temp.SetToFormat("%s %s\n", labels[3], path.Path()); 330 331 message.Append(temp); 332 333 BAlert* alert = new BAlert(B_TRANSLATE("Info"), message.String(), 334 B_TRANSLATE("OK")); 335 BTextView* view = alert->TextView(); 336 BFont font; 337 338 view->SetStylable(true); 339 340 view->GetFont(&font); 341 font.SetFace(B_BOLD_FACE); 342 343 for (int32 i = 0; labels[i]; i++) { 344 view->SetFontAndColor(offsets[i], offsets[i] + strlen(labels[i]), &font); 345 } 346 347 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 348 alert->Go(); 349 } 350 351 352 void 353 DataTranslationsWindow::MessageReceived(BMessage* message) 354 { 355 switch (message->what) { 356 case kMsgTranslatorInfo: 357 { 358 int32 selected = fTranslatorListView->CurrentSelection(0); 359 if (selected < 0) 360 break; 361 362 TranslatorItem* item = fTranslatorListView->TranslatorAt(selected); 363 if (item != NULL) 364 _ShowInfoAlert(item->ID()); 365 break; 366 } 367 368 case kMsgSelectedTranslator: 369 { 370 // Update the icon and translator info panel 371 // to match the new selection 372 373 int32 selected = fTranslatorListView->CurrentSelection(0); 374 if (selected < 0) { 375 // If none selected, clear the old one 376 fIconView->DrawIcon(false); 377 fButton->SetEnabled(false); 378 fRightBox->RemoveChild(fConfigView); 379 _ShowInfoView(); 380 break; 381 } 382 383 TranslatorItem* item = fTranslatorListView->TranslatorAt(selected); 384 if (item == NULL) 385 break; 386 387 _ShowConfigView(item->ID()); 388 389 const char* name = NULL; 390 const char* info = NULL; 391 int32 version = 0; 392 BPath path; 393 _GetTranslatorInfo(item->ID(), name, info, version, path); 394 fIconView->SetIcon(path); 395 fButton->SetEnabled(true); 396 break; 397 } 398 399 case B_COLORS_UPDATED: 400 { 401 if (fInfoText == NULL || fInfoText->Parent() == NULL) 402 break; 403 404 rgb_color color; 405 if (message->FindColor(ui_color_name(B_PANEL_TEXT_COLOR), &color) 406 == B_OK) { 407 fInfoText->SetFontAndColor(be_plain_font, B_FONT_ALL, &color); 408 } 409 break; 410 } 411 412 case B_TRANSLATOR_ADDED: 413 { 414 int32 index = 0; 415 int32 id; 416 while (message->FindInt32("translator_id", index++, &id) == B_OK) { 417 const char* name; 418 const char* info; 419 int32 version; 420 BPath path; 421 if (_GetTranslatorInfo(id, name, info, version, path) == B_OK) 422 fTranslatorListView->AddItem(new TranslatorItem(id, name)); 423 } 424 425 fTranslatorListView->SortItems(); 426 break; 427 } 428 429 case B_TRANSLATOR_REMOVED: 430 { 431 int32 index = 0; 432 int32 id; 433 while (message->FindInt32("translator_id", index++, &id) == B_OK) { 434 for (int32 i = 0; i < fTranslatorListView->CountItems(); i++) { 435 TranslatorItem* item = fTranslatorListView->TranslatorAt(i); 436 437 if (item == NULL) 438 continue; 439 440 if (item->ID() == (translator_id)id) { 441 fTranslatorListView->RemoveItem(i); 442 delete item; 443 break; 444 } 445 } 446 } 447 break; 448 } 449 450 default: 451 BWindow::MessageReceived(message); 452 break; 453 } 454 } 455