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 224 225 void 226 DataTranslationsWindow::_SetupViews() 227 { 228 fInfoText = NULL; 229 fConfigView = NULL; 230 // This is NULL until a translator is 231 // selected from the listview 232 233 // Add the translators list view 234 fTranslatorListView = new TranslatorListView("TransList"); 235 fTranslatorListView->SetSelectionMessage( 236 new BMessage(kMsgSelectedTranslator)); 237 238 BScrollView* scrollView = new BScrollView("scroll_trans", 239 fTranslatorListView, B_WILL_DRAW | B_FRAME_EVENTS, false, 240 true, B_FANCY_BORDER); 241 242 // Box around the config and info panels 243 fRightBox = new BBox("Right_Side"); 244 fRightBox->SetExplicitAlignment(BAlignment(B_ALIGN_USE_FULL_WIDTH, 245 B_ALIGN_USE_FULL_HEIGHT)); 246 247 // Add the translator icon view 248 fIconView = new IconView(); 249 250 // Add the translator info button 251 fButton = new BButton("info", B_TRANSLATE("Info"), 252 new BMessage(kMsgTranslatorInfo), 253 B_WILL_DRAW | B_FRAME_EVENTS | B_NAVIGABLE); 254 fButton->SetEnabled(false); 255 256 // Populate the translators list view 257 _PopulateListView(); 258 259 // Build the layout 260 BLayoutBuilder::Group<>(this, B_HORIZONTAL) 261 .SetInsets(B_USE_WINDOW_SPACING) 262 .Add(scrollView, 3) 263 .AddGroup(B_VERTICAL) 264 .Add(fRightBox) 265 .AddGroup(B_HORIZONTAL) 266 .Add(fIconView) 267 .AddGlue() 268 .Add(fButton) 269 .End() 270 .End() 271 .End(); 272 273 fTranslatorListView->MakeFocus(); 274 _ShowInfoView(); 275 } 276 277 278 bool 279 DataTranslationsWindow::QuitRequested() 280 { 281 BPoint pt(Frame().LeftTop()); 282 DataTranslationsSettings::Instance()->SetWindowCorner(pt); 283 be_app->PostMessage(B_QUIT_REQUESTED); 284 return true; 285 } 286 287 288 void 289 DataTranslationsWindow::_ShowInfoAlert(int32 id) 290 { 291 const char* name = NULL; 292 const char* info = NULL; 293 BPath path; 294 int32 version = 0; 295 _GetTranslatorInfo(id, name, info, version, path); 296 297 const char* labels[] = { B_TRANSLATE("Name:"), B_TRANSLATE("Version:"), 298 B_TRANSLATE("Info:"), B_TRANSLATE("Path:"), NULL }; 299 int offsets[4]; 300 301 BString message; 302 BString temp; 303 304 offsets[0] = 0; 305 temp.SetToFormat("%s %s\n", labels[0], name); 306 307 message.Append(temp); 308 309 offsets[1] = message.Length(); 310 // Convert the version number into a readable format 311 temp.SetToFormat("%s %" B_PRId32 ".%" B_PRId32 ".%" B_PRId32 "\n\n", labels[1], 312 B_TRANSLATION_MAJOR_VERSION(version), 313 B_TRANSLATION_MINOR_VERSION(version), 314 B_TRANSLATION_REVISION_VERSION(version)); 315 316 message.Append(temp); 317 318 offsets[2] = message.Length(); 319 temp.SetToFormat("%s\n%s\n\n", labels[2], info); 320 321 message.Append(temp); 322 323 offsets[3] = message.Length(); 324 temp.SetToFormat("%s %s\n", labels[3], path.Path()); 325 326 message.Append(temp); 327 328 BAlert* alert = new BAlert(B_TRANSLATE("Info"), message.String(), 329 B_TRANSLATE("OK")); 330 BTextView* view = alert->TextView(); 331 BFont font; 332 333 view->SetStylable(true); 334 335 view->GetFont(&font); 336 font.SetFace(B_BOLD_FACE); 337 338 for (int32 i = 0; labels[i]; i++) { 339 view->SetFontAndColor(offsets[i], offsets[i] + strlen(labels[i]), &font); 340 } 341 342 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 343 alert->Go(); 344 } 345 346 347 void 348 DataTranslationsWindow::MessageReceived(BMessage* message) 349 { 350 switch (message->what) { 351 case kMsgTranslatorInfo: 352 { 353 int32 selected = fTranslatorListView->CurrentSelection(0); 354 if (selected < 0) 355 break; 356 357 TranslatorItem* item = fTranslatorListView->TranslatorAt(selected); 358 if (item != NULL) 359 _ShowInfoAlert(item->ID()); 360 break; 361 } 362 363 case kMsgSelectedTranslator: 364 { 365 // Update the icon and translator info panel 366 // to match the new selection 367 368 int32 selected = fTranslatorListView->CurrentSelection(0); 369 if (selected < 0) { 370 // If none selected, clear the old one 371 fIconView->DrawIcon(false); 372 fButton->SetEnabled(false); 373 fRightBox->RemoveChild(fConfigView); 374 _ShowInfoView(); 375 break; 376 } 377 378 TranslatorItem* item = fTranslatorListView->TranslatorAt(selected); 379 if (item == NULL) 380 break; 381 382 _ShowConfigView(item->ID()); 383 384 const char* name = NULL; 385 const char* info = NULL; 386 int32 version = 0; 387 BPath path; 388 _GetTranslatorInfo(item->ID(), name, info, version, path); 389 fIconView->SetIcon(path); 390 fButton->SetEnabled(true); 391 break; 392 } 393 394 case B_COLORS_UPDATED: 395 { 396 if (fInfoText == NULL || fInfoText->Parent() == NULL) 397 break; 398 399 rgb_color color; 400 if (message->FindColor(ui_color_name(B_PANEL_TEXT_COLOR), &color) 401 == B_OK) { 402 fInfoText->SetFontAndColor(be_plain_font, B_FONT_ALL, &color); 403 } 404 break; 405 } 406 407 case B_TRANSLATOR_ADDED: 408 { 409 int32 index = 0; 410 int32 id; 411 while (message->FindInt32("translator_id", index++, &id) == B_OK) { 412 const char* name; 413 const char* info; 414 int32 version; 415 BPath path; 416 if (_GetTranslatorInfo(id, name, info, version, path) == B_OK) 417 fTranslatorListView->AddItem(new TranslatorItem(id, name)); 418 } 419 420 fTranslatorListView->SortItems(); 421 break; 422 } 423 424 case B_TRANSLATOR_REMOVED: 425 { 426 int32 index = 0; 427 int32 id; 428 while (message->FindInt32("translator_id", index++, &id) == B_OK) { 429 for (int32 i = 0; i < fTranslatorListView->CountItems(); i++) { 430 TranslatorItem* item = fTranslatorListView->TranslatorAt(i); 431 432 if (item == NULL) 433 continue; 434 435 if (item->ID() == (translator_id)id) { 436 fTranslatorListView->RemoveItem(i); 437 delete item; 438 break; 439 } 440 } 441 } 442 break; 443 } 444 445 default: 446 BWindow::MessageReceived(message); 447 break; 448 } 449 } 450