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