1 /* 2 * Copyright 2006-2010, Axel Dörfler, axeld@pinc-software.de. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7 #include "ExtensionWindow.h" 8 #include "FileTypes.h" 9 #include "FileTypesWindow.h" 10 11 #include <Button.h> 12 #include <Catalog.h> 13 #include <ControlLook.h> 14 #include <LayoutBuilder.h> 15 #include <Locale.h> 16 #include <MenuField.h> 17 #include <MenuItem.h> 18 #include <Mime.h> 19 #include <PopUpMenu.h> 20 #include <String.h> 21 #include <TextControl.h> 22 23 #include <strings.h> 24 25 26 #undef B_TRANSLATION_CONTEXT 27 #define B_TRANSLATION_CONTEXT "Extension Window" 28 29 30 const uint32 kMsgExtensionUpdated = 'exup'; 31 const uint32 kMsgAccept = 'acpt'; 32 33 34 static int 35 compare_extensions(const void* _a, const void* _b) 36 { 37 const char* a = *(const char **)_a; 38 const char* b = *(const char **)_b; 39 40 int compare = strcasecmp(a, b); 41 if (compare != 0) 42 return compare; 43 44 // sort lower case characters first 45 return -strcmp(a, b); 46 } 47 48 49 //! newExtensionsList contains all the entries (char*) which are to be added. 50 status_t 51 merge_extensions(BMimeType& type, const BList& newExtensionsList, 52 const char* removeExtension) 53 { 54 BMessage extensions; 55 status_t status = type.GetFileExtensions(&extensions); 56 if (status < B_OK) 57 return status; 58 59 // replace the entry, and remove any equivalent entries 60 BList mergedList; 61 mergedList.AddList(&newExtensionsList); 62 int32 originalCount = mergedList.CountItems(); 63 64 const char* extension; 65 for (int32 i = 0; extensions.FindString("extensions", i, 66 &extension) == B_OK; i++) { 67 68 for (int32 j = originalCount; j-- > 0;) { 69 if (!strcmp((const char*)mergedList.ItemAt(j), extension)) { 70 // Do not add this old item again, since it's already 71 // there. 72 mergedList.RemoveItem(j); 73 originalCount--; 74 } 75 } 76 77 // The item will be added behind "originalCount", so we cannot 78 // remove it accidentally in the next iterations, it's is added 79 // for good. 80 if (removeExtension == NULL || strcmp(removeExtension, extension)) 81 mergedList.AddItem((void *)extension); 82 } 83 84 mergedList.SortItems(compare_extensions); 85 86 // Copy them to a new message (their memory is still part of the 87 // original BMessage) 88 BMessage newExtensions; 89 for (int32 i = 0; i < mergedList.CountItems(); i++) { 90 newExtensions.AddString("extensions", 91 (const char*)mergedList.ItemAt(i)); 92 } 93 94 return type.SetFileExtensions(&newExtensions); 95 } 96 97 98 status_t 99 replace_extension(BMimeType& type, const char* newExtension, 100 const char* oldExtension) 101 { 102 BList list; 103 list.AddItem((void *)newExtension); 104 105 return merge_extensions(type, list, oldExtension); 106 } 107 108 109 // #pragma mark - 110 111 112 ExtensionWindow::ExtensionWindow(FileTypesWindow* target, BMimeType& type, 113 const char* extension) 114 : BWindow(BRect(100, 100, 350, 200), B_TRANSLATE("Extension"), 115 B_MODAL_WINDOW_LOOK, B_MODAL_SUBSET_WINDOW_FEEL, 116 B_NOT_ZOOMABLE | B_NOT_RESIZABLE 117 | B_ASYNCHRONOUS_CONTROLS | B_AUTO_UPDATE_SIZE_LIMITS), 118 fTarget(target), 119 fMimeType(type.Type()), 120 fExtension(extension) 121 { 122 fExtensionControl = new BTextControl(B_TRANSLATE("Extension:"), 123 extension, NULL); 124 fExtensionControl->SetModificationMessage( 125 new BMessage(kMsgExtensionUpdated)); 126 fExtensionControl->SetAlignment(B_ALIGN_LEFT, B_ALIGN_LEFT); 127 128 // filter out invalid characters that can't be part of an extension 129 BTextView* textView = fExtensionControl->TextView(); 130 const char* disallowedCharacters = "/:"; 131 for (int32 i = 0; disallowedCharacters[i]; i++) { 132 textView->DisallowChar(disallowedCharacters[i]); 133 } 134 135 fAcceptButton = new BButton(extension 136 ? B_TRANSLATE("Done") : B_TRANSLATE("Add"), 137 new BMessage(kMsgAccept)); 138 fAcceptButton->SetEnabled(false); 139 140 BButton* cancelButton = new BButton(B_TRANSLATE("Cancel"), 141 new BMessage(B_QUIT_REQUESTED)); 142 143 float padding = be_control_look->DefaultItemSpacing(); 144 BLayoutBuilder::Grid<>(this, padding, padding) 145 .SetInsets(padding, padding, padding, padding) 146 .AddTextControl(fExtensionControl, 0, 0, B_ALIGN_HORIZONTAL_UNSET, 1, 2) 147 .Add(BSpaceLayoutItem::CreateGlue(), 0, 1) 148 .Add(cancelButton, 1, 1) 149 .Add(fAcceptButton, 2, 1); 150 151 // omit the leading dot 152 if (fExtension.ByteAt(0) == '.') 153 fExtension.Remove(0, 1); 154 155 fAcceptButton->MakeDefault(true); 156 fExtensionControl->MakeFocus(true); 157 158 target->PlaceSubWindow(this); 159 AddToSubset(target); 160 } 161 162 163 ExtensionWindow::~ExtensionWindow() 164 { 165 } 166 167 168 void 169 ExtensionWindow::MessageReceived(BMessage* message) 170 { 171 switch (message->what) { 172 case kMsgExtensionUpdated: 173 { 174 bool enabled = fExtensionControl->Text() != NULL 175 && fExtensionControl->Text()[0] != '\0'; 176 if (enabled) { 177 // There is some text, but we only accept it, if it 178 // changed the previous extension 179 enabled = strcmp(fExtensionControl->Text(), fExtension.String()); 180 } 181 182 if (fAcceptButton->IsEnabled() != enabled) 183 fAcceptButton->SetEnabled(enabled); 184 break; 185 } 186 187 case kMsgAccept: 188 { 189 const char* newExtension = fExtensionControl->Text(); 190 // omit the leading dot 191 if (newExtension[0] == '.') 192 newExtension++; 193 194 status_t status = replace_extension(fMimeType, newExtension, 195 fExtension.String()); 196 if (status != B_OK) 197 error_alert(B_TRANSLATE("Could not change file extensions"), 198 status); 199 200 PostMessage(B_QUIT_REQUESTED); 201 break; 202 } 203 204 default: 205 BWindow::MessageReceived(message); 206 break; 207 } 208 } 209