1 /* 2 * Copyright 2002-2010, Haiku, Inc. All Rights Reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Mattias Sundblad 7 * Andrew Bachmann 8 * Jonas Sundström 9 */ 10 11 12 #include "Constants.h" 13 #include "StyledEditApp.h" 14 #include "StyledEditWindow.h" 15 16 #include <Alert.h> 17 #include <Autolock.h> 18 #include <Catalog.h> 19 #include <Locale.h> 20 #include <MenuBar.h> 21 #include <CharacterSet.h> 22 #include <CharacterSetRoster.h> 23 #include <FilePanel.h> 24 #include <MenuItem.h> 25 #include <Message.h> 26 #include <Path.h> 27 #include <Screen.h> 28 29 #include <stdio.h> 30 31 32 using namespace BPrivate; 33 34 35 BRect gWindowRect(7 - 15, 26 - 15, 507, 426); 36 37 38 namespace 39 { 40 void 41 cascade() 42 { 43 BScreen screen; 44 BRect screenBorder = screen.Frame(); 45 float left = gWindowRect.left + 15; 46 if (left + gWindowRect.Width() > screenBorder.right) 47 left = 7; 48 49 float top = gWindowRect.top + 15; 50 if (top + gWindowRect.Height() > screenBorder.bottom) 51 top = 26; 52 53 gWindowRect.OffsetTo(BPoint(left, top)); 54 } 55 56 57 void 58 uncascade() 59 { 60 BScreen screen; 61 BRect screenBorder = screen.Frame(); 62 63 float left = gWindowRect.left - 15; 64 if (left < 7) { 65 left = screenBorder.right - gWindowRect.Width() - 7; 66 left = left - ((int)left % 15) + 7; 67 } 68 69 float top = gWindowRect.top - 15; 70 if (top < 26) { 71 top = screenBorder.bottom - gWindowRect.Height() - 26; 72 top = top - ((int)left % 15) + 26; 73 } 74 75 gWindowRect.OffsetTo(BPoint(left, top)); 76 } 77 } 78 79 80 // #pragma mark - 81 82 83 #undef B_TRANSLATION_CONTEXT 84 #define B_TRANSLATION_CONTEXT "Open_and_SaveAsPanel" 85 86 87 StyledEditApp::StyledEditApp() 88 : 89 BApplication(APP_SIGNATURE), 90 fOpenPanel(NULL) 91 { 92 B_TRANSLATE_MARK_SYSTEM_NAME_VOID("StyledEdit"); 93 94 fOpenPanel = new BFilePanel(); 95 fOpenAsEncoding = 0; 96 97 BMenuBar* menuBar 98 = dynamic_cast<BMenuBar*>(fOpenPanel->Window()->FindView("MenuBar")); 99 if (menuBar != NULL) { 100 fOpenPanelEncodingMenu = new BMenu(B_TRANSLATE("Encoding")); 101 fOpenPanelEncodingMenu->SetRadioMode(true); 102 103 menuBar->AddItem(fOpenPanelEncodingMenu); 104 105 BCharacterSetRoster roster; 106 BCharacterSet charset; 107 while (roster.GetNextCharacterSet(&charset) == B_NO_ERROR) { 108 BString name; 109 if (charset.GetFontID() == B_UNICODE_UTF8) 110 name = B_TRANSLATE("Default"); 111 else 112 name = charset.GetPrintName(); 113 114 const char* mime = charset.GetMIMEName(); 115 if (mime != NULL) { 116 name.Append(" ("); 117 name.Append(mime); 118 name.Append(")"); 119 } 120 BMenuItem* item 121 = new BMenuItem(name.String(), new BMessage(OPEN_AS_ENCODING)); 122 item->SetTarget(this); 123 fOpenPanelEncodingMenu->AddItem(item); 124 if (charset.GetFontID() == fOpenAsEncoding) 125 item->SetMarked(true); 126 } 127 } else 128 fOpenPanelEncodingMenu = NULL; 129 130 fWindowCount = 0; 131 fNextUntitledWindow = 1; 132 fBadArguments = false; 133 } 134 135 136 StyledEditApp::~StyledEditApp() 137 { 138 delete fOpenPanel; 139 } 140 141 142 void 143 StyledEditApp::MessageReceived(BMessage* message) 144 { 145 switch (message->what) { 146 case MENU_NEW: 147 OpenDocument(); 148 break; 149 case MENU_OPEN: 150 fOpenPanel->Show(); 151 break; 152 case B_SILENT_RELAUNCH: 153 OpenDocument(); 154 break; 155 case OPEN_AS_ENCODING: 156 void* ptr; 157 if (message->FindPointer("source", &ptr) == B_OK 158 && fOpenPanelEncodingMenu != NULL) { 159 fOpenAsEncoding = (uint32)fOpenPanelEncodingMenu->IndexOf( 160 (BMenuItem*)ptr); 161 } 162 break; 163 164 default: 165 BApplication::MessageReceived(message); 166 break; 167 } 168 } 169 170 171 void 172 StyledEditApp::OpenDocument() 173 { 174 cascade(); 175 new StyledEditWindow(gWindowRect, fNextUntitledWindow++, fOpenAsEncoding); 176 fWindowCount++; 177 } 178 179 180 status_t 181 StyledEditApp::OpenDocument(entry_ref* ref, BMessage* message) 182 { 183 // traverse eventual symlink 184 BEntry entry(ref, true); 185 entry.GetRef(ref); 186 187 if (entry.IsDirectory()) { 188 BPath path(&entry); 189 fprintf(stderr, 190 "Can't open directory \"%s\" for editing.\n", 191 path.Path()); 192 return B_ERROR; 193 } 194 195 BEntry parent; 196 entry.GetParent(&parent); 197 198 if (!entry.Exists() && !parent.Exists()) { 199 fprintf(stderr, 200 "Can't create file. Missing parent directory.\n"); 201 return B_ERROR; 202 } 203 204 BWindow* window = NULL; 205 StyledEditWindow* document = NULL; 206 207 for (int32 index = 0; ; index++) { 208 window = WindowAt(index); 209 if (window == NULL) 210 break; 211 212 document = dynamic_cast<StyledEditWindow*>(window); 213 if (document == NULL) 214 continue; 215 216 if (document->IsDocumentEntryRef(ref)) { 217 if (document->Lock()) { 218 document->Activate(); 219 document->Unlock(); 220 if (message != NULL) 221 document->PostMessage(message); 222 return B_OK; 223 } 224 } 225 } 226 227 cascade(); 228 document = new StyledEditWindow(gWindowRect, ref, fOpenAsEncoding); 229 230 if (message != NULL) 231 document->PostMessage(message); 232 233 fWindowCount++; 234 235 return B_OK; 236 } 237 238 239 void 240 StyledEditApp::CloseDocument() 241 { 242 uncascade(); 243 fWindowCount--; 244 if (fWindowCount == 0) { 245 BAutolock lock(this); 246 Quit(); 247 } 248 } 249 250 251 void 252 StyledEditApp::RefsReceived(BMessage* message) 253 { 254 int32 index = 0; 255 entry_ref ref; 256 257 while (message->FindRef("refs", index, &ref) == B_OK) { 258 int32 line; 259 if (message->FindInt32("be:line", index, &line) != B_OK) 260 line = -1; 261 int32 start, length; 262 if (message->FindInt32("be:selection_length", index, &length) != B_OK 263 || message->FindInt32("be:selection_offset", index, &start) != B_OK) 264 { 265 start = -1; 266 length = -1; 267 } 268 269 BMessage* selection = NULL; 270 if (line >= 0 || (start >= 0 && length >= 0)) { 271 selection = new BMessage(UPDATE_LINE_SELECTION); 272 if (line >= 0) 273 selection->AddInt32("be:line", line); 274 if (start >= 0) { 275 selection->AddInt32("be:selection_offset", start); 276 selection->AddInt32("be:selection_length", max_c(0, length)); 277 } 278 } 279 280 OpenDocument(&ref, selection); 281 index++; 282 } 283 } 284 285 286 void 287 StyledEditApp::ArgvReceived(int32 argc, char* argv[]) 288 { 289 // If StyledEdit is already running and gets invoked again 290 // we need to account for a possible mismatch in current 291 // working directory. The paths of the new arguments are 292 // relative to the cwd of the invocation, if they are not 293 // absolute. This cwd we find as a string named "cwd" in 294 // the BLooper's current message. 295 296 const char* cwd = ""; 297 BMessage* message = CurrentMessage(); 298 299 if (message != NULL) { 300 if (message->FindString("cwd", &cwd) != B_OK) 301 cwd = ""; 302 } 303 304 for (int i = 1 ; (i < argc) ; i++) { 305 BPath path; 306 if (argv[i][0] == '/') { 307 path.SetTo(argv[i]); 308 } else { 309 path.SetTo(cwd, argv[i]); 310 // patch relative paths only 311 } 312 313 entry_ref ref; 314 get_ref_for_path(path.Path(), &ref); 315 316 status_t status; 317 status = OpenDocument(&ref); 318 319 if (status != B_OK && IsLaunching()) 320 fBadArguments = true; 321 } 322 } 323 324 325 void 326 StyledEditApp::ReadyToRun() 327 { 328 if (fWindowCount > 0) 329 return; 330 331 if (fBadArguments) 332 Quit(); 333 else 334 OpenDocument(); 335 } 336 337 338 int32 339 StyledEditApp::NumberOfWindows() 340 { 341 return fWindowCount; 342 } 343 344 345 // #pragma mark - 346 347 348 int 349 main(int argc, char** argv) 350 { 351 StyledEditApp styledEdit; 352 styledEdit.Run(); 353 return 0; 354 } 355 356