Bug Summary

File:home/HaikuArchives/ArtPaint/artpaint/paintwindow/ImageView.cpp
Warning:line 1610, column 6
Called C++ object pointer is null

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-unknown-haiku -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name ImageView.cpp -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model static -mframe-pointer=all -fmath-errno -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -tune-cpu generic -fno-split-dwarf-inlining -debugger-tuning=gdb -resource-dir /boot/system/lib/clang/12.0.1 -iquote ./ -iquote artpaint/ -iquote artpaint/Utilities/ -iquote artpaint/application/ -iquote artpaint/controls/ -iquote artpaint/layers/ -iquote artpaint/paintwindow/ -iquote artpaint/tools/ -iquote artpaint/viewmanipulators/ -iquote artpaint/windows/ -iquote objects_artpaint/ -isystem /boot/system/develop/headers/private/interface -internal-isystem /system/develop/headers/c++ -internal-isystem /system/develop/headers/c++/x86_64-unknown-haiku -internal-isystem /system/develop/headers/c++/backward -O3 -fdeprecated-macro -fdebug-compilation-dir /boot/home/HaikuArchives/ArtPaint -ferror-limit 19 -fgnuc-version=4.2.1 -fcxx-exceptions -fexceptions -vectorize-loops -vectorize-slp -analyzer-output=html -faddrsig -o /tmp/scan-build-2022-06-19-103017-1294-1 -x c++ artpaint/paintwindow/ImageView.cpp
1/*
2 * Copyright 2003, Heikki Suhonen
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 * Heikki Suhonen <heikki.suhonen@gmail.com>
7 * Dale Cieslak <dcieslak@yahoo.com>
8 *
9 */
10
11#include "Cursors.h"
12#include "DrawingTools.h"
13#include "Image.h"
14#include "ImageAdapter.h"
15#include "ImageView.h"
16#include "Layer.h"
17#include "LayerWindow.h"
18#include "Manipulator.h"
19#include "ManipulatorServer.h"
20#include "ManipulatorWindow.h"
21#include "MessageConstants.h"
22#include "PaintApplication.h"
23#include "PaintWindow.h"
24#include "Patterns.h"
25#include "ProjectFileFunctions.h"
26#include "Selection.h"
27#include "SettingsServer.h"
28#include "StatusBarGUIManipulator.h"
29#include "StatusView.h"
30#include "TextManipulator.h"
31#include "ToolManager.h"
32#include "Tools.h"
33#include "UndoQueue.h"
34#include "UtilityClasses.h"
35#include "WindowGUIManipulator.h"
36
37
38#include <Alert.h>
39#include <Catalog.h>
40#include <ClassInfo.h>
41#include <Clipboard.h>
42#include <MenuBar.h>
43#include <MenuItem.h>
44#include <Message.h>
45#include <PopUpMenu.h>
46#include <Screen.h>
47#include <ScrollBar.h>
48#include <Slider.h>
49#include <StatusBar.h>
50#include <StringFormat.h>
51#include <Window.h>
52
53
54#include <new>
55
56
57#undef B_TRANSLATION_CONTEXT"ImageView"
58#define B_TRANSLATION_CONTEXT"ImageView" "ImageView"
59
60
61ImageView::ImageView(BRect frame, float width, float height)
62 : BView(frame,"image_view",B_FOLLOW_NONE0,B_WILL_DRAW)
63{
64 // Initialize the undo-queue
65 undo_queue = new UndoQueue(NULL__null,NULL__null,this);
66
67// This will only be set once after reafing the prefs-file
68// UndoQueue::SetQueueDepth(((PaintApplication*)be_app)->GlobalSettings()->undo_queue_depth);
69
70 // Initialize the image.
71 the_image = new Image(this,width,height,undo_queue);
72 current_display_mode = FULL_RGB_DISPLAY_MODE;
73
74 // here set the magnify_scale for image
75 magnify_scale = 1.0;
76
77 // here we set the grid
78 setGrid(BPoint(0,0),1);
79
80 // We are not yet inside any actions, so...
81 mouse_mutex = create_sem(1,"mouse_mutex");
82 action_semaphore = create_sem(1,"action_semaphore");
83
84 // Set the correct color for view (i.e. B_TRANSPARENT_32_BIT)
85 SetViewColor(B_TRANSPARENT_32_BIT);
86
87 fManipulator = NULL__null;
88 manipulator_window = NULL__null;
89 manipulator_finishing_message = NULL__null;
90
91 // Create an empty selection.
92 selection = new Selection(BRect(0,0,width-1,height-1));
93 selection->StartDrawing(this,magnify_scale);
94
95 cursor_mode = NORMAL_CURSOR_MODE;
96 manipulated_layers = HS_MANIPULATE_NO_LAYER'Mnnl';
97
98 // Initialize the magnify-scale array
99 mag_scale_array_length = 15;
100 mag_scale_array = new float[mag_scale_array_length];
101
102 mag_scale_array[0] = 0.10;
103 mag_scale_array[1] = 0.125;
104 mag_scale_array[2] = 0.175;
105 mag_scale_array[3] = 0.25;
106 mag_scale_array[4] = 0.35;
107 mag_scale_array[5] = 0.50;
108 mag_scale_array[6] = 0.65;
109 mag_scale_array[7] = 0.80;
110 mag_scale_array[8] = 1.00;
111 mag_scale_array[9] = 1.50;
112 mag_scale_array[10] = 2.00;
113 mag_scale_array[11] = 3.00;
114 mag_scale_array[12] = 4.00;
115 mag_scale_array[13] = 8.00;
116 mag_scale_array[14] = 16.00;
117
118 mag_scale_array_index = -1; // This has not yet been decided
119
120 reference_point = BPoint(0,0);
121 use_reference_point = FALSE0;
122
123 project_changed = 0;
124 image_changed = 0;
125
126 project_name = NULL__null;
127 image_name = NULL__null;
128
129 AddFilter(new BMessageFilter(B_KEY_DOWN, KeyFilterFunction));
130}
131
132
133ImageView::~ImageView()
134{
135 // Here we free all allocated memory.
136
137 // Delete the view manipulator and close it's possible window.
138 if (manipulator_window != NULL__null) {
139 manipulator_window->Lock();
140 manipulator_window->Close();
141 }
142 delete fManipulator;
143
144 // Delete the undo-queue
145 delete undo_queue;
146
147 // Delete the selection
148 delete selection;
149
150 // Delete the image
151 delete the_image;
152
153 delete[] mag_scale_array;
154
155 // Delete the semaphores
156 delete_sem(mouse_mutex);
157 delete_sem(action_semaphore);
158}
159
160void ImageView::AttachedToWindow()
161{
162 // Set the type (dithered or full color).
163 BScreen screen(Window());
164 if (screen.ColorSpace() == B_CMAP8)
165 SetDisplayMode(DITHERED_8_BIT_DISPLAY_MODE);
166 else
167 SetDisplayMode(FULL_RGB_DISPLAY_MODE);
168
169 // Make the composite picture (and also update miniature images).
170 the_image->Render();
171
172 // Initialize the undo-queue
173 undo_queue->SetMenuItems(Window()->KeyMenuBar()->FindItem(HS_UNDO'unDo'),Window()->KeyMenuBar()->FindItem(HS_REDO'reDo'));
174
175
176 // Make this view the focus-view (receives key-down events).
177 MakeFocus();
178}
179
180void ImageView::Draw(BRect updateRect)
181{
182 // copy image from bitmap to the part requiring updating
183
184 SetDrawingMode(B_OP_COPY);
185
186 BRegion a_region;
187 GetClippingRegion(&a_region);
188 for (int32 i=0;i<a_region.CountRects();i++) {
189 BlitImage(convertViewRectToBitmap(a_region.RectAt(i) & updateRect));
190 }
191
192 // Draw the selection also
193 selection->Draw();
194
195 // Make the manipulator draw it's UI here.
196 DrawManipulatorGUI(FALSE0);
197
198 // finally Flush() after drawing asynchronously
199 Flush();
200
201}
202
203
204void ImageView::BlitImage(BRect bitmap_rect)
205{
206 BRect image_rect;
207 float mag_scale = getMagScale();
208
209 BBitmap *source_bitmap;
210 if ((current_display_mode == FULL_RGB_DISPLAY_MODE) || (the_image->IsDitheredUpToDate() == FALSE0))
211 source_bitmap = the_image->ReturnRenderedImage();
212 else
213 source_bitmap = the_image->ReturnDitheredImage();
214
215 if (mag_scale == 1.0) {
216 image_rect = bitmap_rect;
217 DrawBitmapAsync(source_bitmap,image_rect,bitmap_rect);
218 }
219 else if (mag_scale != 1.0) {
220 image_rect = convertBitmapRectToView(bitmap_rect);
221 DrawBitmapAsync(source_bitmap,bitmap_rect,image_rect);
222 }
223}
224
225
226void ImageView::UpdateImage(BRect bitmap_rect)
227{
228 bitmap_rect = bitmap_rect & the_image->ReturnRenderedImage()->Bounds();
229 the_image->Render(bitmap_rect);
230 BlitImage(bitmap_rect);
231}
232
233
234void ImageView::DrawManipulatorGUI(bool blit_image)
235{
236 GUIManipulator *gui_manipulator = cast_as(fManipulator,GUIManipulator)(dynamic_cast<GUIManipulator*>(fManipulator));
237 if (gui_manipulator != NULL__null) {
238 if (blit_image == TRUE1) {
239 for (int32 i=0;i<region_drawn_by_manipulator.CountRects();i++) {
240 BlitImage(convertViewRectToBitmap(region_drawn_by_manipulator.RectAt(i)));
241 }
242 }
243
244 region_drawn_by_manipulator = gui_manipulator->Draw(this,getMagScale());
245 }
246 else {
247 region_drawn_by_manipulator = BRegion();
248 }
249}
250
251
252void ImageView::KeyDown(const char *bytes, int32 numBytes)
253{
254 if (*bytes == B_LEFT_ARROW) {
255 BRect bounds = Bounds();
256 float delta = bounds.Width() / 2;
257 if (bounds.left - delta < 0) delta = bounds.left;
258 ScrollBy(-delta, 0);
259 }
260 else if (*bytes == B_UP_ARROW) {
261 BRect bounds = Bounds();
262 float delta = bounds.Height() / 2;
263 if (bounds.top - delta < 0) delta = bounds.top;
264 ScrollBy(0, -delta);
265 }
266 else if (*bytes == B_RIGHT_ARROW) {
267 BRect bounds = Bounds();
268 float delta = bounds.Width() / 2;
269 BRect bitmap_rect = convertBitmapRectToView(the_image->ReturnRenderedImage()->Bounds());
270 if (bounds.right + delta > bitmap_rect.right) delta = bitmap_rect.right - bounds.right;
271 ScrollBy(delta, 0);
272 }
273 else if (*bytes == B_DOWN_ARROW) {
274 BRect bounds = Bounds();
275 float delta = bounds.Height() / 2;
276 BRect bitmap_rect = convertBitmapRectToView(the_image->ReturnRenderedImage()->Bounds());
277 if (bounds.bottom + delta > bitmap_rect.bottom) delta = bitmap_rect.bottom - bounds.bottom;
278 ScrollBy(0, delta);
279 }
280 else if (fManipulator == NULL__null) {
281 ToolManager::Instance().KeyDown(this,bytes,numBytes);
282 }
283}
284
285void ImageView::MessageReceived(BMessage *message)
286{
287 BMessage message_to_window;
288 BMessage message_to_app;
289
290 // here check what the message is all about and initiate proper action
291 switch (message->what) {
292 // This comes from layer's view and tells us to change the active layer.
293 case HS_LAYER_ACTIVATED'LaAc':
294 // Only change the layer if we can acquire the action_semaphore.
295 if (acquire_sem_etc(action_semaphore,1,B_TIMEOUT,0) == B_OK((int)0)) {
296 int32 activated_layer_id;
297 Layer *activated_layer;
298 message->FindInt32("layer_id",&activated_layer_id);
299 message->FindPointer("layer_pointer",(void**)&activated_layer);
300 if (the_image->ChangeActiveLayer(activated_layer,activated_layer_id) == TRUE1)
301 ActiveLayerChanged();
302
303 release_sem(action_semaphore);
304 }
305 break;
306
307 case HS_ZOOM_IMAGE_IN'ZImI': {
308 if (mag_scale_array_index == -1) {
309 // Search for the right index.
310 mag_scale_array_index = 0;
311 while ((mag_scale_array_index < mag_scale_array_length - 1)
312 && (mag_scale_array[mag_scale_array_index] <= magnify_scale)) {
313 mag_scale_array_index++;
314 }
315 } else {
316 mag_scale_array_index = min_c(mag_scale_array_index + 1,((mag_scale_array_index + 1)>(mag_scale_array_length - 1)?
(mag_scale_array_length - 1):(mag_scale_array_index + 1))
317 mag_scale_array_length - 1)((mag_scale_array_index + 1)>(mag_scale_array_length - 1)?
(mag_scale_array_length - 1):(mag_scale_array_index + 1))
;
318 }
319
320 setMagScale(mag_scale_array[mag_scale_array_index]);
321 } break;
322
323 case HS_ZOOM_IMAGE_OUT'ZImO': {
324 if (mag_scale_array_index == -1) {
325 // Search for the right index.
326 mag_scale_array_index = mag_scale_array_length - 1;
327 while ((mag_scale_array_index > 0)
328 && (mag_scale_array[mag_scale_array_index] >= magnify_scale)) {
329 mag_scale_array_index--;
330 }
331 } else {
332 mag_scale_array_index = max_c(mag_scale_array_index - 1, 0)((mag_scale_array_index - 1)>(0)?(mag_scale_array_index - 1
):(0))
;
333 }
334
335 setMagScale(mag_scale_array[mag_scale_array_index]);
336 } break;
337
338 case HS_SET_MAGNIFYING_SCALE'SmgS': {
339 mag_scale_array_index = -1;
340 float newMagScale;
341 if (message->FindFloat("magnifying_scale", &newMagScale) == B_OK((int)0)) {
342 setMagScale(newMagScale);
343 ((PaintWindow*)Window())->displayMag(newMagScale);
344 } else {
345 void *source;
346 if (message->FindPointer("source", &source) == B_OK((int)0)) {
347 BSlider *slider = static_cast<BSlider*> (source);
348 if (slider == NULL__null)
349 break;
350
351 // Convert nonlinear from [10,1600] -> [0.1,16]
352 float scale = (pow(10.0, (slider->Value() - 10.0) / 1590.0)
353 - 1.0) * (15.9 / 9.0) + 0.1;
354 setMagScale(scale);
355 }
356 }
357 } break;
358
359 case HS_GRID_ADJUSTED'Grdj': {
360 BPoint origin;
361 if (message->FindPoint("origin",&origin) == B_OK((int)0)) {
362 int32 unit;
363 if (message->FindInt32("unit",&unit) == B_OK((int)0)) {
364 grid_unit = unit;
365 grid_origin = origin;
366 }
367 }
368 } break;
369
370 // This comes from layer's view and tells us to change the visibility for that layer.
371 case HS_LAYER_VISIBILITY_CHANGED'LviC':
372 int32 changed_layer_id;
373 Layer *changed_layer;
374 message->FindInt32("layer_id",&changed_layer_id);
375 message->FindPointer("layer_pointer",(void**)&changed_layer);
376 if (the_image->ToggleLayerVisibility(changed_layer,changed_layer_id) == TRUE1) {
377 Invalidate();
378 AddChange(); // Is this really necessary?
379 }
380 break;
381
382 // This comes from layer's view and tells us to move layer to another position in the list.
383 case HS_LAYER_POSITION_CHANGED'LpoC':
384 int32 positions_moved;
385 message->FindInt32("layer_id",&changed_layer_id);
386 message->FindPointer("layer_pointer",(void**)&changed_layer);
387 message->FindInt32("positions_moved",&positions_moved);
388 if (the_image->ChangeLayerPosition(changed_layer,changed_layer_id,positions_moved) == TRUE1) {
389 Invalidate();
390 AddChange();
391 }
392 break;
393
394 // This comes from layer's view and tells us to delete the layer.
395 case HS_DELETE_LAYER'DelL':
396 if (!PostponeMessageAndFinishManipulator()) {
397 Layer *removed_layer;
398 int32 removed_layer_id;
399 message->FindInt32("layer_id",&removed_layer_id);
400 message->FindPointer("layer_pointer",(void**)&removed_layer);
401 if (the_image->RemoveLayer(removed_layer,removed_layer_id) == TRUE1) {
402 ActiveLayerChanged();
403 AddChange();
404 }
405 LayerWindow::ActiveWindowChanged(Window(),the_image->LayerList(),the_image->ReturnThumbnailImage());
406 Invalidate();
407 }
408 break;
409
410 // This comes from layer's view and tells us to merge that layer with the one
411 // that is on top of it. If no layer is on top of it nothing should be done.
412 // The merged layer will then be made visible if necessary. But the active layer
413 // stays the same unless one of the merged layers was the active layer
414 case HS_MERGE_WITH_UPPER_LAYER'MrWu':
415 if (!PostponeMessageAndFinishManipulator()) {
416 Layer *merged_layer;
417 int32 merged_layer_id;
418 message->FindInt32("layer_id",&merged_layer_id);
419 message->FindPointer("layer_pointer",(void**)&merged_layer);
420 if (the_image->MergeLayers(merged_layer,merged_layer_id,TRUE1) == TRUE1) {
421 LayerWindow::ActiveWindowChanged(Window(),the_image->LayerList(),the_image->ReturnThumbnailImage());
422 Invalidate();
423 AddChange();
424 }
425 }
426 break;
427
428 // This comes from layer's view and tells us to merge that layer with the one
429 // that is under it. If no layer is under it nothing should be done.
430 // The merged layer will then be made visible if necessary. But the active layer
431 // stays the same unless one of the merged layers was the active layer
432 case HS_MERGE_WITH_LOWER_LAYER'MrWl':
433 if (!PostponeMessageAndFinishManipulator()) {
434 Layer *merged_layer;
435 int32 merged_layer_id;
436 message->FindInt32("layer_id",&merged_layer_id);
437 message->FindPointer("layer_pointer",(void**)&merged_layer);
438 if (the_image->MergeLayers(merged_layer,merged_layer_id,FALSE0) == TRUE1) {
439 LayerWindow::ActiveWindowChanged(Window(),the_image->LayerList(),the_image->ReturnThumbnailImage());
440 Invalidate();
441 AddChange();
442 }
443 }
444 break;
445
446 case HS_ADD_LAYER_FRONT'AdLf':
447 case HS_ADD_LAYER_BEHIND'AdLb':
448 {
449 Layer *other_layer;
450 message->FindPointer("layer_pointer",(void**)&other_layer);
451 try {
452 the_image->AddLayer(NULL__null,other_layer,message->what == HS_ADD_LAYER_FRONT'AdLf');
453 LayerWindow::ActiveWindowChanged(Window(),the_image->LayerList(),the_image->ReturnThumbnailImage());
454 ActiveLayerChanged();
455 Invalidate();
456 AddChange();
457 }
458 catch (std::bad_alloc){
459 ShowAlert(CANNOT_ADD_LAYER_ALERT);
460 }
461 break;
462 }
463
464
465 case HS_DUPLICATE_LAYER'DupL':
466 {
467 Layer *duplicated_layer;
468 int32 duplicated_layer_id;
469 message->FindInt32("layer_id",&duplicated_layer_id);
470 message->FindPointer("layer_pointer",(void**)&duplicated_layer);
471 try {
472 if (the_image->DuplicateLayer(duplicated_layer,duplicated_layer_id) == TRUE1) {
473 LayerWindow::ActiveWindowChanged(Window(),the_image->LayerList(),the_image->ReturnThumbnailImage());
474 ActiveLayerChanged();
475 Invalidate();
476 AddChange();
477 }
478 }
479 catch (std::bad_alloc e) {
480 ShowAlert(CANNOT_ADD_LAYER_ALERT);
481 }
482 break;
483 }
484 // This comes when a layer's miniature image is dragged on the view.
485 // We should copy the image from that layer and add it to this image.
486 case HS_LAYER_DRAGGED'Drla':
487 if (message->WasDropped()) {
488 BBitmap *to_be_copied;
489 if (message->FindPointer("layer_bitmap",(void**)&to_be_copied) == B_OK((int)0)) {
490 try {
491 BBitmap *new_bitmap = new BBitmap(to_be_copied);
492 if (the_image->AddLayer(new_bitmap,NULL__null,TRUE1) != NULL__null) {
493 Invalidate();
494 LayerWindow::ActiveWindowChanged(Window(),the_image->LayerList(),the_image->ReturnThumbnailImage());
495 AddChange();
496 }
497 }
498 catch (std::bad_alloc) {
499 ShowAlert(CANNOT_ADD_LAYER_ALERT);
500 }
501 }
502 }
503 break;
504
505 // this comes from menubar->"Edit"->"Invert Selection", we should then invert the
506 // selected area and redisplay it
507 case HS_INVERT_SELECTION'InSl':
508 if (!fManipulator) {
509 selection->Invert();
510 if (!(*undo_queue->ReturnSelectionData() == *selection->ReturnSelectionData())) {
511 UndoEvent *new_event = undo_queue->AddUndoEvent(B_TRANSLATE("Invert selection")BLocaleRoster::Default()->GetCatalog()->GetString(("Invert selection"
), "ImageView")
,the_image->ReturnThumbnailImage());
512 if (new_event != NULL__null) {
513 new_event->SetSelectionData(undo_queue->ReturnSelectionData());
514 undo_queue->SetSelectionData(selection->ReturnSelectionData());
515 }
516 }
517
518 Invalidate();
519 }
520 break;
521
522 // this comes from menubar->"Edit"->"Clear Selection", we should then clear the
523 // selection and redisplay the image
524 case HS_CLEAR_SELECTION'ClSl':
525 if (!fManipulator) {
526 selection->Clear();
527 if (!(*undo_queue->ReturnSelectionData() == *selection->ReturnSelectionData())) {
528 UndoEvent *new_event = undo_queue->AddUndoEvent(B_TRANSLATE("Clear selection")BLocaleRoster::Default()->GetCatalog()->GetString(("Clear selection"
), "ImageView")
,the_image->ReturnThumbnailImage());
529 if (new_event != NULL__null) {
530 new_event->SetSelectionData(undo_queue->ReturnSelectionData());
531 undo_queue->SetSelectionData(selection->ReturnSelectionData());
532 }
533 }
534 Invalidate();
535 }
536 break;
537
538 case HS_GROW_SELECTION'GrSl':
539 if (!fManipulator) {
540 selection->Dilatate();
541 if (!(*undo_queue->ReturnSelectionData() == *selection->ReturnSelectionData())) {
542 UndoEvent *new_event = undo_queue->AddUndoEvent(B_TRANSLATE("Grow selection")BLocaleRoster::Default()->GetCatalog()->GetString(("Grow selection"
), "ImageView")
,the_image->ReturnThumbnailImage());
543 if (new_event != NULL__null) {
544 new_event->SetSelectionData(undo_queue->ReturnSelectionData());
545 undo_queue->SetSelectionData(selection->ReturnSelectionData());
546 }
547 }
548 Invalidate();
549 }
550 break;
551
552 case HS_SHRINK_SELECTION'SrSL':
553 if (!fManipulator) {
554 selection->Erode();
555 if (!(*undo_queue->ReturnSelectionData() == *selection->ReturnSelectionData())) {
556 UndoEvent *new_event = undo_queue->AddUndoEvent(B_TRANSLATE("Shrink selection")BLocaleRoster::Default()->GetCatalog()->GetString(("Shrink selection"
), "ImageView")
,the_image->ReturnThumbnailImage());
557 if (new_event != NULL__null) {
558 new_event->SetSelectionData(undo_queue->ReturnSelectionData());
559 undo_queue->SetSelectionData(selection->ReturnSelectionData());
560 }
561 }
562 Invalidate();
563 }
564 break;
565
566 // this comes from menubar->"Canvas"->"Clear Canvas", we should then clear all the
567 // layers and recalculate the composite picture and redisplay
568 case HS_CLEAR_CANVAS'Clcv':
569 if (acquire_sem_etc(action_semaphore,1,B_TIMEOUT,0) == B_OK((int)0)) {
570 rgb_color c = ((PaintApplication*)be_app)->Color(FALSE0);
571 if (the_image->ClearLayers(c) == TRUE1) {
572 Invalidate();
573 AddChange();
574 }
575 release_sem(action_semaphore);
576 }
577 break;
578
579 // This comes from menubar->"Layer"->"Clear Layer". We should clear the layer, recalculate
580 // the composite picture and redisplay the image.
581 case HS_CLEAR_LAYER'Clla':
582 if (acquire_sem_etc(action_semaphore,1,B_TIMEOUT,0) == B_OK((int)0)) {
583 rgb_color c = ((PaintApplication*)be_app)->Color(FALSE0);
584 if (the_image->ClearCurrentLayer(c) == TRUE1) {
585 Invalidate();
586 AddChange();
587 }
588 release_sem(action_semaphore);
589 }
590 break;
591
592 case B_COPY:
593 case B_CUT:
594 {
595 if (!fManipulator) {
596 int32 copied_layers;
597 if (message->FindInt32("layers",&copied_layers) == B_OK((int)0)) {
598 DoCopyOrCut(copied_layers,message->what == B_CUT);
599 }
600 }
601 break;
602 }
603
604 case B_PASTE:
605 if (!fManipulator)
606 DoPaste();
607 break;
608
609 case HS_UNDO'unDo':
610 if (!fManipulator)
611 Undo();
612 else
613 PostponeMessageAndFinishManipulator();
614
615 break;
616
617 case HS_REDO'reDo':
618 if (!fManipulator)
619 Redo();
620 else
621 PostponeMessageAndFinishManipulator();
622
623 break;
624
625 case HS_START_MANIPULATOR'Stmi': {
626 if (PostponeMessageAndFinishManipulator())
627 break;
628
629 if (message->FindInt32("manipulator_type", &manip_type) != B_OK((int)0))
630 break;
631
632 message->FindInt32("image_id", &add_on_id);
633 try {
634 ManipulatorServer* server = ManipulatorServer::Instance();
635 if (!server)
636 throw std::bad_alloc();
637
638 fManipulator = server->ManipulatorFor((manipulator_type)manip_type,
639 add_on_id);
640 status_t err = message->FindInt32("layers",&manipulated_layers);
641 if ((err == B_OK((int)0)) && (fManipulator != NULL__null)) {
642 GUIManipulator *gui_manipulator = cast_as(fManipulator,GUIManipulator)(dynamic_cast<GUIManipulator*>(fManipulator));
643 ImageAdapter *adapter = cast_as(fManipulator,ImageAdapter)(dynamic_cast<ImageAdapter*>(fManipulator));
644 if (adapter != NULL__null)
645 adapter->SetImage(the_image);
646
647 SetCursor();
648 // If the manipulator is not a GUIManipulator we put it to finish its
649 // business. If the manipulator is actually a GUIManipulator we give the
650 // correct preview-bitmap to the manipulator and set up its GUI.
651 if (gui_manipulator == NULL__null) {
652 start_thread(MANIPULATOR_FINISHER_THREAD);
653 } else {
654 // The order is important, first set the preview-bitmap
655 // and only after that open the GUI for the manipulator.
656 if (manipulated_layers == HS_MANIPULATE_CURRENT_LAYER'Mncl') {
657 gui_manipulator->SetPreviewBitmap(the_image->ReturnActiveBitmap());
658 }
659 else {
660 gui_manipulator->SetPreviewBitmap(the_image->ReturnRenderedImage());
661 }
662 //StatusBarGUIManipulator *status_bar_gui_manipulator = cast_as(gui_manipulator,StatusBarGUIManipulator);
663 WindowGUIManipulator *window_gui_manipulator = cast_as(gui_manipulator,WindowGUIManipulator)(dynamic_cast<WindowGUIManipulator*>(gui_manipulator));
664
665 ((PaintWindow*)Window())->SetHelpString(gui_manipulator->ReturnHelpString(),HS_TOOL_HELP_MESSAGE'TolM');
666 if (window_gui_manipulator != NULL__null) {
667 char window_name[256];
668 sprintf(window_name, "%s: %s",
669 ReturnProjectName(),
670 window_gui_manipulator->ReturnName());
671
672 BRect frame(100, 100, 200, 200);
673 if (SettingsServer* server = SettingsServer::Instance()) {
674 BMessage settings;
675 server->GetApplicationSettings(&settings);
676 settings.FindRect(skAddOnWindowFrame, &frame);
677 }
678
679 frame = FitRectToScreen(frame);
680 manipulator_window = new ManipulatorWindow(frame,
681 window_gui_manipulator->MakeConfigurationView(this),
682 window_name, Window(), this);
683 }
684
685 cursor_mode = MANIPULATOR_CURSOR_MODE;
686 SetCursor();
687
688 // The manipulator might have updated the bitmap and
689 // might also want to draw some GUI. We send a
690 // HS_MANIPULATOR_ADJUSTING_FINISHED to this view
691 // to get the manipulator update the correct bitmap.
692 if (BWindow* window = Window())
693 window->PostMessage(HS_MANIPULATOR_ADJUSTING_FINISHED'Mafi', this);
694 }
695 }
696 }
697 catch (std::bad_alloc) {
698 ShowAlert(CANNOT_START_MANIPULATOR_ALERT);
699 delete fManipulator;
700 fManipulator = NULL__null;
701 }
702 } break;
703
704 case HS_MANIPULATOR_FINISHED'MnFi': {
705 // First find whether the manipulator finished cancelling or OKing.
706 bool finish_status = false;
707 message->FindBool("status", &finish_status);
708 if (GUIManipulator* guiManipulator =
709 dynamic_cast<GUIManipulator*> (fManipulator)) {
710 if (WindowGUIManipulator* windowGuiManipulator =
711 dynamic_cast<WindowGUIManipulator*> (guiManipulator)) {
712 (void)windowGuiManipulator; // suppress warning
713 if (manipulator_window) {
714 manipulator_window->Lock();
715 manipulator_window->Quit();
716 manipulator_window = NULL__null;
717 }
718 }
719
720 if (finish_status) {
721 start_thread(MANIPULATOR_FINISHER_THREAD);
722 AddChange();
723 } else {
724 // The manipulator should be instructed to restore
725 // whatever changes it has made and should be quit then.
726 guiManipulator->Reset(selection);
727 ((PaintWindow*)Window())->ReturnStatusView()->DisplayToolsAndColors();
728 delete fManipulator;
729 fManipulator = NULL__null;
730 the_image->Render();
731 manipulated_layers = HS_MANIPULATE_NO_LAYER'Mnnl';
732 Invalidate();
733 }
734 }
735 cursor_mode = NORMAL_CURSOR_MODE;
736 SetCursor();
737 // changes the help-string
738 ToolManager::Instance().NotifyViewEvent(this, TOOL_ACTIVATED);
739 } break;
740
741 case HS_MANIPULATOR_ADJUSTING_STARTED'Mast': {
742 case HS_MANIPULATOR_ADJUSTING_FINISHED'Mafi':
743 continue_manipulator_updating = false;
744 if (message->what == HS_MANIPULATOR_ADJUSTING_STARTED'Mast')
745 continue_manipulator_updating = true;
746 start_thread(MANIPULATOR_UPDATER_THREAD);
747 } break;
748
749 default: {
750 BView::MessageReceived(message);
751 } break;
752 }
753}
754
755void ImageView::MouseDown(BPoint view_point)
756{
757 // From this function we call view-manipulator's MouseDown-function.
758 // It will be the correct function depending on what manipulator is
759 // in use currently.
760
761 // here read the modifier keys
762 BMessage *message = Window()->CurrentMessage();
763 //int32 modifiers = message->FindInt32("modifiers");
764
765 // here we read which mousebutton was pressed
766 uint32 buttons = message->FindInt32("buttons");
767
768 // The window activation should probably be made a user preference.
769 // Also backgroundview should activate the window if the user wants it.
770// // activate the window if necessary
771 if (Window()->IsActive() == FALSE0)
772 Window()->Activate(TRUE1);
773
774 // If the view is not focus view, make it the focus view
775 if (!IsFocus())
776 MakeFocus(true);
777
778 // try to acquire the mouse_mutex
779 if (acquire_sem_etc(mouse_mutex,1,B_TIMEOUT,0) == B_OK((int)0)) {
780 GUIManipulator *gui_manipulator = cast_as(fManipulator,GUIManipulator)(dynamic_cast<GUIManipulator*>(fManipulator));
781 if (gui_manipulator != NULL__null) {
782 start_thread(MANIPULATOR_MOUSE_THREAD);
783 }
784 else if (fManipulator == NULL__null){
785 if (buttons & B_SECONDARY_MOUSE_BUTTON) {
786 BMenuItem *item = ToolManager::Instance().ToolPopUpMenu()->Go(ConvertToScreen(view_point));
787 if (item != NULL__null) {
788 ToolManager::Instance().ChangeTool(item->Command());
789 SetCursor(); // If the tool changes, the cursor should change too.
790 }
791 release_sem(mouse_mutex);
792 }
793 else if (buttons & B_TERTIARY_MOUSE_BUTTON) {
794 BPoint point,prev_point;
795 uint32 buttons;
796 GetMouse(&point,&buttons);
797 while (buttons) {
798 prev_point = point;
799 GetMouse(&point,&buttons);
800 ScrollBy(point.x-prev_point.x,point.y-prev_point.y);
801 snooze(25 * 1000);
802 }
803 release_sem(mouse_mutex);
804 }
805 else {
806 start_thread(PAINTER_THREAD);
807 }
808 }
809 else {
810 release_sem(mouse_mutex);
811 }
812 }
813 else {
814 if (fManipulator == NULL__null) {
815 BPoint bitmap_point;
816 uint32 buttons;
817 getCoords(&bitmap_point,&buttons,&view_point);
818 int32 clicks;
819 if (Window()->CurrentMessage()->FindInt32("clicks",&clicks) == B_OK((int)0))
820 ToolManager::Instance().MouseDown(this,view_point,bitmap_point,buttons,clicks);
821 }
822 }
823}
824
825void ImageView::MouseMoved(BPoint where, uint32 transit, const BMessage *message)
826{
827 // here we will display the coordinates
828 // we will also change the cursor whenever mouse enters the view or leaves it
829
830 // If we have a message being dragged over us, do not change the cursor
831 if (message == NULL__null) {
832 if ((transit == B_ENTERED_VIEW) || (transit == B_EXITED_VIEW)) {
833 if (fManipulator == NULL__null) {
834 if (transit == B_ENTERED_VIEW)
835 ToolManager::Instance().NotifyViewEvent(this,CURSOR_ENTERED_VIEW);
836 else
837 ToolManager::Instance().NotifyViewEvent(this,CURSOR_EXITED_VIEW);
838 }
839 SetCursor();
840 }
841 }
842 else {
843 if (message->what == HS_LAYER_DRAGGED'Drla') {
844 BMessage help_view_message;
845
846 if (transit == B_ENTERED_VIEW) {
847 help_view_message.what = HS_TEMPORARY_HELP_MESSAGE'ThlM';
848 help_view_message.AddString("message",
849 B_TRANSLATE("Drop layer to copy it to this image.")BLocaleRoster::Default()->GetCatalog()->GetString(("Drop layer to copy it to this image."
), "ImageView")
);
850 Window()->PostMessage(&help_view_message,Window());
851 }
852 else if (transit == B_EXITED_VIEW) {
853 help_view_message.what = HS_TOOL_HELP_MESSAGE'TolM';
854 Window()->PostMessage(&help_view_message,Window());
855 }
856 }
857 }
858 if (transit != B_EXITED_VIEW) {
859 // change the point according to magnifying scale
860 where.x = floor(where.x / getMagScale());
861 where.y = floor(where.y / getMagScale());
862
863 // round the point to grid units
864 where = roundToGrid(where);
865
866 // Here we set the window to display coordinates.
867 ((PaintWindow *)Window())->DisplayCoordinates(where,reference_point,use_reference_point);
868 }
869 else {
870 where.x = the_image->Width();
871 where.y = the_image->Height();
872
873 ((PaintWindow *)Window())->DisplayCoordinates(where,BPoint(0,0),false);
874 }
875}
876
877
878bool
879ImageView::Quit()
880{
881 int32 mode = B_CONTROL_ON;
882 if (SettingsServer* server = SettingsServer::Instance()) {
883 BMessage settings;
884 server->GetApplicationSettings(&settings);
885 settings.FindInt32(skQuitConfirmMode, &mode);
886 }
887
888 if (mode == B_CONTROL_ON) {
889 if (project_changed > 0) {
890 BString text;
891 static BStringFormat format(B_TRANSLATE("{0, plural,"BLocaleRoster::Default()->GetCatalog()->GetString(("{0, plural,"
"one{%project_name%: You have made a change since the last time "
"the project was saved.\nDo you want to save the change?}" "other{%project_name%: You have made # changes since the last time "
"the project was saved.\nDo you want to save the changes?}}"
), "ImageView")
892 "one{%project_name%: You have made a change since the last time "BLocaleRoster::Default()->GetCatalog()->GetString(("{0, plural,"
"one{%project_name%: You have made a change since the last time "
"the project was saved.\nDo you want to save the change?}" "other{%project_name%: You have made # changes since the last time "
"the project was saved.\nDo you want to save the changes?}}"
), "ImageView")
893 "the project was saved.\nDo you want to save the change?}"BLocaleRoster::Default()->GetCatalog()->GetString(("{0, plural,"
"one{%project_name%: You have made a change since the last time "
"the project was saved.\nDo you want to save the change?}" "other{%project_name%: You have made # changes since the last time "
"the project was saved.\nDo you want to save the changes?}}"
), "ImageView")
894 "other{%project_name%: You have made # changes since the last time "BLocaleRoster::Default()->GetCatalog()->GetString(("{0, plural,"
"one{%project_name%: You have made a change since the last time "
"the project was saved.\nDo you want to save the change?}" "other{%project_name%: You have made # changes since the last time "
"the project was saved.\nDo you want to save the changes?}}"
), "ImageView")
895 "the project was saved.\nDo you want to save the changes?}}")BLocaleRoster::Default()->GetCatalog()->GetString(("{0, plural,"
"one{%project_name%: You have made a change since the last time "
"the project was saved.\nDo you want to save the change?}" "other{%project_name%: You have made # changes since the last time "
"the project was saved.\nDo you want to save the changes?}}"
), "ImageView")
);
896 format.Format(text, project_changed);
897 text.ReplaceFirst("%project_name%", project_name);
898
899 BAlert* alert = new BAlert(B_TRANSLATE("Unsaved changes!")BLocaleRoster::Default()->GetCatalog()->GetString(("Unsaved changes!"
), "ImageView")
, text,
900 B_TRANSLATE("Cancel")BLocaleRoster::Default()->GetCatalog()->GetString(("Cancel"
), "ImageView")
,
901 B_TRANSLATE("Don't save")BLocaleRoster::Default()->GetCatalog()->GetString(("Don't save"
), "ImageView")
,
902 B_TRANSLATE("Save")BLocaleRoster::Default()->GetCatalog()->GetString(("Save"
), "ImageView")
, B_WIDTH_AS_USUAL,
903 B_OFFSET_SPACING);
904
905 int32 value = alert->Go();
906 if (value == 0) // Cancel
907 return false;
908
909 if (value == 1) // Don't save
910 return true;
911
912 if (value == 2) { // Save
913 BMessage* message = new BMessage(HS_SAVE_PROJECT'Sapr');
914 message->SetInt32("TryAgain", 1);
915 message->SetInt32("quitAll", ((PaintApplication*)be_app)->ShuttingDown());
916 Window()->PostMessage(message, Window());
917 return false;
918 }
919
920 return true;
921 }
922 }
923
924 if (acquire_sem_etc(action_semaphore, 1, B_TIMEOUT, 0) != B_OK((int)0))
925 return false;
926
927 return true;
928}
929
930
931status_t ImageView::Freeze()
932{
933 if (acquire_sem(mouse_mutex) == B_OK((int)0)) {
934 if (acquire_sem(action_semaphore) == B_OK((int)0))
935 return B_OK((int)0);
936 release_sem(mouse_mutex);
937 }
938
939 // This must restore whatever the manipulator had previewed. Here we
940 // also acquire mouse_mutex and action_semaphore semaphores to protect
941 // the image from any further changes. These restorations must of course
942 // be done only after we have acquired the desired semaphores.
943 GUIManipulator *gui_manipulator = cast_as(fManipulator,GUIManipulator)(dynamic_cast<GUIManipulator*>(fManipulator));
944 if (gui_manipulator != NULL__null) {
945 gui_manipulator->Reset(selection);
946 the_image->Render();
947 }
948 return B_ERROR(-1);
949}
950
951
952status_t ImageView::UnFreeze()
953{
954 // Release the semaphores. We could also tell the manipulator to preview
955 // with the settings that it last had, but we will not do it yet.
956 release_sem(mouse_mutex);
957 release_sem(action_semaphore);
958
959 return B_OK((int)0);
960}
961
962
963void ImageView::getCoords(BPoint *bitmap_point, uint32 *buttons,BPoint *view_point)
964{
965 // this function converts the points using magnifying_scale
966 // to correspond to real points in buffer
967 // we also take any possible grids into account
968 BPoint point;
969 GetMouse(&point,buttons);
970 (*bitmap_point).x = floor(point.x / getMagScale());
971 (*bitmap_point).y = floor(point.y / getMagScale());
972 // and finally we round the point to grid units
973 (*bitmap_point) = roundToGrid(*bitmap_point);
974
975 // Also if the view_point is not NULL put the point into it
976 if (view_point != NULL__null) {
977 view_point->x = (*bitmap_point).x*getMagScale();
978 view_point->y = (*bitmap_point).y*getMagScale();
979 }
980}
981
982
983void ImageView::setGrid(BPoint origin, int32 unit)
984{
985 // here we give the grid-variables new values
986 grid_unit = (unit >= 1 ? unit : 1);
987 grid_origin = origin;
988}
989
990
991void ImageView::setMagScale(float scale)
992{
993 // this will be used to see if the scale was actually changed
994 // and is there a need to draw the view
995 float prev_scale = magnify_scale;
996
997 if (scale <= HS_MAX_MAG_SCALE16.0) {
998 if (scale >= HS_MIN_MAG_SCALE0.1) {
999 magnify_scale = scale;
1000 }
1001 else
1002 magnify_scale = HS_MIN_MAG_SCALE0.1;
1003 }
1004 else
1005 magnify_scale = HS_MAX_MAG_SCALE16.0;
1006
1007 selection->ChangeMagnifyingScale(magnify_scale);
1008
1009
1010 // if the scale was changed and the view is on screen draw the view
1011 // also update the scrollbars
1012 if ((prev_scale != magnify_scale) && !IsHidden()) {
1013 adjustSize();
1014 adjustPosition();
1015 adjustScrollBars();
1016 // we should here also draw the view
1017 Invalidate(Bounds());
1018 }
1019
1020 ((PaintWindow*)Window())->displayMag(magnify_scale);
1021}
1022
1023
1024BPoint ImageView::roundToGrid(BPoint point)
1025{
1026 // here we round the coordinates of point to nearest grid-unit
1027 // this version does not take the grid_origin into account
1028 point.x = point.x - (((int)point.x) % grid_unit);
1029 point.y = point.y - (((int)point.y) % grid_unit);
1030
1031 return point;
1032}
1033
1034
1035BRect ImageView::convertBitmapRectToView(BRect rect)
1036{
1037 // bitmap might also have some offset that should be taken
1038 // into account
1039
1040 // this only takes the mag_scale into account
1041 float scale = getMagScale();
1042
1043 if (scale > 1.0) {
1044 rect.left = rect.left * scale;
1045 rect.top = rect.top * scale;
1046 rect.right = rect.right * scale + scale - 1;
1047 rect.bottom = rect.bottom * scale + scale - 1;
1048 }
1049 else if (scale < 1.0) {
1050 rect.left = floor(rect.left * scale);
1051 rect.top = floor(rect.top * scale);
1052 rect.right = floor(rect.right * scale);
1053 rect.bottom = floor(rect.bottom * scale);
1054 }
1055
1056
1057 // here convert the rect to use offset
1058 //rect.OffsetBy(((Layer*)layer_list->ItemAt(current_layer_index))->Offset().LeftTop());
1059
1060 return rect;
1061}
1062
1063BRect ImageView::convertViewRectToBitmap(BRect rect)
1064{
1065 // It is not possible to convert a view rectangle into
1066 // a bitmap rectangle perfectly if magnifying scale is
1067 // not 1. The rectangle will be converted to the smallest
1068 // possible rectangle. Half pixels will be converted so that the
1069 // rectangle is extended.
1070 float scale = getMagScale();
1071
1072 BRect bitmap_rect;
1073
1074 bitmap_rect.left = floor(rect.left / scale);
1075 bitmap_rect.top = floor(rect.top / scale);
1076 bitmap_rect.right = ceil(rect.right / scale);
1077 bitmap_rect.bottom = ceil(rect.bottom / scale);
1078
1079 bitmap_rect = bitmap_rect & the_image->ReturnRenderedImage()->Bounds();
1080 return bitmap_rect;
1081}
1082
1083
1084
1085void ImageView::ActiveLayerChanged()
1086{
1087 GUIManipulator *gui_manipulator = cast_as(fManipulator,GUIManipulator)(dynamic_cast<GUIManipulator*>(fManipulator));
1088
1089 if ((gui_manipulator != NULL__null) && (manipulated_layers == HS_MANIPULATE_CURRENT_LAYER'Mncl')) {
1090 gui_manipulator->Reset(selection);
1091 gui_manipulator->SetPreviewBitmap(the_image->ReturnActiveBitmap());
1092
1093 // We should also tell the manipulator to recalculate its preview.
1094 // This can be best achieved by sending a HS_MANIPULATOR_ADJUSTING_FINISHED
1095 // message to this view.
1096 Window()->PostMessage(HS_MANIPULATOR_ADJUSTING_FINISHED'Mafi',this);
1097 }
1098}
1099
1100void ImageView::adjustSize()
1101{
1102 if (LockLooper() == TRUE1) {
1103 // resize the view to proper size
1104 BRect bg_bounds = Parent()->Bounds();
1105 BRect vertBounds = ScrollBar(B_VERTICAL)->Bounds();
1106 BRect horzBounds = ScrollBar(B_HORIZONTAL)->Bounds();
1107 ResizeTo(
1108 min_c(((bg_bounds.Width() - vertBounds.Width() - 2)>(getMagScale
() * the_image->Width() - 1)?(getMagScale() * the_image->
Width() - 1):(bg_bounds.Width() - vertBounds.Width() - 2))
1109 bg_bounds.Width() - vertBounds.Width() - 2,((bg_bounds.Width() - vertBounds.Width() - 2)>(getMagScale
() * the_image->Width() - 1)?(getMagScale() * the_image->
Width() - 1):(bg_bounds.Width() - vertBounds.Width() - 2))
1110 getMagScale() * the_image->Width() - 1)((bg_bounds.Width() - vertBounds.Width() - 2)>(getMagScale
() * the_image->Width() - 1)?(getMagScale() * the_image->
Width() - 1):(bg_bounds.Width() - vertBounds.Width() - 2))
,
1111 min_c(((bg_bounds.Height() - horzBounds.Height() - 2)>(getMagScale
() * the_image->Height() - 1)?(getMagScale() * the_image->
Height() - 1):(bg_bounds.Height() - horzBounds.Height() - 2))
1112 bg_bounds.Height() - horzBounds.Height() - 2,((bg_bounds.Height() - horzBounds.Height() - 2)>(getMagScale
() * the_image->Height() - 1)?(getMagScale() * the_image->
Height() - 1):(bg_bounds.Height() - horzBounds.Height() - 2))
1113 getMagScale() * the_image->Height() - 1)((bg_bounds.Height() - horzBounds.Height() - 2)>(getMagScale
() * the_image->Height() - 1)?(getMagScale() * the_image->
Height() - 1):(bg_bounds.Height() - horzBounds.Height() - 2))
);
1114 UnlockLooper();
1115 }
1116}
1117
1118void ImageView::adjustPosition()
1119{
1120 if (LockLooper() == TRUE1) {
1121 BPoint top_left;
1122 BRect bg_bounds = Parent()->Bounds();
1123 BRect vertBounds = ScrollBar(B_VERTICAL)->Bounds();
1124 BRect horzBounds = ScrollBar(B_HORIZONTAL)->Bounds();
1125 top_left.x = (bg_bounds.Width() - vertBounds.Width() -
1126 getMagScale()*the_image->Width()) / 2;
1127 top_left.y = (bg_bounds.Height() - horzBounds.Height() -
1128 getMagScale()*the_image->Height()) / 2;
1129
1130 top_left.x = max_c(0,(int32)top_left.x)((0)>((int32)top_left.x)?(0):((int32)top_left.x));
1131 top_left.y = max_c(0,(int32)top_left.y)((0)>((int32)top_left.y)?(0):((int32)top_left.y));
1132
1133 if (top_left != Frame().LeftTop())
1134 MoveTo(top_left);
1135
1136 UnlockLooper();
1137 }
1138}
1139
1140
1141void ImageView::adjustScrollBars()
1142{
1143 if (LockLooper() == TRUE1) {
1144 // set the horizontal bar
1145 if ((getMagScale()*the_image->Width() - (Frame().Width() + 1)) <= 0) {
1146 ScrollBar(B_HORIZONTAL)->SetRange(0,0);
1147 ScrollBar(B_HORIZONTAL)->SetProportion(1);
1148 }
1149 else {
1150 ScrollBar(B_HORIZONTAL)->SetRange(0,getMagScale()*the_image->Width() - (Frame().Width() + 1));
1151 ScrollBar(B_HORIZONTAL)->SetProportion((Frame().Width() + 1)/(getMagScale()*the_image->Width()));
1152 }
1153
1154 // set the vertical bar
1155 if ((getMagScale()*the_image->Height() - (Frame().Height() + 1)) <= 0) {
1156 ScrollBar(B_VERTICAL)->SetRange(0,0);
1157 ScrollBar(B_VERTICAL)->SetProportion(1);
1158 }
1159 else {
1160 ScrollBar(B_VERTICAL)->SetRange(0,getMagScale()*the_image->Height() - (Frame().Height() + 1));
1161 ScrollBar(B_VERTICAL)->SetProportion((Frame().Height() + 1)/(getMagScale()*the_image->Height()));
1162 }
1163
1164 UnlockLooper();
1165 }
1166}
1167
1168Selection* ImageView::GetSelection()
1169{
1170 return selection;
1171}
1172
1173void ImageView::start_thread(int32 thread_type)
1174{
1175 thread_id a_thread = spawn_thread(enter_thread,"ImageView thread",B_NORMAL_PRIORITY10,this);
1176 if (a_thread >= 0) {
1177 resume_thread(a_thread);
1178 send_data(a_thread,thread_type,NULL__null,0);
1179 }
1180}
1181
1182int32 ImageView::enter_thread(void *data)
1183{
1184 ImageView *this_pointer = (ImageView*)data;
1185 int32 thread_type = receive_data(NULL__null,NULL__null,0);
1186 int32 err = B_OK((int)0);
1187
1188 if (thread_type == MANIPULATOR_FINISHER_THREAD) {
1
Assuming 'thread_type' is equal to MANIPULATOR_FINISHER_THREAD
2
Taking true branch
1189 acquire_sem(this_pointer->action_semaphore);
1190 }
1191 else if (acquire_sem_etc(this_pointer->action_semaphore,1,B_TIMEOUT,0) != B_OK((int)0)) {
1192 if ((thread_type == PAINTER_THREAD) || (thread_type == MANIPULATOR_MOUSE_THREAD))
1193 release_sem(this_pointer->mouse_mutex);
1194 return B_ERROR(-1);
1195 }
1196
1197 if (this_pointer
2.1
'this_pointer' is non-null
) {
3
Taking true branch
1198 switch (thread_type) {
4
Control jumps to 'case MANIPULATOR_FINISHER_THREAD:' at line 1208
1199 case PAINTER_THREAD:
1200 err = this_pointer->PaintToolThread();
1201 break;
1202 case MANIPULATOR_MOUSE_THREAD:
1203 err = this_pointer->ManipulatorMouseTrackerThread();
1204 break;
1205 case MANIPULATOR_UPDATER_THREAD:
1206 err = this_pointer->GUIManipulatorUpdaterThread();
1207 break;
1208 case MANIPULATOR_FINISHER_THREAD:
1209 err = this_pointer->ManipulatorFinisherThread();
5
Calling 'ImageView::ManipulatorFinisherThread'
1210 break;
1211 default:
1212 break;
1213 }
1214 }
1215
1216 // When the work has been done we may once again permit
1217 // the reading of the mouse.
1218 if ((thread_type == PAINTER_THREAD) || (thread_type == MANIPULATOR_MOUSE_THREAD))
1219 release_sem(this_pointer->mouse_mutex);
1220
1221 release_sem(this_pointer->action_semaphore);
1222
1223 this_pointer->SetReferencePoint(BPoint(0,0),FALSE0);
1224
1225 return err;
1226}
1227
1228
1229int32 ImageView::PaintToolThread()
1230{
1231 uint32 buttons;
1232 BPoint point;
1233 BPoint view_point;
1234 if (LockLooper() == TRUE1) {
1235 getCoords(&point,&buttons,&view_point);
1236 UnlockLooper();
1237 } else
1238 return B_ERROR(-1);
1239
1240 SetReferencePoint(point,TRUE1);
1241
1242 int32 tool_type = ToolManager::Instance().ReturnActiveToolType();
1243
1244 if (modifiers() & B_COMMAND_KEY) {
1245 tool_type = COLOR_SELECTOR_TOOL;
1246 }
1247
1248 if (tool_type != TEXT_TOOL) {
1249 if (tool_type != NO_TOOL) {
1250 // When this function returns the tool has finished. This function
1251 // might not return even if the user releases the mouse-button (in
1252 // which case for example a double-click might make it return).
1253 ToolScript* script = ToolManager::Instance().StartTool(this, buttons, point,
1254 view_point, tool_type);
1255 if (script && tool_type != SELECTOR_TOOL) {
1256 const DrawingTool* tool = ToolManager::Instance().ReturnTool(tool_type);
1257 UndoEvent *new_event = undo_queue->AddUndoEvent(tool->Name(),
1258 the_image->ReturnThumbnailImage());
1259 BList *layer_list = the_image->LayerList();
1260 if (new_event != NULL__null) {
1261 for (int32 i=0;i<layer_list->CountItems();i++) {
1262 Layer *layer = (Layer*)layer_list->ItemAt(i);
1263 UndoAction *new_action;
1264 if (layer->IsActive() == FALSE0)
1265 new_action = new UndoAction(layer->Id());
1266 else
1267 new_action = new UndoAction(layer->Id(), script,ToolManager::Instance().LastUpdatedRect(this));
1268
1269 new_event->AddAction(new_action);
1270 new_action->StoreUndo(layer->Bitmap());
1271 }
1272 if ((new_event != NULL__null) && (new_event->IsEmpty() == TRUE1)) {
1273 undo_queue->RemoveEvent(new_event);
1274 delete new_event;
1275 }
1276 AddChange();
1277 }
1278 else {
1279 delete script;
1280 ToolManager::Instance().LastUpdatedRect(this);
1281 }
1282 }
1283 else if (tool_type == SELECTOR_TOOL) {
1284 // Add selection-change to the undo-queue.
1285 if (!(*undo_queue->ReturnSelectionData() == *selection->ReturnSelectionData())) {
1286 const DrawingTool *used_tool = ToolManager::Instance().ReturnTool(tool_type);
1287 UndoEvent *new_event = undo_queue->AddUndoEvent(used_tool->Name(),the_image->ReturnThumbnailImage());
1288 if (new_event != NULL__null) {
1289 new_event->SetSelectionData(undo_queue->ReturnSelectionData());
1290 undo_queue->SetSelectionData(selection->ReturnSelectionData());
1291 }
1292 }
1293
1294 // Tell the selection to start drawing itself.
1295 selection->StartDrawing(this,magnify_scale);
1296 }
1297 return B_OK((int)0);
1298 }
1299 } else {
1300 if (ManipulatorServer* server = ManipulatorServer::Instance()) {
1301 if (!fManipulator) {
1302 if (TextManipulator* manipulator = dynamic_cast<TextManipulator*>
1303 (server->ManipulatorFor(TEXT_MANIPULATOR))) {
1304 manipulator->SetStartingPoint(point);
1305 manipulated_layers = HS_MANIPULATE_CURRENT_LAYER'Mncl';
1306 manipulator->SetPreviewBitmap(the_image->ReturnActiveBitmap());
1307
1308 PaintWindow* window = dynamic_cast<PaintWindow*> (Window());
1309 if (window && LockLooper()) {
1310 window->SetHelpString(manipulator->ReturnHelpString(),
1311 HS_TOOL_HELP_MESSAGE'TolM');
1312 UnlockLooper();
1313 }
1314
1315 BString name = ReturnProjectName();
1316 name << ": " << manipulator->ReturnName();
1317
1318 manipulator_window = new ManipulatorWindow(BRect(100, 100,
1319 200.0, 200.0), manipulator->MakeConfigurationView(this),
1320 name.String(), window, this);
1321
1322 if (window) {
1323 window->PostMessage(HS_MANIPULATOR_ADJUSTING_FINISHED'Mafi',
1324 this);
1325 }
1326
1327 fManipulator = manipulator;
1328 cursor_mode = MANIPULATOR_CURSOR_MODE;
1329
1330 SetCursor();
1331 return B_OK((int)0);
1332 }
1333 }
1334 }
1335 }
1336 return B_ERROR(-1);
1337}
1338
1339
1340int32 ImageView::ManipulatorMouseTrackerThread()
1341{
1342 GUIManipulator *gui_manipulator = cast_as(fManipulator,GUIManipulator)(dynamic_cast<GUIManipulator*>(fManipulator));
1343 if (gui_manipulator == NULL__null)
1344 return B_ERROR(-1);
1345
1346
1347 BPoint point;
1348 uint32 buttons;
1349 if (LockLooper() == TRUE1) {
1350 getCoords(&point,&buttons);
1351 UnlockLooper();
1352 } else
1353 return B_ERROR(-1);
1354
1355 int32 preview_quality;
1356 bool first_call_to_mouse_down = TRUE1;
1357 BRegion *updated_region = new BRegion();
1358
1359 float number_of_frames = 0;
1360 float time = system_time();
1361 while (buttons) {
1362 updated_region->MakeEmpty();
1363 if (LockLooper() == TRUE1) {
1364 gui_manipulator->MouseDown(point,buttons,this,first_call_to_mouse_down);
1365 first_call_to_mouse_down = FALSE0;
1366 preview_quality = gui_manipulator->PreviewBitmap(selection,FALSE0,updated_region);
1367 if (preview_quality != DRAW_NOTHING) {
1368 if ((preview_quality != DRAW_ONLY_GUI) && (updated_region->Frame().IsValid())) {
1369 if (manipulated_layers != HS_MANIPULATE_ALL_LAYERS'Mnal') {
1370 the_image->RenderPreview(updated_region->Frame(),preview_quality);
1371 }
1372 else {
1373 the_image->MultiplyRenderedImagePixels(preview_quality);
1374 }
1375 for (int32 i=0;i<updated_region->CountRects();i++)
1376 Draw(convertBitmapRectToView(updated_region->RectAt(i)));
1377 }
1378 else if (preview_quality == DRAW_ONLY_GUI) {
1379 DrawManipulatorGUI(TRUE1);
1380 selection->Draw();
1381 Flush();
1382 }
1383 number_of_frames++;
1384 }
1385 else {
1386 snooze(50 * 1000);
1387 }
1388 getCoords(&point,&buttons);
1389 UnlockLooper();
1390 }
1391 }
1392
1393 time = system_time() - time;
1394 time = time / 1000000.0;
1395
1396// printf("Frames per second: %f\n",number_of_frames/time);
1397
1398 cursor_mode = BLOCKING_CURSOR_MODE;
1399 SetCursor();
1400
1401 updated_region->Set(BRect(0,0,-1,-1));
1402 preview_quality = gui_manipulator->PreviewBitmap(selection,TRUE1,updated_region);
1403 if (preview_quality != DRAW_NOTHING) {
1404 if ((preview_quality != DRAW_ONLY_GUI) && (updated_region->Frame().IsValid())) {
1405 if (manipulated_layers != HS_MANIPULATE_ALL_LAYERS'Mnal') {
1406 the_image->RenderPreview(updated_region->Frame(),preview_quality);
1407 }
1408 else
1409 the_image->MultiplyRenderedImagePixels(preview_quality);
1410
1411 if (LockLooper() == TRUE1) {
1412 for (int32 i=0;i<updated_region->CountRects();i++)
1413 Draw(convertBitmapRectToView(updated_region->RectAt(i)));
1414 UnlockLooper();
1415 }
1416 }
1417 else if (preview_quality == DRAW_ONLY_GUI) {
1418 if (LockLooper() == TRUE1) {
1419 DrawManipulatorGUI(TRUE1);
1420 selection->Draw();
1421 Flush();
1422 UnlockLooper();
1423 }
1424 }
1425 }
1426
1427 cursor_mode = MANIPULATOR_CURSOR_MODE;
1428 SetCursor();
1429
1430 delete updated_region;
1431 return B_OK((int)0);
1432}
1433
1434
1435int32 ImageView::GUIManipulatorUpdaterThread()
1436{
1437 GUIManipulator *gui_manipulator = cast_as(fManipulator,GUIManipulator)(dynamic_cast<GUIManipulator*>(fManipulator));
1438 if (gui_manipulator == NULL__null)
1439 return B_ERROR(-1);
1440
1441
1442 int32 preview_quality = 0;
1443 int32 lowest_quality = 0;
1444 BRegion *updated_region = new BRegion();
1445
1446 float number_of_frames = 0;
1447
1448 while (continue_manipulator_updating) {
1449 if (LockLooper() == TRUE1) {
1450 preview_quality = gui_manipulator->PreviewBitmap(selection,FALSE0,updated_region);
1451 lowest_quality = max_c(lowest_quality,preview_quality)((lowest_quality)>(preview_quality)?(lowest_quality):(preview_quality
))
;
1452
1453 if ((preview_quality != DRAW_NOTHING) && (updated_region->Frame().IsValid())) {
1454 if (preview_quality != DRAW_ONLY_GUI) {
1455 if (manipulated_layers != HS_MANIPULATE_ALL_LAYERS'Mnal') {
1456 the_image->RenderPreview(updated_region->Frame(),preview_quality);
1457 }
1458 else
1459 the_image->MultiplyRenderedImagePixels(preview_quality);
1460
1461 for (int32 i=0;i<updated_region->CountRects();i++)
1462 Draw(convertBitmapRectToView(updated_region->RectAt(i)));
1463 }
1464 else if (preview_quality == DRAW_ONLY_GUI) {
1465 DrawManipulatorGUI(TRUE1);
1466 selection->Draw();
1467 Flush();
1468 }
1469 number_of_frames++;
1470 }
1471 else {
1472 snooze(50 * 1000);
1473 }
1474 UnlockLooper();
1475 }
1476 if (preview_quality == lowest_quality) {
1477 snooze(50 * 1000);
1478 }
1479 else {
1480 snooze(20 * 1000);
1481 }
1482 }
1483
1484 updated_region->Set(BRect(0,0,-1,-1));
1485
1486 cursor_mode = BLOCKING_CURSOR_MODE;
1487 SetCursor();
1488
1489 preview_quality = gui_manipulator->PreviewBitmap(selection,TRUE1,updated_region);
1490
1491
1492 if (preview_quality != DRAW_NOTHING) {
1493 if ((preview_quality != DRAW_ONLY_GUI) && (updated_region->Frame().IsValid())) {
1494 if (manipulated_layers != HS_MANIPULATE_ALL_LAYERS'Mnal') {
1495 the_image->RenderPreview(updated_region->Frame(),preview_quality);
1496 }
1497 else
1498 the_image->MultiplyRenderedImagePixels(preview_quality);
1499
1500 if (LockLooper() == TRUE1) {
1501 for (int32 i=0;i<updated_region->CountRects();i++)
1502 Draw(convertBitmapRectToView(updated_region->RectAt(i)));
1503 UnlockLooper();
1504 }
1505 }
1506 else if (preview_quality == DRAW_ONLY_GUI){
1507 if (LockLooper() == TRUE1) {
1508 DrawManipulatorGUI(TRUE1);
1509 selection->Draw();
1510 Flush();
1511 UnlockLooper();
1512 }
1513 }
1514 }
1515
1516 cursor_mode = MANIPULATOR_CURSOR_MODE;
1517 SetCursor();
1518
1519 delete updated_region;
1520 return B_OK((int)0);
1521}
1522
1523
1524int32 ImageView::ManipulatorFinisherThread()
1525{
1526 if (fManipulator == NULL__null) {
6
Assuming field 'fManipulator' is not equal to NULL
7
Taking false branch
1527 if (manipulator_finishing_message != NULL__null) {
1528 if (LockLooper() == TRUE1) {
1529 Window()->PostMessage(manipulator_finishing_message,this);
1530 delete manipulator_finishing_message;
1531 manipulator_finishing_message = NULL__null;
1532 UnlockLooper();
1533 }
1534 }
1535 return B_ERROR(-1);
1536 }
1537 cursor_mode = BLOCKING_CURSOR_MODE;
1538 SetCursor();
1539
1540 // Here we should set up the status-bar
1541 BStatusBar *status_bar = ((PaintWindow*)Window())->ReturnStatusView()->DisplayProgressIndicator();
8
'status_bar' initialized here
1542 if (status_bar != NULL__null) {
9
Assuming 'status_bar' is equal to NULL
10
Taking false branch
1543 if (LockLooper() == TRUE1) {
1544 status_bar->Reset();
1545 status_bar->SetText(B_TRANSLATE("Finishing")BLocaleRoster::Default()->GetCatalog()->GetString(("Finishing"
), "ImageView")
);
1546 UnlockLooper();
1547 }
1548 }
1549 GUIManipulator *gui_manipulator = cast_as(fManipulator,GUIManipulator)(dynamic_cast<GUIManipulator*>(fManipulator));
1550 UndoEvent *new_event = NULL__null;
1551
1552 try {
1553 if (manipulated_layers == HS_MANIPULATE_CURRENT_LAYER'Mncl') {
11
Assuming the condition is false
12
Taking false branch
1554 BBitmap *buffer = the_image->ReturnActiveBitmap();
1555 BBitmap *new_buffer = NULL__null;
1556 if (gui_manipulator != NULL__null) {
1557 ManipulatorSettings *settings = gui_manipulator->ReturnSettings();
1558 new_buffer = gui_manipulator->ManipulateBitmap(settings,buffer,selection,status_bar);
1559 delete settings;
1560 }
1561 else {
1562 new_buffer = fManipulator->ManipulateBitmap(buffer,selection,status_bar);
1563 }
1564
1565 Layer *the_layer = the_image->ReturnActiveLayer();
1566 if (new_buffer && new_buffer != buffer)
1567 the_layer->ChangeBitmap(new_buffer);
1568
1569 new_event = undo_queue->AddUndoEvent(fManipulator->ReturnName(),
1570 the_image->ReturnThumbnailImage());
1571 if (new_event != NULL__null) {
1572 BList *layer_list = the_image->LayerList();
1573 for (int32 i=0;i<layer_list->CountItems();i++) {
1574 Layer *layer = (Layer*)layer_list->ItemAt(i);
1575
1576 UndoAction *new_action;
1577 if ((layer != the_layer) || (new_buffer == NULL__null))
1578 new_action = new UndoAction(layer->Id());
1579 else {
1580 BRegion affected_region(new_buffer->Bounds());
1581 new_action = new UndoAction(layer->Id(),
1582 fManipulator->ReturnSettings(),
1583 new_buffer->Bounds(),
1584 (manipulator_type)manip_type, add_on_id);
1585 }
1586 new_event->AddAction(new_action);
1587 new_action->StoreUndo(layer->Bitmap());
1588 }
1589 }
1590 }
1591 else {
1592 // Do all the layers.
1593 // Here we record the dimensions of all the bitmaps. If the
1594 // dimensions change the composite bitmap should be changed to
1595 // smallest layer dimension.
1596
1597 BList* layer_list = the_image->LayerList();
1598 int32 layerCount = layer_list->CountItems();
1599 if (status_bar
12.1
'status_bar' is equal to NULL
!= NULL__null)
13
Taking false branch
1600 status_bar->SetMaxValue(layerCount * 100);
1601
1602 new_event = undo_queue->AddUndoEvent(fManipulator->ReturnName(),
1603 the_image->ReturnThumbnailImage());
1604
1605 for (int32 i = 0; i < layerCount; ++i) {
14
Assuming 'i' is < 'layerCount'
15
Loop condition is true. Entering loop body
1606 if (LockLooper()) {
16
Assuming the condition is true
17
Taking true branch
1607 BString format(B_TRANSLATE("Layer %ld / %ld")BLocaleRoster::Default()->GetCatalog()->GetString(("Layer %ld / %ld"
), "ImageView")
);
1608 BString text;
1609 text.SetToFormat(format, i + 1, layerCount);
1610 status_bar->SetTrailingText(text);
18
Called C++ object pointer is null
1611 UnlockLooper();
1612 }
1613
1614 Layer* the_layer = static_cast<Layer*> (layer_list->ItemAt(i));
1615 BBitmap *buffer = the_layer->Bitmap();
1616 BBitmap *new_buffer;
1617 if (gui_manipulator != NULL__null) {
1618 ManipulatorSettings *settings = gui_manipulator->ReturnSettings();
1619 new_buffer = gui_manipulator->ManipulateBitmap(settings,buffer,selection,status_bar);
1620 delete settings;
1621 }
1622 else {
1623 new_buffer = fManipulator->ManipulateBitmap(buffer,selection,status_bar);
1624 }
1625
1626 if (new_buffer && new_buffer != buffer)
1627 the_layer->ChangeBitmap(new_buffer);
1628
1629 if (new_event != NULL__null) {
1630 if (new_buffer != NULL__null) {
1631 BRegion affected_region(new_buffer->Bounds());
1632 UndoAction *new_action;
1633 new_action = new UndoAction(the_layer->Id(),fManipulator->ReturnSettings(),new_buffer->Bounds(),(manipulator_type)manip_type,add_on_id);
1634
1635 new_event->AddAction(new_action);
1636 new_action->StoreUndo(the_layer->Bitmap());
1637 thread_id a_thread = spawn_thread(Layer::CreateMiniatureImage,"create mini picture",B_LOW_PRIORITY5,the_layer);
1638 resume_thread(a_thread);
1639 }
1640 else {
1641 new_event->AddAction(new UndoAction(the_layer->Id()));
1642 }
1643 }
1644 }
1645 }
1646 }
1647 catch (std::bad_alloc e) {
1648 ShowAlert(CANNOT_FINISH_MANIPULATOR_ALERT);
1649 // The manipulator should be asked to reset the preview-bitmap, if it is
1650 // a GUIManipulator.
1651 if (gui_manipulator != NULL__null) {
1652 gui_manipulator->Reset(selection);
1653 }
1654 }
1655
1656 manipulated_layers = HS_MANIPULATE_NO_LAYER'Mnnl';
1657
1658
1659 // If the added UndoEvent is empty then destroy it.
1660 if ((new_event != NULL__null) && (new_event->IsEmpty() == TRUE1)) {
1661 undo_queue->RemoveEvent(new_event);
1662 delete new_event;
1663 }
1664
1665 the_image->SetImageSize();
1666 the_image->Render();
1667
1668 // also recalculate the selection
1669 selection->Recalculate();
1670
1671 // Change the selection for the undo-queue if necessary.
1672 if ((new_event != NULL__null) && !(*undo_queue->ReturnSelectionData() == *selection->ReturnSelectionData())) {
1673 new_event->SetSelectionData(undo_queue->ReturnSelectionData());
1674 undo_queue->SetSelectionData(selection->ReturnSelectionData());
1675 }
1676
1677 // Store the manipulator settings.
1678 if (ManipulatorServer* server = ManipulatorServer::Instance())
1679 server->StoreManipulatorSettings(fManipulator);
1680
1681
1682 // Finally return the window to normal state and redisplay it.
1683 if (LockLooper() == true) {
1684 ((PaintWindow*)Window())->ReturnStatusView()->DisplayToolsAndColors();
1685
1686 Invalidate();
1687 UnlockLooper();
1688 }
1689
1690 // The manipulator has finished, now finish the manipulator
1691 delete fManipulator;
1692 fManipulator = NULL__null;
1693
1694 cursor_mode = NORMAL_CURSOR_MODE;
1695 SetCursor();
1696
1697 if (manipulator_finishing_message != NULL__null) {
1698 if (LockLooper() == TRUE1) {
1699 Window()->PostMessage(manipulator_finishing_message,this);
1700 delete manipulator_finishing_message;
1701 manipulator_finishing_message = NULL__null;
1702 UnlockLooper();
1703 }
1704 }
1705
1706
1707 return B_OK((int)0);
1708}
1709
1710
1711
1712
1713void ImageView::Undo()
1714{
1715 if (acquire_sem_etc(action_semaphore,1,B_TIMEOUT,0) == B_OK((int)0)) {
1716 // If there is a GUI-manipulator, it should reset the bitmap before undo can be done.
1717 cursor_mode = BLOCKING_CURSOR_MODE;
1718 SetCursor();
1719
1720 GUIManipulator *gui_manipulator = cast_as(fManipulator,GUIManipulator)(dynamic_cast<GUIManipulator*>(fManipulator));
1721 if (gui_manipulator != NULL__null) {
1722 gui_manipulator->Reset(selection);
1723 }
1724
1725 UndoEvent *event = undo_queue->Undo();
1726 if (event != NULL__null) {
1727 if (event->IsEmpty() == FALSE0) {
1728 the_image->UpdateImageStructure(event);
1729
1730 cursor_mode = NORMAL_CURSOR_MODE;
1731 // After the undo, the current buffer might have changed and the
1732 // possible gui_manipulator should be informed about it.
1733 if (gui_manipulator != NULL__null) {
1734 gui_manipulator->SetPreviewBitmap(the_image->ReturnActiveBitmap());
1735 // We should also tell the manipulator to recalculate its preview.
1736 Window()->PostMessage(HS_MANIPULATOR_ADJUSTING_FINISHED'Mafi',this);
1737 cursor_mode = MANIPULATOR_CURSOR_MODE;
1738 }
1739 LayerWindow::ActiveWindowChanged(Window(),the_image->LayerList(),the_image->ReturnThumbnailImage());
1740 AddChange(); // Removing a change here is not a good thing
1741 }
1742
1743 if (event->ReturnSelectionData() != NULL__null) {
1744 SelectionData *data = new SelectionData(event->ReturnSelectionData());
1745 event->SetSelectionData(selection->ReturnSelectionData());
1746 selection->SetSelectionData(data);
1747
1748 undo_queue->SetSelectionData(data);
1749 delete data;
1750 }
1751
1752 Invalidate();
1753 }
1754
1755 if (gui_manipulator != NULL__null)
1756 cursor_mode = MANIPULATOR_CURSOR_MODE;
1757 else
1758 cursor_mode = NORMAL_CURSOR_MODE;
1759
1760 SetCursor();
1761
1762 release_sem(action_semaphore);
1763 }
1764}
1765
1766
1767
1768void ImageView::Redo()
1769{
1770 if (acquire_sem_etc(action_semaphore,1,B_TIMEOUT,0) == B_OK((int)0)) {
1771 // If there is a GUI-manipulator, it should reset the bitmap before redo
1772 // can be done.
1773 GUIManipulator *gui_manipulator = cast_as(fManipulator,GUIManipulator)(dynamic_cast<GUIManipulator*>(fManipulator));
1774 if (gui_manipulator != NULL__null) {
1775 gui_manipulator->Reset(selection);
1776 }
1777
1778 UndoEvent *event = undo_queue->Redo();
1779 if (event != NULL__null) {
1780 if (event->IsEmpty() == FALSE0) {
1781 the_image->UpdateImageStructure(event);
1782
1783 // After the redo, the current buffer might have changed and the
1784 // possible gui_manipulator should be informed about it.
1785 if (gui_manipulator != NULL__null) {
1786 gui_manipulator->SetPreviewBitmap(the_image->ReturnActiveBitmap());
1787 // We should also tell the manipulator to recalculate its preview.
1788 Window()->PostMessage(HS_MANIPULATOR_ADJUSTING_FINISHED'Mafi',this);
1789 }
1790
1791 LayerWindow::ActiveWindowChanged(Window(),the_image->LayerList(),the_image->ReturnThumbnailImage());
1792 AddChange();
1793 }
1794 if (event->ReturnSelectionData() != NULL__null) {
1795 SelectionData *data = new SelectionData(event->ReturnSelectionData());
1796 event->SetSelectionData(selection->ReturnSelectionData());
1797 selection->SetSelectionData(data);
1798
1799 undo_queue->SetSelectionData(data);
1800 delete data;
1801 }
1802
1803 Invalidate();
1804 }
1805
1806 release_sem(action_semaphore);
1807 }
1808}
1809
1810status_t
1811ImageView::DoCopyOrCut(int32 layers,bool cut)
1812{
1813 if (acquire_sem_etc(action_semaphore,1,B_TIMEOUT,0) == B_OK((int)0)) {
1814 BBitmap *buffer;
1815 BRect* offset;
1816 bool ok_to_archive = TRUE1;
1817 if (layers == HS_MANIPULATE_CURRENT_LAYER'Mncl')
1818 buffer = the_image->ReturnActiveBitmap();
1819 else
1820 buffer = the_image->ReturnRenderedImage();
1821 BMessage *bitmap_archive = new BMessage();
1822 if (selection->IsEmpty() == TRUE1) {
1823 if (buffer->Archive(bitmap_archive) != B_OK((int)0))
1824 ok_to_archive = FALSE0;
1825 }
1826 else {
1827 BRect selection_bounds = selection->GetBoundingRect();
1828 selection_bounds.left = floor(selection_bounds.left);
1829 selection_bounds.top = floor(selection_bounds.top);
1830 selection_bounds.right = ceil(selection_bounds.right);
1831 selection_bounds.bottom = ceil(selection_bounds.bottom);
1832 selection_bounds = selection_bounds & buffer->Bounds();
1833 BRect bounds = selection_bounds;
1834
1835 bounds.OffsetTo(0,0);
1836 BBitmap *to_be_archived = new BBitmap(bounds,B_RGBA32,0); //stargater, Pete
1837 uint32 *target_bits = (uint32*)to_be_archived->Bits();
1838 int32 bits_length = to_be_archived->BitsLength()/4;
1839 union {
1840 uint8 bytes[4];
1841 uint32 word;
1842 } color;
1843 color.bytes[0] = 0xFF;
1844 color.bytes[1] = 0xFF;
1845 color.bytes[2] = 0xFF;
1846 color.bytes[3] = 0x00;
1847
1848
1849 for (int32 i=0;i<bits_length;i++) {
1850 *target_bits++ = color.word;
1851 }
1852 target_bits = (uint32*)to_be_archived->Bits();
1853 int32 left = (int32)selection_bounds.left;
1854 int32 right = (int32)selection_bounds.right;
1855 int32 top = (int32)selection_bounds.top;
1856 int32 bottom = (int32)selection_bounds.bottom;
1857
1858 uint32 *source_bits = (uint32*)buffer->Bits();
1859 int32 source_bpr = buffer->BytesPerRow()/4;
1860
1861 for (int32 y=top;y<=bottom;y++) {
1862 for (int32 x=left;x<=right;x++) {
1863 if (selection->ContainsPoint(x,y))
1864 *target_bits = *(source_bits + x + y*source_bpr);
1865
1866 target_bits++;
1867 }
1868 }
1869 if (to_be_archived->Archive(bitmap_archive) != B_OK((int)0))
1870 ok_to_archive = FALSE0;
1871
1872 delete to_be_archived;
1873 }
1874
1875 if (ok_to_archive == TRUE1) {
1876 be_clipboard->Lock();
1877 be_clipboard->Clear();
1878 BMessage *clipboard_message = be_clipboard->Data();
1879 clipboard_message->AddMessage("image/bitmap",bitmap_archive);
1880 if (selection->IsEmpty() == FALSE0)
1881 clipboard_message->AddRect("offset", selection->GetBoundingRect());
1882 be_clipboard->Commit();
1883 be_clipboard->Unlock();
1884 delete bitmap_archive;
1885 }
1886 if (cut == TRUE1) {
1887 if (layers == HS_MANIPULATE_CURRENT_LAYER'Mncl')
1888 Window()->PostMessage(HS_CLEAR_LAYER'Clla',this);
1889 else
1890 Window()->PostMessage(HS_CLEAR_CANVAS'Clcv',this);
1891 }
1892
1893 release_sem(action_semaphore);
1894 return B_OK((int)0);
1895 }
1896 else
1897 return B_ERROR(-1);
1898}
1899
1900
1901status_t
1902ImageView::DoPaste()
1903{
1904 be_clipboard->Lock();
1905 BMessage *clipboard_message = be_clipboard->Data();
1906 if (clipboard_message != NULL__null) {
1907 BMessage *bitmap_message = new BMessage();
1908 if (clipboard_message->FindMessage("image/bitmap",bitmap_message) == B_OK((int)0)) {
1909 if (bitmap_message != NULL__null) {
1910 BBitmap *pasted_bitmap = new BBitmap(bitmap_message);
1911 BRect offset;
1912 clipboard_message->FindRect("offset", &offset);
1913 delete bitmap_message;
1914 if ((pasted_bitmap != NULL__null) && (pasted_bitmap->IsValid() == TRUE1)) {
1915 try {
1916 if (the_image->AddLayer(pasted_bitmap, NULL__null, TRUE1,
1917 1.0, &offset) != NULL__null) {
1918 // delete pasted_bitmap;
1919 Invalidate();
1920 LayerWindow::ActiveWindowChanged(Window(),
1921 the_image->LayerList(),
1922 the_image->ReturnThumbnailImage());
1923 AddChange();
1924 }
1925 }
1926 catch (std::bad_alloc e) {
1927 ShowAlert(CANNOT_ADD_LAYER_ALERT);
1928 }
1929 }
1930 }
1931 } else
1932 delete bitmap_message;
1933 }
1934 be_clipboard->Unlock();
1935 return B_OK((int)0);
1936}
1937
1938
1939
1940status_t ImageView::ShowAlert(int32 alert)
1941{
1942 const char *text;
1943 switch (alert) {
1944 case CANNOT_ADD_LAYER_ALERT:
1945 text = B_TRANSLATE("Not enough free memory to add a layer ."\BLocaleRoster::Default()->GetCatalog()->GetString(("Not enough free memory to add a layer ."
"You can free more memory by disabling the undo and closing other images. It is also a good idea "
"to save the image now because running out of memory later on might make saving difficult or "
"impossible. I am very sorry about this inconvenience."), "ImageView"
)
1946 "You can free more memory by disabling the undo and closing other images. It is also a good idea "\BLocaleRoster::Default()->GetCatalog()->GetString(("Not enough free memory to add a layer ."
"You can free more memory by disabling the undo and closing other images. It is also a good idea "
"to save the image now because running out of memory later on might make saving difficult or "
"impossible. I am very sorry about this inconvenience."), "ImageView"
)
1947 "to save the image now because running out of memory later on might make saving difficult or "\BLocaleRoster::Default()->GetCatalog()->GetString(("Not enough free memory to add a layer ."
"You can free more memory by disabling the undo and closing other images. It is also a good idea "
"to save the image now because running out of memory later on might make saving difficult or "
"impossible. I am very sorry about this inconvenience."), "ImageView"
)
1948 "impossible. I am very sorry about this inconvenience.")BLocaleRoster::Default()->GetCatalog()->GetString(("Not enough free memory to add a layer ."
"You can free more memory by disabling the undo and closing other images. It is also a good idea "
"to save the image now because running out of memory later on might make saving difficult or "
"impossible. I am very sorry about this inconvenience."), "ImageView"
)
;
1949 break;
1950 case CANNOT_START_MANIPULATOR_ALERT:
1951 text = B_TRANSLATE("Not enough free memory to start the effect you requested. You may close other images and try again. "\BLocaleRoster::Default()->GetCatalog()->GetString(("Not enough free memory to start the effect you requested. You may close other images and try again. "
"Also shortening the depth of undo or disabling undo altogether helps in achieving more memory. "
"If you have other applications running, closing them gives you more free memory. "
"It is also a good idea to save your work at this point, because if the memory runs out completely "
"saving might become impossible. I am very sorry about this inconvenience."
), "ImageView")
1952 "Also shortening the depth of undo or disabling undo altogether helps in achieving more memory. "\BLocaleRoster::Default()->GetCatalog()->GetString(("Not enough free memory to start the effect you requested. You may close other images and try again. "
"Also shortening the depth of undo or disabling undo altogether helps in achieving more memory. "
"If you have other applications running, closing them gives you more free memory. "
"It is also a good idea to save your work at this point, because if the memory runs out completely "
"saving might become impossible. I am very sorry about this inconvenience."
), "ImageView")
1953 "If you have other applications running, closing them gives you more free memory. "\BLocaleRoster::Default()->GetCatalog()->GetString(("Not enough free memory to start the effect you requested. You may close other images and try again. "
"Also shortening the depth of undo or disabling undo altogether helps in achieving more memory. "
"If you have other applications running, closing them gives you more free memory. "
"It is also a good idea to save your work at this point, because if the memory runs out completely "
"saving might become impossible. I am very sorry about this inconvenience."
), "ImageView")
1954 "It is also a good idea to save your work at this point, because if the memory runs out completely "\BLocaleRoster::Default()->GetCatalog()->GetString(("Not enough free memory to start the effect you requested. You may close other images and try again. "
"Also shortening the depth of undo or disabling undo altogether helps in achieving more memory. "
"If you have other applications running, closing them gives you more free memory. "
"It is also a good idea to save your work at this point, because if the memory runs out completely "
"saving might become impossible. I am very sorry about this inconvenience."
), "ImageView")
1955 "saving might become impossible. I am very sorry about this inconvenience.")BLocaleRoster::Default()->GetCatalog()->GetString(("Not enough free memory to start the effect you requested. You may close other images and try again. "
"Also shortening the depth of undo or disabling undo altogether helps in achieving more memory. "
"If you have other applications running, closing them gives you more free memory. "
"It is also a good idea to save your work at this point, because if the memory runs out completely "
"saving might become impossible. I am very sorry about this inconvenience."
), "ImageView")
;
1956 break;
1957
1958 case CANNOT_FINISH_MANIPULATOR_ALERT:
1959 text = B_TRANSLATE("Not enough free memory to finish the effect you requested. You may close other images and try again. "\BLocaleRoster::Default()->GetCatalog()->GetString(("Not enough free memory to finish the effect you requested. You may close other images and try again. "
"Also shortening the depth of undo or disabling undo altogether helps in achieving more memory. "
"If you have other applications running, closing them gives you more free memory. "
"It is also a good idea to save your work at this point, because if the memory runs out completely "
"saving might become impossible. I am very sorry about this inconvenience."
), "ImageView")
1960 "Also shortening the depth of undo or disabling undo altogether helps in achieving more memory. "\BLocaleRoster::Default()->GetCatalog()->GetString(("Not enough free memory to finish the effect you requested. You may close other images and try again. "
"Also shortening the depth of undo or disabling undo altogether helps in achieving more memory. "
"If you have other applications running, closing them gives you more free memory. "
"It is also a good idea to save your work at this point, because if the memory runs out completely "
"saving might become impossible. I am very sorry about this inconvenience."
), "ImageView")
1961 "If you have other applications running, closing them gives you more free memory. "\BLocaleRoster::Default()->GetCatalog()->GetString(("Not enough free memory to finish the effect you requested. You may close other images and try again. "
"Also shortening the depth of undo or disabling undo altogether helps in achieving more memory. "
"If you have other applications running, closing them gives you more free memory. "
"It is also a good idea to save your work at this point, because if the memory runs out completely "
"saving might become impossible. I am very sorry about this inconvenience."
), "ImageView")
1962 "It is also a good idea to save your work at this point, because if the memory runs out completely "\BLocaleRoster::Default()->GetCatalog()->GetString(("Not enough free memory to finish the effect you requested. You may close other images and try again. "
"Also shortening the depth of undo or disabling undo altogether helps in achieving more memory. "
"If you have other applications running, closing them gives you more free memory. "
"It is also a good idea to save your work at this point, because if the memory runs out completely "
"saving might become impossible. I am very sorry about this inconvenience."
), "ImageView")
1963 "saving might become impossible. I am very sorry about this inconvenience.")BLocaleRoster::Default()->GetCatalog()->GetString(("Not enough free memory to finish the effect you requested. You may close other images and try again. "
"Also shortening the depth of undo or disabling undo altogether helps in achieving more memory. "
"If you have other applications running, closing them gives you more free memory. "
"It is also a good idea to save your work at this point, because if the memory runs out completely "
"saving might become impossible. I am very sorry about this inconvenience."
), "ImageView")
;
1964 break;
1965
1966 default:
1967 text = "This alert should never show up";
1968 break;
1969 }
1970
1971 BAlert *alert_box = new BAlert("alert_box",text,B_TRANSLATE("OK")BLocaleRoster::Default()->GetCatalog()->GetString(("OK"
), "ImageView")
,NULL__null,NULL__null,B_WIDTH_AS_USUAL,B_WARNING_ALERT);
1972 alert_box->Go();
1973
1974
1975 return B_OK((int)0);
1976}
1977
1978
1979void ImageView::SetCursor()
1980{
1981 BPoint point;
1982 uint32 buttons;
1983 BRegion region;
1984
1985 if (LockLooper() == TRUE1) {
1986 GetClippingRegion(&region);
1987 GetMouse(&point,&buttons);
1988 UnlockLooper();
1989 }
1990
1991 if (region.Contains(point)) {
1992 if (cursor_mode == NORMAL_CURSOR_MODE) {
1993 be_app->SetCursor(ToolManager::Instance().ReturnCursor());
1994 }
1995 else if (cursor_mode == MANIPULATOR_CURSOR_MODE) {
1996 GUIManipulator *gui_manipulator = cast_as(fManipulator,GUIManipulator)(dynamic_cast<GUIManipulator*>(fManipulator));
1997 if (gui_manipulator != NULL__null) {
1998 if (gui_manipulator->ManipulatorCursor() != NULL__null)
1999 be_app->SetCursor(gui_manipulator->ManipulatorCursor());
2000 else
2001 be_app->SetCursor(B_HAND_CURSOR);
2002 }
2003 }
2004 else if (cursor_mode == BLOCKING_CURSOR_MODE) {
2005 be_app->SetCursor(HS_BLOCKING_CURSOR);
2006 }
2007 }
2008 else {
2009 be_app->SetCursor(B_HAND_CURSOR);
2010 }
2011}
2012
2013
2014void ImageView::SetDisplayMode(int32 new_display_mode)
2015{
2016 if (current_display_mode != new_display_mode) {
2017 current_display_mode = new_display_mode;
2018
2019 if (current_display_mode == DITHERED_8_BIT_DISPLAY_MODE) {
2020 if (the_image->RegisterDitheredUser(this) == B_ERROR(-1)) {
2021 current_display_mode = FULL_RGB_DISPLAY_MODE;
2022 }
2023 }
2024 else {
2025 the_image->UnregisterDitheredUser(this);
2026 }
2027
2028 Invalidate();
2029 }
2030}
2031
2032
2033void ImageView::SetReferencePoint(BPoint point,bool use)
2034{
2035 reference_point = point;
2036 use_reference_point = use;
2037}
2038
2039void ImageView::SetToolHelpString(const char *string)
2040{
2041 if (LockLooper() == TRUE1) {
2042 ((PaintWindow*)Window())->SetHelpString(string,HS_TOOL_HELP_MESSAGE'TolM');
2043 UnlockLooper();
2044 }
2045}
2046
2047
2048void ImageView::SetProjectName(const char *name)
2049{
2050 delete[] project_name;
2051
2052 project_name = new char[strlen(name) + 1];
2053 strcpy(project_name,name);
2054
2055 setWindowTitle();
2056}
2057
2058
2059void ImageView::SetImageName(const char *name)
2060{
2061 delete[] image_name;
2062
2063 image_name = new char[strlen(name) + 1];
2064 strcpy(image_name,name);
2065
2066 setWindowTitle();
2067}
2068
2069
2070
2071void
2072ImageView::setWindowTitle()
2073{
2074 BString title;
2075 if (true) {
2076 // Experimental style title
2077 BString pname = project_name;
2078 BString iname = image_name;
2079
2080 BString pchanged = "";
2081 BString ichanged = "";
2082
2083 if (project_name == NULL__null)
2084 pname = "";
2085
2086 if (project_changed > 0)
2087 pchanged = "(*) ";
2088 if (image_changed > 0)
2089 ichanged = "(*)";
2090
2091 title << pchanged << pname;
2092 if (iname != NULL__null)
2093 title << " | (" << iname << ")";
2094 }
2095
2096 if (LockLooper()) {
2097 Window()->SetTitle(title);
2098 UnlockLooper();
2099 }
2100}
2101
2102
2103void ImageView::AddChange()
2104{
2105 project_changed++;
2106 image_changed++;
2107
2108 if ((project_changed == 1) || (image_changed == 1)) {
2109 setWindowTitle();
2110 }
2111}
2112
2113
2114void ImageView::RemoveChange()
2115{
2116 project_changed = max_c(0,project_changed-1)((0)>(project_changed-1)?(0):(project_changed-1));
2117 image_changed = max_c(0,image_changed-1)((0)>(image_changed-1)?(0):(image_changed-1));
2118}
2119
2120
2121void ImageView::ResetChangeStatistics(bool project, bool image)
2122{
2123 if (project)
2124 project_changed = 0;
2125 if (image)
2126 image_changed = 0;
2127
2128 setWindowTitle();
2129}
2130
2131
2132bool ImageView::PostponeMessageAndFinishManipulator()
2133{
2134 if (fManipulator) {
2135 BMessage message(HS_MANIPULATOR_FINISHED'MnFi');
2136 message.AddBool("status", true);
2137 Window()->PostMessage(&message, this);
2138
2139 manipulator_finishing_message = Window()->DetachCurrentMessage();
2140 return true;
2141 }
2142 return false;
2143}
2144
2145
2146
2147
2148
2149
2150
2151filter_result KeyFilterFunction(BMessage *message,BHandler **handler,BMessageFilter*)
2152{
2153// message->PrintToStream();
2154 ImageView *view = dynamic_cast<ImageView*>(*handler);
2155 if (view != NULL__null) {
2156 if (acquire_sem_etc(view->mouse_mutex,1,B_RELATIVE_TIMEOUT,0) == B_OK((int)0)) {
2157 const char *bytes;
2158 if ((!(modifiers() & B_COMMAND_KEY)) && (!(modifiers() & B_CONTROL_KEY))) {
2159 if (message->FindString("bytes",&bytes) == B_OK((int)0)) {
2160 switch (bytes[0]) {
2161 case 'b':
2162 ToolManager::Instance().ChangeTool(BRUSH_TOOL);
2163 view->SetCursor();
2164 break;
2165
2166 case 'a':
2167 ToolManager::Instance().ChangeTool(AIR_BRUSH_TOOL);
2168 view->SetCursor();
2169 break;
2170
2171 case 'e':
2172 ToolManager::Instance().ChangeTool(ERASER_TOOL);
2173 view->SetCursor();
2174 break;
2175
2176 case 'f':
2177 ToolManager::Instance().ChangeTool(FREE_LINE_TOOL);
2178 view->SetCursor();
2179 break;
2180
2181 case 's':
2182 ToolManager::Instance().ChangeTool(SELECTOR_TOOL);
2183 view->SetCursor();
2184 break;
2185
2186 case 'r':
2187 ToolManager::Instance().ChangeTool(RECTANGLE_TOOL);
2188 view->SetCursor();
2189 break;
2190
2191 case 'l':
2192 ToolManager::Instance().ChangeTool(STRAIGHT_LINE_TOOL);
2193 view->SetCursor();
2194 break;
2195
2196 case 'h':
2197 ToolManager::Instance().ChangeTool(HAIRY_BRUSH_TOOL);
2198 view->SetCursor();
2199 break;
2200
2201 case 'u':
2202 ToolManager::Instance().ChangeTool(BLUR_TOOL);
2203 view->SetCursor();
2204 break;
2205
2206 case 'i':
2207 ToolManager::Instance().ChangeTool(FILL_TOOL);
2208 view->SetCursor();
2209 break;
2210
2211 case 't':
2212 ToolManager::Instance().ChangeTool(TEXT_TOOL);
2213 view->SetCursor();
2214 break;
2215
2216 case 'n':
2217 ToolManager::Instance().ChangeTool(TRANSPARENCY_TOOL);
2218 view->SetCursor();
2219 break;
2220
2221 case 'c':
2222 ToolManager::Instance().ChangeTool(COLOR_SELECTOR_TOOL);
2223 view->SetCursor();
2224 break;
2225
2226 case 'p':
2227 ToolManager::Instance().ChangeTool(ELLIPSE_TOOL);
2228 view->SetCursor();
2229 break;
2230 }
2231 }
2232 }
2233
2234 release_sem(view->mouse_mutex);
2235 }
2236 }
2237 return B_DISPATCH_MESSAGE;
2238}