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 status_t 42 add_extensions(BMimeType& type, BList& list, const char* removeExtension) 43 { 44 BMessage extensions; 45 status_t status = type.GetFileExtensions(&extensions); 46 if (status < B_OK) 47 return status; 48 49 // replace the entry, and remove any equivalent entries 50 BList newList; 51 newList.AddList(&list); 52 53 const char* extension; 54 for (int32 i = 0; extensions.FindString("extensions", i, 55 &extension) == B_OK; i++) { 56 bool add = true; 57 for (int32 j = list.CountItems(); j-- > 0;) { 58 if ((removeExtension && !strcmp(removeExtension, extension)) 59 || !strcmp((const char*)list.ItemAt(j), extension)) { 60 // remove this item 61 continue; 62 } 63 } 64 65 if (add) 66 newList.AddItem((void *)extension); 67 } 68 69 newList.SortItems(compare_extensions); 70 71 // Copy them to a new message (their memory is still part of the 72 // original BMessage) 73 BMessage newExtensions; 74 for (int32 i = 0; i < newList.CountItems(); i++) { 75 newExtensions.AddString("extensions", (const char*)newList.ItemAt(i)); 76 } 77 78 return type.SetFileExtensions(&newExtensions); 79 } 80 81 82 status_t 83 replace_extension(BMimeType& type, const char* newExtension, const char* oldExtension) 84 { 85 BList list; 86 list.AddItem((void *)newExtension); 87 88 return add_extensions(type, list, oldExtension); 89 } 90 91 92 // #pragma mark - 93 94 95 ExtensionWindow::ExtensionWindow(FileTypesWindow* target, BMimeType& type, 96 const char* extension) 97 : BWindow(BRect(100, 100, 350, 200), "Extension", B_MODAL_WINDOW_LOOK, 98 B_MODAL_SUBSET_WINDOW_FEEL, B_NOT_ZOOMABLE | B_NOT_V_RESIZABLE 99 | B_ASYNCHRONOUS_CONTROLS), 100 fTarget(target), 101 fMimeType(type.Type()), 102 fExtension(extension) 103 { 104 BRect rect = Bounds(); 105 BView* topView = new BView(rect, NULL, B_FOLLOW_ALL, B_WILL_DRAW); 106 topView->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 107 AddChild(topView); 108 109 rect.InsetBy(8.0f, 8.0f); 110 fExtensionControl = new BTextControl(rect, "extension", "Extension:", extension, 111 NULL, B_FOLLOW_LEFT_RIGHT); 112 113 float labelWidth = fExtensionControl->StringWidth(fExtensionControl->Label()) + 2.0f; 114 fExtensionControl->SetModificationMessage(new BMessage(kMsgExtensionUpdated)); 115 fExtensionControl->SetDivider(labelWidth); 116 fExtensionControl->SetAlignment(B_ALIGN_RIGHT, B_ALIGN_LEFT); 117 118 // filter out invalid characters that can't be part of an extension 119 BTextView* textView = fExtensionControl->TextView(); 120 const char* disallowedCharacters = "/:"; 121 for (int32 i = 0; disallowedCharacters[i]; i++) { 122 textView->DisallowChar(disallowedCharacters[i]); 123 } 124 125 float width, height; 126 fExtensionControl->GetPreferredSize(&width, &height); 127 fExtensionControl->ResizeTo(rect.Width(), height); 128 topView->AddChild(fExtensionControl); 129 130 fAcceptButton = new BButton(rect, "add", extension ? "Done" : "Add", 131 new BMessage(kMsgAccept), B_FOLLOW_RIGHT | B_FOLLOW_BOTTOM); 132 fAcceptButton->ResizeToPreferred(); 133 fAcceptButton->MoveTo(Bounds().Width() - 8.0f - fAcceptButton->Bounds().Width(), 134 Bounds().Height() - 8.0f - fAcceptButton->Bounds().Height()); 135 fAcceptButton->SetEnabled(false); 136 topView->AddChild(fAcceptButton); 137 138 BButton* button = new BButton(rect, "cancel", "Cancel", 139 new BMessage(B_QUIT_REQUESTED), B_FOLLOW_RIGHT | B_FOLLOW_BOTTOM); 140 button->ResizeToPreferred(); 141 button->MoveTo(fAcceptButton->Frame().left - 10.0f - button->Bounds().Width(), 142 fAcceptButton->Frame().top); 143 topView->AddChild(button); 144 145 ResizeTo(labelWidth * 4.0f + 24.0f, fExtensionControl->Bounds().Height() 146 + fAcceptButton->Bounds().Height() + 28.0f); 147 SetSizeLimits(button->Bounds().Width() + fAcceptButton->Bounds().Width() + 26.0f, 148 32767.0f, Frame().Height(), Frame().Height()); 149 150 // omit the leading dot 151 if (fExtension.ByteAt(0) == '.') 152 fExtension.Remove(0, 1); 153 154 fAcceptButton->MakeDefault(true); 155 fExtensionControl->MakeFocus(true); 156 157 target->PlaceSubWindow(this); 158 AddToSubset(target); 159 } 160 161 162 ExtensionWindow::~ExtensionWindow() 163 { 164 } 165 166 167 void 168 ExtensionWindow::MessageReceived(BMessage* message) 169 { 170 switch (message->what) { 171 case kMsgExtensionUpdated: 172 { 173 bool enabled = fExtensionControl->Text() != NULL 174 && fExtensionControl->Text()[0] != '\0'; 175 if (enabled) { 176 // There is some text, but we only accept it, if it 177 // changed the previous extension 178 enabled = strcmp(fExtensionControl->Text(), fExtension.String()); 179 } 180 181 if (fAcceptButton->IsEnabled() != enabled) 182 fAcceptButton->SetEnabled(enabled); 183 break; 184 } 185 186 case kMsgAccept: 187 { 188 const char* newExtension = fExtensionControl->Text(); 189 // omit the leading dot 190 if (newExtension[0] == '.') 191 newExtension++; 192 193 status_t status = replace_extension(fMimeType, newExtension, 194 fExtension.String()); 195 if (status != B_OK) 196 error_alert("Could not change file extensions", status); 197 198 PostMessage(B_QUIT_REQUESTED); 199 break; 200 } 201 202 default: 203 BWindow::MessageReceived(message); 204 break; 205 } 206 } 207 208