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