2020-01-18 09:38:21 +01:00
/*
* Copyright ( c ) 2018 - 2020 , Andreas Kling < kling @ serenityos . org >
* All rights reserved .
*
* Redistribution and use in source and binary forms , with or without
* modification , are permitted provided that the following conditions are met :
*
* 1. Redistributions of source code must retain the above copyright notice , this
* list of conditions and the following disclaimer .
*
* 2. Redistributions in binary form must reproduce the above copyright notice ,
* this list of conditions and the following disclaimer in the documentation
* and / or other materials provided with the distribution .
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS " AS IS "
* AND ANY EXPRESS OR IMPLIED WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED . IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT , INDIRECT , INCIDENTAL , SPECIAL , EXEMPLARY , OR CONSEQUENTIAL
* DAMAGES ( INCLUDING , BUT NOT LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES ; LOSS OF USE , DATA , OR PROFITS ; OR BUSINESS INTERRUPTION ) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY ,
* OR TORT ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE .
*/
2019-06-23 16:35:43 +02:00
# include "QSWidget.h"
2020-04-12 14:35:15 +03:00
# include <AK/StringBuilder.h>
# include <LibCore/DirIterator.h>
2020-02-06 20:33:02 +01:00
# include <LibGUI/MessageBox.h>
# include <LibGUI/Painter.h>
# include <LibGUI/Window.h>
2020-02-14 13:18:34 +01:00
# include <LibGfx/Bitmap.h>
2020-04-12 17:40:34 +03:00
# include <LibGfx/Orientation.h>
2020-04-05 13:13:32 +02:00
# include <LibGfx/Palette.h>
2019-06-23 16:35:43 +02:00
2020-02-23 12:07:13 +01:00
QSWidget : : QSWidget ( )
2019-06-23 16:35:43 +02:00
{
2020-04-05 13:13:32 +02:00
set_fill_with_background_color ( false ) ;
2019-06-23 16:35:43 +02:00
}
QSWidget : : ~ QSWidget ( )
{
}
2020-04-12 16:40:05 +03:00
void QSWidget : : clear ( )
{
m_bitmap = nullptr ;
m_path = { } ;
on_scale_change ( 100 ) ;
update ( ) ;
}
2020-04-12 16:03:31 +03:00
void QSWidget : : flip ( Gfx : : Orientation orientation )
{
m_bitmap = m_bitmap - > flipped ( orientation ) ;
set_scale ( m_scale ) ;
resize_window ( ) ;
}
void QSWidget : : rotate ( Gfx : : RotationDirection rotation_direction )
{
m_bitmap = m_bitmap - > rotated ( rotation_direction ) ;
set_scale ( m_scale ) ;
resize_window ( ) ;
}
2020-04-12 14:35:15 +03:00
void QSWidget : : navigate ( Directions direction )
{
if ( m_path = = nullptr )
return ;
auto parts = m_path . split ( ' / ' ) ;
parts . remove ( parts . size ( ) - 1 ) ;
StringBuilder sb ;
sb . append ( " / " ) ;
sb . join ( " / " , parts ) ;
AK : : String current_dir = sb . to_string ( ) ;
if ( m_files_in_same_dir . is_empty ( ) ) {
Core : : DirIterator iterator ( current_dir , Core : : DirIterator : : Flags : : SkipDots ) ;
while ( iterator . has_next ( ) ) {
String file = iterator . next_full_path ( ) ;
if ( ! file . ends_with ( " .png " ) ) // TODO: Find a batter way to filter supported images.
continue ;
m_files_in_same_dir . append ( file ) ;
}
}
auto current_index = m_files_in_same_dir . find_first_index ( m_path ) ;
if ( ! current_index . has_value ( ) ) {
return ;
}
size_t index = current_index . value ( ) ;
if ( direction = = Directions : : Back ) {
if ( index = = 0 ) {
GUI : : MessageBox : : show ( String : : format ( " This is the first file. " , index ) , " Cannot open image " , GUI : : MessageBox : : Type : : Error , GUI : : MessageBox : : InputType : : OK , window ( ) ) ;
return ;
}
index - - ;
} else if ( direction = = Directions : : Forward ) {
if ( index = = m_files_in_same_dir . size ( ) - 1 ) {
GUI : : MessageBox : : show ( String : : format ( " This is the last file. " , index ) , " Cannot open image " , GUI : : MessageBox : : Type : : Error , GUI : : MessageBox : : InputType : : OK , window ( ) ) ;
return ;
}
index + + ;
} else if ( direction = = Directions : : First ) {
index = 0 ;
} else if ( direction = = Directions : : Last ) {
index = m_files_in_same_dir . size ( ) - 1 ;
}
this - > load_from_file ( m_files_in_same_dir . at ( index ) ) ;
}
2020-04-12 16:24:31 +03:00
void QSWidget : : set_scale ( int scale )
{
if ( scale < 10 )
scale = 10 ;
if ( scale > 1000 )
scale = 1000 ;
m_scale = scale ;
relayout ( ) ;
}
2019-06-23 16:35:43 +02:00
void QSWidget : : relayout ( )
{
2020-04-05 09:26:29 +02:00
if ( m_bitmap . is_null ( ) )
return ;
2019-06-23 16:35:43 +02:00
float scale_factor = ( float ) m_scale / 100.0f ;
2020-04-05 20:25:14 +02:00
Gfx : : Size new_size ;
2019-06-23 16:35:43 +02:00
new_size . set_width ( m_bitmap - > width ( ) * scale_factor ) ;
new_size . set_height ( m_bitmap - > height ( ) * scale_factor ) ;
m_bitmap_rect . set_size ( new_size ) ;
2020-04-05 20:25:14 +02:00
Gfx : : Point new_location ;
new_location . set_x ( ( width ( ) / 2 ) - ( new_size . width ( ) / 2 ) - ( m_pan_origin . x ( ) * scale_factor ) ) ;
new_location . set_y ( ( height ( ) / 2 ) - ( new_size . height ( ) / 2 ) - ( m_pan_origin . y ( ) * scale_factor ) ) ;
m_bitmap_rect . set_location ( new_location ) ;
2020-04-12 17:40:34 +03:00
if ( on_scale_change )
on_scale_change ( m_scale ) ;
2019-06-23 16:35:43 +02:00
update ( ) ;
}
2020-02-02 15:07:41 +01:00
void QSWidget : : resize_event ( GUI : : ResizeEvent & event )
2019-06-23 16:35:43 +02:00
{
relayout ( ) ;
2020-02-02 15:07:41 +01:00
GUI : : Widget : : resize_event ( event ) ;
2019-06-23 16:35:43 +02:00
}
2020-02-02 15:07:41 +01:00
void QSWidget : : paint_event ( GUI : : PaintEvent & event )
2019-06-23 16:35:43 +02:00
{
2020-02-02 15:07:41 +01:00
GUI : : Painter painter ( * this ) ;
2019-06-23 16:35:43 +02:00
painter . add_clip_rect ( event . rect ( ) ) ;
2020-04-05 13:13:32 +02:00
painter . fill_rect_with_checkerboard ( rect ( ) , { 8 , 8 } , palette ( ) . base ( ) . darkened ( 0.9 ) , palette ( ) . base ( ) ) ;
2020-04-05 20:25:14 +02:00
if ( ! m_bitmap . is_null ( ) )
painter . draw_scaled_bitmap ( m_bitmap_rect , * m_bitmap , m_bitmap - > rect ( ) ) ;
2019-06-23 16:35:43 +02:00
}
2020-02-02 15:07:41 +01:00
void QSWidget : : mousedown_event ( GUI : : MouseEvent & event )
2019-06-23 16:35:43 +02:00
{
2020-02-02 15:07:41 +01:00
if ( event . button ( ) ! = GUI : : MouseButton : : Left )
2019-06-23 16:35:43 +02:00
return ;
2020-04-05 20:25:14 +02:00
m_click_position = event . position ( ) ;
m_saved_pan_origin = m_pan_origin ;
2019-06-23 16:35:43 +02:00
}
2020-02-02 15:07:41 +01:00
void QSWidget : : mouseup_event ( GUI : : MouseEvent & event )
2019-06-23 16:35:43 +02:00
{
UNUSED_PARAM ( event ) ;
}
2020-02-02 15:07:41 +01:00
void QSWidget : : mousemove_event ( GUI : : MouseEvent & event )
2019-06-23 16:35:43 +02:00
{
2020-02-02 15:07:41 +01:00
if ( ! ( event . buttons ( ) & GUI : : MouseButton : : Left ) )
2019-06-23 16:35:43 +02:00
return ;
2020-04-05 20:25:14 +02:00
auto delta = event . position ( ) - m_click_position ;
float scale_factor = ( float ) m_scale / 100.0f ;
m_pan_origin = m_saved_pan_origin . translated (
- delta . x ( ) / scale_factor ,
- delta . y ( ) / scale_factor ) ;
relayout ( ) ;
2019-06-23 16:35:43 +02:00
}
2020-02-02 15:07:41 +01:00
void QSWidget : : mousewheel_event ( GUI : : MouseEvent & event )
2019-06-23 16:35:43 +02:00
{
auto old_scale = m_scale ;
2019-06-30 15:01:35 +02:00
auto old_scale_factor = ( float ) m_scale / 100.0f ;
2020-04-05 20:25:14 +02:00
2019-06-23 16:35:43 +02:00
m_scale + = - event . wheel_delta ( ) * 10 ;
if ( m_scale < 10 )
m_scale = 10 ;
if ( m_scale > 1000 )
m_scale = 1000 ;
2020-04-05 20:25:14 +02:00
2019-06-30 15:01:35 +02:00
auto new_scale_factor = ( float ) m_scale / 100.0f ;
2020-04-05 20:25:14 +02:00
auto focus_point = Gfx : : FloatPoint (
m_pan_origin . x ( ) - ( ( float ) event . x ( ) - ( float ) width ( ) / 2.0 ) / old_scale_factor ,
m_pan_origin . y ( ) - ( ( float ) event . y ( ) - ( float ) height ( ) / 2.0 ) / old_scale_factor ) ;
m_pan_origin = Gfx : : FloatPoint (
focus_point . x ( ) - new_scale_factor / old_scale_factor * ( focus_point . x ( ) - m_pan_origin . x ( ) ) ,
focus_point . y ( ) - new_scale_factor / old_scale_factor * ( focus_point . y ( ) - m_pan_origin . y ( ) ) ) ;
2019-06-23 16:35:43 +02:00
if ( old_scale ! = m_scale ) {
2020-04-12 17:40:34 +03:00
relayout ( ) ;
2019-06-23 16:35:43 +02:00
}
}
2019-12-19 20:39:11 +01:00
2020-04-05 20:25:14 +02:00
void QSWidget : : load_from_file ( const String & path )
2019-12-19 20:39:11 +01:00
{
2020-04-05 20:25:14 +02:00
auto bitmap = Gfx : : Bitmap : : load_from_file ( path ) ;
if ( ! bitmap ) {
GUI : : MessageBox : : show ( String : : format ( " Failed to open %s " , path . characters ( ) ) , " Cannot open image " , GUI : : MessageBox : : Type : : Error , GUI : : MessageBox : : InputType : : OK , window ( ) ) ;
return ;
}
2019-12-19 20:39:11 +01:00
m_path = path ;
2020-04-05 20:25:14 +02:00
m_bitmap = bitmap ;
m_scale = 100 ;
m_pan_origin = { 0 , 0 } ;
2020-04-12 16:24:31 +03:00
resize_window ( ) ;
2020-04-05 20:25:14 +02:00
if ( on_scale_change )
on_scale_change ( m_scale ) ;
relayout ( ) ;
2019-12-19 20:39:11 +01:00
}
2020-02-02 15:07:41 +01:00
void QSWidget : : drop_event ( GUI : : DropEvent & event )
2019-12-19 20:39:11 +01:00
{
event . accept ( ) ;
2020-04-05 20:25:14 +02:00
if ( on_drop )
on_drop ( event ) ;
2019-12-19 20:39:11 +01:00
}
2020-04-12 16:03:31 +03:00
void QSWidget : : resize_window ( )
{
if ( window ( ) - > is_fullscreen ( ) )
return ;
if ( ! m_bitmap )
return ;
auto new_size = m_bitmap - > size ( ) ;
if ( new_size . width ( ) < 300 )
new_size . set_width ( 300 ) ;
if ( new_size . height ( ) < 200 )
new_size . set_height ( 200 ) ;
2020-04-12 17:40:34 +03:00
new_size . set_height ( new_size . height ( ) + m_toolbar_height ) ;
2020-04-12 16:03:31 +03:00
window ( ) - > resize ( new_size ) ;
}